fixed acid animation for elements falling/moving into (GFX engine change)
[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 other actions */
831 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
832 #define MOVE_STEPSIZE_MIN       (1)
833 #define MOVE_STEPSIZE_MAX       (TILEX)
834
835 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
836 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
837
838 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
839
840 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
841                                  RND(element_info[e].push_delay_random))
842 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
843                                  RND(element_info[e].drop_delay_random))
844 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
845                                  RND(element_info[e].move_delay_random))
846 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
847                                     (element_info[e].move_delay_random))
848 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
849                                  RND(element_info[e].ce_value_random_initial))
850 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
851 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
852                                  RND((c)->delay_random * (c)->delay_frames))
853 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
854                                  RND((c)->delay_random))
855
856
857 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
858          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
859
860 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
861         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
862          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
863          (be) + (e) - EL_SELF)
864
865 #define GET_PLAYER_FROM_BITS(p)                                         \
866         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
867
868 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
869         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
870          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
871          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
872          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
873          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
874          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
875          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
876          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
877          (e))
878
879 #define CAN_GROW_INTO(e)                                                \
880         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
881
882 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
883                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
884                                         (condition)))
885
886 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
887                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
888                                         (CAN_MOVE_INTO_ACID(e) &&       \
889                                          Feld[x][y] == EL_ACID) ||      \
890                                         (condition)))
891
892 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
893                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
894                                         (CAN_MOVE_INTO_ACID(e) &&       \
895                                          Feld[x][y] == EL_ACID) ||      \
896                                         (condition)))
897
898 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
899                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
900                                         (condition) ||                  \
901                                         (CAN_MOVE_INTO_ACID(e) &&       \
902                                          Feld[x][y] == EL_ACID) ||      \
903                                         (DONT_COLLIDE_WITH(e) &&        \
904                                          IS_PLAYER(x, y) &&             \
905                                          !PLAYER_ENEMY_PROTECTED(x, y))))
906
907 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
908         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
909
910 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
911         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
912
913 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
914         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
915
916 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
917         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
918                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
919
920 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
921         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
922
923 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
924         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
925
926 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
927         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
928
929 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
930         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
931
932 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
933         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
934
935 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
936         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
939                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
940                                                  IS_FOOD_PENGUIN(Feld[x][y])))
941 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
942         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
943
944 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
946
947 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
949
950 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
951         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
952                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
953
954 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
955
956 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
957                 (!IS_PLAYER(x, y) &&                                    \
958                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
959
960 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
961         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
962
963 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
964 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
965
966 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
967 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
969 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
970
971 /* game button identifiers */
972 #define GAME_CTRL_ID_STOP               0
973 #define GAME_CTRL_ID_PAUSE              1
974 #define GAME_CTRL_ID_PLAY               2
975 #define GAME_CTRL_ID_UNDO               3
976 #define GAME_CTRL_ID_REDO               4
977 #define GAME_CTRL_ID_SAVE               5
978 #define GAME_CTRL_ID_PAUSE2             6
979 #define GAME_CTRL_ID_LOAD               7
980 #define SOUND_CTRL_ID_MUSIC             8
981 #define SOUND_CTRL_ID_LOOPS             9
982 #define SOUND_CTRL_ID_SIMPLE            10
983
984 #define NUM_GAME_BUTTONS                11
985
986
987 /* forward declaration for internal use */
988
989 static void CreateField(int, int, int);
990
991 static void ResetGfxAnimation(int, int);
992
993 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
994 static void AdvanceFrameAndPlayerCounters(int);
995
996 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
997 static boolean MovePlayer(struct PlayerInfo *, int, int);
998 static void ScrollPlayer(struct PlayerInfo *, int);
999 static void ScrollScreen(struct PlayerInfo *, int);
1000
1001 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1002 static boolean DigFieldByCE(int, int, int);
1003 static boolean SnapField(struct PlayerInfo *, int, int);
1004 static boolean DropElement(struct PlayerInfo *);
1005
1006 static void InitBeltMovement(void);
1007 static void CloseAllOpenTimegates(void);
1008 static void CheckGravityMovement(struct PlayerInfo *);
1009 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1010 static void KillPlayerUnlessEnemyProtected(int, int);
1011 static void KillPlayerUnlessExplosionProtected(int, int);
1012
1013 static void TestIfPlayerTouchesCustomElement(int, int);
1014 static void TestIfElementTouchesCustomElement(int, int);
1015 static void TestIfElementHitsCustomElement(int, int, int);
1016
1017 static void HandleElementChange(int, int, int);
1018 static void ExecuteCustomElementAction(int, int, int, int);
1019 static boolean ChangeElement(int, int, int, int);
1020
1021 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1022 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1023         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1024 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1026 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1027         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1028 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1029         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1030
1031 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1032 #define CheckElementChange(x, y, e, te, ev)                             \
1033         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1034 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1035         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1036 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1037         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1038
1039 static void PlayLevelSound(int, int, int);
1040 static void PlayLevelSoundNearest(int, int, int);
1041 static void PlayLevelSoundAction(int, int, int);
1042 static void PlayLevelSoundElementAction(int, int, int, int);
1043 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1044 static void PlayLevelSoundActionIfLoop(int, int, int);
1045 static void StopLevelSoundActionIfLoop(int, int, int);
1046 static void PlayLevelMusic();
1047
1048 static void HandleGameButtons(struct GadgetInfo *);
1049
1050 int AmoebeNachbarNr(int, int);
1051 void AmoebeUmwandeln(int, int);
1052 void ContinueMoving(int, int);
1053 void Bang(int, int);
1054 void InitMovDir(int, int);
1055 void InitAmoebaNr(int, int);
1056 int NewHiScore(void);
1057
1058 void TestIfGoodThingHitsBadThing(int, int, int);
1059 void TestIfBadThingHitsGoodThing(int, int, int);
1060 void TestIfPlayerTouchesBadThing(int, int);
1061 void TestIfPlayerRunsIntoBadThing(int, int, int);
1062 void TestIfBadThingTouchesPlayer(int, int);
1063 void TestIfBadThingRunsIntoPlayer(int, int, int);
1064 void TestIfFriendTouchesBadThing(int, int);
1065 void TestIfBadThingTouchesFriend(int, int);
1066 void TestIfBadThingTouchesOtherBadThing(int, int);
1067 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1068
1069 void KillPlayer(struct PlayerInfo *);
1070 void BuryPlayer(struct PlayerInfo *);
1071 void RemovePlayer(struct PlayerInfo *);
1072
1073 static int getInvisibleActiveFromInvisibleElement(int);
1074 static int getInvisibleFromInvisibleActiveElement(int);
1075
1076 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1077
1078 /* for detection of endless loops, caused by custom element programming */
1079 /* (using maximal playfield width x 10 is just a rough approximation) */
1080 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1081
1082 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1083 {                                                                       \
1084   if (recursion_loop_detected)                                          \
1085     return (rc);                                                        \
1086                                                                         \
1087   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1088   {                                                                     \
1089     recursion_loop_detected = TRUE;                                     \
1090     recursion_loop_element = (e);                                       \
1091   }                                                                     \
1092                                                                         \
1093   recursion_loop_depth++;                                               \
1094 }
1095
1096 #define RECURSION_LOOP_DETECTION_END()                                  \
1097 {                                                                       \
1098   recursion_loop_depth--;                                               \
1099 }
1100
1101 static int recursion_loop_depth;
1102 static boolean recursion_loop_detected;
1103 static boolean recursion_loop_element;
1104
1105 static int map_player_action[MAX_PLAYERS];
1106
1107
1108 /* ------------------------------------------------------------------------- */
1109 /* definition of elements that automatically change to other elements after  */
1110 /* a specified time, eventually calling a function when changing             */
1111 /* ------------------------------------------------------------------------- */
1112
1113 /* forward declaration for changer functions */
1114 static void InitBuggyBase(int, int);
1115 static void WarnBuggyBase(int, int);
1116
1117 static void InitTrap(int, int);
1118 static void ActivateTrap(int, int);
1119 static void ChangeActiveTrap(int, int);
1120
1121 static void InitRobotWheel(int, int);
1122 static void RunRobotWheel(int, int);
1123 static void StopRobotWheel(int, int);
1124
1125 static void InitTimegateWheel(int, int);
1126 static void RunTimegateWheel(int, int);
1127
1128 static void InitMagicBallDelay(int, int);
1129 static void ActivateMagicBall(int, int);
1130
1131 struct ChangingElementInfo
1132 {
1133   int element;
1134   int target_element;
1135   int change_delay;
1136   void (*pre_change_function)(int x, int y);
1137   void (*change_function)(int x, int y);
1138   void (*post_change_function)(int x, int y);
1139 };
1140
1141 static struct ChangingElementInfo change_delay_list[] =
1142 {
1143   {
1144     EL_NUT_BREAKING,
1145     EL_EMERALD,
1146     6,
1147     NULL,
1148     NULL,
1149     NULL
1150   },
1151   {
1152     EL_PEARL_BREAKING,
1153     EL_EMPTY,
1154     8,
1155     NULL,
1156     NULL,
1157     NULL
1158   },
1159   {
1160     EL_EXIT_OPENING,
1161     EL_EXIT_OPEN,
1162     29,
1163     NULL,
1164     NULL,
1165     NULL
1166   },
1167   {
1168     EL_EXIT_CLOSING,
1169     EL_EXIT_CLOSED,
1170     29,
1171     NULL,
1172     NULL,
1173     NULL
1174   },
1175   {
1176     EL_STEEL_EXIT_OPENING,
1177     EL_STEEL_EXIT_OPEN,
1178     29,
1179     NULL,
1180     NULL,
1181     NULL
1182   },
1183   {
1184     EL_STEEL_EXIT_CLOSING,
1185     EL_STEEL_EXIT_CLOSED,
1186     29,
1187     NULL,
1188     NULL,
1189     NULL
1190   },
1191   {
1192     EL_EM_EXIT_OPENING,
1193     EL_EM_EXIT_OPEN,
1194     29,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_EM_EXIT_CLOSING,
1201     EL_EMPTY,
1202     29,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EM_STEEL_EXIT_OPENING,
1209     EL_EM_STEEL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EM_STEEL_EXIT_CLOSING,
1217     EL_STEELWALL,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_SP_EXIT_OPENING,
1225     EL_SP_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_SP_EXIT_CLOSING,
1233     EL_SP_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_SWITCHGATE_OPENING,
1241     EL_SWITCHGATE_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_SWITCHGATE_CLOSING,
1249     EL_SWITCHGATE_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_TIMEGATE_OPENING,
1257     EL_TIMEGATE_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_TIMEGATE_CLOSING,
1265     EL_TIMEGATE_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271
1272   {
1273     EL_ACID_SPLASH_LEFT,
1274     EL_EMPTY,
1275     8,
1276     NULL,
1277     NULL,
1278     NULL
1279   },
1280   {
1281     EL_ACID_SPLASH_RIGHT,
1282     EL_EMPTY,
1283     8,
1284     NULL,
1285     NULL,
1286     NULL
1287   },
1288   {
1289     EL_SP_BUGGY_BASE,
1290     EL_SP_BUGGY_BASE_ACTIVATING,
1291     0,
1292     InitBuggyBase,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_SP_BUGGY_BASE_ACTIVATING,
1298     EL_SP_BUGGY_BASE_ACTIVE,
1299     0,
1300     InitBuggyBase,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_BUGGY_BASE_ACTIVE,
1306     EL_SP_BUGGY_BASE,
1307     0,
1308     InitBuggyBase,
1309     WarnBuggyBase,
1310     NULL
1311   },
1312   {
1313     EL_TRAP,
1314     EL_TRAP_ACTIVE,
1315     0,
1316     InitTrap,
1317     NULL,
1318     ActivateTrap
1319   },
1320   {
1321     EL_TRAP_ACTIVE,
1322     EL_TRAP,
1323     31,
1324     NULL,
1325     ChangeActiveTrap,
1326     NULL
1327   },
1328   {
1329     EL_ROBOT_WHEEL_ACTIVE,
1330     EL_ROBOT_WHEEL,
1331     0,
1332     InitRobotWheel,
1333     RunRobotWheel,
1334     StopRobotWheel
1335   },
1336   {
1337     EL_TIMEGATE_SWITCH_ACTIVE,
1338     EL_TIMEGATE_SWITCH,
1339     0,
1340     InitTimegateWheel,
1341     RunTimegateWheel,
1342     NULL
1343   },
1344   {
1345     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1346     EL_DC_TIMEGATE_SWITCH,
1347     0,
1348     InitTimegateWheel,
1349     RunTimegateWheel,
1350     NULL
1351   },
1352   {
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     EL_EMC_MAGIC_BALL_ACTIVE,
1355     0,
1356     InitMagicBallDelay,
1357     NULL,
1358     ActivateMagicBall
1359   },
1360   {
1361     EL_EMC_SPRING_BUMPER_ACTIVE,
1362     EL_EMC_SPRING_BUMPER,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_DIAGONAL_SHRINKING,
1370     EL_UNDEFINED,
1371     0,
1372     NULL,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_DIAGONAL_GROWING,
1378     EL_UNDEFINED,
1379     0,
1380     NULL,
1381     NULL,
1382     NULL,
1383   },
1384
1385   {
1386     EL_UNDEFINED,
1387     EL_UNDEFINED,
1388     -1,
1389     NULL,
1390     NULL,
1391     NULL
1392   }
1393 };
1394
1395 struct
1396 {
1397   int element;
1398   int push_delay_fixed, push_delay_random;
1399 }
1400 push_delay_list[] =
1401 {
1402   { EL_SPRING,                  0, 0 },
1403   { EL_BALLOON,                 0, 0 },
1404
1405   { EL_SOKOBAN_OBJECT,          2, 0 },
1406   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1407   { EL_SATELLITE,               2, 0 },
1408   { EL_SP_DISK_YELLOW,          2, 0 },
1409
1410   { EL_UNDEFINED,               0, 0 },
1411 };
1412
1413 struct
1414 {
1415   int element;
1416   int move_stepsize;
1417 }
1418 move_stepsize_list[] =
1419 {
1420   { EL_AMOEBA_DROP,             2 },
1421   { EL_AMOEBA_DROPPING,         2 },
1422   { EL_QUICKSAND_FILLING,       1 },
1423   { EL_QUICKSAND_EMPTYING,      1 },
1424   { EL_QUICKSAND_FAST_FILLING,  2 },
1425   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1426   { EL_MAGIC_WALL_FILLING,      2 },
1427   { EL_MAGIC_WALL_EMPTYING,     2 },
1428   { EL_BD_MAGIC_WALL_FILLING,   2 },
1429   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1430   { EL_DC_MAGIC_WALL_FILLING,   2 },
1431   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1432
1433   { EL_UNDEFINED,               0 },
1434 };
1435
1436 struct
1437 {
1438   int element;
1439   int count;
1440 }
1441 collect_count_list[] =
1442 {
1443   { EL_EMERALD,                 1 },
1444   { EL_BD_DIAMOND,              1 },
1445   { EL_EMERALD_YELLOW,          1 },
1446   { EL_EMERALD_RED,             1 },
1447   { EL_EMERALD_PURPLE,          1 },
1448   { EL_DIAMOND,                 3 },
1449   { EL_SP_INFOTRON,             1 },
1450   { EL_PEARL,                   5 },
1451   { EL_CRYSTAL,                 8 },
1452
1453   { EL_UNDEFINED,               0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int direction;
1460 }
1461 access_direction_list[] =
1462 {
1463   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1464   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1465   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1466   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1467   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1468   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1469   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1470   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1471   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1472   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1473   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1474
1475   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1476   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1477   { EL_SP_PORT_UP,                                                   MV_DOWN },
1478   { EL_SP_PORT_DOWN,                                         MV_UP           },
1479   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1480   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1481   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1482   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1483   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1484   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1485   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1486   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1487   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1488   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1489   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1490   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1491   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1492   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1493   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1494
1495   { EL_UNDEFINED,                       MV_NONE                              }
1496 };
1497
1498 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1499
1500 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1501 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1502 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1503                                  IS_JUST_CHANGING(x, y))
1504
1505 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1506
1507 /* static variables for playfield scan mode (scanning forward or backward) */
1508 static int playfield_scan_start_x = 0;
1509 static int playfield_scan_start_y = 0;
1510 static int playfield_scan_delta_x = 1;
1511 static int playfield_scan_delta_y = 1;
1512
1513 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1514                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1515                                      (y) += playfield_scan_delta_y)     \
1516                                 for ((x) = playfield_scan_start_x;      \
1517                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1518                                      (x) += playfield_scan_delta_x)
1519
1520 #ifdef DEBUG
1521 void DEBUG_SetMaximumDynamite()
1522 {
1523   int i;
1524
1525   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1526     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1527       local_player->inventory_element[local_player->inventory_size++] =
1528         EL_DYNAMITE;
1529 }
1530 #endif
1531
1532 static void InitPlayfieldScanModeVars()
1533 {
1534   if (game.use_reverse_scan_direction)
1535   {
1536     playfield_scan_start_x = lev_fieldx - 1;
1537     playfield_scan_start_y = lev_fieldy - 1;
1538
1539     playfield_scan_delta_x = -1;
1540     playfield_scan_delta_y = -1;
1541   }
1542   else
1543   {
1544     playfield_scan_start_x = 0;
1545     playfield_scan_start_y = 0;
1546
1547     playfield_scan_delta_x = 1;
1548     playfield_scan_delta_y = 1;
1549   }
1550 }
1551
1552 static void InitPlayfieldScanMode(int mode)
1553 {
1554   game.use_reverse_scan_direction =
1555     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1556
1557   InitPlayfieldScanModeVars();
1558 }
1559
1560 static int get_move_delay_from_stepsize(int move_stepsize)
1561 {
1562   move_stepsize =
1563     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1564
1565   /* make sure that stepsize value is always a power of 2 */
1566   move_stepsize = (1 << log_2(move_stepsize));
1567
1568   return TILEX / move_stepsize;
1569 }
1570
1571 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1572                                boolean init_game)
1573 {
1574   int player_nr = player->index_nr;
1575   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1576   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1577
1578   /* do no immediately change move delay -- the player might just be moving */
1579   player->move_delay_value_next = move_delay;
1580
1581   /* information if player can move must be set separately */
1582   player->cannot_move = cannot_move;
1583
1584   if (init_game)
1585   {
1586     player->move_delay       = game.initial_move_delay[player_nr];
1587     player->move_delay_value = game.initial_move_delay_value[player_nr];
1588
1589     player->move_delay_value_next = -1;
1590
1591     player->move_delay_reset_counter = 0;
1592   }
1593 }
1594
1595 void GetPlayerConfig()
1596 {
1597   GameFrameDelay = setup.game_frame_delay;
1598
1599   if (!audio.sound_available)
1600     setup.sound_simple = FALSE;
1601
1602   if (!audio.loops_available)
1603     setup.sound_loops = FALSE;
1604
1605   if (!audio.music_available)
1606     setup.sound_music = FALSE;
1607
1608   if (!video.fullscreen_available)
1609     setup.fullscreen = FALSE;
1610
1611   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1612
1613   SetAudioMode(setup.sound);
1614   InitJoysticks();
1615 }
1616
1617 int GetElementFromGroupElement(int element)
1618 {
1619   if (IS_GROUP_ELEMENT(element))
1620   {
1621     struct ElementGroupInfo *group = element_info[element].group;
1622     int last_anim_random_frame = gfx.anim_random_frame;
1623     int element_pos;
1624
1625     if (group->choice_mode == ANIM_RANDOM)
1626       gfx.anim_random_frame = RND(group->num_elements_resolved);
1627
1628     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1629                                     group->choice_mode, 0,
1630                                     group->choice_pos);
1631
1632     if (group->choice_mode == ANIM_RANDOM)
1633       gfx.anim_random_frame = last_anim_random_frame;
1634
1635     group->choice_pos++;
1636
1637     element = group->element_resolved[element_pos];
1638   }
1639
1640   return element;
1641 }
1642
1643 static void InitPlayerField(int x, int y, int element, boolean init_game)
1644 {
1645   if (element == EL_SP_MURPHY)
1646   {
1647     if (init_game)
1648     {
1649       if (stored_player[0].present)
1650       {
1651         Feld[x][y] = EL_SP_MURPHY_CLONE;
1652
1653         return;
1654       }
1655       else
1656       {
1657         stored_player[0].initial_element = element;
1658         stored_player[0].use_murphy = TRUE;
1659
1660         if (!level.use_artwork_element[0])
1661           stored_player[0].artwork_element = EL_SP_MURPHY;
1662       }
1663
1664       Feld[x][y] = EL_PLAYER_1;
1665     }
1666   }
1667
1668   if (init_game)
1669   {
1670     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1671     int jx = player->jx, jy = player->jy;
1672
1673     player->present = TRUE;
1674
1675     player->block_last_field = (element == EL_SP_MURPHY ?
1676                                 level.sp_block_last_field :
1677                                 level.block_last_field);
1678
1679     /* ---------- initialize player's last field block delay --------------- */
1680
1681     /* always start with reliable default value (no adjustment needed) */
1682     player->block_delay_adjustment = 0;
1683
1684     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1685     if (player->block_last_field && element == EL_SP_MURPHY)
1686       player->block_delay_adjustment = 1;
1687
1688     /* special case 2: in game engines before 3.1.1, blocking was different */
1689     if (game.use_block_last_field_bug)
1690       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1691
1692     if (!options.network || player->connected)
1693     {
1694       player->active = TRUE;
1695
1696       /* remove potentially duplicate players */
1697       if (StorePlayer[jx][jy] == Feld[x][y])
1698         StorePlayer[jx][jy] = 0;
1699
1700       StorePlayer[x][y] = Feld[x][y];
1701
1702 #if DEBUG_INIT_PLAYER
1703       if (options.debug)
1704       {
1705         printf("- player element %d activated", player->element_nr);
1706         printf(" (local player is %d and currently %s)\n",
1707                local_player->element_nr,
1708                local_player->active ? "active" : "not active");
1709       }
1710     }
1711 #endif
1712
1713     Feld[x][y] = EL_EMPTY;
1714
1715     player->jx = player->last_jx = x;
1716     player->jy = player->last_jy = y;
1717   }
1718
1719   if (!init_game)
1720   {
1721     int player_nr = GET_PLAYER_NR(element);
1722     struct PlayerInfo *player = &stored_player[player_nr];
1723
1724     if (player->active && player->killed)
1725       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1726   }
1727 }
1728
1729 static void InitField(int x, int y, boolean init_game)
1730 {
1731   int element = Feld[x][y];
1732
1733   switch (element)
1734   {
1735     case EL_SP_MURPHY:
1736     case EL_PLAYER_1:
1737     case EL_PLAYER_2:
1738     case EL_PLAYER_3:
1739     case EL_PLAYER_4:
1740       InitPlayerField(x, y, element, init_game);
1741       break;
1742
1743     case EL_SOKOBAN_FIELD_PLAYER:
1744       element = Feld[x][y] = EL_PLAYER_1;
1745       InitField(x, y, init_game);
1746
1747       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1748       InitField(x, y, init_game);
1749       break;
1750
1751     case EL_SOKOBAN_FIELD_EMPTY:
1752       local_player->sokobanfields_still_needed++;
1753       break;
1754
1755     case EL_STONEBLOCK:
1756       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1758       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1759         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1764       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1765         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1766       break;
1767
1768     case EL_BUG:
1769     case EL_BUG_RIGHT:
1770     case EL_BUG_UP:
1771     case EL_BUG_LEFT:
1772     case EL_BUG_DOWN:
1773     case EL_SPACESHIP:
1774     case EL_SPACESHIP_RIGHT:
1775     case EL_SPACESHIP_UP:
1776     case EL_SPACESHIP_LEFT:
1777     case EL_SPACESHIP_DOWN:
1778     case EL_BD_BUTTERFLY:
1779     case EL_BD_BUTTERFLY_RIGHT:
1780     case EL_BD_BUTTERFLY_UP:
1781     case EL_BD_BUTTERFLY_LEFT:
1782     case EL_BD_BUTTERFLY_DOWN:
1783     case EL_BD_FIREFLY:
1784     case EL_BD_FIREFLY_RIGHT:
1785     case EL_BD_FIREFLY_UP:
1786     case EL_BD_FIREFLY_LEFT:
1787     case EL_BD_FIREFLY_DOWN:
1788     case EL_PACMAN_RIGHT:
1789     case EL_PACMAN_UP:
1790     case EL_PACMAN_LEFT:
1791     case EL_PACMAN_DOWN:
1792     case EL_YAMYAM:
1793     case EL_YAMYAM_LEFT:
1794     case EL_YAMYAM_RIGHT:
1795     case EL_YAMYAM_UP:
1796     case EL_YAMYAM_DOWN:
1797     case EL_DARK_YAMYAM:
1798     case EL_ROBOT:
1799     case EL_PACMAN:
1800     case EL_SP_SNIKSNAK:
1801     case EL_SP_ELECTRON:
1802     case EL_MOLE:
1803     case EL_MOLE_LEFT:
1804     case EL_MOLE_RIGHT:
1805     case EL_MOLE_UP:
1806     case EL_MOLE_DOWN:
1807       InitMovDir(x, y);
1808       break;
1809
1810     case EL_AMOEBA_FULL:
1811     case EL_BD_AMOEBA:
1812       InitAmoebaNr(x, y);
1813       break;
1814
1815     case EL_AMOEBA_DROP:
1816       if (y == lev_fieldy - 1)
1817       {
1818         Feld[x][y] = EL_AMOEBA_GROWING;
1819         Store[x][y] = EL_AMOEBA_WET;
1820       }
1821       break;
1822
1823     case EL_DYNAMITE_ACTIVE:
1824     case EL_SP_DISK_RED_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1828     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1829       MovDelay[x][y] = 96;
1830       break;
1831
1832     case EL_EM_DYNAMITE_ACTIVE:
1833       MovDelay[x][y] = 32;
1834       break;
1835
1836     case EL_LAMP:
1837       local_player->lights_still_needed++;
1838       break;
1839
1840     case EL_PENGUIN:
1841       local_player->friends_still_needed++;
1842       break;
1843
1844     case EL_PIG:
1845     case EL_DRAGON:
1846       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1847       break;
1848
1849     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1850     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1851     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1853     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1854     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1856     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1857     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1859     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1860     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1861       if (init_game)
1862       {
1863         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1865         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1866
1867         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1868         {
1869           game.belt_dir[belt_nr] = belt_dir;
1870           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1871         }
1872         else    /* more than one switch -- set it like the first switch */
1873         {
1874           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1875         }
1876       }
1877       break;
1878
1879     case EL_LIGHT_SWITCH_ACTIVE:
1880       if (init_game)
1881         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1882       break;
1883
1884     case EL_INVISIBLE_STEELWALL:
1885     case EL_INVISIBLE_WALL:
1886     case EL_INVISIBLE_SAND:
1887       if (game.light_time_left > 0 ||
1888           game.lenses_time_left > 0)
1889         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1890       break;
1891
1892     case EL_EMC_MAGIC_BALL:
1893       if (game.ball_state)
1894         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1895       break;
1896
1897     case EL_EMC_MAGIC_BALL_SWITCH:
1898       if (game.ball_state)
1899         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1900       break;
1901
1902     case EL_TRIGGER_PLAYER:
1903     case EL_TRIGGER_ELEMENT:
1904     case EL_TRIGGER_CE_VALUE:
1905     case EL_TRIGGER_CE_SCORE:
1906     case EL_SELF:
1907     case EL_ANY_ELEMENT:
1908     case EL_CURRENT_CE_VALUE:
1909     case EL_CURRENT_CE_SCORE:
1910     case EL_PREV_CE_1:
1911     case EL_PREV_CE_2:
1912     case EL_PREV_CE_3:
1913     case EL_PREV_CE_4:
1914     case EL_PREV_CE_5:
1915     case EL_PREV_CE_6:
1916     case EL_PREV_CE_7:
1917     case EL_PREV_CE_8:
1918     case EL_NEXT_CE_1:
1919     case EL_NEXT_CE_2:
1920     case EL_NEXT_CE_3:
1921     case EL_NEXT_CE_4:
1922     case EL_NEXT_CE_5:
1923     case EL_NEXT_CE_6:
1924     case EL_NEXT_CE_7:
1925     case EL_NEXT_CE_8:
1926       /* reference elements should not be used on the playfield */
1927       Feld[x][y] = EL_EMPTY;
1928       break;
1929
1930     default:
1931       if (IS_CUSTOM_ELEMENT(element))
1932       {
1933         if (CAN_MOVE(element))
1934           InitMovDir(x, y);
1935
1936         if (!element_info[element].use_last_ce_value || init_game)
1937           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1938       }
1939       else if (IS_GROUP_ELEMENT(element))
1940       {
1941         Feld[x][y] = GetElementFromGroupElement(element);
1942
1943         InitField(x, y, init_game);
1944       }
1945
1946       break;
1947   }
1948
1949   if (!init_game)
1950     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1951 }
1952
1953 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1954 {
1955   InitField(x, y, init_game);
1956
1957   /* not needed to call InitMovDir() -- already done by InitField()! */
1958   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1959       CAN_MOVE(Feld[x][y]))
1960     InitMovDir(x, y);
1961 }
1962
1963 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1964 {
1965   int old_element = Feld[x][y];
1966
1967   InitField(x, y, init_game);
1968
1969   /* not needed to call InitMovDir() -- already done by InitField()! */
1970   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971       CAN_MOVE(old_element) &&
1972       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1973     InitMovDir(x, y);
1974
1975   /* this case is in fact a combination of not less than three bugs:
1976      first, it calls InitMovDir() for elements that can move, although this is
1977      already done by InitField(); then, it checks the element that was at this
1978      field _before_ the call to InitField() (which can change it); lastly, it
1979      was not called for "mole with direction" elements, which were treated as
1980      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1981   */
1982 }
1983
1984 static int get_key_element_from_nr(int key_nr)
1985 {
1986   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1987                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1988                           EL_EM_KEY_1 : EL_KEY_1);
1989
1990   return key_base_element + key_nr;
1991 }
1992
1993 static int get_next_dropped_element(struct PlayerInfo *player)
1994 {
1995   return (player->inventory_size > 0 ?
1996           player->inventory_element[player->inventory_size - 1] :
1997           player->inventory_infinite_element != EL_UNDEFINED ?
1998           player->inventory_infinite_element :
1999           player->dynabombs_left > 0 ?
2000           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2001           EL_UNDEFINED);
2002 }
2003
2004 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2005 {
2006   /* pos >= 0: get element from bottom of the stack;
2007      pos <  0: get element from top of the stack */
2008
2009   if (pos < 0)
2010   {
2011     int min_inventory_size = -pos;
2012     int inventory_pos = player->inventory_size - min_inventory_size;
2013     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2014
2015     return (player->inventory_size >= min_inventory_size ?
2016             player->inventory_element[inventory_pos] :
2017             player->inventory_infinite_element != EL_UNDEFINED ?
2018             player->inventory_infinite_element :
2019             player->dynabombs_left >= min_dynabombs_left ?
2020             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2021             EL_UNDEFINED);
2022   }
2023   else
2024   {
2025     int min_dynabombs_left = pos + 1;
2026     int min_inventory_size = pos + 1 - player->dynabombs_left;
2027     int inventory_pos = pos - player->dynabombs_left;
2028
2029     return (player->inventory_infinite_element != EL_UNDEFINED ?
2030             player->inventory_infinite_element :
2031             player->dynabombs_left >= min_dynabombs_left ?
2032             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2033             player->inventory_size >= min_inventory_size ?
2034             player->inventory_element[inventory_pos] :
2035             EL_UNDEFINED);
2036   }
2037 }
2038
2039 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2040 {
2041   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2042   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2043   int compare_result;
2044
2045   if (gpo1->sort_priority != gpo2->sort_priority)
2046     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2047   else
2048     compare_result = gpo1->nr - gpo2->nr;
2049
2050   return compare_result;
2051 }
2052
2053 int getPlayerInventorySize(int player_nr)
2054 {
2055   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2056     return level.native_em_level->ply[player_nr]->dynamite;
2057   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2058     return level.native_sp_level->game_sp->red_disk_count;
2059   else
2060     return stored_player[player_nr].inventory_size;
2061 }
2062
2063 void InitGameControlValues()
2064 {
2065   int i;
2066
2067   for (i = 0; game_panel_controls[i].nr != -1; i++)
2068   {
2069     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2070     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2071     struct TextPosInfo *pos = gpc->pos;
2072     int nr = gpc->nr;
2073     int type = gpc->type;
2074
2075     if (nr != i)
2076     {
2077       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2078       Error(ERR_EXIT, "this should not happen -- please debug");
2079     }
2080
2081     /* force update of game controls after initialization */
2082     gpc->value = gpc->last_value = -1;
2083     gpc->frame = gpc->last_frame = -1;
2084     gpc->gfx_frame = -1;
2085
2086     /* determine panel value width for later calculation of alignment */
2087     if (type == TYPE_INTEGER || type == TYPE_STRING)
2088     {
2089       pos->width = pos->size * getFontWidth(pos->font);
2090       pos->height = getFontHeight(pos->font);
2091     }
2092     else if (type == TYPE_ELEMENT)
2093     {
2094       pos->width = pos->size;
2095       pos->height = pos->size;
2096     }
2097
2098     /* fill structure for game panel draw order */
2099     gpo->nr = gpc->nr;
2100     gpo->sort_priority = pos->sort_priority;
2101   }
2102
2103   /* sort game panel controls according to sort_priority and control number */
2104   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2105         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2106 }
2107
2108 void UpdatePlayfieldElementCount()
2109 {
2110   boolean use_element_count = FALSE;
2111   int i, j, x, y;
2112
2113   /* first check if it is needed at all to calculate playfield element count */
2114   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2115     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2116       use_element_count = TRUE;
2117
2118   if (!use_element_count)
2119     return;
2120
2121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2122     element_info[i].element_count = 0;
2123
2124   SCAN_PLAYFIELD(x, y)
2125   {
2126     element_info[Feld[x][y]].element_count++;
2127   }
2128
2129   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2130     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2131       if (IS_IN_GROUP(j, i))
2132         element_info[EL_GROUP_START + i].element_count +=
2133           element_info[j].element_count;
2134 }
2135
2136 void UpdateGameControlValues()
2137 {
2138   int i, k;
2139   int time = (local_player->LevelSolved ?
2140               local_player->LevelSolved_CountingTime :
2141               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142               level.native_em_level->lev->time :
2143               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144               level.native_sp_level->game_sp->time_played :
2145               game.no_time_limit ? TimePlayed : TimeLeft);
2146   int score = (local_player->LevelSolved ?
2147                local_player->LevelSolved_CountingScore :
2148                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149                level.native_em_level->lev->score :
2150                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151                level.native_sp_level->game_sp->score :
2152                local_player->score);
2153   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2154               level.native_em_level->lev->required :
2155               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2156               level.native_sp_level->game_sp->infotrons_still_needed :
2157               local_player->gems_still_needed);
2158   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2159                      level.native_em_level->lev->required > 0 :
2160                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2161                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2162                      local_player->gems_still_needed > 0 ||
2163                      local_player->sokobanfields_still_needed > 0 ||
2164                      local_player->lights_still_needed > 0);
2165
2166   UpdatePlayfieldElementCount();
2167
2168   /* update game panel control values */
2169
2170   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2171   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2172
2173   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2174   for (i = 0; i < MAX_NUM_KEYS; i++)
2175     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2176   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2177   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2178
2179   if (game.centered_player_nr == -1)
2180   {
2181     for (i = 0; i < MAX_PLAYERS; i++)
2182     {
2183       /* only one player in Supaplex game engine */
2184       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2185         break;
2186
2187       for (k = 0; k < MAX_NUM_KEYS; k++)
2188       {
2189         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2190         {
2191           if (level.native_em_level->ply[i]->keys & (1 << k))
2192             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2193               get_key_element_from_nr(k);
2194         }
2195         else if (stored_player[i].key[k])
2196           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2197             get_key_element_from_nr(k);
2198       }
2199
2200       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2201         getPlayerInventorySize(i);
2202
2203       if (stored_player[i].num_white_keys > 0)
2204         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2205           EL_DC_KEY_WHITE;
2206
2207       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2208         stored_player[i].num_white_keys;
2209     }
2210   }
2211   else
2212   {
2213     int player_nr = game.centered_player_nr;
2214
2215     for (k = 0; k < MAX_NUM_KEYS; k++)
2216     {
2217       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2218       {
2219         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2220           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221             get_key_element_from_nr(k);
2222       }
2223       else if (stored_player[player_nr].key[k])
2224         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2225           get_key_element_from_nr(k);
2226     }
2227
2228     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229       getPlayerInventorySize(player_nr);
2230
2231     if (stored_player[player_nr].num_white_keys > 0)
2232       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2233
2234     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2235       stored_player[player_nr].num_white_keys;
2236   }
2237
2238   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2239   {
2240     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2241       get_inventory_element_from_pos(local_player, i);
2242     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2243       get_inventory_element_from_pos(local_player, -i - 1);
2244   }
2245
2246   game_panel_controls[GAME_PANEL_SCORE].value = score;
2247   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2248
2249   game_panel_controls[GAME_PANEL_TIME].value = time;
2250
2251   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2252   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2253   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2254
2255   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2256
2257   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2258     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2259      EL_EMPTY);
2260   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2261     local_player->shield_normal_time_left;
2262   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2263     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2264      EL_EMPTY);
2265   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2266     local_player->shield_deadly_time_left;
2267
2268   game_panel_controls[GAME_PANEL_EXIT].value =
2269     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2270
2271   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2272     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2273   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2274     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2275      EL_EMC_MAGIC_BALL_SWITCH);
2276
2277   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2278     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2279   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2280     game.light_time_left;
2281
2282   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2283     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2284   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2285     game.timegate_time_left;
2286
2287   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2288     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2289
2290   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2291     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2292   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2293     game.lenses_time_left;
2294
2295   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2296     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2297   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2298     game.magnify_time_left;
2299
2300   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2301     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2302      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2303      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2304      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2305      EL_BALLOON_SWITCH_NONE);
2306
2307   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2308     local_player->dynabomb_count;
2309   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2310     local_player->dynabomb_size;
2311   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2312     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2313
2314   game_panel_controls[GAME_PANEL_PENGUINS].value =
2315     local_player->friends_still_needed;
2316
2317   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2318     local_player->sokobanfields_still_needed;
2319   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2320     local_player->sokobanfields_still_needed;
2321
2322   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2323     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2324
2325   for (i = 0; i < NUM_BELTS; i++)
2326   {
2327     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2328       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2329        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2330     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2331       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2332   }
2333
2334   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2335     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2336   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2337     game.magic_wall_time_left;
2338
2339   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2340     local_player->gravity;
2341
2342   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2343     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2344
2345   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2346     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2347       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2348        game.panel.element[i].id : EL_UNDEFINED);
2349
2350   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2351     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2352       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2353        element_info[game.panel.element_count[i].id].element_count : 0);
2354
2355   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2356     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2357       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2358        element_info[game.panel.ce_score[i].id].collect_score : 0);
2359
2360   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2361     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2362       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2363        element_info[game.panel.ce_score_element[i].id].collect_score :
2364        EL_UNDEFINED);
2365
2366   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2367   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2368   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2369
2370   /* update game panel control frames */
2371
2372   for (i = 0; game_panel_controls[i].nr != -1; i++)
2373   {
2374     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2375
2376     if (gpc->type == TYPE_ELEMENT)
2377     {
2378       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2379       {
2380         int last_anim_random_frame = gfx.anim_random_frame;
2381         int element = gpc->value;
2382         int graphic = el2panelimg(element);
2383
2384         if (gpc->value != gpc->last_value)
2385         {
2386           gpc->gfx_frame = 0;
2387           gpc->gfx_random = INIT_GFX_RANDOM();
2388         }
2389         else
2390         {
2391           gpc->gfx_frame++;
2392
2393           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2394               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2395             gpc->gfx_random = INIT_GFX_RANDOM();
2396         }
2397
2398         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2399           gfx.anim_random_frame = gpc->gfx_random;
2400
2401         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2402           gpc->gfx_frame = element_info[element].collect_score;
2403
2404         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2405                                               gpc->gfx_frame);
2406
2407         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2408           gfx.anim_random_frame = last_anim_random_frame;
2409       }
2410     }
2411   }
2412 }
2413
2414 void DisplayGameControlValues()
2415 {
2416   boolean redraw_panel = FALSE;
2417   int i;
2418
2419   for (i = 0; game_panel_controls[i].nr != -1; i++)
2420   {
2421     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2422
2423     if (PANEL_DEACTIVATED(gpc->pos))
2424       continue;
2425
2426     if (gpc->value == gpc->last_value &&
2427         gpc->frame == gpc->last_frame)
2428       continue;
2429
2430     redraw_panel = TRUE;
2431   }
2432
2433   if (!redraw_panel)
2434     return;
2435
2436   /* copy default game door content to main double buffer */
2437
2438   /* !!! CHECK AGAIN !!! */
2439   SetPanelBackground();
2440   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2441   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2442
2443   /* redraw game control buttons */
2444   RedrawGameButtons();
2445
2446   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2447
2448   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2449   {
2450     int nr = game_panel_order[i].nr;
2451     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2452     struct TextPosInfo *pos = gpc->pos;
2453     int type = gpc->type;
2454     int value = gpc->value;
2455     int frame = gpc->frame;
2456     int size = pos->size;
2457     int font = pos->font;
2458     boolean draw_masked = pos->draw_masked;
2459     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2460
2461     if (PANEL_DEACTIVATED(pos))
2462       continue;
2463
2464     gpc->last_value = value;
2465     gpc->last_frame = frame;
2466
2467     if (type == TYPE_INTEGER)
2468     {
2469       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2470           nr == GAME_PANEL_TIME)
2471       {
2472         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2473
2474         if (use_dynamic_size)           /* use dynamic number of digits */
2475         {
2476           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2477           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2478           int size2 = size1 + 1;
2479           int font1 = pos->font;
2480           int font2 = pos->font_alt;
2481
2482           size = (value < value_change ? size1 : size2);
2483           font = (value < value_change ? font1 : font2);
2484         }
2485       }
2486
2487       /* correct text size if "digits" is zero or less */
2488       if (size <= 0)
2489         size = strlen(int2str(value, size));
2490
2491       /* dynamically correct text alignment */
2492       pos->width = size * getFontWidth(font);
2493
2494       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2495                   int2str(value, size), font, mask_mode);
2496     }
2497     else if (type == TYPE_ELEMENT)
2498     {
2499       int element, graphic;
2500       Bitmap *src_bitmap;
2501       int src_x, src_y;
2502       int width, height;
2503       int dst_x = PANEL_XPOS(pos);
2504       int dst_y = PANEL_YPOS(pos);
2505
2506       if (value != EL_UNDEFINED && value != EL_EMPTY)
2507       {
2508         element = value;
2509         graphic = el2panelimg(value);
2510
2511         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2512
2513         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2514           size = TILESIZE;
2515
2516         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2517                               &src_x, &src_y);
2518
2519         width  = graphic_info[graphic].width  * size / TILESIZE;
2520         height = graphic_info[graphic].height * size / TILESIZE;
2521
2522         if (draw_masked)
2523           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2524                            dst_x, dst_y);
2525         else
2526           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2527                      dst_x, dst_y);
2528       }
2529     }
2530     else if (type == TYPE_STRING)
2531     {
2532       boolean active = (value != 0);
2533       char *state_normal = "off";
2534       char *state_active = "on";
2535       char *state = (active ? state_active : state_normal);
2536       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2537                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2538                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2539                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2540
2541       if (nr == GAME_PANEL_GRAVITY_STATE)
2542       {
2543         int font1 = pos->font;          /* (used for normal state) */
2544         int font2 = pos->font_alt;      /* (used for active state) */
2545
2546         font = (active ? font2 : font1);
2547       }
2548
2549       if (s != NULL)
2550       {
2551         char *s_cut;
2552
2553         if (size <= 0)
2554         {
2555           /* don't truncate output if "chars" is zero or less */
2556           size = strlen(s);
2557
2558           /* dynamically correct text alignment */
2559           pos->width = size * getFontWidth(font);
2560         }
2561
2562         s_cut = getStringCopyN(s, size);
2563
2564         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2565                     s_cut, font, mask_mode);
2566
2567         free(s_cut);
2568       }
2569     }
2570
2571     redraw_mask |= REDRAW_DOOR_1;
2572   }
2573
2574   SetGameStatus(GAME_MODE_PLAYING);
2575 }
2576
2577 void UpdateAndDisplayGameControlValues()
2578 {
2579   if (tape.deactivate_display)
2580     return;
2581
2582   UpdateGameControlValues();
2583   DisplayGameControlValues();
2584 }
2585
2586 void UpdateGameDoorValues()
2587 {
2588   UpdateGameControlValues();
2589 }
2590
2591 void DrawGameDoorValues()
2592 {
2593   DisplayGameControlValues();
2594 }
2595
2596
2597 /*
2598   =============================================================================
2599   InitGameEngine()
2600   -----------------------------------------------------------------------------
2601   initialize game engine due to level / tape version number
2602   =============================================================================
2603 */
2604
2605 static void InitGameEngine()
2606 {
2607   int i, j, k, l, x, y;
2608
2609   /* set game engine from tape file when re-playing, else from level file */
2610   game.engine_version = (tape.playing ? tape.engine_version :
2611                          level.game_version);
2612
2613   /* set single or multi-player game mode (needed for re-playing tapes) */
2614   game.team_mode = setup.team_mode;
2615
2616   if (tape.playing)
2617   {
2618     int num_players = 0;
2619
2620     for (i = 0; i < MAX_PLAYERS; i++)
2621       if (tape.player_participates[i])
2622         num_players++;
2623
2624     /* multi-player tapes contain input data for more than one player */
2625     game.team_mode = (num_players > 1);
2626   }
2627
2628   /* ---------------------------------------------------------------------- */
2629   /* set flags for bugs and changes according to active game engine version */
2630   /* ---------------------------------------------------------------------- */
2631
2632   /*
2633     Summary of bugfix/change:
2634     Fixed handling for custom elements that change when pushed by the player.
2635
2636     Fixed/changed in version:
2637     3.1.0
2638
2639     Description:
2640     Before 3.1.0, custom elements that "change when pushing" changed directly
2641     after the player started pushing them (until then handled in "DigField()").
2642     Since 3.1.0, these custom elements are not changed until the "pushing"
2643     move of the element is finished (now handled in "ContinueMoving()").
2644
2645     Affected levels/tapes:
2646     The first condition is generally needed for all levels/tapes before version
2647     3.1.0, which might use the old behaviour before it was changed; known tapes
2648     that are affected are some tapes from the level set "Walpurgis Gardens" by
2649     Jamie Cullen.
2650     The second condition is an exception from the above case and is needed for
2651     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2652     above (including some development versions of 3.1.0), but before it was
2653     known that this change would break tapes like the above and was fixed in
2654     3.1.1, so that the changed behaviour was active although the engine version
2655     while recording maybe was before 3.1.0. There is at least one tape that is
2656     affected by this exception, which is the tape for the one-level set "Bug
2657     Machine" by Juergen Bonhagen.
2658   */
2659
2660   game.use_change_when_pushing_bug =
2661     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2662      !(tape.playing &&
2663        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2664        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2665
2666   /*
2667     Summary of bugfix/change:
2668     Fixed handling for blocking the field the player leaves when moving.
2669
2670     Fixed/changed in version:
2671     3.1.1
2672
2673     Description:
2674     Before 3.1.1, when "block last field when moving" was enabled, the field
2675     the player is leaving when moving was blocked for the time of the move,
2676     and was directly unblocked afterwards. This resulted in the last field
2677     being blocked for exactly one less than the number of frames of one player
2678     move. Additionally, even when blocking was disabled, the last field was
2679     blocked for exactly one frame.
2680     Since 3.1.1, due to changes in player movement handling, the last field
2681     is not blocked at all when blocking is disabled. When blocking is enabled,
2682     the last field is blocked for exactly the number of frames of one player
2683     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2684     last field is blocked for exactly one more than the number of frames of
2685     one player move.
2686
2687     Affected levels/tapes:
2688     (!!! yet to be determined -- probably many !!!)
2689   */
2690
2691   game.use_block_last_field_bug =
2692     (game.engine_version < VERSION_IDENT(3,1,1,0));
2693
2694   /* ---------------------------------------------------------------------- */
2695
2696   /* set maximal allowed number of custom element changes per game frame */
2697   game.max_num_changes_per_frame = 1;
2698
2699   /* default scan direction: scan playfield from top/left to bottom/right */
2700   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2701
2702   /* dynamically adjust element properties according to game engine version */
2703   InitElementPropertiesEngine(game.engine_version);
2704
2705 #if 0
2706   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2707   printf("          tape version == %06d [%s] [file: %06d]\n",
2708          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2709          tape.file_version);
2710   printf("       => game.engine_version == %06d\n", game.engine_version);
2711 #endif
2712
2713   /* ---------- initialize player's initial move delay --------------------- */
2714
2715   /* dynamically adjust player properties according to level information */
2716   for (i = 0; i < MAX_PLAYERS; i++)
2717     game.initial_move_delay_value[i] =
2718       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2719
2720   /* dynamically adjust player properties according to game engine version */
2721   for (i = 0; i < MAX_PLAYERS; i++)
2722     game.initial_move_delay[i] =
2723       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2724        game.initial_move_delay_value[i] : 0);
2725
2726   /* ---------- initialize player's initial push delay --------------------- */
2727
2728   /* dynamically adjust player properties according to game engine version */
2729   game.initial_push_delay_value =
2730     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2731
2732   /* ---------- initialize changing elements ------------------------------- */
2733
2734   /* initialize changing elements information */
2735   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2736   {
2737     struct ElementInfo *ei = &element_info[i];
2738
2739     /* this pointer might have been changed in the level editor */
2740     ei->change = &ei->change_page[0];
2741
2742     if (!IS_CUSTOM_ELEMENT(i))
2743     {
2744       ei->change->target_element = EL_EMPTY_SPACE;
2745       ei->change->delay_fixed = 0;
2746       ei->change->delay_random = 0;
2747       ei->change->delay_frames = 1;
2748     }
2749
2750     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2751     {
2752       ei->has_change_event[j] = FALSE;
2753
2754       ei->event_page_nr[j] = 0;
2755       ei->event_page[j] = &ei->change_page[0];
2756     }
2757   }
2758
2759   /* add changing elements from pre-defined list */
2760   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2761   {
2762     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2763     struct ElementInfo *ei = &element_info[ch_delay->element];
2764
2765     ei->change->target_element       = ch_delay->target_element;
2766     ei->change->delay_fixed          = ch_delay->change_delay;
2767
2768     ei->change->pre_change_function  = ch_delay->pre_change_function;
2769     ei->change->change_function      = ch_delay->change_function;
2770     ei->change->post_change_function = ch_delay->post_change_function;
2771
2772     ei->change->can_change = TRUE;
2773     ei->change->can_change_or_has_action = TRUE;
2774
2775     ei->has_change_event[CE_DELAY] = TRUE;
2776
2777     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2778     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2779   }
2780
2781   /* ---------- initialize internal run-time variables --------------------- */
2782
2783   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2784   {
2785     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2786
2787     for (j = 0; j < ei->num_change_pages; j++)
2788     {
2789       ei->change_page[j].can_change_or_has_action =
2790         (ei->change_page[j].can_change |
2791          ei->change_page[j].has_action);
2792     }
2793   }
2794
2795   /* add change events from custom element configuration */
2796   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2797   {
2798     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2799
2800     for (j = 0; j < ei->num_change_pages; j++)
2801     {
2802       if (!ei->change_page[j].can_change_or_has_action)
2803         continue;
2804
2805       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2806       {
2807         /* only add event page for the first page found with this event */
2808         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2809         {
2810           ei->has_change_event[k] = TRUE;
2811
2812           ei->event_page_nr[k] = j;
2813           ei->event_page[k] = &ei->change_page[j];
2814         }
2815       }
2816     }
2817   }
2818
2819   /* ---------- initialize reference elements in change conditions --------- */
2820
2821   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2822   {
2823     int element = EL_CUSTOM_START + i;
2824     struct ElementInfo *ei = &element_info[element];
2825
2826     for (j = 0; j < ei->num_change_pages; j++)
2827     {
2828       int trigger_element = ei->change_page[j].initial_trigger_element;
2829
2830       if (trigger_element >= EL_PREV_CE_8 &&
2831           trigger_element <= EL_NEXT_CE_8)
2832         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2833
2834       ei->change_page[j].trigger_element = trigger_element;
2835     }
2836   }
2837
2838   /* ---------- initialize run-time trigger player and element ------------- */
2839
2840   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2841   {
2842     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2843
2844     for (j = 0; j < ei->num_change_pages; j++)
2845     {
2846       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2847       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2848       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2849       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2850       ei->change_page[j].actual_trigger_ce_value = 0;
2851       ei->change_page[j].actual_trigger_ce_score = 0;
2852     }
2853   }
2854
2855   /* ---------- initialize trigger events ---------------------------------- */
2856
2857   /* initialize trigger events information */
2858   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2859     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2860       trigger_events[i][j] = FALSE;
2861
2862   /* add trigger events from element change event properties */
2863   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2864   {
2865     struct ElementInfo *ei = &element_info[i];
2866
2867     for (j = 0; j < ei->num_change_pages; j++)
2868     {
2869       if (!ei->change_page[j].can_change_or_has_action)
2870         continue;
2871
2872       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2873       {
2874         int trigger_element = ei->change_page[j].trigger_element;
2875
2876         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2877         {
2878           if (ei->change_page[j].has_event[k])
2879           {
2880             if (IS_GROUP_ELEMENT(trigger_element))
2881             {
2882               struct ElementGroupInfo *group =
2883                 element_info[trigger_element].group;
2884
2885               for (l = 0; l < group->num_elements_resolved; l++)
2886                 trigger_events[group->element_resolved[l]][k] = TRUE;
2887             }
2888             else if (trigger_element == EL_ANY_ELEMENT)
2889               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2890                 trigger_events[l][k] = TRUE;
2891             else
2892               trigger_events[trigger_element][k] = TRUE;
2893           }
2894         }
2895       }
2896     }
2897   }
2898
2899   /* ---------- initialize push delay -------------------------------------- */
2900
2901   /* initialize push delay values to default */
2902   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2903   {
2904     if (!IS_CUSTOM_ELEMENT(i))
2905     {
2906       /* set default push delay values (corrected since version 3.0.7-1) */
2907       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2908       {
2909         element_info[i].push_delay_fixed = 2;
2910         element_info[i].push_delay_random = 8;
2911       }
2912       else
2913       {
2914         element_info[i].push_delay_fixed = 8;
2915         element_info[i].push_delay_random = 8;
2916       }
2917     }
2918   }
2919
2920   /* set push delay value for certain elements from pre-defined list */
2921   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2922   {
2923     int e = push_delay_list[i].element;
2924
2925     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2926     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2927   }
2928
2929   /* set push delay value for Supaplex elements for newer engine versions */
2930   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2931   {
2932     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2933     {
2934       if (IS_SP_ELEMENT(i))
2935       {
2936         /* set SP push delay to just enough to push under a falling zonk */
2937         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2938
2939         element_info[i].push_delay_fixed  = delay;
2940         element_info[i].push_delay_random = 0;
2941       }
2942     }
2943   }
2944
2945   /* ---------- initialize move stepsize ----------------------------------- */
2946
2947   /* initialize move stepsize values to default */
2948   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2949     if (!IS_CUSTOM_ELEMENT(i))
2950       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2951
2952   /* set move stepsize value for certain elements from pre-defined list */
2953   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2954   {
2955     int e = move_stepsize_list[i].element;
2956
2957     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2958   }
2959
2960   /* ---------- initialize collect score ----------------------------------- */
2961
2962   /* initialize collect score values for custom elements from initial value */
2963   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2964     if (IS_CUSTOM_ELEMENT(i))
2965       element_info[i].collect_score = element_info[i].collect_score_initial;
2966
2967   /* ---------- initialize collect count ----------------------------------- */
2968
2969   /* initialize collect count values for non-custom elements */
2970   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2971     if (!IS_CUSTOM_ELEMENT(i))
2972       element_info[i].collect_count_initial = 0;
2973
2974   /* add collect count values for all elements from pre-defined list */
2975   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2976     element_info[collect_count_list[i].element].collect_count_initial =
2977       collect_count_list[i].count;
2978
2979   /* ---------- initialize access direction -------------------------------- */
2980
2981   /* initialize access direction values to default (access from every side) */
2982   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2983     if (!IS_CUSTOM_ELEMENT(i))
2984       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2985
2986   /* set access direction value for certain elements from pre-defined list */
2987   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2988     element_info[access_direction_list[i].element].access_direction =
2989       access_direction_list[i].direction;
2990
2991   /* ---------- initialize explosion content ------------------------------- */
2992   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993   {
2994     if (IS_CUSTOM_ELEMENT(i))
2995       continue;
2996
2997     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2998     {
2999       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3000
3001       element_info[i].content.e[x][y] =
3002         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3003          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3004          i == EL_PLAYER_3 ? EL_EMERALD :
3005          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3006          i == EL_MOLE ? EL_EMERALD_RED :
3007          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3008          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3009          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3010          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3011          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3012          i == EL_WALL_EMERALD ? EL_EMERALD :
3013          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3014          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3015          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3016          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3017          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3018          i == EL_WALL_PEARL ? EL_PEARL :
3019          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3020          EL_EMPTY);
3021     }
3022   }
3023
3024   /* ---------- initialize recursion detection ------------------------------ */
3025   recursion_loop_depth = 0;
3026   recursion_loop_detected = FALSE;
3027   recursion_loop_element = EL_UNDEFINED;
3028
3029   /* ---------- initialize graphics engine ---------------------------------- */
3030   game.scroll_delay_value =
3031     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3032      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3033   game.scroll_delay_value =
3034     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3035
3036   /* ---------- initialize game engine snapshots ---------------------------- */
3037   for (i = 0; i < MAX_PLAYERS; i++)
3038     game.snapshot.last_action[i] = 0;
3039   game.snapshot.changed_action = FALSE;
3040   game.snapshot.collected_item = FALSE;
3041   game.snapshot.mode =
3042     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3043      SNAPSHOT_MODE_EVERY_STEP :
3044      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3045      SNAPSHOT_MODE_EVERY_MOVE :
3046      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3047      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3048   game.snapshot.save_snapshot = FALSE;
3049 }
3050
3051 int get_num_special_action(int element, int action_first, int action_last)
3052 {
3053   int num_special_action = 0;
3054   int i, j;
3055
3056   for (i = action_first; i <= action_last; i++)
3057   {
3058     boolean found = FALSE;
3059
3060     for (j = 0; j < NUM_DIRECTIONS; j++)
3061       if (el_act_dir2img(element, i, j) !=
3062           el_act_dir2img(element, ACTION_DEFAULT, j))
3063         found = TRUE;
3064
3065     if (found)
3066       num_special_action++;
3067     else
3068       break;
3069   }
3070
3071   return num_special_action;
3072 }
3073
3074
3075 /*
3076   =============================================================================
3077   InitGame()
3078   -----------------------------------------------------------------------------
3079   initialize and start new game
3080   =============================================================================
3081 */
3082
3083 void InitGame()
3084 {
3085   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3086   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3087   int fade_mask = REDRAW_FIELD;
3088
3089   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3090   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3091   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3092   int initial_move_dir = MV_DOWN;
3093   int i, j, x, y;
3094
3095   // required here to update video display before fading (FIX THIS)
3096   DrawMaskedBorder(REDRAW_DOOR_2);
3097
3098   if (!game.restart_level)
3099     CloseDoor(DOOR_CLOSE_1);
3100
3101   SetGameStatus(GAME_MODE_PLAYING);
3102
3103   if (level_editor_test_game)
3104     FadeSkipNextFadeIn();
3105   else
3106     FadeSetEnterScreen();
3107
3108   if (CheckIfGlobalBorderHasChanged())
3109     fade_mask = REDRAW_ALL;
3110
3111   FadeSoundsAndMusic();
3112
3113   ExpireSoundLoops(TRUE);
3114
3115   FadeOut(fade_mask);
3116
3117   /* needed if different viewport properties defined for playing */
3118   ChangeViewportPropertiesIfNeeded();
3119
3120   ClearField();
3121
3122   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3123
3124   DrawCompleteVideoDisplay();
3125
3126   InitGameEngine();
3127   InitGameControlValues();
3128
3129   /* don't play tapes over network */
3130   network_playing = (options.network && !tape.playing);
3131
3132   for (i = 0; i < MAX_PLAYERS; i++)
3133   {
3134     struct PlayerInfo *player = &stored_player[i];
3135
3136     player->index_nr = i;
3137     player->index_bit = (1 << i);
3138     player->element_nr = EL_PLAYER_1 + i;
3139
3140     player->present = FALSE;
3141     player->active = FALSE;
3142     player->mapped = FALSE;
3143
3144     player->killed = FALSE;
3145     player->reanimated = FALSE;
3146
3147     player->action = 0;
3148     player->effective_action = 0;
3149     player->programmed_action = 0;
3150
3151     player->score = 0;
3152     player->score_final = 0;
3153
3154     player->gems_still_needed = level.gems_needed;
3155     player->sokobanfields_still_needed = 0;
3156     player->lights_still_needed = 0;
3157     player->friends_still_needed = 0;
3158
3159     for (j = 0; j < MAX_NUM_KEYS; j++)
3160       player->key[j] = FALSE;
3161
3162     player->num_white_keys = 0;
3163
3164     player->dynabomb_count = 0;
3165     player->dynabomb_size = 1;
3166     player->dynabombs_left = 0;
3167     player->dynabomb_xl = FALSE;
3168
3169     player->MovDir = initial_move_dir;
3170     player->MovPos = 0;
3171     player->GfxPos = 0;
3172     player->GfxDir = initial_move_dir;
3173     player->GfxAction = ACTION_DEFAULT;
3174     player->Frame = 0;
3175     player->StepFrame = 0;
3176
3177     player->initial_element = player->element_nr;
3178     player->artwork_element =
3179       (level.use_artwork_element[i] ? level.artwork_element[i] :
3180        player->element_nr);
3181     player->use_murphy = FALSE;
3182
3183     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3184     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3185
3186     player->gravity = level.initial_player_gravity[i];
3187
3188     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3189
3190     player->actual_frame_counter = 0;
3191
3192     player->step_counter = 0;
3193
3194     player->last_move_dir = initial_move_dir;
3195
3196     player->is_active = FALSE;
3197
3198     player->is_waiting = FALSE;
3199     player->is_moving = FALSE;
3200     player->is_auto_moving = FALSE;
3201     player->is_digging = FALSE;
3202     player->is_snapping = FALSE;
3203     player->is_collecting = FALSE;
3204     player->is_pushing = FALSE;
3205     player->is_switching = FALSE;
3206     player->is_dropping = FALSE;
3207     player->is_dropping_pressed = FALSE;
3208
3209     player->is_bored = FALSE;
3210     player->is_sleeping = FALSE;
3211
3212     player->was_waiting = TRUE;
3213     player->was_moving = FALSE;
3214     player->was_snapping = FALSE;
3215     player->was_dropping = FALSE;
3216
3217     player->frame_counter_bored = -1;
3218     player->frame_counter_sleeping = -1;
3219
3220     player->anim_delay_counter = 0;
3221     player->post_delay_counter = 0;
3222
3223     player->dir_waiting = initial_move_dir;
3224     player->action_waiting = ACTION_DEFAULT;
3225     player->last_action_waiting = ACTION_DEFAULT;
3226     player->special_action_bored = ACTION_DEFAULT;
3227     player->special_action_sleeping = ACTION_DEFAULT;
3228
3229     player->switch_x = -1;
3230     player->switch_y = -1;
3231
3232     player->drop_x = -1;
3233     player->drop_y = -1;
3234
3235     player->show_envelope = 0;
3236
3237     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3238
3239     player->push_delay       = -1;      /* initialized when pushing starts */
3240     player->push_delay_value = game.initial_push_delay_value;
3241
3242     player->drop_delay = 0;
3243     player->drop_pressed_delay = 0;
3244
3245     player->last_jx = -1;
3246     player->last_jy = -1;
3247     player->jx = -1;
3248     player->jy = -1;
3249
3250     player->shield_normal_time_left = 0;
3251     player->shield_deadly_time_left = 0;
3252
3253     player->inventory_infinite_element = EL_UNDEFINED;
3254     player->inventory_size = 0;
3255
3256     if (level.use_initial_inventory[i])
3257     {
3258       for (j = 0; j < level.initial_inventory_size[i]; j++)
3259       {
3260         int element = level.initial_inventory_content[i][j];
3261         int collect_count = element_info[element].collect_count_initial;
3262         int k;
3263
3264         if (!IS_CUSTOM_ELEMENT(element))
3265           collect_count = 1;
3266
3267         if (collect_count == 0)
3268           player->inventory_infinite_element = element;
3269         else
3270           for (k = 0; k < collect_count; k++)
3271             if (player->inventory_size < MAX_INVENTORY_SIZE)
3272               player->inventory_element[player->inventory_size++] = element;
3273       }
3274     }
3275
3276     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3277     SnapField(player, 0, 0);
3278
3279     player->LevelSolved = FALSE;
3280     player->GameOver = FALSE;
3281
3282     player->LevelSolved_GameWon = FALSE;
3283     player->LevelSolved_GameEnd = FALSE;
3284     player->LevelSolved_PanelOff = FALSE;
3285     player->LevelSolved_SaveTape = FALSE;
3286     player->LevelSolved_SaveScore = FALSE;
3287     player->LevelSolved_CountingTime = 0;
3288     player->LevelSolved_CountingScore = 0;
3289
3290     map_player_action[i] = i;
3291   }
3292
3293   network_player_action_received = FALSE;
3294
3295 #if defined(NETWORK_AVALIABLE)
3296   /* initial null action */
3297   if (network_playing)
3298     SendToServer_MovePlayer(MV_NONE);
3299 #endif
3300
3301   ZX = ZY = -1;
3302   ExitX = ExitY = -1;
3303
3304   FrameCounter = 0;
3305   TimeFrames = 0;
3306   TimePlayed = 0;
3307   TimeLeft = level.time;
3308   TapeTime = 0;
3309
3310   ScreenMovDir = MV_NONE;
3311   ScreenMovPos = 0;
3312   ScreenGfxPos = 0;
3313
3314   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3315
3316   AllPlayersGone = FALSE;
3317
3318   game.no_time_limit = (level.time == 0);
3319
3320   game.yamyam_content_nr = 0;
3321   game.robot_wheel_active = FALSE;
3322   game.magic_wall_active = FALSE;
3323   game.magic_wall_time_left = 0;
3324   game.light_time_left = 0;
3325   game.timegate_time_left = 0;
3326   game.switchgate_pos = 0;
3327   game.wind_direction = level.wind_direction_initial;
3328
3329   game.lenses_time_left = 0;
3330   game.magnify_time_left = 0;
3331
3332   game.ball_state = level.ball_state_initial;
3333   game.ball_content_nr = 0;
3334
3335   game.envelope_active = FALSE;
3336
3337   /* set focus to local player for network games, else to all players */
3338   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3339   game.centered_player_nr_next = game.centered_player_nr;
3340   game.set_centered_player = FALSE;
3341
3342   if (network_playing && tape.recording)
3343   {
3344     /* store client dependent player focus when recording network games */
3345     tape.centered_player_nr_next = game.centered_player_nr_next;
3346     tape.set_centered_player = TRUE;
3347   }
3348
3349   for (i = 0; i < NUM_BELTS; i++)
3350   {
3351     game.belt_dir[i] = MV_NONE;
3352     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3353   }
3354
3355   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3356     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3357
3358 #if DEBUG_INIT_PLAYER
3359   if (options.debug)
3360   {
3361     printf("Player status at level initialization:\n");
3362   }
3363 #endif
3364
3365   SCAN_PLAYFIELD(x, y)
3366   {
3367     Feld[x][y] = level.field[x][y];
3368     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3369     ChangeDelay[x][y] = 0;
3370     ChangePage[x][y] = -1;
3371     CustomValue[x][y] = 0;              /* initialized in InitField() */
3372     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3373     AmoebaNr[x][y] = 0;
3374     WasJustMoving[x][y] = 0;
3375     WasJustFalling[x][y] = 0;
3376     CheckCollision[x][y] = 0;
3377     CheckImpact[x][y] = 0;
3378     Stop[x][y] = FALSE;
3379     Pushed[x][y] = FALSE;
3380
3381     ChangeCount[x][y] = 0;
3382     ChangeEvent[x][y] = -1;
3383
3384     ExplodePhase[x][y] = 0;
3385     ExplodeDelay[x][y] = 0;
3386     ExplodeField[x][y] = EX_TYPE_NONE;
3387
3388     RunnerVisit[x][y] = 0;
3389     PlayerVisit[x][y] = 0;
3390
3391     GfxFrame[x][y] = 0;
3392     GfxRandom[x][y] = INIT_GFX_RANDOM();
3393     GfxElement[x][y] = EL_UNDEFINED;
3394     GfxAction[x][y] = ACTION_DEFAULT;
3395     GfxDir[x][y] = MV_NONE;
3396     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3397   }
3398
3399   SCAN_PLAYFIELD(x, y)
3400   {
3401     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3402       emulate_bd = FALSE;
3403     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3404       emulate_sb = FALSE;
3405     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3406       emulate_sp = FALSE;
3407
3408     InitField(x, y, TRUE);
3409
3410     ResetGfxAnimation(x, y);
3411   }
3412
3413   InitBeltMovement();
3414
3415   for (i = 0; i < MAX_PLAYERS; i++)
3416   {
3417     struct PlayerInfo *player = &stored_player[i];
3418
3419     /* set number of special actions for bored and sleeping animation */
3420     player->num_special_action_bored =
3421       get_num_special_action(player->artwork_element,
3422                              ACTION_BORING_1, ACTION_BORING_LAST);
3423     player->num_special_action_sleeping =
3424       get_num_special_action(player->artwork_element,
3425                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3426   }
3427
3428   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3429                     emulate_sb ? EMU_SOKOBAN :
3430                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3431
3432   /* initialize type of slippery elements */
3433   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3434   {
3435     if (!IS_CUSTOM_ELEMENT(i))
3436     {
3437       /* default: elements slip down either to the left or right randomly */
3438       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3439
3440       /* SP style elements prefer to slip down on the left side */
3441       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3442         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3443
3444       /* BD style elements prefer to slip down on the left side */
3445       if (game.emulation == EMU_BOULDERDASH)
3446         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3447     }
3448   }
3449
3450   /* initialize explosion and ignition delay */
3451   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3452   {
3453     if (!IS_CUSTOM_ELEMENT(i))
3454     {
3455       int num_phase = 8;
3456       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3457                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3458                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3459       int last_phase = (num_phase + 1) * delay;
3460       int half_phase = (num_phase / 2) * delay;
3461
3462       element_info[i].explosion_delay = last_phase - 1;
3463       element_info[i].ignition_delay = half_phase;
3464
3465       if (i == EL_BLACK_ORB)
3466         element_info[i].ignition_delay = 1;
3467     }
3468   }
3469
3470   /* correct non-moving belts to start moving left */
3471   for (i = 0; i < NUM_BELTS; i++)
3472     if (game.belt_dir[i] == MV_NONE)
3473       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3474
3475 #if USE_NEW_PLAYER_ASSIGNMENTS
3476   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3477   /* choose default local player */
3478   local_player = &stored_player[0];
3479
3480   for (i = 0; i < MAX_PLAYERS; i++)
3481     stored_player[i].connected = FALSE;
3482
3483   local_player->connected = TRUE;
3484   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3485
3486   if (tape.playing)
3487   {
3488     for (i = 0; i < MAX_PLAYERS; i++)
3489       stored_player[i].connected = tape.player_participates[i];
3490   }
3491   else if (game.team_mode && !options.network)
3492   {
3493     /* try to guess locally connected team mode players (needed for correct
3494        assignment of player figures from level to locally playing players) */
3495
3496     for (i = 0; i < MAX_PLAYERS; i++)
3497       if (setup.input[i].use_joystick ||
3498           setup.input[i].key.left != KSYM_UNDEFINED)
3499         stored_player[i].connected = TRUE;
3500   }
3501
3502 #if DEBUG_INIT_PLAYER
3503   if (options.debug)
3504   {
3505     printf("Player status after level initialization:\n");
3506
3507     for (i = 0; i < MAX_PLAYERS; i++)
3508     {
3509       struct PlayerInfo *player = &stored_player[i];
3510
3511       printf("- player %d: present == %d, connected == %d, active == %d",
3512              i + 1,
3513              player->present,
3514              player->connected,
3515              player->active);
3516
3517       if (local_player == player)
3518         printf(" (local player)");
3519
3520       printf("\n");
3521     }
3522   }
3523 #endif
3524
3525 #if DEBUG_INIT_PLAYER
3526   if (options.debug)
3527     printf("Reassigning players ...\n");
3528 #endif
3529
3530   /* check if any connected player was not found in playfield */
3531   for (i = 0; i < MAX_PLAYERS; i++)
3532   {
3533     struct PlayerInfo *player = &stored_player[i];
3534
3535     if (player->connected && !player->present)
3536     {
3537       struct PlayerInfo *field_player = NULL;
3538
3539 #if DEBUG_INIT_PLAYER
3540       if (options.debug)
3541         printf("- looking for field player for player %d ...\n", i + 1);
3542 #endif
3543
3544       /* assign first free player found that is present in the playfield */
3545
3546       /* first try: look for unmapped playfield player that is not connected */
3547       for (j = 0; j < MAX_PLAYERS; j++)
3548         if (field_player == NULL &&
3549             stored_player[j].present &&
3550             !stored_player[j].mapped &&
3551             !stored_player[j].connected)
3552           field_player = &stored_player[j];
3553
3554       /* second try: look for *any* unmapped playfield player */
3555       for (j = 0; j < MAX_PLAYERS; j++)
3556         if (field_player == NULL &&
3557             stored_player[j].present &&
3558             !stored_player[j].mapped)
3559           field_player = &stored_player[j];
3560
3561       if (field_player != NULL)
3562       {
3563         int jx = field_player->jx, jy = field_player->jy;
3564
3565 #if DEBUG_INIT_PLAYER
3566         if (options.debug)
3567           printf("- found player %d\n", field_player->index_nr + 1);
3568 #endif
3569
3570         player->present = FALSE;
3571         player->active = FALSE;
3572
3573         field_player->present = TRUE;
3574         field_player->active = TRUE;
3575
3576         /*
3577         player->initial_element = field_player->initial_element;
3578         player->artwork_element = field_player->artwork_element;
3579
3580         player->block_last_field       = field_player->block_last_field;
3581         player->block_delay_adjustment = field_player->block_delay_adjustment;
3582         */
3583
3584         StorePlayer[jx][jy] = field_player->element_nr;
3585
3586         field_player->jx = field_player->last_jx = jx;
3587         field_player->jy = field_player->last_jy = jy;
3588
3589         if (local_player == player)
3590           local_player = field_player;
3591
3592         map_player_action[field_player->index_nr] = i;
3593
3594         field_player->mapped = TRUE;
3595
3596 #if DEBUG_INIT_PLAYER
3597         if (options.debug)
3598           printf("- map_player_action[%d] == %d\n",
3599                  field_player->index_nr + 1, i + 1);
3600 #endif
3601       }
3602     }
3603
3604     if (player->connected && player->present)
3605       player->mapped = TRUE;
3606   }
3607
3608 #if DEBUG_INIT_PLAYER
3609   if (options.debug)
3610   {
3611     printf("Player status after player assignment (first stage):\n");
3612
3613     for (i = 0; i < MAX_PLAYERS; i++)
3614     {
3615       struct PlayerInfo *player = &stored_player[i];
3616
3617       printf("- player %d: present == %d, connected == %d, active == %d",
3618              i + 1,
3619              player->present,
3620              player->connected,
3621              player->active);
3622
3623       if (local_player == player)
3624         printf(" (local player)");
3625
3626       printf("\n");
3627     }
3628   }
3629 #endif
3630
3631 #else
3632
3633   /* check if any connected player was not found in playfield */
3634   for (i = 0; i < MAX_PLAYERS; i++)
3635   {
3636     struct PlayerInfo *player = &stored_player[i];
3637
3638     if (player->connected && !player->present)
3639     {
3640       for (j = 0; j < MAX_PLAYERS; j++)
3641       {
3642         struct PlayerInfo *field_player = &stored_player[j];
3643         int jx = field_player->jx, jy = field_player->jy;
3644
3645         /* assign first free player found that is present in the playfield */
3646         if (field_player->present && !field_player->connected)
3647         {
3648           player->present = TRUE;
3649           player->active = TRUE;
3650
3651           field_player->present = FALSE;
3652           field_player->active = FALSE;
3653
3654           player->initial_element = field_player->initial_element;
3655           player->artwork_element = field_player->artwork_element;
3656
3657           player->block_last_field       = field_player->block_last_field;
3658           player->block_delay_adjustment = field_player->block_delay_adjustment;
3659
3660           StorePlayer[jx][jy] = player->element_nr;
3661
3662           player->jx = player->last_jx = jx;
3663           player->jy = player->last_jy = jy;
3664
3665           break;
3666         }
3667       }
3668     }
3669   }
3670 #endif
3671
3672 #if 0
3673   printf("::: local_player->present == %d\n", local_player->present);
3674 #endif
3675
3676   if (tape.playing)
3677   {
3678     /* when playing a tape, eliminate all players who do not participate */
3679
3680 #if USE_NEW_PLAYER_ASSIGNMENTS
3681
3682     if (!game.team_mode)
3683     {
3684       for (i = 0; i < MAX_PLAYERS; i++)
3685       {
3686         if (stored_player[i].active &&
3687             !tape.player_participates[map_player_action[i]])
3688         {
3689           struct PlayerInfo *player = &stored_player[i];
3690           int jx = player->jx, jy = player->jy;
3691
3692 #if DEBUG_INIT_PLAYER
3693           if (options.debug)
3694             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3695 #endif
3696
3697           player->active = FALSE;
3698           StorePlayer[jx][jy] = 0;
3699           Feld[jx][jy] = EL_EMPTY;
3700         }
3701       }
3702     }
3703
3704 #else
3705
3706     for (i = 0; i < MAX_PLAYERS; i++)
3707     {
3708       if (stored_player[i].active &&
3709           !tape.player_participates[i])
3710       {
3711         struct PlayerInfo *player = &stored_player[i];
3712         int jx = player->jx, jy = player->jy;
3713
3714         player->active = FALSE;
3715         StorePlayer[jx][jy] = 0;
3716         Feld[jx][jy] = EL_EMPTY;
3717       }
3718     }
3719 #endif
3720   }
3721   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3722   {
3723     /* when in single player mode, eliminate all but the first active player */
3724
3725     for (i = 0; i < MAX_PLAYERS; i++)
3726     {
3727       if (stored_player[i].active)
3728       {
3729         for (j = i + 1; j < MAX_PLAYERS; j++)
3730         {
3731           if (stored_player[j].active)
3732           {
3733             struct PlayerInfo *player = &stored_player[j];
3734             int jx = player->jx, jy = player->jy;
3735
3736             player->active = FALSE;
3737             player->present = FALSE;
3738
3739             StorePlayer[jx][jy] = 0;
3740             Feld[jx][jy] = EL_EMPTY;
3741           }
3742         }
3743       }
3744     }
3745   }
3746
3747   /* when recording the game, store which players take part in the game */
3748   if (tape.recording)
3749   {
3750 #if USE_NEW_PLAYER_ASSIGNMENTS
3751     for (i = 0; i < MAX_PLAYERS; i++)
3752       if (stored_player[i].connected)
3753         tape.player_participates[i] = TRUE;
3754 #else
3755     for (i = 0; i < MAX_PLAYERS; i++)
3756       if (stored_player[i].active)
3757         tape.player_participates[i] = TRUE;
3758 #endif
3759   }
3760
3761 #if DEBUG_INIT_PLAYER
3762   if (options.debug)
3763   {
3764     printf("Player status after player assignment (final stage):\n");
3765
3766     for (i = 0; i < MAX_PLAYERS; i++)
3767     {
3768       struct PlayerInfo *player = &stored_player[i];
3769
3770       printf("- player %d: present == %d, connected == %d, active == %d",
3771              i + 1,
3772              player->present,
3773              player->connected,
3774              player->active);
3775
3776       if (local_player == player)
3777         printf(" (local player)");
3778
3779       printf("\n");
3780     }
3781   }
3782 #endif
3783
3784   if (BorderElement == EL_EMPTY)
3785   {
3786     SBX_Left = 0;
3787     SBX_Right = lev_fieldx - SCR_FIELDX;
3788     SBY_Upper = 0;
3789     SBY_Lower = lev_fieldy - SCR_FIELDY;
3790   }
3791   else
3792   {
3793     SBX_Left = -1;
3794     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3795     SBY_Upper = -1;
3796     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3797   }
3798
3799   if (full_lev_fieldx <= SCR_FIELDX)
3800     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3801   if (full_lev_fieldy <= SCR_FIELDY)
3802     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3803
3804   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3805     SBX_Left--;
3806   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3807     SBY_Upper--;
3808
3809   /* if local player not found, look for custom element that might create
3810      the player (make some assumptions about the right custom element) */
3811   if (!local_player->present)
3812   {
3813     int start_x = 0, start_y = 0;
3814     int found_rating = 0;
3815     int found_element = EL_UNDEFINED;
3816     int player_nr = local_player->index_nr;
3817
3818     SCAN_PLAYFIELD(x, y)
3819     {
3820       int element = Feld[x][y];
3821       int content;
3822       int xx, yy;
3823       boolean is_player;
3824
3825       if (level.use_start_element[player_nr] &&
3826           level.start_element[player_nr] == element &&
3827           found_rating < 4)
3828       {
3829         start_x = x;
3830         start_y = y;
3831
3832         found_rating = 4;
3833         found_element = element;
3834       }
3835
3836       if (!IS_CUSTOM_ELEMENT(element))
3837         continue;
3838
3839       if (CAN_CHANGE(element))
3840       {
3841         for (i = 0; i < element_info[element].num_change_pages; i++)
3842         {
3843           /* check for player created from custom element as single target */
3844           content = element_info[element].change_page[i].target_element;
3845           is_player = ELEM_IS_PLAYER(content);
3846
3847           if (is_player && (found_rating < 3 ||
3848                             (found_rating == 3 && element < found_element)))
3849           {
3850             start_x = x;
3851             start_y = y;
3852
3853             found_rating = 3;
3854             found_element = element;
3855           }
3856         }
3857       }
3858
3859       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3860       {
3861         /* check for player created from custom element as explosion content */
3862         content = element_info[element].content.e[xx][yy];
3863         is_player = ELEM_IS_PLAYER(content);
3864
3865         if (is_player && (found_rating < 2 ||
3866                           (found_rating == 2 && element < found_element)))
3867         {
3868           start_x = x + xx - 1;
3869           start_y = y + yy - 1;
3870
3871           found_rating = 2;
3872           found_element = element;
3873         }
3874
3875         if (!CAN_CHANGE(element))
3876           continue;
3877
3878         for (i = 0; i < element_info[element].num_change_pages; i++)
3879         {
3880           /* check for player created from custom element as extended target */
3881           content =
3882             element_info[element].change_page[i].target_content.e[xx][yy];
3883
3884           is_player = ELEM_IS_PLAYER(content);
3885
3886           if (is_player && (found_rating < 1 ||
3887                             (found_rating == 1 && element < found_element)))
3888           {
3889             start_x = x + xx - 1;
3890             start_y = y + yy - 1;
3891
3892             found_rating = 1;
3893             found_element = element;
3894           }
3895         }
3896       }
3897     }
3898
3899     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3900                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3901                 start_x - MIDPOSX);
3902
3903     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3904                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3905                 start_y - MIDPOSY);
3906   }
3907   else
3908   {
3909     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3910                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3911                 local_player->jx - MIDPOSX);
3912
3913     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3914                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3915                 local_player->jy - MIDPOSY);
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 (level_nr < leveldir_current->last_level)
4467     raise_level = TRUE;                 /* advance to next level */
4468
4469   if ((hi_pos = NewHiScore()) >= 0) 
4470   {
4471     SetGameStatus(GAME_MODE_SCORES);
4472
4473     DrawHallOfFame(hi_pos);
4474
4475     if (raise_level)
4476     {
4477       level_nr++;
4478       TapeErase();
4479     }
4480   }
4481   else
4482   {
4483     SetGameStatus(GAME_MODE_MAIN);
4484
4485     if (raise_level)
4486     {
4487       level_nr++;
4488       TapeErase();
4489     }
4490
4491     DrawMainMenu();
4492   }
4493 }
4494
4495 int NewHiScore()
4496 {
4497   int k, l;
4498   int position = -1;
4499
4500   LoadScore(level_nr);
4501
4502   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4503       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4504     return -1;
4505
4506   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4507   {
4508     if (local_player->score_final > highscore[k].Score)
4509     {
4510       /* player has made it to the hall of fame */
4511
4512       if (k < MAX_SCORE_ENTRIES - 1)
4513       {
4514         int m = MAX_SCORE_ENTRIES - 1;
4515
4516 #ifdef ONE_PER_NAME
4517         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4518           if (strEqual(setup.player_name, highscore[l].Name))
4519             m = l;
4520         if (m == k)     /* player's new highscore overwrites his old one */
4521           goto put_into_list;
4522 #endif
4523
4524         for (l = m; l > k; l--)
4525         {
4526           strcpy(highscore[l].Name, highscore[l - 1].Name);
4527           highscore[l].Score = highscore[l - 1].Score;
4528         }
4529       }
4530
4531 #ifdef ONE_PER_NAME
4532       put_into_list:
4533 #endif
4534       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4535       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4536       highscore[k].Score = local_player->score_final; 
4537       position = k;
4538       break;
4539     }
4540
4541 #ifdef ONE_PER_NAME
4542     else if (!strncmp(setup.player_name, highscore[k].Name,
4543                       MAX_PLAYER_NAME_LEN))
4544       break;    /* player already there with a higher score */
4545 #endif
4546
4547   }
4548
4549   if (position >= 0) 
4550     SaveScore(level_nr);
4551
4552   return position;
4553 }
4554
4555 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4556 {
4557   int element = Feld[x][y];
4558   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4559   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4560   int horiz_move = (dx != 0);
4561   int sign = (horiz_move ? dx : dy);
4562   int step = sign * element_info[element].move_stepsize;
4563
4564   /* special values for move stepsize for spring and things on conveyor belt */
4565   if (horiz_move)
4566   {
4567     if (CAN_FALL(element) &&
4568         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4569       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4570     else if (element == EL_SPRING)
4571       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4572   }
4573
4574   return step;
4575 }
4576
4577 inline static int getElementMoveStepsize(int x, int y)
4578 {
4579   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4580 }
4581
4582 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4583 {
4584   if (player->GfxAction != action || player->GfxDir != dir)
4585   {
4586     player->GfxAction = action;
4587     player->GfxDir = dir;
4588     player->Frame = 0;
4589     player->StepFrame = 0;
4590   }
4591 }
4592
4593 static void ResetGfxFrame(int x, int y)
4594 {
4595   int element = Feld[x][y];
4596   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4597
4598   if (graphic_info[graphic].anim_global_sync)
4599     GfxFrame[x][y] = FrameCounter;
4600   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4601     GfxFrame[x][y] = CustomValue[x][y];
4602   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4603     GfxFrame[x][y] = element_info[element].collect_score;
4604   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4605     GfxFrame[x][y] = ChangeDelay[x][y];
4606 }
4607
4608 static void ResetGfxAnimation(int x, int y)
4609 {
4610   GfxAction[x][y] = ACTION_DEFAULT;
4611   GfxDir[x][y] = MovDir[x][y];
4612   GfxFrame[x][y] = 0;
4613
4614   ResetGfxFrame(x, y);
4615 }
4616
4617 static void ResetRandomAnimationValue(int x, int y)
4618 {
4619   GfxRandom[x][y] = INIT_GFX_RANDOM();
4620 }
4621
4622 void InitMovingField(int x, int y, int direction)
4623 {
4624   int element = Feld[x][y];
4625   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4626   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4627   int newx = x + dx;
4628   int newy = y + dy;
4629   boolean is_moving_before, is_moving_after;
4630
4631   /* check if element was/is moving or being moved before/after mode change */
4632   is_moving_before = (WasJustMoving[x][y] != 0);
4633   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4634
4635   /* reset animation only for moving elements which change direction of moving
4636      or which just started or stopped moving
4637      (else CEs with property "can move" / "not moving" are reset each frame) */
4638   if (is_moving_before != is_moving_after ||
4639       direction != MovDir[x][y])
4640     ResetGfxAnimation(x, y);
4641
4642   MovDir[x][y] = direction;
4643   GfxDir[x][y] = direction;
4644
4645   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4646                      direction == MV_DOWN && CAN_FALL(element) ?
4647                      ACTION_FALLING : ACTION_MOVING);
4648
4649   /* this is needed for CEs with property "can move" / "not moving" */
4650
4651   if (is_moving_after)
4652   {
4653     if (Feld[newx][newy] == EL_EMPTY)
4654       Feld[newx][newy] = EL_BLOCKED;
4655
4656     MovDir[newx][newy] = MovDir[x][y];
4657
4658     CustomValue[newx][newy] = CustomValue[x][y];
4659
4660     GfxFrame[newx][newy] = GfxFrame[x][y];
4661     GfxRandom[newx][newy] = GfxRandom[x][y];
4662     GfxAction[newx][newy] = GfxAction[x][y];
4663     GfxDir[newx][newy] = GfxDir[x][y];
4664   }
4665 }
4666
4667 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4668 {
4669   int direction = MovDir[x][y];
4670   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4671   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4672
4673   *goes_to_x = newx;
4674   *goes_to_y = newy;
4675 }
4676
4677 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4678 {
4679   int oldx = x, oldy = y;
4680   int direction = MovDir[x][y];
4681
4682   if (direction == MV_LEFT)
4683     oldx++;
4684   else if (direction == MV_RIGHT)
4685     oldx--;
4686   else if (direction == MV_UP)
4687     oldy++;
4688   else if (direction == MV_DOWN)
4689     oldy--;
4690
4691   *comes_from_x = oldx;
4692   *comes_from_y = oldy;
4693 }
4694
4695 int MovingOrBlocked2Element(int x, int y)
4696 {
4697   int element = Feld[x][y];
4698
4699   if (element == EL_BLOCKED)
4700   {
4701     int oldx, oldy;
4702
4703     Blocked2Moving(x, y, &oldx, &oldy);
4704     return Feld[oldx][oldy];
4705   }
4706   else
4707     return element;
4708 }
4709
4710 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4711 {
4712   /* like MovingOrBlocked2Element(), but if element is moving
4713      and (x,y) is the field the moving element is just leaving,
4714      return EL_BLOCKED instead of the element value */
4715   int element = Feld[x][y];
4716
4717   if (IS_MOVING(x, y))
4718   {
4719     if (element == EL_BLOCKED)
4720     {
4721       int oldx, oldy;
4722
4723       Blocked2Moving(x, y, &oldx, &oldy);
4724       return Feld[oldx][oldy];
4725     }
4726     else
4727       return EL_BLOCKED;
4728   }
4729   else
4730     return element;
4731 }
4732
4733 static void RemoveField(int x, int y)
4734 {
4735   Feld[x][y] = EL_EMPTY;
4736
4737   MovPos[x][y] = 0;
4738   MovDir[x][y] = 0;
4739   MovDelay[x][y] = 0;
4740
4741   CustomValue[x][y] = 0;
4742
4743   AmoebaNr[x][y] = 0;
4744   ChangeDelay[x][y] = 0;
4745   ChangePage[x][y] = -1;
4746   Pushed[x][y] = FALSE;
4747
4748   GfxElement[x][y] = EL_UNDEFINED;
4749   GfxAction[x][y] = ACTION_DEFAULT;
4750   GfxDir[x][y] = MV_NONE;
4751 }
4752
4753 void RemoveMovingField(int x, int y)
4754 {
4755   int oldx = x, oldy = y, newx = x, newy = y;
4756   int element = Feld[x][y];
4757   int next_element = EL_UNDEFINED;
4758
4759   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4760     return;
4761
4762   if (IS_MOVING(x, y))
4763   {
4764     Moving2Blocked(x, y, &newx, &newy);
4765
4766     if (Feld[newx][newy] != EL_BLOCKED)
4767     {
4768       /* element is moving, but target field is not free (blocked), but
4769          already occupied by something different (example: acid pool);
4770          in this case, only remove the moving field, but not the target */
4771
4772       RemoveField(oldx, oldy);
4773
4774       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4775
4776       TEST_DrawLevelField(oldx, oldy);
4777
4778       return;
4779     }
4780   }
4781   else if (element == EL_BLOCKED)
4782   {
4783     Blocked2Moving(x, y, &oldx, &oldy);
4784     if (!IS_MOVING(oldx, oldy))
4785       return;
4786   }
4787
4788   if (element == EL_BLOCKED &&
4789       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4790        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4791        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4792        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4793        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4794        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4795     next_element = get_next_element(Feld[oldx][oldy]);
4796
4797   RemoveField(oldx, oldy);
4798   RemoveField(newx, newy);
4799
4800   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4801
4802   if (next_element != EL_UNDEFINED)
4803     Feld[oldx][oldy] = next_element;
4804
4805   TEST_DrawLevelField(oldx, oldy);
4806   TEST_DrawLevelField(newx, newy);
4807 }
4808
4809 void DrawDynamite(int x, int y)
4810 {
4811   int sx = SCREENX(x), sy = SCREENY(y);
4812   int graphic = el2img(Feld[x][y]);
4813   int frame;
4814
4815   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4816     return;
4817
4818   if (IS_WALKABLE_INSIDE(Back[x][y]))
4819     return;
4820
4821   if (Back[x][y])
4822     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4823   else if (Store[x][y])
4824     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4825
4826   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4827
4828   if (Back[x][y] || Store[x][y])
4829     DrawGraphicThruMask(sx, sy, graphic, frame);
4830   else
4831     DrawGraphic(sx, sy, graphic, frame);
4832 }
4833
4834 void CheckDynamite(int x, int y)
4835 {
4836   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4837   {
4838     MovDelay[x][y]--;
4839
4840     if (MovDelay[x][y] != 0)
4841     {
4842       DrawDynamite(x, y);
4843       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4844
4845       return;
4846     }
4847   }
4848
4849   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4850
4851   Bang(x, y);
4852 }
4853
4854 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4855 {
4856   boolean num_checked_players = 0;
4857   int i;
4858
4859   for (i = 0; i < MAX_PLAYERS; i++)
4860   {
4861     if (stored_player[i].active)
4862     {
4863       int sx = stored_player[i].jx;
4864       int sy = stored_player[i].jy;
4865
4866       if (num_checked_players == 0)
4867       {
4868         *sx1 = *sx2 = sx;
4869         *sy1 = *sy2 = sy;
4870       }
4871       else
4872       {
4873         *sx1 = MIN(*sx1, sx);
4874         *sy1 = MIN(*sy1, sy);
4875         *sx2 = MAX(*sx2, sx);
4876         *sy2 = MAX(*sy2, sy);
4877       }
4878
4879       num_checked_players++;
4880     }
4881   }
4882 }
4883
4884 static boolean checkIfAllPlayersFitToScreen_RND()
4885 {
4886   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4887
4888   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4889
4890   return (sx2 - sx1 < SCR_FIELDX &&
4891           sy2 - sy1 < SCR_FIELDY);
4892 }
4893
4894 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4895 {
4896   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4897
4898   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4899
4900   *sx = (sx1 + sx2) / 2;
4901   *sy = (sy1 + sy2) / 2;
4902 }
4903
4904 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4905                         boolean center_screen, boolean quick_relocation)
4906 {
4907   unsigned int frame_delay_value_old = GetVideoFrameDelay();
4908   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4909   boolean no_delay = (tape.warp_forward);
4910   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4911   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4912   int new_scroll_x, new_scroll_y;
4913
4914   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4915   {
4916     /* case 1: quick relocation inside visible screen (without scrolling) */
4917
4918     RedrawPlayfield();
4919
4920     return;
4921   }
4922
4923   if (!level.shifted_relocation || center_screen)
4924   {
4925     /* relocation _with_ centering of screen */
4926
4927     new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4928                     x > SBX_Right + MIDPOSX ? SBX_Right :
4929                     x - MIDPOSX);
4930
4931     new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4932                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4933                     y - MIDPOSY);
4934   }
4935   else
4936   {
4937     /* relocation _without_ centering of screen */
4938
4939     int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4940                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
4941                            old_x - MIDPOSX);
4942
4943     int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4944                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4945                            old_y - MIDPOSY);
4946
4947     int offset_x = x + (scroll_x - center_scroll_x);
4948     int offset_y = y + (scroll_y - center_scroll_y);
4949
4950     new_scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4951                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4952                     offset_x - MIDPOSX);
4953
4954     new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4955                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4956                     offset_y - MIDPOSY);
4957   }
4958
4959   if (quick_relocation)
4960   {
4961     /* case 2: quick relocation (redraw without visible scrolling) */
4962
4963     scroll_x = new_scroll_x;
4964     scroll_y = new_scroll_y;
4965
4966     RedrawPlayfield();
4967
4968     return;
4969   }
4970
4971   /* case 3: visible relocation (with scrolling to new position) */
4972
4973   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4974
4975   SetVideoFrameDelay(wait_delay_value);
4976
4977   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4978   {
4979     int dx = 0, dy = 0;
4980     int fx = FX, fy = FY;
4981
4982     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4983     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4984
4985     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4986       break;
4987
4988     scroll_x -= dx;
4989     scroll_y -= dy;
4990
4991     fx += dx * TILEX / 2;
4992     fy += dy * TILEY / 2;
4993
4994     ScrollLevel(dx, dy);
4995     DrawAllPlayers();
4996
4997     /* scroll in two steps of half tile size to make things smoother */
4998     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4999
5000     /* scroll second step to align at full tile size */
5001     BlitScreenToBitmap(window);
5002   }
5003
5004   DrawAllPlayers();
5005   BackToFront();
5006
5007   SetVideoFrameDelay(frame_delay_value_old);
5008 }
5009
5010 void RelocatePlayer(int jx, int jy, int el_player_raw)
5011 {
5012   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5013   int player_nr = GET_PLAYER_NR(el_player);
5014   struct PlayerInfo *player = &stored_player[player_nr];
5015   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5016   boolean no_delay = (tape.warp_forward);
5017   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5018   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5019   int old_jx = player->jx;
5020   int old_jy = player->jy;
5021   int old_element = Feld[old_jx][old_jy];
5022   int element = Feld[jx][jy];
5023   boolean player_relocated = (old_jx != jx || old_jy != jy);
5024
5025   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5026   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5027   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5028   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5029   int leave_side_horiz = move_dir_horiz;
5030   int leave_side_vert  = move_dir_vert;
5031   int enter_side = enter_side_horiz | enter_side_vert;
5032   int leave_side = leave_side_horiz | leave_side_vert;
5033
5034   if (player->GameOver)         /* do not reanimate dead player */
5035     return;
5036
5037   if (!player_relocated)        /* no need to relocate the player */
5038     return;
5039
5040   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5041   {
5042     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5043     DrawLevelField(jx, jy);
5044   }
5045
5046   if (player->present)
5047   {
5048     while (player->MovPos)
5049     {
5050       ScrollPlayer(player, SCROLL_GO_ON);
5051       ScrollScreen(NULL, SCROLL_GO_ON);
5052
5053       AdvanceFrameAndPlayerCounters(player->index_nr);
5054
5055       DrawPlayer(player);
5056
5057       BackToFront_WithFrameDelay(wait_delay_value);
5058     }
5059
5060     DrawPlayer(player);         /* needed here only to cleanup last field */
5061     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5062
5063     player->is_moving = FALSE;
5064   }
5065
5066   if (IS_CUSTOM_ELEMENT(old_element))
5067     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5068                                CE_LEFT_BY_PLAYER,
5069                                player->index_bit, leave_side);
5070
5071   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5072                                       CE_PLAYER_LEAVES_X,
5073                                       player->index_bit, leave_side);
5074
5075   Feld[jx][jy] = el_player;
5076   InitPlayerField(jx, jy, el_player, TRUE);
5077
5078   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5079      possible that the relocation target field did not contain a player element,
5080      but a walkable element, to which the new player was relocated -- in this
5081      case, restore that (already initialized!) element on the player field */
5082   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5083   {
5084     Feld[jx][jy] = element;     /* restore previously existing element */
5085   }
5086
5087   /* only visually relocate centered player */
5088   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5089                      FALSE, level.instant_relocation);
5090
5091   TestIfPlayerTouchesBadThing(jx, jy);
5092   TestIfPlayerTouchesCustomElement(jx, jy);
5093
5094   if (IS_CUSTOM_ELEMENT(element))
5095     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5096                                player->index_bit, enter_side);
5097
5098   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5099                                       player->index_bit, enter_side);
5100
5101   if (player->is_switching)
5102   {
5103     /* ensure that relocation while still switching an element does not cause
5104        a new element to be treated as also switched directly after relocation
5105        (this is important for teleporter switches that teleport the player to
5106        a place where another teleporter switch is in the same direction, which
5107        would then incorrectly be treated as immediately switched before the
5108        direction key that caused the switch was released) */
5109
5110     player->switch_x += jx - old_jx;
5111     player->switch_y += jy - old_jy;
5112   }
5113 }
5114
5115 void Explode(int ex, int ey, int phase, int mode)
5116 {
5117   int x, y;
5118   int last_phase;
5119   int border_element;
5120
5121   /* !!! eliminate this variable !!! */
5122   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5123
5124   if (game.explosions_delayed)
5125   {
5126     ExplodeField[ex][ey] = mode;
5127     return;
5128   }
5129
5130   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5131   {
5132     int center_element = Feld[ex][ey];
5133     int artwork_element, explosion_element;     /* set these values later */
5134
5135     /* remove things displayed in background while burning dynamite */
5136     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5137       Back[ex][ey] = 0;
5138
5139     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5140     {
5141       /* put moving element to center field (and let it explode there) */
5142       center_element = MovingOrBlocked2Element(ex, ey);
5143       RemoveMovingField(ex, ey);
5144       Feld[ex][ey] = center_element;
5145     }
5146
5147     /* now "center_element" is finally determined -- set related values now */
5148     artwork_element = center_element;           /* for custom player artwork */
5149     explosion_element = center_element;         /* for custom player artwork */
5150
5151     if (IS_PLAYER(ex, ey))
5152     {
5153       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5154
5155       artwork_element = stored_player[player_nr].artwork_element;
5156
5157       if (level.use_explosion_element[player_nr])
5158       {
5159         explosion_element = level.explosion_element[player_nr];
5160         artwork_element = explosion_element;
5161       }
5162     }
5163
5164     if (mode == EX_TYPE_NORMAL ||
5165         mode == EX_TYPE_CENTER ||
5166         mode == EX_TYPE_CROSS)
5167       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5168
5169     last_phase = element_info[explosion_element].explosion_delay + 1;
5170
5171     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5172     {
5173       int xx = x - ex + 1;
5174       int yy = y - ey + 1;
5175       int element;
5176
5177       if (!IN_LEV_FIELD(x, y) ||
5178           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5179           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5180         continue;
5181
5182       element = Feld[x][y];
5183
5184       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5185       {
5186         element = MovingOrBlocked2Element(x, y);
5187
5188         if (!IS_EXPLOSION_PROOF(element))
5189           RemoveMovingField(x, y);
5190       }
5191
5192       /* indestructible elements can only explode in center (but not flames) */
5193       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5194                                            mode == EX_TYPE_BORDER)) ||
5195           element == EL_FLAMES)
5196         continue;
5197
5198       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5199          behaviour, for example when touching a yamyam that explodes to rocks
5200          with active deadly shield, a rock is created under the player !!! */
5201       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5202 #if 0
5203       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5204           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5205            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5206 #else
5207       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5208 #endif
5209       {
5210         if (IS_ACTIVE_BOMB(element))
5211         {
5212           /* re-activate things under the bomb like gate or penguin */
5213           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5214           Back[x][y] = 0;
5215         }
5216
5217         continue;
5218       }
5219
5220       /* save walkable background elements while explosion on same tile */
5221       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5222           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5223         Back[x][y] = element;
5224
5225       /* ignite explodable elements reached by other explosion */
5226       if (element == EL_EXPLOSION)
5227         element = Store2[x][y];
5228
5229       if (AmoebaNr[x][y] &&
5230           (element == EL_AMOEBA_FULL ||
5231            element == EL_BD_AMOEBA ||
5232            element == EL_AMOEBA_GROWING))
5233       {
5234         AmoebaCnt[AmoebaNr[x][y]]--;
5235         AmoebaCnt2[AmoebaNr[x][y]]--;
5236       }
5237
5238       RemoveField(x, y);
5239
5240       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5241       {
5242         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5243
5244         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5245
5246         if (PLAYERINFO(ex, ey)->use_murphy)
5247           Store[x][y] = EL_EMPTY;
5248       }
5249
5250       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5251          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5252       else if (ELEM_IS_PLAYER(center_element))
5253         Store[x][y] = EL_EMPTY;
5254       else if (center_element == EL_YAMYAM)
5255         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5256       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5257         Store[x][y] = element_info[center_element].content.e[xx][yy];
5258 #if 1
5259       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5260          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5261          otherwise) -- FIX THIS !!! */
5262       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5263         Store[x][y] = element_info[element].content.e[1][1];
5264 #else
5265       else if (!CAN_EXPLODE(element))
5266         Store[x][y] = element_info[element].content.e[1][1];
5267 #endif
5268       else
5269         Store[x][y] = EL_EMPTY;
5270
5271       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5272           center_element == EL_AMOEBA_TO_DIAMOND)
5273         Store2[x][y] = element;
5274
5275       Feld[x][y] = EL_EXPLOSION;
5276       GfxElement[x][y] = artwork_element;
5277
5278       ExplodePhase[x][y] = 1;
5279       ExplodeDelay[x][y] = last_phase;
5280
5281       Stop[x][y] = TRUE;
5282     }
5283
5284     if (center_element == EL_YAMYAM)
5285       game.yamyam_content_nr =
5286         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5287
5288     return;
5289   }
5290
5291   if (Stop[ex][ey])
5292     return;
5293
5294   x = ex;
5295   y = ey;
5296
5297   if (phase == 1)
5298     GfxFrame[x][y] = 0;         /* restart explosion animation */
5299
5300   last_phase = ExplodeDelay[x][y];
5301
5302   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5303
5304   /* this can happen if the player leaves an explosion just in time */
5305   if (GfxElement[x][y] == EL_UNDEFINED)
5306     GfxElement[x][y] = EL_EMPTY;
5307
5308   border_element = Store2[x][y];
5309   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5310     border_element = StorePlayer[x][y];
5311
5312   if (phase == element_info[border_element].ignition_delay ||
5313       phase == last_phase)
5314   {
5315     boolean border_explosion = FALSE;
5316
5317     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5318         !PLAYER_EXPLOSION_PROTECTED(x, y))
5319     {
5320       KillPlayerUnlessExplosionProtected(x, y);
5321       border_explosion = TRUE;
5322     }
5323     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5324     {
5325       Feld[x][y] = Store2[x][y];
5326       Store2[x][y] = 0;
5327       Bang(x, y);
5328       border_explosion = TRUE;
5329     }
5330     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5331     {
5332       AmoebeUmwandeln(x, y);
5333       Store2[x][y] = 0;
5334       border_explosion = TRUE;
5335     }
5336
5337     /* if an element just explodes due to another explosion (chain-reaction),
5338        do not immediately end the new explosion when it was the last frame of
5339        the explosion (as it would be done in the following "if"-statement!) */
5340     if (border_explosion && phase == last_phase)
5341       return;
5342   }
5343
5344   if (phase == last_phase)
5345   {
5346     int element;
5347
5348     element = Feld[x][y] = Store[x][y];
5349     Store[x][y] = Store2[x][y] = 0;
5350     GfxElement[x][y] = EL_UNDEFINED;
5351
5352     /* player can escape from explosions and might therefore be still alive */
5353     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5354         element <= EL_PLAYER_IS_EXPLODING_4)
5355     {
5356       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5357       int explosion_element = EL_PLAYER_1 + player_nr;
5358       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5359       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5360
5361       if (level.use_explosion_element[player_nr])
5362         explosion_element = level.explosion_element[player_nr];
5363
5364       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5365                     element_info[explosion_element].content.e[xx][yy]);
5366     }
5367
5368     /* restore probably existing indestructible background element */
5369     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5370       element = Feld[x][y] = Back[x][y];
5371     Back[x][y] = 0;
5372
5373     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5374     GfxDir[x][y] = MV_NONE;
5375     ChangeDelay[x][y] = 0;
5376     ChangePage[x][y] = -1;
5377
5378     CustomValue[x][y] = 0;
5379
5380     InitField_WithBug2(x, y, FALSE);
5381
5382     TEST_DrawLevelField(x, y);
5383
5384     TestIfElementTouchesCustomElement(x, y);
5385
5386     if (GFX_CRUMBLED(element))
5387       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5388
5389     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5390       StorePlayer[x][y] = 0;
5391
5392     if (ELEM_IS_PLAYER(element))
5393       RelocatePlayer(x, y, element);
5394   }
5395   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5396   {
5397     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5398     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5399
5400     if (phase == delay)
5401       TEST_DrawLevelFieldCrumbled(x, y);
5402
5403     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5404     {
5405       DrawLevelElement(x, y, Back[x][y]);
5406       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5407     }
5408     else if (IS_WALKABLE_UNDER(Back[x][y]))
5409     {
5410       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5411       DrawLevelElementThruMask(x, y, Back[x][y]);
5412     }
5413     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5414       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5415   }
5416 }
5417
5418 void DynaExplode(int ex, int ey)
5419 {
5420   int i, j;
5421   int dynabomb_element = Feld[ex][ey];
5422   int dynabomb_size = 1;
5423   boolean dynabomb_xl = FALSE;
5424   struct PlayerInfo *player;
5425   static int xy[4][2] =
5426   {
5427     { 0, -1 },
5428     { -1, 0 },
5429     { +1, 0 },
5430     { 0, +1 }
5431   };
5432
5433   if (IS_ACTIVE_BOMB(dynabomb_element))
5434   {
5435     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5436     dynabomb_size = player->dynabomb_size;
5437     dynabomb_xl = player->dynabomb_xl;
5438     player->dynabombs_left++;
5439   }
5440
5441   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5442
5443   for (i = 0; i < NUM_DIRECTIONS; i++)
5444   {
5445     for (j = 1; j <= dynabomb_size; j++)
5446     {
5447       int x = ex + j * xy[i][0];
5448       int y = ey + j * xy[i][1];
5449       int element;
5450
5451       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5452         break;
5453
5454       element = Feld[x][y];
5455
5456       /* do not restart explosions of fields with active bombs */
5457       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5458         continue;
5459
5460       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5461
5462       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5463           !IS_DIGGABLE(element) && !dynabomb_xl)
5464         break;
5465     }
5466   }
5467 }
5468
5469 void Bang(int x, int y)
5470 {
5471   int element = MovingOrBlocked2Element(x, y);
5472   int explosion_type = EX_TYPE_NORMAL;
5473
5474   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5475   {
5476     struct PlayerInfo *player = PLAYERINFO(x, y);
5477
5478     element = Feld[x][y] = player->initial_element;
5479
5480     if (level.use_explosion_element[player->index_nr])
5481     {
5482       int explosion_element = level.explosion_element[player->index_nr];
5483
5484       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5485         explosion_type = EX_TYPE_CROSS;
5486       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5487         explosion_type = EX_TYPE_CENTER;
5488     }
5489   }
5490
5491   switch (element)
5492   {
5493     case EL_BUG:
5494     case EL_SPACESHIP:
5495     case EL_BD_BUTTERFLY:
5496     case EL_BD_FIREFLY:
5497     case EL_YAMYAM:
5498     case EL_DARK_YAMYAM:
5499     case EL_ROBOT:
5500     case EL_PACMAN:
5501     case EL_MOLE:
5502       RaiseScoreElement(element);
5503       break;
5504
5505     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5506     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5507     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5508     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5509     case EL_DYNABOMB_INCREASE_NUMBER:
5510     case EL_DYNABOMB_INCREASE_SIZE:
5511     case EL_DYNABOMB_INCREASE_POWER:
5512       explosion_type = EX_TYPE_DYNA;
5513       break;
5514
5515     case EL_DC_LANDMINE:
5516       explosion_type = EX_TYPE_CENTER;
5517       break;
5518
5519     case EL_PENGUIN:
5520     case EL_LAMP:
5521     case EL_LAMP_ACTIVE:
5522     case EL_AMOEBA_TO_DIAMOND:
5523       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5524         explosion_type = EX_TYPE_CENTER;
5525       break;
5526
5527     default:
5528       if (element_info[element].explosion_type == EXPLODES_CROSS)
5529         explosion_type = EX_TYPE_CROSS;
5530       else if (element_info[element].explosion_type == EXPLODES_1X1)
5531         explosion_type = EX_TYPE_CENTER;
5532       break;
5533   }
5534
5535   if (explosion_type == EX_TYPE_DYNA)
5536     DynaExplode(x, y);
5537   else
5538     Explode(x, y, EX_PHASE_START, explosion_type);
5539
5540   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5541 }
5542
5543 void SplashAcid(int x, int y)
5544 {
5545   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5546       (!IN_LEV_FIELD(x - 1, y - 2) ||
5547        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5548     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5549
5550   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5551       (!IN_LEV_FIELD(x + 1, y - 2) ||
5552        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5553     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5554
5555   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5556 }
5557
5558 static void InitBeltMovement()
5559 {
5560   static int belt_base_element[4] =
5561   {
5562     EL_CONVEYOR_BELT_1_LEFT,
5563     EL_CONVEYOR_BELT_2_LEFT,
5564     EL_CONVEYOR_BELT_3_LEFT,
5565     EL_CONVEYOR_BELT_4_LEFT
5566   };
5567   static int belt_base_active_element[4] =
5568   {
5569     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5570     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5571     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5572     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5573   };
5574
5575   int x, y, i, j;
5576
5577   /* set frame order for belt animation graphic according to belt direction */
5578   for (i = 0; i < NUM_BELTS; i++)
5579   {
5580     int belt_nr = i;
5581
5582     for (j = 0; j < NUM_BELT_PARTS; j++)
5583     {
5584       int element = belt_base_active_element[belt_nr] + j;
5585       int graphic_1 = el2img(element);
5586       int graphic_2 = el2panelimg(element);
5587
5588       if (game.belt_dir[i] == MV_LEFT)
5589       {
5590         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5591         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5592       }
5593       else
5594       {
5595         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5596         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5597       }
5598     }
5599   }
5600
5601   SCAN_PLAYFIELD(x, y)
5602   {
5603     int element = Feld[x][y];
5604
5605     for (i = 0; i < NUM_BELTS; i++)
5606     {
5607       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5608       {
5609         int e_belt_nr = getBeltNrFromBeltElement(element);
5610         int belt_nr = i;
5611
5612         if (e_belt_nr == belt_nr)
5613         {
5614           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5615
5616           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5617         }
5618       }
5619     }
5620   }
5621 }
5622
5623 static void ToggleBeltSwitch(int x, int y)
5624 {
5625   static int belt_base_element[4] =
5626   {
5627     EL_CONVEYOR_BELT_1_LEFT,
5628     EL_CONVEYOR_BELT_2_LEFT,
5629     EL_CONVEYOR_BELT_3_LEFT,
5630     EL_CONVEYOR_BELT_4_LEFT
5631   };
5632   static int belt_base_active_element[4] =
5633   {
5634     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5635     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5636     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5637     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5638   };
5639   static int belt_base_switch_element[4] =
5640   {
5641     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5642     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5643     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5644     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5645   };
5646   static int belt_move_dir[4] =
5647   {
5648     MV_LEFT,
5649     MV_NONE,
5650     MV_RIGHT,
5651     MV_NONE,
5652   };
5653
5654   int element = Feld[x][y];
5655   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5656   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5657   int belt_dir = belt_move_dir[belt_dir_nr];
5658   int xx, yy, i;
5659
5660   if (!IS_BELT_SWITCH(element))
5661     return;
5662
5663   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5664   game.belt_dir[belt_nr] = belt_dir;
5665
5666   if (belt_dir_nr == 3)
5667     belt_dir_nr = 1;
5668
5669   /* set frame order for belt animation graphic according to belt direction */
5670   for (i = 0; i < NUM_BELT_PARTS; i++)
5671   {
5672     int element = belt_base_active_element[belt_nr] + i;
5673     int graphic_1 = el2img(element);
5674     int graphic_2 = el2panelimg(element);
5675
5676     if (belt_dir == MV_LEFT)
5677     {
5678       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5679       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5680     }
5681     else
5682     {
5683       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5684       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5685     }
5686   }
5687
5688   SCAN_PLAYFIELD(xx, yy)
5689   {
5690     int element = Feld[xx][yy];
5691
5692     if (IS_BELT_SWITCH(element))
5693     {
5694       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5695
5696       if (e_belt_nr == belt_nr)
5697       {
5698         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5699         TEST_DrawLevelField(xx, yy);
5700       }
5701     }
5702     else if (IS_BELT(element) && belt_dir != MV_NONE)
5703     {
5704       int e_belt_nr = getBeltNrFromBeltElement(element);
5705
5706       if (e_belt_nr == belt_nr)
5707       {
5708         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5709
5710         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5711         TEST_DrawLevelField(xx, yy);
5712       }
5713     }
5714     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5715     {
5716       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5717
5718       if (e_belt_nr == belt_nr)
5719       {
5720         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5721
5722         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5723         TEST_DrawLevelField(xx, yy);
5724       }
5725     }
5726   }
5727 }
5728
5729 static void ToggleSwitchgateSwitch(int x, int y)
5730 {
5731   int xx, yy;
5732
5733   game.switchgate_pos = !game.switchgate_pos;
5734
5735   SCAN_PLAYFIELD(xx, yy)
5736   {
5737     int element = Feld[xx][yy];
5738
5739     if (element == EL_SWITCHGATE_SWITCH_UP)
5740     {
5741       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5742       TEST_DrawLevelField(xx, yy);
5743     }
5744     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5745     {
5746       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5747       TEST_DrawLevelField(xx, yy);
5748     }
5749     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5750     {
5751       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5752       TEST_DrawLevelField(xx, yy);
5753     }
5754     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5755     {
5756       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5757       TEST_DrawLevelField(xx, yy);
5758     }
5759     else if (element == EL_SWITCHGATE_OPEN ||
5760              element == EL_SWITCHGATE_OPENING)
5761     {
5762       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5763
5764       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5765     }
5766     else if (element == EL_SWITCHGATE_CLOSED ||
5767              element == EL_SWITCHGATE_CLOSING)
5768     {
5769       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5770
5771       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5772     }
5773   }
5774 }
5775
5776 static int getInvisibleActiveFromInvisibleElement(int element)
5777 {
5778   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5779           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5780           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5781           element);
5782 }
5783
5784 static int getInvisibleFromInvisibleActiveElement(int element)
5785 {
5786   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5787           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5788           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5789           element);
5790 }
5791
5792 static void RedrawAllLightSwitchesAndInvisibleElements()
5793 {
5794   int x, y;
5795
5796   SCAN_PLAYFIELD(x, y)
5797   {
5798     int element = Feld[x][y];
5799
5800     if (element == EL_LIGHT_SWITCH &&
5801         game.light_time_left > 0)
5802     {
5803       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5804       TEST_DrawLevelField(x, y);
5805     }
5806     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5807              game.light_time_left == 0)
5808     {
5809       Feld[x][y] = EL_LIGHT_SWITCH;
5810       TEST_DrawLevelField(x, y);
5811     }
5812     else if (element == EL_EMC_DRIPPER &&
5813              game.light_time_left > 0)
5814     {
5815       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5816       TEST_DrawLevelField(x, y);
5817     }
5818     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5819              game.light_time_left == 0)
5820     {
5821       Feld[x][y] = EL_EMC_DRIPPER;
5822       TEST_DrawLevelField(x, y);
5823     }
5824     else if (element == EL_INVISIBLE_STEELWALL ||
5825              element == EL_INVISIBLE_WALL ||
5826              element == EL_INVISIBLE_SAND)
5827     {
5828       if (game.light_time_left > 0)
5829         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5830
5831       TEST_DrawLevelField(x, y);
5832
5833       /* uncrumble neighbour fields, if needed */
5834       if (element == EL_INVISIBLE_SAND)
5835         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5836     }
5837     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5838              element == EL_INVISIBLE_WALL_ACTIVE ||
5839              element == EL_INVISIBLE_SAND_ACTIVE)
5840     {
5841       if (game.light_time_left == 0)
5842         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5843
5844       TEST_DrawLevelField(x, y);
5845
5846       /* re-crumble neighbour fields, if needed */
5847       if (element == EL_INVISIBLE_SAND)
5848         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5849     }
5850   }
5851 }
5852
5853 static void RedrawAllInvisibleElementsForLenses()
5854 {
5855   int x, y;
5856
5857   SCAN_PLAYFIELD(x, y)
5858   {
5859     int element = Feld[x][y];
5860
5861     if (element == EL_EMC_DRIPPER &&
5862         game.lenses_time_left > 0)
5863     {
5864       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5865       TEST_DrawLevelField(x, y);
5866     }
5867     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5868              game.lenses_time_left == 0)
5869     {
5870       Feld[x][y] = EL_EMC_DRIPPER;
5871       TEST_DrawLevelField(x, y);
5872     }
5873     else if (element == EL_INVISIBLE_STEELWALL ||
5874              element == EL_INVISIBLE_WALL ||
5875              element == EL_INVISIBLE_SAND)
5876     {
5877       if (game.lenses_time_left > 0)
5878         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5879
5880       TEST_DrawLevelField(x, y);
5881
5882       /* uncrumble neighbour fields, if needed */
5883       if (element == EL_INVISIBLE_SAND)
5884         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5885     }
5886     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5887              element == EL_INVISIBLE_WALL_ACTIVE ||
5888              element == EL_INVISIBLE_SAND_ACTIVE)
5889     {
5890       if (game.lenses_time_left == 0)
5891         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5892
5893       TEST_DrawLevelField(x, y);
5894
5895       /* re-crumble neighbour fields, if needed */
5896       if (element == EL_INVISIBLE_SAND)
5897         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5898     }
5899   }
5900 }
5901
5902 static void RedrawAllInvisibleElementsForMagnifier()
5903 {
5904   int x, y;
5905
5906   SCAN_PLAYFIELD(x, y)
5907   {
5908     int element = Feld[x][y];
5909
5910     if (element == EL_EMC_FAKE_GRASS &&
5911         game.magnify_time_left > 0)
5912     {
5913       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5914       TEST_DrawLevelField(x, y);
5915     }
5916     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5917              game.magnify_time_left == 0)
5918     {
5919       Feld[x][y] = EL_EMC_FAKE_GRASS;
5920       TEST_DrawLevelField(x, y);
5921     }
5922     else if (IS_GATE_GRAY(element) &&
5923              game.magnify_time_left > 0)
5924     {
5925       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5926                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5927                     IS_EM_GATE_GRAY(element) ?
5928                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5929                     IS_EMC_GATE_GRAY(element) ?
5930                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5931                     IS_DC_GATE_GRAY(element) ?
5932                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5933                     element);
5934       TEST_DrawLevelField(x, y);
5935     }
5936     else if (IS_GATE_GRAY_ACTIVE(element) &&
5937              game.magnify_time_left == 0)
5938     {
5939       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5940                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5941                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5942                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5943                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5944                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5945                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5946                     EL_DC_GATE_WHITE_GRAY :
5947                     element);
5948       TEST_DrawLevelField(x, y);
5949     }
5950   }
5951 }
5952
5953 static void ToggleLightSwitch(int x, int y)
5954 {
5955   int element = Feld[x][y];
5956
5957   game.light_time_left =
5958     (element == EL_LIGHT_SWITCH ?
5959      level.time_light * FRAMES_PER_SECOND : 0);
5960
5961   RedrawAllLightSwitchesAndInvisibleElements();
5962 }
5963
5964 static void ActivateTimegateSwitch(int x, int y)
5965 {
5966   int xx, yy;
5967
5968   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5969
5970   SCAN_PLAYFIELD(xx, yy)
5971   {
5972     int element = Feld[xx][yy];
5973
5974     if (element == EL_TIMEGATE_CLOSED ||
5975         element == EL_TIMEGATE_CLOSING)
5976     {
5977       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5978       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5979     }
5980
5981     /*
5982     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5983     {
5984       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5985       TEST_DrawLevelField(xx, yy);
5986     }
5987     */
5988
5989   }
5990
5991   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5992                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5993 }
5994
5995 void Impact(int x, int y)
5996 {
5997   boolean last_line = (y == lev_fieldy - 1);
5998   boolean object_hit = FALSE;
5999   boolean impact = (last_line || object_hit);
6000   int element = Feld[x][y];
6001   int smashed = EL_STEELWALL;
6002
6003   if (!last_line)       /* check if element below was hit */
6004   {
6005     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6006       return;
6007
6008     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6009                                          MovDir[x][y + 1] != MV_DOWN ||
6010                                          MovPos[x][y + 1] <= TILEY / 2));
6011
6012     /* do not smash moving elements that left the smashed field in time */
6013     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6014         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6015       object_hit = FALSE;
6016
6017 #if USE_QUICKSAND_IMPACT_BUGFIX
6018     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6019     {
6020       RemoveMovingField(x, y + 1);
6021       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6022       Feld[x][y + 2] = EL_ROCK;
6023       TEST_DrawLevelField(x, y + 2);
6024
6025       object_hit = TRUE;
6026     }
6027
6028     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6029     {
6030       RemoveMovingField(x, y + 1);
6031       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6032       Feld[x][y + 2] = EL_ROCK;
6033       TEST_DrawLevelField(x, y + 2);
6034
6035       object_hit = TRUE;
6036     }
6037 #endif
6038
6039     if (object_hit)
6040       smashed = MovingOrBlocked2Element(x, y + 1);
6041
6042     impact = (last_line || object_hit);
6043   }
6044
6045   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6046   {
6047     SplashAcid(x, y + 1);
6048     return;
6049   }
6050
6051   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6052   /* only reset graphic animation if graphic really changes after impact */
6053   if (impact &&
6054       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6055   {
6056     ResetGfxAnimation(x, y);
6057     TEST_DrawLevelField(x, y);
6058   }
6059
6060   if (impact && CAN_EXPLODE_IMPACT(element))
6061   {
6062     Bang(x, y);
6063     return;
6064   }
6065   else if (impact && element == EL_PEARL &&
6066            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6067   {
6068     ResetGfxAnimation(x, y);
6069
6070     Feld[x][y] = EL_PEARL_BREAKING;
6071     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6072     return;
6073   }
6074   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6075   {
6076     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6077
6078     return;
6079   }
6080
6081   if (impact && element == EL_AMOEBA_DROP)
6082   {
6083     if (object_hit && IS_PLAYER(x, y + 1))
6084       KillPlayerUnlessEnemyProtected(x, y + 1);
6085     else if (object_hit && smashed == EL_PENGUIN)
6086       Bang(x, y + 1);
6087     else
6088     {
6089       Feld[x][y] = EL_AMOEBA_GROWING;
6090       Store[x][y] = EL_AMOEBA_WET;
6091
6092       ResetRandomAnimationValue(x, y);
6093     }
6094     return;
6095   }
6096
6097   if (object_hit)               /* check which object was hit */
6098   {
6099     if ((CAN_PASS_MAGIC_WALL(element) && 
6100          (smashed == EL_MAGIC_WALL ||
6101           smashed == EL_BD_MAGIC_WALL)) ||
6102         (CAN_PASS_DC_MAGIC_WALL(element) &&
6103          smashed == EL_DC_MAGIC_WALL))
6104     {
6105       int xx, yy;
6106       int activated_magic_wall =
6107         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6108          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6109          EL_DC_MAGIC_WALL_ACTIVE);
6110
6111       /* activate magic wall / mill */
6112       SCAN_PLAYFIELD(xx, yy)
6113       {
6114         if (Feld[xx][yy] == smashed)
6115           Feld[xx][yy] = activated_magic_wall;
6116       }
6117
6118       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6119       game.magic_wall_active = TRUE;
6120
6121       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6122                             SND_MAGIC_WALL_ACTIVATING :
6123                             smashed == EL_BD_MAGIC_WALL ?
6124                             SND_BD_MAGIC_WALL_ACTIVATING :
6125                             SND_DC_MAGIC_WALL_ACTIVATING));
6126     }
6127
6128     if (IS_PLAYER(x, y + 1))
6129     {
6130       if (CAN_SMASH_PLAYER(element))
6131       {
6132         KillPlayerUnlessEnemyProtected(x, y + 1);
6133         return;
6134       }
6135     }
6136     else if (smashed == EL_PENGUIN)
6137     {
6138       if (CAN_SMASH_PLAYER(element))
6139       {
6140         Bang(x, y + 1);
6141         return;
6142       }
6143     }
6144     else if (element == EL_BD_DIAMOND)
6145     {
6146       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6147       {
6148         Bang(x, y + 1);
6149         return;
6150       }
6151     }
6152     else if (((element == EL_SP_INFOTRON ||
6153                element == EL_SP_ZONK) &&
6154               (smashed == EL_SP_SNIKSNAK ||
6155                smashed == EL_SP_ELECTRON ||
6156                smashed == EL_SP_DISK_ORANGE)) ||
6157              (element == EL_SP_INFOTRON &&
6158               smashed == EL_SP_DISK_YELLOW))
6159     {
6160       Bang(x, y + 1);
6161       return;
6162     }
6163     else if (CAN_SMASH_EVERYTHING(element))
6164     {
6165       if (IS_CLASSIC_ENEMY(smashed) ||
6166           CAN_EXPLODE_SMASHED(smashed))
6167       {
6168         Bang(x, y + 1);
6169         return;
6170       }
6171       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6172       {
6173         if (smashed == EL_LAMP ||
6174             smashed == EL_LAMP_ACTIVE)
6175         {
6176           Bang(x, y + 1);
6177           return;
6178         }
6179         else if (smashed == EL_NUT)
6180         {
6181           Feld[x][y + 1] = EL_NUT_BREAKING;
6182           PlayLevelSound(x, y, SND_NUT_BREAKING);
6183           RaiseScoreElement(EL_NUT);
6184           return;
6185         }
6186         else if (smashed == EL_PEARL)
6187         {
6188           ResetGfxAnimation(x, y);
6189
6190           Feld[x][y + 1] = EL_PEARL_BREAKING;
6191           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6192           return;
6193         }
6194         else if (smashed == EL_DIAMOND)
6195         {
6196           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6197           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6198           return;
6199         }
6200         else if (IS_BELT_SWITCH(smashed))
6201         {
6202           ToggleBeltSwitch(x, y + 1);
6203         }
6204         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6205                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6206                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6207                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6208         {
6209           ToggleSwitchgateSwitch(x, y + 1);
6210         }
6211         else if (smashed == EL_LIGHT_SWITCH ||
6212                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6213         {
6214           ToggleLightSwitch(x, y + 1);
6215         }
6216         else
6217         {
6218           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6219
6220           CheckElementChangeBySide(x, y + 1, smashed, element,
6221                                    CE_SWITCHED, CH_SIDE_TOP);
6222           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6223                                             CH_SIDE_TOP);
6224         }
6225       }
6226       else
6227       {
6228         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6229       }
6230     }
6231   }
6232
6233   /* play sound of magic wall / mill */
6234   if (!last_line &&
6235       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6236        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6237        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6238   {
6239     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6240       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6241     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6242       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6243     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6244       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6245
6246     return;
6247   }
6248
6249   /* play sound of object that hits the ground */
6250   if (last_line || object_hit)
6251     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6252 }
6253
6254 inline static void TurnRoundExt(int x, int y)
6255 {
6256   static struct
6257   {
6258     int dx, dy;
6259   } move_xy[] =
6260   {
6261     {  0,  0 },
6262     { -1,  0 },
6263     { +1,  0 },
6264     {  0,  0 },
6265     {  0, -1 },
6266     {  0,  0 }, { 0, 0 }, { 0, 0 },
6267     {  0, +1 }
6268   };
6269   static struct
6270   {
6271     int left, right, back;
6272   } turn[] =
6273   {
6274     { 0,        0,              0        },
6275     { MV_DOWN,  MV_UP,          MV_RIGHT },
6276     { MV_UP,    MV_DOWN,        MV_LEFT  },
6277     { 0,        0,              0        },
6278     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6279     { 0,        0,              0        },
6280     { 0,        0,              0        },
6281     { 0,        0,              0        },
6282     { MV_RIGHT, MV_LEFT,        MV_UP    }
6283   };
6284
6285   int element = Feld[x][y];
6286   int move_pattern = element_info[element].move_pattern;
6287
6288   int old_move_dir = MovDir[x][y];
6289   int left_dir  = turn[old_move_dir].left;
6290   int right_dir = turn[old_move_dir].right;
6291   int back_dir  = turn[old_move_dir].back;
6292
6293   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6294   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6295   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6296   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6297
6298   int left_x  = x + left_dx,  left_y  = y + left_dy;
6299   int right_x = x + right_dx, right_y = y + right_dy;
6300   int move_x  = x + move_dx,  move_y  = y + move_dy;
6301
6302   int xx, yy;
6303
6304   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6305   {
6306     TestIfBadThingTouchesOtherBadThing(x, y);
6307
6308     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6309       MovDir[x][y] = right_dir;
6310     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6311       MovDir[x][y] = left_dir;
6312
6313     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6314       MovDelay[x][y] = 9;
6315     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6316       MovDelay[x][y] = 1;
6317   }
6318   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6319   {
6320     TestIfBadThingTouchesOtherBadThing(x, y);
6321
6322     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6323       MovDir[x][y] = left_dir;
6324     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6325       MovDir[x][y] = right_dir;
6326
6327     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6328       MovDelay[x][y] = 9;
6329     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6330       MovDelay[x][y] = 1;
6331   }
6332   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6333   {
6334     TestIfBadThingTouchesOtherBadThing(x, y);
6335
6336     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6337       MovDir[x][y] = left_dir;
6338     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6339       MovDir[x][y] = right_dir;
6340
6341     if (MovDir[x][y] != old_move_dir)
6342       MovDelay[x][y] = 9;
6343   }
6344   else if (element == EL_YAMYAM)
6345   {
6346     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6347     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6348
6349     if (can_turn_left && can_turn_right)
6350       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6351     else if (can_turn_left)
6352       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6353     else if (can_turn_right)
6354       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6355     else
6356       MovDir[x][y] = back_dir;
6357
6358     MovDelay[x][y] = 16 + 16 * RND(3);
6359   }
6360   else if (element == EL_DARK_YAMYAM)
6361   {
6362     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6363                                                          left_x, left_y);
6364     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6365                                                          right_x, right_y);
6366
6367     if (can_turn_left && can_turn_right)
6368       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6369     else if (can_turn_left)
6370       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6371     else if (can_turn_right)
6372       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6373     else
6374       MovDir[x][y] = back_dir;
6375
6376     MovDelay[x][y] = 16 + 16 * RND(3);
6377   }
6378   else if (element == EL_PACMAN)
6379   {
6380     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6381     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6382
6383     if (can_turn_left && can_turn_right)
6384       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6385     else if (can_turn_left)
6386       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6387     else if (can_turn_right)
6388       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6389     else
6390       MovDir[x][y] = back_dir;
6391
6392     MovDelay[x][y] = 6 + RND(40);
6393   }
6394   else if (element == EL_PIG)
6395   {
6396     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6397     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6398     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6399     boolean should_turn_left, should_turn_right, should_move_on;
6400     int rnd_value = 24;
6401     int rnd = RND(rnd_value);
6402
6403     should_turn_left = (can_turn_left &&
6404                         (!can_move_on ||
6405                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6406                                                    y + back_dy + left_dy)));
6407     should_turn_right = (can_turn_right &&
6408                          (!can_move_on ||
6409                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6410                                                     y + back_dy + right_dy)));
6411     should_move_on = (can_move_on &&
6412                       (!can_turn_left ||
6413                        !can_turn_right ||
6414                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6415                                                  y + move_dy + left_dy) ||
6416                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6417                                                  y + move_dy + right_dy)));
6418
6419     if (should_turn_left || should_turn_right || should_move_on)
6420     {
6421       if (should_turn_left && should_turn_right && should_move_on)
6422         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6423                         rnd < 2 * rnd_value / 3 ? right_dir :
6424                         old_move_dir);
6425       else if (should_turn_left && should_turn_right)
6426         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6427       else if (should_turn_left && should_move_on)
6428         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6429       else if (should_turn_right && should_move_on)
6430         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6431       else if (should_turn_left)
6432         MovDir[x][y] = left_dir;
6433       else if (should_turn_right)
6434         MovDir[x][y] = right_dir;
6435       else if (should_move_on)
6436         MovDir[x][y] = old_move_dir;
6437     }
6438     else if (can_move_on && rnd > rnd_value / 8)
6439       MovDir[x][y] = old_move_dir;
6440     else if (can_turn_left && can_turn_right)
6441       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6442     else if (can_turn_left && rnd > rnd_value / 8)
6443       MovDir[x][y] = left_dir;
6444     else if (can_turn_right && rnd > rnd_value/8)
6445       MovDir[x][y] = right_dir;
6446     else
6447       MovDir[x][y] = back_dir;
6448
6449     xx = x + move_xy[MovDir[x][y]].dx;
6450     yy = y + move_xy[MovDir[x][y]].dy;
6451
6452     if (!IN_LEV_FIELD(xx, yy) ||
6453         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6454       MovDir[x][y] = old_move_dir;
6455
6456     MovDelay[x][y] = 0;
6457   }
6458   else if (element == EL_DRAGON)
6459   {
6460     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6461     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6462     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6463     int rnd_value = 24;
6464     int rnd = RND(rnd_value);
6465
6466     if (can_move_on && rnd > rnd_value / 8)
6467       MovDir[x][y] = old_move_dir;
6468     else if (can_turn_left && can_turn_right)
6469       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6470     else if (can_turn_left && rnd > rnd_value / 8)
6471       MovDir[x][y] = left_dir;
6472     else if (can_turn_right && rnd > rnd_value / 8)
6473       MovDir[x][y] = right_dir;
6474     else
6475       MovDir[x][y] = back_dir;
6476
6477     xx = x + move_xy[MovDir[x][y]].dx;
6478     yy = y + move_xy[MovDir[x][y]].dy;
6479
6480     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6481       MovDir[x][y] = old_move_dir;
6482
6483     MovDelay[x][y] = 0;
6484   }
6485   else if (element == EL_MOLE)
6486   {
6487     boolean can_move_on =
6488       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6489                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6490                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6491     if (!can_move_on)
6492     {
6493       boolean can_turn_left =
6494         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6495                               IS_AMOEBOID(Feld[left_x][left_y])));
6496
6497       boolean can_turn_right =
6498         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6499                               IS_AMOEBOID(Feld[right_x][right_y])));
6500
6501       if (can_turn_left && can_turn_right)
6502         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6503       else if (can_turn_left)
6504         MovDir[x][y] = left_dir;
6505       else
6506         MovDir[x][y] = right_dir;
6507     }
6508
6509     if (MovDir[x][y] != old_move_dir)
6510       MovDelay[x][y] = 9;
6511   }
6512   else if (element == EL_BALLOON)
6513   {
6514     MovDir[x][y] = game.wind_direction;
6515     MovDelay[x][y] = 0;
6516   }
6517   else if (element == EL_SPRING)
6518   {
6519     if (MovDir[x][y] & MV_HORIZONTAL)
6520     {
6521       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6522           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6523       {
6524         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6525         ResetGfxAnimation(move_x, move_y);
6526         TEST_DrawLevelField(move_x, move_y);
6527
6528         MovDir[x][y] = back_dir;
6529       }
6530       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6531                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6532         MovDir[x][y] = MV_NONE;
6533     }
6534
6535     MovDelay[x][y] = 0;
6536   }
6537   else if (element == EL_ROBOT ||
6538            element == EL_SATELLITE ||
6539            element == EL_PENGUIN ||
6540            element == EL_EMC_ANDROID)
6541   {
6542     int attr_x = -1, attr_y = -1;
6543
6544     if (AllPlayersGone)
6545     {
6546       attr_x = ExitX;
6547       attr_y = ExitY;
6548     }
6549     else
6550     {
6551       int i;
6552
6553       for (i = 0; i < MAX_PLAYERS; i++)
6554       {
6555         struct PlayerInfo *player = &stored_player[i];
6556         int jx = player->jx, jy = player->jy;
6557
6558         if (!player->active)
6559           continue;
6560
6561         if (attr_x == -1 ||
6562             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6563         {
6564           attr_x = jx;
6565           attr_y = jy;
6566         }
6567       }
6568     }
6569
6570     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6571         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6572          game.engine_version < VERSION_IDENT(3,1,0,0)))
6573     {
6574       attr_x = ZX;
6575       attr_y = ZY;
6576     }
6577
6578     if (element == EL_PENGUIN)
6579     {
6580       int i;
6581       static int xy[4][2] =
6582       {
6583         { 0, -1 },
6584         { -1, 0 },
6585         { +1, 0 },
6586         { 0, +1 }
6587       };
6588
6589       for (i = 0; i < NUM_DIRECTIONS; i++)
6590       {
6591         int ex = x + xy[i][0];
6592         int ey = y + xy[i][1];
6593
6594         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6595                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6596                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6597                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6598         {
6599           attr_x = ex;
6600           attr_y = ey;
6601           break;
6602         }
6603       }
6604     }
6605
6606     MovDir[x][y] = MV_NONE;
6607     if (attr_x < x)
6608       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6609     else if (attr_x > x)
6610       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6611     if (attr_y < y)
6612       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6613     else if (attr_y > y)
6614       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6615
6616     if (element == EL_ROBOT)
6617     {
6618       int newx, newy;
6619
6620       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6621         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6622       Moving2Blocked(x, y, &newx, &newy);
6623
6624       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6625         MovDelay[x][y] = 8 + 8 * !RND(3);
6626       else
6627         MovDelay[x][y] = 16;
6628     }
6629     else if (element == EL_PENGUIN)
6630     {
6631       int newx, newy;
6632
6633       MovDelay[x][y] = 1;
6634
6635       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6636       {
6637         boolean first_horiz = RND(2);
6638         int new_move_dir = MovDir[x][y];
6639
6640         MovDir[x][y] =
6641           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6642         Moving2Blocked(x, y, &newx, &newy);
6643
6644         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6645           return;
6646
6647         MovDir[x][y] =
6648           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6649         Moving2Blocked(x, y, &newx, &newy);
6650
6651         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6652           return;
6653
6654         MovDir[x][y] = old_move_dir;
6655         return;
6656       }
6657     }
6658     else if (element == EL_SATELLITE)
6659     {
6660       int newx, newy;
6661
6662       MovDelay[x][y] = 1;
6663
6664       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6665       {
6666         boolean first_horiz = RND(2);
6667         int new_move_dir = MovDir[x][y];
6668
6669         MovDir[x][y] =
6670           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6671         Moving2Blocked(x, y, &newx, &newy);
6672
6673         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6674           return;
6675
6676         MovDir[x][y] =
6677           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6678         Moving2Blocked(x, y, &newx, &newy);
6679
6680         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6681           return;
6682
6683         MovDir[x][y] = old_move_dir;
6684         return;
6685       }
6686     }
6687     else if (element == EL_EMC_ANDROID)
6688     {
6689       static int check_pos[16] =
6690       {
6691         -1,             /*  0 => (invalid)          */
6692         7,              /*  1 => MV_LEFT            */
6693         3,              /*  2 => MV_RIGHT           */
6694         -1,             /*  3 => (invalid)          */
6695         1,              /*  4 =>            MV_UP   */
6696         0,              /*  5 => MV_LEFT  | MV_UP   */
6697         2,              /*  6 => MV_RIGHT | MV_UP   */
6698         -1,             /*  7 => (invalid)          */
6699         5,              /*  8 =>            MV_DOWN */
6700         6,              /*  9 => MV_LEFT  | MV_DOWN */
6701         4,              /* 10 => MV_RIGHT | MV_DOWN */
6702         -1,             /* 11 => (invalid)          */
6703         -1,             /* 12 => (invalid)          */
6704         -1,             /* 13 => (invalid)          */
6705         -1,             /* 14 => (invalid)          */
6706         -1,             /* 15 => (invalid)          */
6707       };
6708       static struct
6709       {
6710         int dx, dy;
6711         int dir;
6712       } check_xy[8] =
6713       {
6714         { -1, -1,       MV_LEFT  | MV_UP   },
6715         {  0, -1,                  MV_UP   },
6716         { +1, -1,       MV_RIGHT | MV_UP   },
6717         { +1,  0,       MV_RIGHT           },
6718         { +1, +1,       MV_RIGHT | MV_DOWN },
6719         {  0, +1,                  MV_DOWN },
6720         { -1, +1,       MV_LEFT  | MV_DOWN },
6721         { -1,  0,       MV_LEFT            },
6722       };
6723       int start_pos, check_order;
6724       boolean can_clone = FALSE;
6725       int i;
6726
6727       /* check if there is any free field around current position */
6728       for (i = 0; i < 8; i++)
6729       {
6730         int newx = x + check_xy[i].dx;
6731         int newy = y + check_xy[i].dy;
6732
6733         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6734         {
6735           can_clone = TRUE;
6736
6737           break;
6738         }
6739       }
6740
6741       if (can_clone)            /* randomly find an element to clone */
6742       {
6743         can_clone = FALSE;
6744
6745         start_pos = check_pos[RND(8)];
6746         check_order = (RND(2) ? -1 : +1);
6747
6748         for (i = 0; i < 8; i++)
6749         {
6750           int pos_raw = start_pos + i * check_order;
6751           int pos = (pos_raw + 8) % 8;
6752           int newx = x + check_xy[pos].dx;
6753           int newy = y + check_xy[pos].dy;
6754
6755           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6756           {
6757             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6758             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6759
6760             Store[x][y] = Feld[newx][newy];
6761
6762             can_clone = TRUE;
6763
6764             break;
6765           }
6766         }
6767       }
6768
6769       if (can_clone)            /* randomly find a direction to move */
6770       {
6771         can_clone = FALSE;
6772
6773         start_pos = check_pos[RND(8)];
6774         check_order = (RND(2) ? -1 : +1);
6775
6776         for (i = 0; i < 8; i++)
6777         {
6778           int pos_raw = start_pos + i * check_order;
6779           int pos = (pos_raw + 8) % 8;
6780           int newx = x + check_xy[pos].dx;
6781           int newy = y + check_xy[pos].dy;
6782           int new_move_dir = check_xy[pos].dir;
6783
6784           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6785           {
6786             MovDir[x][y] = new_move_dir;
6787             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6788
6789             can_clone = TRUE;
6790
6791             break;
6792           }
6793         }
6794       }
6795
6796       if (can_clone)            /* cloning and moving successful */
6797         return;
6798
6799       /* cannot clone -- try to move towards player */
6800
6801       start_pos = check_pos[MovDir[x][y] & 0x0f];
6802       check_order = (RND(2) ? -1 : +1);
6803
6804       for (i = 0; i < 3; i++)
6805       {
6806         /* first check start_pos, then previous/next or (next/previous) pos */
6807         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6808         int pos = (pos_raw + 8) % 8;
6809         int newx = x + check_xy[pos].dx;
6810         int newy = y + check_xy[pos].dy;
6811         int new_move_dir = check_xy[pos].dir;
6812
6813         if (IS_PLAYER(newx, newy))
6814           break;
6815
6816         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6817         {
6818           MovDir[x][y] = new_move_dir;
6819           MovDelay[x][y] = level.android_move_time * 8 + 1;
6820
6821           break;
6822         }
6823       }
6824     }
6825   }
6826   else if (move_pattern == MV_TURNING_LEFT ||
6827            move_pattern == MV_TURNING_RIGHT ||
6828            move_pattern == MV_TURNING_LEFT_RIGHT ||
6829            move_pattern == MV_TURNING_RIGHT_LEFT ||
6830            move_pattern == MV_TURNING_RANDOM ||
6831            move_pattern == MV_ALL_DIRECTIONS)
6832   {
6833     boolean can_turn_left =
6834       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6835     boolean can_turn_right =
6836       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6837
6838     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6839       return;
6840
6841     if (move_pattern == MV_TURNING_LEFT)
6842       MovDir[x][y] = left_dir;
6843     else if (move_pattern == MV_TURNING_RIGHT)
6844       MovDir[x][y] = right_dir;
6845     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6846       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6847     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6848       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6849     else if (move_pattern == MV_TURNING_RANDOM)
6850       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6851                       can_turn_right && !can_turn_left ? right_dir :
6852                       RND(2) ? left_dir : right_dir);
6853     else if (can_turn_left && can_turn_right)
6854       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6855     else if (can_turn_left)
6856       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6857     else if (can_turn_right)
6858       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6859     else
6860       MovDir[x][y] = back_dir;
6861
6862     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6863   }
6864   else if (move_pattern == MV_HORIZONTAL ||
6865            move_pattern == MV_VERTICAL)
6866   {
6867     if (move_pattern & old_move_dir)
6868       MovDir[x][y] = back_dir;
6869     else if (move_pattern == MV_HORIZONTAL)
6870       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6871     else if (move_pattern == MV_VERTICAL)
6872       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6873
6874     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6875   }
6876   else if (move_pattern & MV_ANY_DIRECTION)
6877   {
6878     MovDir[x][y] = move_pattern;
6879     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6880   }
6881   else if (move_pattern & MV_WIND_DIRECTION)
6882   {
6883     MovDir[x][y] = game.wind_direction;
6884     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6885   }
6886   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6887   {
6888     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6889       MovDir[x][y] = left_dir;
6890     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6891       MovDir[x][y] = right_dir;
6892
6893     if (MovDir[x][y] != old_move_dir)
6894       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6895   }
6896   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6897   {
6898     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6899       MovDir[x][y] = right_dir;
6900     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6901       MovDir[x][y] = left_dir;
6902
6903     if (MovDir[x][y] != old_move_dir)
6904       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6905   }
6906   else if (move_pattern == MV_TOWARDS_PLAYER ||
6907            move_pattern == MV_AWAY_FROM_PLAYER)
6908   {
6909     int attr_x = -1, attr_y = -1;
6910     int newx, newy;
6911     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6912
6913     if (AllPlayersGone)
6914     {
6915       attr_x = ExitX;
6916       attr_y = ExitY;
6917     }
6918     else
6919     {
6920       int i;
6921
6922       for (i = 0; i < MAX_PLAYERS; i++)
6923       {
6924         struct PlayerInfo *player = &stored_player[i];
6925         int jx = player->jx, jy = player->jy;
6926
6927         if (!player->active)
6928           continue;
6929
6930         if (attr_x == -1 ||
6931             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6932         {
6933           attr_x = jx;
6934           attr_y = jy;
6935         }
6936       }
6937     }
6938
6939     MovDir[x][y] = MV_NONE;
6940     if (attr_x < x)
6941       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6942     else if (attr_x > x)
6943       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6944     if (attr_y < y)
6945       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6946     else if (attr_y > y)
6947       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6948
6949     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6950
6951     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6952     {
6953       boolean first_horiz = RND(2);
6954       int new_move_dir = MovDir[x][y];
6955
6956       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6957       {
6958         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6959         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6960
6961         return;
6962       }
6963
6964       MovDir[x][y] =
6965         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6966       Moving2Blocked(x, y, &newx, &newy);
6967
6968       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6969         return;
6970
6971       MovDir[x][y] =
6972         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6973       Moving2Blocked(x, y, &newx, &newy);
6974
6975       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6976         return;
6977
6978       MovDir[x][y] = old_move_dir;
6979     }
6980   }
6981   else if (move_pattern == MV_WHEN_PUSHED ||
6982            move_pattern == MV_WHEN_DROPPED)
6983   {
6984     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6985       MovDir[x][y] = MV_NONE;
6986
6987     MovDelay[x][y] = 0;
6988   }
6989   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6990   {
6991     static int test_xy[7][2] =
6992     {
6993       { 0, -1 },
6994       { -1, 0 },
6995       { +1, 0 },
6996       { 0, +1 },
6997       { 0, -1 },
6998       { -1, 0 },
6999       { +1, 0 },
7000     };
7001     static int test_dir[7] =
7002     {
7003       MV_UP,
7004       MV_LEFT,
7005       MV_RIGHT,
7006       MV_DOWN,
7007       MV_UP,
7008       MV_LEFT,
7009       MV_RIGHT,
7010     };
7011     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7012     int move_preference = -1000000;     /* start with very low preference */
7013     int new_move_dir = MV_NONE;
7014     int start_test = RND(4);
7015     int i;
7016
7017     for (i = 0; i < NUM_DIRECTIONS; i++)
7018     {
7019       int move_dir = test_dir[start_test + i];
7020       int move_dir_preference;
7021
7022       xx = x + test_xy[start_test + i][0];
7023       yy = y + test_xy[start_test + i][1];
7024
7025       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7026           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7027       {
7028         new_move_dir = move_dir;
7029
7030         break;
7031       }
7032
7033       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7034         continue;
7035
7036       move_dir_preference = -1 * RunnerVisit[xx][yy];
7037       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7038         move_dir_preference = PlayerVisit[xx][yy];
7039
7040       if (move_dir_preference > move_preference)
7041       {
7042         /* prefer field that has not been visited for the longest time */
7043         move_preference = move_dir_preference;
7044         new_move_dir = move_dir;
7045       }
7046       else if (move_dir_preference == move_preference &&
7047                move_dir == old_move_dir)
7048       {
7049         /* prefer last direction when all directions are preferred equally */
7050         move_preference = move_dir_preference;
7051         new_move_dir = move_dir;
7052       }
7053     }
7054
7055     MovDir[x][y] = new_move_dir;
7056     if (old_move_dir != new_move_dir)
7057       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7058   }
7059 }
7060
7061 static void TurnRound(int x, int y)
7062 {
7063   int direction = MovDir[x][y];
7064
7065   TurnRoundExt(x, y);
7066
7067   GfxDir[x][y] = MovDir[x][y];
7068
7069   if (direction != MovDir[x][y])
7070     GfxFrame[x][y] = 0;
7071
7072   if (MovDelay[x][y])
7073     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7074
7075   ResetGfxFrame(x, y);
7076 }
7077
7078 static boolean JustBeingPushed(int x, int y)
7079 {
7080   int i;
7081
7082   for (i = 0; i < MAX_PLAYERS; i++)
7083   {
7084     struct PlayerInfo *player = &stored_player[i];
7085
7086     if (player->active && player->is_pushing && player->MovPos)
7087     {
7088       int next_jx = player->jx + (player->jx - player->last_jx);
7089       int next_jy = player->jy + (player->jy - player->last_jy);
7090
7091       if (x == next_jx && y == next_jy)
7092         return TRUE;
7093     }
7094   }
7095
7096   return FALSE;
7097 }
7098
7099 void StartMoving(int x, int y)
7100 {
7101   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7102   int element = Feld[x][y];
7103
7104   if (Stop[x][y])
7105     return;
7106
7107   if (MovDelay[x][y] == 0)
7108     GfxAction[x][y] = ACTION_DEFAULT;
7109
7110   if (CAN_FALL(element) && y < lev_fieldy - 1)
7111   {
7112     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7113         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7114       if (JustBeingPushed(x, y))
7115         return;
7116
7117     if (element == EL_QUICKSAND_FULL)
7118     {
7119       if (IS_FREE(x, y + 1))
7120       {
7121         InitMovingField(x, y, MV_DOWN);
7122         started_moving = TRUE;
7123
7124         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7125 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7126         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7127           Store[x][y] = EL_ROCK;
7128 #else
7129         Store[x][y] = EL_ROCK;
7130 #endif
7131
7132         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7133       }
7134       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7135       {
7136         if (!MovDelay[x][y])
7137         {
7138           MovDelay[x][y] = TILEY + 1;
7139
7140           ResetGfxAnimation(x, y);
7141           ResetGfxAnimation(x, y + 1);
7142         }
7143
7144         if (MovDelay[x][y])
7145         {
7146           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7147           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7148
7149           MovDelay[x][y]--;
7150           if (MovDelay[x][y])
7151             return;
7152         }
7153
7154         Feld[x][y] = EL_QUICKSAND_EMPTY;
7155         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7156         Store[x][y + 1] = Store[x][y];
7157         Store[x][y] = 0;
7158
7159         PlayLevelSoundAction(x, y, ACTION_FILLING);
7160       }
7161       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7162       {
7163         if (!MovDelay[x][y])
7164         {
7165           MovDelay[x][y] = TILEY + 1;
7166
7167           ResetGfxAnimation(x, y);
7168           ResetGfxAnimation(x, y + 1);
7169         }
7170
7171         if (MovDelay[x][y])
7172         {
7173           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7174           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7175
7176           MovDelay[x][y]--;
7177           if (MovDelay[x][y])
7178             return;
7179         }
7180
7181         Feld[x][y] = EL_QUICKSAND_EMPTY;
7182         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7183         Store[x][y + 1] = Store[x][y];
7184         Store[x][y] = 0;
7185
7186         PlayLevelSoundAction(x, y, ACTION_FILLING);
7187       }
7188     }
7189     else if (element == EL_QUICKSAND_FAST_FULL)
7190     {
7191       if (IS_FREE(x, y + 1))
7192       {
7193         InitMovingField(x, y, MV_DOWN);
7194         started_moving = TRUE;
7195
7196         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7197 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7198         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7199           Store[x][y] = EL_ROCK;
7200 #else
7201         Store[x][y] = EL_ROCK;
7202 #endif
7203
7204         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7205       }
7206       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7207       {
7208         if (!MovDelay[x][y])
7209         {
7210           MovDelay[x][y] = TILEY + 1;
7211
7212           ResetGfxAnimation(x, y);
7213           ResetGfxAnimation(x, y + 1);
7214         }
7215
7216         if (MovDelay[x][y])
7217         {
7218           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7219           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7220
7221           MovDelay[x][y]--;
7222           if (MovDelay[x][y])
7223             return;
7224         }
7225
7226         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7227         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7228         Store[x][y + 1] = Store[x][y];
7229         Store[x][y] = 0;
7230
7231         PlayLevelSoundAction(x, y, ACTION_FILLING);
7232       }
7233       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7234       {
7235         if (!MovDelay[x][y])
7236         {
7237           MovDelay[x][y] = TILEY + 1;
7238
7239           ResetGfxAnimation(x, y);
7240           ResetGfxAnimation(x, y + 1);
7241         }
7242
7243         if (MovDelay[x][y])
7244         {
7245           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7246           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7247
7248           MovDelay[x][y]--;
7249           if (MovDelay[x][y])
7250             return;
7251         }
7252
7253         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7254         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7255         Store[x][y + 1] = Store[x][y];
7256         Store[x][y] = 0;
7257
7258         PlayLevelSoundAction(x, y, ACTION_FILLING);
7259       }
7260     }
7261     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7262              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7263     {
7264       InitMovingField(x, y, MV_DOWN);
7265       started_moving = TRUE;
7266
7267       Feld[x][y] = EL_QUICKSAND_FILLING;
7268       Store[x][y] = element;
7269
7270       PlayLevelSoundAction(x, y, ACTION_FILLING);
7271     }
7272     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7273              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7274     {
7275       InitMovingField(x, y, MV_DOWN);
7276       started_moving = TRUE;
7277
7278       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7279       Store[x][y] = element;
7280
7281       PlayLevelSoundAction(x, y, ACTION_FILLING);
7282     }
7283     else if (element == EL_MAGIC_WALL_FULL)
7284     {
7285       if (IS_FREE(x, y + 1))
7286       {
7287         InitMovingField(x, y, MV_DOWN);
7288         started_moving = TRUE;
7289
7290         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7291         Store[x][y] = EL_CHANGED(Store[x][y]);
7292       }
7293       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7294       {
7295         if (!MovDelay[x][y])
7296           MovDelay[x][y] = TILEY / 4 + 1;
7297
7298         if (MovDelay[x][y])
7299         {
7300           MovDelay[x][y]--;
7301           if (MovDelay[x][y])
7302             return;
7303         }
7304
7305         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7306         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7307         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7308         Store[x][y] = 0;
7309       }
7310     }
7311     else if (element == EL_BD_MAGIC_WALL_FULL)
7312     {
7313       if (IS_FREE(x, y + 1))
7314       {
7315         InitMovingField(x, y, MV_DOWN);
7316         started_moving = TRUE;
7317
7318         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7319         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7320       }
7321       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7322       {
7323         if (!MovDelay[x][y])
7324           MovDelay[x][y] = TILEY / 4 + 1;
7325
7326         if (MovDelay[x][y])
7327         {
7328           MovDelay[x][y]--;
7329           if (MovDelay[x][y])
7330             return;
7331         }
7332
7333         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7334         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7335         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7336         Store[x][y] = 0;
7337       }
7338     }
7339     else if (element == EL_DC_MAGIC_WALL_FULL)
7340     {
7341       if (IS_FREE(x, y + 1))
7342       {
7343         InitMovingField(x, y, MV_DOWN);
7344         started_moving = TRUE;
7345
7346         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7347         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7348       }
7349       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7350       {
7351         if (!MovDelay[x][y])
7352           MovDelay[x][y] = TILEY / 4 + 1;
7353
7354         if (MovDelay[x][y])
7355         {
7356           MovDelay[x][y]--;
7357           if (MovDelay[x][y])
7358             return;
7359         }
7360
7361         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7362         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7363         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7364         Store[x][y] = 0;
7365       }
7366     }
7367     else if ((CAN_PASS_MAGIC_WALL(element) &&
7368               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7369                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7370              (CAN_PASS_DC_MAGIC_WALL(element) &&
7371               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7372
7373     {
7374       InitMovingField(x, y, MV_DOWN);
7375       started_moving = TRUE;
7376
7377       Feld[x][y] =
7378         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7379          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7380          EL_DC_MAGIC_WALL_FILLING);
7381       Store[x][y] = element;
7382     }
7383     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7384     {
7385       SplashAcid(x, y + 1);
7386
7387       InitMovingField(x, y, MV_DOWN);
7388       started_moving = TRUE;
7389
7390       Store[x][y] = EL_ACID;
7391     }
7392     else if (
7393              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7394               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7395              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7396               CAN_FALL(element) && WasJustFalling[x][y] &&
7397               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7398
7399              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7400               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7401               (Feld[x][y + 1] == EL_BLOCKED)))
7402     {
7403       /* this is needed for a special case not covered by calling "Impact()"
7404          from "ContinueMoving()": if an element moves to a tile directly below
7405          another element which was just falling on that tile (which was empty
7406          in the previous frame), the falling element above would just stop
7407          instead of smashing the element below (in previous version, the above
7408          element was just checked for "moving" instead of "falling", resulting
7409          in incorrect smashes caused by horizontal movement of the above
7410          element; also, the case of the player being the element to smash was
7411          simply not covered here... :-/ ) */
7412
7413       CheckCollision[x][y] = 0;
7414       CheckImpact[x][y] = 0;
7415
7416       Impact(x, y);
7417     }
7418     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7419     {
7420       if (MovDir[x][y] == MV_NONE)
7421       {
7422         InitMovingField(x, y, MV_DOWN);
7423         started_moving = TRUE;
7424       }
7425     }
7426     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7427     {
7428       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7429         MovDir[x][y] = MV_DOWN;
7430
7431       InitMovingField(x, y, MV_DOWN);
7432       started_moving = TRUE;
7433     }
7434     else if (element == EL_AMOEBA_DROP)
7435     {
7436       Feld[x][y] = EL_AMOEBA_GROWING;
7437       Store[x][y] = EL_AMOEBA_WET;
7438     }
7439     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7440               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7441              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7442              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7443     {
7444       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7445                                 (IS_FREE(x - 1, y + 1) ||
7446                                  Feld[x - 1][y + 1] == EL_ACID));
7447       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7448                                 (IS_FREE(x + 1, y + 1) ||
7449                                  Feld[x + 1][y + 1] == EL_ACID));
7450       boolean can_fall_any  = (can_fall_left || can_fall_right);
7451       boolean can_fall_both = (can_fall_left && can_fall_right);
7452       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7453
7454       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7455       {
7456         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7457           can_fall_right = FALSE;
7458         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7459           can_fall_left = FALSE;
7460         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7461           can_fall_right = FALSE;
7462         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7463           can_fall_left = FALSE;
7464
7465         can_fall_any  = (can_fall_left || can_fall_right);
7466         can_fall_both = FALSE;
7467       }
7468
7469       if (can_fall_both)
7470       {
7471         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7472           can_fall_right = FALSE;       /* slip down on left side */
7473         else
7474           can_fall_left = !(can_fall_right = RND(2));
7475
7476         can_fall_both = FALSE;
7477       }
7478
7479       if (can_fall_any)
7480       {
7481         /* if not determined otherwise, prefer left side for slipping down */
7482         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7483         started_moving = TRUE;
7484       }
7485     }
7486     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7487     {
7488       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7489       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7490       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7491       int belt_dir = game.belt_dir[belt_nr];
7492
7493       if ((belt_dir == MV_LEFT  && left_is_free) ||
7494           (belt_dir == MV_RIGHT && right_is_free))
7495       {
7496         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7497
7498         InitMovingField(x, y, belt_dir);
7499         started_moving = TRUE;
7500
7501         Pushed[x][y] = TRUE;
7502         Pushed[nextx][y] = TRUE;
7503
7504         GfxAction[x][y] = ACTION_DEFAULT;
7505       }
7506       else
7507       {
7508         MovDir[x][y] = 0;       /* if element was moving, stop it */
7509       }
7510     }
7511   }
7512
7513   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7514   if (CAN_MOVE(element) && !started_moving)
7515   {
7516     int move_pattern = element_info[element].move_pattern;
7517     int newx, newy;
7518
7519     Moving2Blocked(x, y, &newx, &newy);
7520
7521     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7522       return;
7523
7524     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7525         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7526     {
7527       WasJustMoving[x][y] = 0;
7528       CheckCollision[x][y] = 0;
7529
7530       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7531
7532       if (Feld[x][y] != element)        /* element has changed */
7533         return;
7534     }
7535
7536     if (!MovDelay[x][y])        /* start new movement phase */
7537     {
7538       /* all objects that can change their move direction after each step
7539          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7540
7541       if (element != EL_YAMYAM &&
7542           element != EL_DARK_YAMYAM &&
7543           element != EL_PACMAN &&
7544           !(move_pattern & MV_ANY_DIRECTION) &&
7545           move_pattern != MV_TURNING_LEFT &&
7546           move_pattern != MV_TURNING_RIGHT &&
7547           move_pattern != MV_TURNING_LEFT_RIGHT &&
7548           move_pattern != MV_TURNING_RIGHT_LEFT &&
7549           move_pattern != MV_TURNING_RANDOM)
7550       {
7551         TurnRound(x, y);
7552
7553         if (MovDelay[x][y] && (element == EL_BUG ||
7554                                element == EL_SPACESHIP ||
7555                                element == EL_SP_SNIKSNAK ||
7556                                element == EL_SP_ELECTRON ||
7557                                element == EL_MOLE))
7558           TEST_DrawLevelField(x, y);
7559       }
7560     }
7561
7562     if (MovDelay[x][y])         /* wait some time before next movement */
7563     {
7564       MovDelay[x][y]--;
7565
7566       if (element == EL_ROBOT ||
7567           element == EL_YAMYAM ||
7568           element == EL_DARK_YAMYAM)
7569       {
7570         DrawLevelElementAnimationIfNeeded(x, y, element);
7571         PlayLevelSoundAction(x, y, ACTION_WAITING);
7572       }
7573       else if (element == EL_SP_ELECTRON)
7574         DrawLevelElementAnimationIfNeeded(x, y, element);
7575       else if (element == EL_DRAGON)
7576       {
7577         int i;
7578         int dir = MovDir[x][y];
7579         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7580         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7581         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7582                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7583                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7584                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7585         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7586
7587         GfxAction[x][y] = ACTION_ATTACKING;
7588
7589         if (IS_PLAYER(x, y))
7590           DrawPlayerField(x, y);
7591         else
7592           TEST_DrawLevelField(x, y);
7593
7594         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7595
7596         for (i = 1; i <= 3; i++)
7597         {
7598           int xx = x + i * dx;
7599           int yy = y + i * dy;
7600           int sx = SCREENX(xx);
7601           int sy = SCREENY(yy);
7602           int flame_graphic = graphic + (i - 1);
7603
7604           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7605             break;
7606
7607           if (MovDelay[x][y])
7608           {
7609             int flamed = MovingOrBlocked2Element(xx, yy);
7610
7611             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7612               Bang(xx, yy);
7613             else
7614               RemoveMovingField(xx, yy);
7615
7616             ChangeDelay[xx][yy] = 0;
7617
7618             Feld[xx][yy] = EL_FLAMES;
7619
7620             if (IN_SCR_FIELD(sx, sy))
7621             {
7622               TEST_DrawLevelFieldCrumbled(xx, yy);
7623               DrawGraphic(sx, sy, flame_graphic, frame);
7624             }
7625           }
7626           else
7627           {
7628             if (Feld[xx][yy] == EL_FLAMES)
7629               Feld[xx][yy] = EL_EMPTY;
7630             TEST_DrawLevelField(xx, yy);
7631           }
7632         }
7633       }
7634
7635       if (MovDelay[x][y])       /* element still has to wait some time */
7636       {
7637         PlayLevelSoundAction(x, y, ACTION_WAITING);
7638
7639         return;
7640       }
7641     }
7642
7643     /* now make next step */
7644
7645     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7646
7647     if (DONT_COLLIDE_WITH(element) &&
7648         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7649         !PLAYER_ENEMY_PROTECTED(newx, newy))
7650     {
7651       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7652
7653       return;
7654     }
7655
7656     else if (CAN_MOVE_INTO_ACID(element) &&
7657              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7658              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7659              (MovDir[x][y] == MV_DOWN ||
7660               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7661     {
7662       SplashAcid(newx, newy);
7663       Store[x][y] = EL_ACID;
7664     }
7665     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7666     {
7667       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7668           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7669           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7670           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7671       {
7672         RemoveField(x, y);
7673         TEST_DrawLevelField(x, y);
7674
7675         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7676         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7677           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7678
7679         local_player->friends_still_needed--;
7680         if (!local_player->friends_still_needed &&
7681             !local_player->GameOver && AllPlayersGone)
7682           PlayerWins(local_player);
7683
7684         return;
7685       }
7686       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7687       {
7688         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7689           TEST_DrawLevelField(newx, newy);
7690         else
7691           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7692       }
7693       else if (!IS_FREE(newx, newy))
7694       {
7695         GfxAction[x][y] = ACTION_WAITING;
7696
7697         if (IS_PLAYER(x, y))
7698           DrawPlayerField(x, y);
7699         else
7700           TEST_DrawLevelField(x, y);
7701
7702         return;
7703       }
7704     }
7705     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7706     {
7707       if (IS_FOOD_PIG(Feld[newx][newy]))
7708       {
7709         if (IS_MOVING(newx, newy))
7710           RemoveMovingField(newx, newy);
7711         else
7712         {
7713           Feld[newx][newy] = EL_EMPTY;
7714           TEST_DrawLevelField(newx, newy);
7715         }
7716
7717         PlayLevelSound(x, y, SND_PIG_DIGGING);
7718       }
7719       else if (!IS_FREE(newx, newy))
7720       {
7721         if (IS_PLAYER(x, y))
7722           DrawPlayerField(x, y);
7723         else
7724           TEST_DrawLevelField(x, y);
7725
7726         return;
7727       }
7728     }
7729     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7730     {
7731       if (Store[x][y] != EL_EMPTY)
7732       {
7733         boolean can_clone = FALSE;
7734         int xx, yy;
7735
7736         /* check if element to clone is still there */
7737         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7738         {
7739           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7740           {
7741             can_clone = TRUE;
7742
7743             break;
7744           }
7745         }
7746
7747         /* cannot clone or target field not free anymore -- do not clone */
7748         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7749           Store[x][y] = EL_EMPTY;
7750       }
7751
7752       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7753       {
7754         if (IS_MV_DIAGONAL(MovDir[x][y]))
7755         {
7756           int diagonal_move_dir = MovDir[x][y];
7757           int stored = Store[x][y];
7758           int change_delay = 8;
7759           int graphic;
7760
7761           /* android is moving diagonally */
7762
7763           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7764
7765           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7766           GfxElement[x][y] = EL_EMC_ANDROID;
7767           GfxAction[x][y] = ACTION_SHRINKING;
7768           GfxDir[x][y] = diagonal_move_dir;
7769           ChangeDelay[x][y] = change_delay;
7770
7771           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7772                                    GfxDir[x][y]);
7773
7774           DrawLevelGraphicAnimation(x, y, graphic);
7775           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7776
7777           if (Feld[newx][newy] == EL_ACID)
7778           {
7779             SplashAcid(newx, newy);
7780
7781             return;
7782           }
7783
7784           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7785
7786           Store[newx][newy] = EL_EMC_ANDROID;
7787           GfxElement[newx][newy] = EL_EMC_ANDROID;
7788           GfxAction[newx][newy] = ACTION_GROWING;
7789           GfxDir[newx][newy] = diagonal_move_dir;
7790           ChangeDelay[newx][newy] = change_delay;
7791
7792           graphic = el_act_dir2img(GfxElement[newx][newy],
7793                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7794
7795           DrawLevelGraphicAnimation(newx, newy, graphic);
7796           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7797
7798           return;
7799         }
7800         else
7801         {
7802           Feld[newx][newy] = EL_EMPTY;
7803           TEST_DrawLevelField(newx, newy);
7804
7805           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7806         }
7807       }
7808       else if (!IS_FREE(newx, newy))
7809       {
7810         return;
7811       }
7812     }
7813     else if (IS_CUSTOM_ELEMENT(element) &&
7814              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7815     {
7816       if (!DigFieldByCE(newx, newy, element))
7817         return;
7818
7819       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7820       {
7821         RunnerVisit[x][y] = FrameCounter;
7822         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7823       }
7824     }
7825     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7826     {
7827       if (!IS_FREE(newx, newy))
7828       {
7829         if (IS_PLAYER(x, y))
7830           DrawPlayerField(x, y);
7831         else
7832           TEST_DrawLevelField(x, y);
7833
7834         return;
7835       }
7836       else
7837       {
7838         boolean wanna_flame = !RND(10);
7839         int dx = newx - x, dy = newy - y;
7840         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7841         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7842         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7843                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7844         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7845                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7846
7847         if ((wanna_flame ||
7848              IS_CLASSIC_ENEMY(element1) ||
7849              IS_CLASSIC_ENEMY(element2)) &&
7850             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7851             element1 != EL_FLAMES && element2 != EL_FLAMES)
7852         {
7853           ResetGfxAnimation(x, y);
7854           GfxAction[x][y] = ACTION_ATTACKING;
7855
7856           if (IS_PLAYER(x, y))
7857             DrawPlayerField(x, y);
7858           else
7859             TEST_DrawLevelField(x, y);
7860
7861           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7862
7863           MovDelay[x][y] = 50;
7864
7865           Feld[newx][newy] = EL_FLAMES;
7866           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7867             Feld[newx1][newy1] = EL_FLAMES;
7868           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7869             Feld[newx2][newy2] = EL_FLAMES;
7870
7871           return;
7872         }
7873       }
7874     }
7875     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7876              Feld[newx][newy] == EL_DIAMOND)
7877     {
7878       if (IS_MOVING(newx, newy))
7879         RemoveMovingField(newx, newy);
7880       else
7881       {
7882         Feld[newx][newy] = EL_EMPTY;
7883         TEST_DrawLevelField(newx, newy);
7884       }
7885
7886       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7887     }
7888     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7889              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7890     {
7891       if (AmoebaNr[newx][newy])
7892       {
7893         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7894         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7895             Feld[newx][newy] == EL_BD_AMOEBA)
7896           AmoebaCnt[AmoebaNr[newx][newy]]--;
7897       }
7898
7899       if (IS_MOVING(newx, newy))
7900       {
7901         RemoveMovingField(newx, newy);
7902       }
7903       else
7904       {
7905         Feld[newx][newy] = EL_EMPTY;
7906         TEST_DrawLevelField(newx, newy);
7907       }
7908
7909       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7910     }
7911     else if ((element == EL_PACMAN || element == EL_MOLE)
7912              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7913     {
7914       if (AmoebaNr[newx][newy])
7915       {
7916         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7917         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7918             Feld[newx][newy] == EL_BD_AMOEBA)
7919           AmoebaCnt[AmoebaNr[newx][newy]]--;
7920       }
7921
7922       if (element == EL_MOLE)
7923       {
7924         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7925         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7926
7927         ResetGfxAnimation(x, y);
7928         GfxAction[x][y] = ACTION_DIGGING;
7929         TEST_DrawLevelField(x, y);
7930
7931         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7932
7933         return;                         /* wait for shrinking amoeba */
7934       }
7935       else      /* element == EL_PACMAN */
7936       {
7937         Feld[newx][newy] = EL_EMPTY;
7938         TEST_DrawLevelField(newx, newy);
7939         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7940       }
7941     }
7942     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7943              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7944               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7945     {
7946       /* wait for shrinking amoeba to completely disappear */
7947       return;
7948     }
7949     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7950     {
7951       /* object was running against a wall */
7952
7953       TurnRound(x, y);
7954
7955       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7956         DrawLevelElementAnimation(x, y, element);
7957
7958       if (DONT_TOUCH(element))
7959         TestIfBadThingTouchesPlayer(x, y);
7960
7961       return;
7962     }
7963
7964     InitMovingField(x, y, MovDir[x][y]);
7965
7966     PlayLevelSoundAction(x, y, ACTION_MOVING);
7967   }
7968
7969   if (MovDir[x][y])
7970     ContinueMoving(x, y);
7971 }
7972
7973 void ContinueMoving(int x, int y)
7974 {
7975   int element = Feld[x][y];
7976   struct ElementInfo *ei = &element_info[element];
7977   int direction = MovDir[x][y];
7978   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7979   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7980   int newx = x + dx, newy = y + dy;
7981   int stored = Store[x][y];
7982   int stored_new = Store[newx][newy];
7983   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7984   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7985   boolean last_line = (newy == lev_fieldy - 1);
7986
7987   MovPos[x][y] += getElementMoveStepsize(x, y);
7988
7989   if (pushed_by_player) /* special case: moving object pushed by player */
7990     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7991
7992   if (ABS(MovPos[x][y]) < TILEX)
7993   {
7994     TEST_DrawLevelField(x, y);
7995
7996     return;     /* element is still moving */
7997   }
7998
7999   /* element reached destination field */
8000
8001   Feld[x][y] = EL_EMPTY;
8002   Feld[newx][newy] = element;
8003   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8004
8005   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8006   {
8007     element = Feld[newx][newy] = EL_ACID;
8008   }
8009   else if (element == EL_MOLE)
8010   {
8011     Feld[x][y] = EL_SAND;
8012
8013     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8014   }
8015   else if (element == EL_QUICKSAND_FILLING)
8016   {
8017     element = Feld[newx][newy] = get_next_element(element);
8018     Store[newx][newy] = Store[x][y];
8019   }
8020   else if (element == EL_QUICKSAND_EMPTYING)
8021   {
8022     Feld[x][y] = get_next_element(element);
8023     element = Feld[newx][newy] = Store[x][y];
8024   }
8025   else if (element == EL_QUICKSAND_FAST_FILLING)
8026   {
8027     element = Feld[newx][newy] = get_next_element(element);
8028     Store[newx][newy] = Store[x][y];
8029   }
8030   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8031   {
8032     Feld[x][y] = get_next_element(element);
8033     element = Feld[newx][newy] = Store[x][y];
8034   }
8035   else if (element == EL_MAGIC_WALL_FILLING)
8036   {
8037     element = Feld[newx][newy] = get_next_element(element);
8038     if (!game.magic_wall_active)
8039       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8040     Store[newx][newy] = Store[x][y];
8041   }
8042   else if (element == EL_MAGIC_WALL_EMPTYING)
8043   {
8044     Feld[x][y] = get_next_element(element);
8045     if (!game.magic_wall_active)
8046       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8047     element = Feld[newx][newy] = Store[x][y];
8048
8049     InitField(newx, newy, FALSE);
8050   }
8051   else if (element == EL_BD_MAGIC_WALL_FILLING)
8052   {
8053     element = Feld[newx][newy] = get_next_element(element);
8054     if (!game.magic_wall_active)
8055       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8056     Store[newx][newy] = Store[x][y];
8057   }
8058   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8059   {
8060     Feld[x][y] = get_next_element(element);
8061     if (!game.magic_wall_active)
8062       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8063     element = Feld[newx][newy] = Store[x][y];
8064
8065     InitField(newx, newy, FALSE);
8066   }
8067   else if (element == EL_DC_MAGIC_WALL_FILLING)
8068   {
8069     element = Feld[newx][newy] = get_next_element(element);
8070     if (!game.magic_wall_active)
8071       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8072     Store[newx][newy] = Store[x][y];
8073   }
8074   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8075   {
8076     Feld[x][y] = get_next_element(element);
8077     if (!game.magic_wall_active)
8078       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8079     element = Feld[newx][newy] = Store[x][y];
8080
8081     InitField(newx, newy, FALSE);
8082   }
8083   else if (element == EL_AMOEBA_DROPPING)
8084   {
8085     Feld[x][y] = get_next_element(element);
8086     element = Feld[newx][newy] = Store[x][y];
8087   }
8088   else if (element == EL_SOKOBAN_OBJECT)
8089   {
8090     if (Back[x][y])
8091       Feld[x][y] = Back[x][y];
8092
8093     if (Back[newx][newy])
8094       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8095
8096     Back[x][y] = Back[newx][newy] = 0;
8097   }
8098
8099   Store[x][y] = EL_EMPTY;
8100   MovPos[x][y] = 0;
8101   MovDir[x][y] = 0;
8102   MovDelay[x][y] = 0;
8103
8104   MovDelay[newx][newy] = 0;
8105
8106   if (CAN_CHANGE_OR_HAS_ACTION(element))
8107   {
8108     /* copy element change control values to new field */
8109     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8110     ChangePage[newx][newy]  = ChangePage[x][y];
8111     ChangeCount[newx][newy] = ChangeCount[x][y];
8112     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8113   }
8114
8115   CustomValue[newx][newy] = CustomValue[x][y];
8116
8117   ChangeDelay[x][y] = 0;
8118   ChangePage[x][y] = -1;
8119   ChangeCount[x][y] = 0;
8120   ChangeEvent[x][y] = -1;
8121
8122   CustomValue[x][y] = 0;
8123
8124   /* copy animation control values to new field */
8125   GfxFrame[newx][newy]  = GfxFrame[x][y];
8126   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8127   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8128   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8129
8130   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8131
8132   /* some elements can leave other elements behind after moving */
8133   if (ei->move_leave_element != EL_EMPTY &&
8134       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8135       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8136   {
8137     int move_leave_element = ei->move_leave_element;
8138
8139     /* this makes it possible to leave the removed element again */
8140     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8141       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8142
8143     Feld[x][y] = move_leave_element;
8144
8145     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8146       MovDir[x][y] = direction;
8147
8148     InitField(x, y, FALSE);
8149
8150     if (GFX_CRUMBLED(Feld[x][y]))
8151       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8152
8153     if (ELEM_IS_PLAYER(move_leave_element))
8154       RelocatePlayer(x, y, move_leave_element);
8155   }
8156
8157   /* do this after checking for left-behind element */
8158   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8159
8160   if (!CAN_MOVE(element) ||
8161       (CAN_FALL(element) && direction == MV_DOWN &&
8162        (element == EL_SPRING ||
8163         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8164         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8165     GfxDir[x][y] = MovDir[newx][newy] = 0;
8166
8167   TEST_DrawLevelField(x, y);
8168   TEST_DrawLevelField(newx, newy);
8169
8170   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8171
8172   /* prevent pushed element from moving on in pushed direction */
8173   if (pushed_by_player && CAN_MOVE(element) &&
8174       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8175       !(element_info[element].move_pattern & direction))
8176     TurnRound(newx, newy);
8177
8178   /* prevent elements on conveyor belt from moving on in last direction */
8179   if (pushed_by_conveyor && CAN_FALL(element) &&
8180       direction & MV_HORIZONTAL)
8181     MovDir[newx][newy] = 0;
8182
8183   if (!pushed_by_player)
8184   {
8185     int nextx = newx + dx, nexty = newy + dy;
8186     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8187
8188     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8189
8190     if (CAN_FALL(element) && direction == MV_DOWN)
8191       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8192
8193     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8194       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8195
8196     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8197       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8198   }
8199
8200   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8201   {
8202     TestIfBadThingTouchesPlayer(newx, newy);
8203     TestIfBadThingTouchesFriend(newx, newy);
8204
8205     if (!IS_CUSTOM_ELEMENT(element))
8206       TestIfBadThingTouchesOtherBadThing(newx, newy);
8207   }
8208   else if (element == EL_PENGUIN)
8209     TestIfFriendTouchesBadThing(newx, newy);
8210
8211   if (DONT_GET_HIT_BY(element))
8212   {
8213     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8214   }
8215
8216   /* give the player one last chance (one more frame) to move away */
8217   if (CAN_FALL(element) && direction == MV_DOWN &&
8218       (last_line || (!IS_FREE(x, newy + 1) &&
8219                      (!IS_PLAYER(x, newy + 1) ||
8220                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8221     Impact(x, newy);
8222
8223   if (pushed_by_player && !game.use_change_when_pushing_bug)
8224   {
8225     int push_side = MV_DIR_OPPOSITE(direction);
8226     struct PlayerInfo *player = PLAYERINFO(x, y);
8227
8228     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8229                                player->index_bit, push_side);
8230     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8231                                         player->index_bit, push_side);
8232   }
8233
8234   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8235     MovDelay[newx][newy] = 1;
8236
8237   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8238
8239   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8240   TestIfElementHitsCustomElement(newx, newy, direction);
8241   TestIfPlayerTouchesCustomElement(newx, newy);
8242   TestIfElementTouchesCustomElement(newx, newy);
8243
8244   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8245       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8246     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8247                              MV_DIR_OPPOSITE(direction));
8248 }
8249
8250 int AmoebeNachbarNr(int ax, int ay)
8251 {
8252   int i;
8253   int element = Feld[ax][ay];
8254   int group_nr = 0;
8255   static int xy[4][2] =
8256   {
8257     { 0, -1 },
8258     { -1, 0 },
8259     { +1, 0 },
8260     { 0, +1 }
8261   };
8262
8263   for (i = 0; i < NUM_DIRECTIONS; i++)
8264   {
8265     int x = ax + xy[i][0];
8266     int y = ay + xy[i][1];
8267
8268     if (!IN_LEV_FIELD(x, y))
8269       continue;
8270
8271     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8272       group_nr = AmoebaNr[x][y];
8273   }
8274
8275   return group_nr;
8276 }
8277
8278 void AmoebenVereinigen(int ax, int ay)
8279 {
8280   int i, x, y, xx, yy;
8281   int new_group_nr = AmoebaNr[ax][ay];
8282   static int xy[4][2] =
8283   {
8284     { 0, -1 },
8285     { -1, 0 },
8286     { +1, 0 },
8287     { 0, +1 }
8288   };
8289
8290   if (new_group_nr == 0)
8291     return;
8292
8293   for (i = 0; i < NUM_DIRECTIONS; i++)
8294   {
8295     x = ax + xy[i][0];
8296     y = ay + xy[i][1];
8297
8298     if (!IN_LEV_FIELD(x, y))
8299       continue;
8300
8301     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8302          Feld[x][y] == EL_BD_AMOEBA ||
8303          Feld[x][y] == EL_AMOEBA_DEAD) &&
8304         AmoebaNr[x][y] != new_group_nr)
8305     {
8306       int old_group_nr = AmoebaNr[x][y];
8307
8308       if (old_group_nr == 0)
8309         return;
8310
8311       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8312       AmoebaCnt[old_group_nr] = 0;
8313       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8314       AmoebaCnt2[old_group_nr] = 0;
8315
8316       SCAN_PLAYFIELD(xx, yy)
8317       {
8318         if (AmoebaNr[xx][yy] == old_group_nr)
8319           AmoebaNr[xx][yy] = new_group_nr;
8320       }
8321     }
8322   }
8323 }
8324
8325 void AmoebeUmwandeln(int ax, int ay)
8326 {
8327   int i, x, y;
8328
8329   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8330   {
8331     int group_nr = AmoebaNr[ax][ay];
8332
8333 #ifdef DEBUG
8334     if (group_nr == 0)
8335     {
8336       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8337       printf("AmoebeUmwandeln(): This should never happen!\n");
8338       return;
8339     }
8340 #endif
8341
8342     SCAN_PLAYFIELD(x, y)
8343     {
8344       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8345       {
8346         AmoebaNr[x][y] = 0;
8347         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8348       }
8349     }
8350
8351     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8352                             SND_AMOEBA_TURNING_TO_GEM :
8353                             SND_AMOEBA_TURNING_TO_ROCK));
8354     Bang(ax, ay);
8355   }
8356   else
8357   {
8358     static int xy[4][2] =
8359     {
8360       { 0, -1 },
8361       { -1, 0 },
8362       { +1, 0 },
8363       { 0, +1 }
8364     };
8365
8366     for (i = 0; i < NUM_DIRECTIONS; i++)
8367     {
8368       x = ax + xy[i][0];
8369       y = ay + xy[i][1];
8370
8371       if (!IN_LEV_FIELD(x, y))
8372         continue;
8373
8374       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8375       {
8376         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8377                               SND_AMOEBA_TURNING_TO_GEM :
8378                               SND_AMOEBA_TURNING_TO_ROCK));
8379         Bang(x, y);
8380       }
8381     }
8382   }
8383 }
8384
8385 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8386 {
8387   int x, y;
8388   int group_nr = AmoebaNr[ax][ay];
8389   boolean done = FALSE;
8390
8391 #ifdef DEBUG
8392   if (group_nr == 0)
8393   {
8394     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8395     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8396     return;
8397   }
8398 #endif
8399
8400   SCAN_PLAYFIELD(x, y)
8401   {
8402     if (AmoebaNr[x][y] == group_nr &&
8403         (Feld[x][y] == EL_AMOEBA_DEAD ||
8404          Feld[x][y] == EL_BD_AMOEBA ||
8405          Feld[x][y] == EL_AMOEBA_GROWING))
8406     {
8407       AmoebaNr[x][y] = 0;
8408       Feld[x][y] = new_element;
8409       InitField(x, y, FALSE);
8410       TEST_DrawLevelField(x, y);
8411       done = TRUE;
8412     }
8413   }
8414
8415   if (done)
8416     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8417                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8418                             SND_BD_AMOEBA_TURNING_TO_GEM));
8419 }
8420
8421 void AmoebeWaechst(int x, int y)
8422 {
8423   static unsigned int sound_delay = 0;
8424   static unsigned int sound_delay_value = 0;
8425
8426   if (!MovDelay[x][y])          /* start new growing cycle */
8427   {
8428     MovDelay[x][y] = 7;
8429
8430     if (DelayReached(&sound_delay, sound_delay_value))
8431     {
8432       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8433       sound_delay_value = 30;
8434     }
8435   }
8436
8437   if (MovDelay[x][y])           /* wait some time before growing bigger */
8438   {
8439     MovDelay[x][y]--;
8440     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8441     {
8442       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8443                                            6 - MovDelay[x][y]);
8444
8445       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8446     }
8447
8448     if (!MovDelay[x][y])
8449     {
8450       Feld[x][y] = Store[x][y];
8451       Store[x][y] = 0;
8452       TEST_DrawLevelField(x, y);
8453     }
8454   }
8455 }
8456
8457 void AmoebaDisappearing(int x, int y)
8458 {
8459   static unsigned int sound_delay = 0;
8460   static unsigned int sound_delay_value = 0;
8461
8462   if (!MovDelay[x][y])          /* start new shrinking cycle */
8463   {
8464     MovDelay[x][y] = 7;
8465
8466     if (DelayReached(&sound_delay, sound_delay_value))
8467       sound_delay_value = 30;
8468   }
8469
8470   if (MovDelay[x][y])           /* wait some time before shrinking */
8471   {
8472     MovDelay[x][y]--;
8473     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8474     {
8475       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8476                                            6 - MovDelay[x][y]);
8477
8478       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8479     }
8480
8481     if (!MovDelay[x][y])
8482     {
8483       Feld[x][y] = EL_EMPTY;
8484       TEST_DrawLevelField(x, y);
8485
8486       /* don't let mole enter this field in this cycle;
8487          (give priority to objects falling to this field from above) */
8488       Stop[x][y] = TRUE;
8489     }
8490   }
8491 }
8492
8493 void AmoebeAbleger(int ax, int ay)
8494 {
8495   int i;
8496   int element = Feld[ax][ay];
8497   int graphic = el2img(element);
8498   int newax = ax, neway = ay;
8499   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8500   static int xy[4][2] =
8501   {
8502     { 0, -1 },
8503     { -1, 0 },
8504     { +1, 0 },
8505     { 0, +1 }
8506   };
8507
8508   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8509   {
8510     Feld[ax][ay] = EL_AMOEBA_DEAD;
8511     TEST_DrawLevelField(ax, ay);
8512     return;
8513   }
8514
8515   if (IS_ANIMATED(graphic))
8516     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8517
8518   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8519     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8520
8521   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8522   {
8523     MovDelay[ax][ay]--;
8524     if (MovDelay[ax][ay])
8525       return;
8526   }
8527
8528   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8529   {
8530     int start = RND(4);
8531     int x = ax + xy[start][0];
8532     int y = ay + xy[start][1];
8533
8534     if (!IN_LEV_FIELD(x, y))
8535       return;
8536
8537     if (IS_FREE(x, y) ||
8538         CAN_GROW_INTO(Feld[x][y]) ||
8539         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8540         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8541     {
8542       newax = x;
8543       neway = y;
8544     }
8545
8546     if (newax == ax && neway == ay)
8547       return;
8548   }
8549   else                          /* normal or "filled" (BD style) amoeba */
8550   {
8551     int start = RND(4);
8552     boolean waiting_for_player = FALSE;
8553
8554     for (i = 0; i < NUM_DIRECTIONS; i++)
8555     {
8556       int j = (start + i) % 4;
8557       int x = ax + xy[j][0];
8558       int y = ay + xy[j][1];
8559
8560       if (!IN_LEV_FIELD(x, y))
8561         continue;
8562
8563       if (IS_FREE(x, y) ||
8564           CAN_GROW_INTO(Feld[x][y]) ||
8565           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8566           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8567       {
8568         newax = x;
8569         neway = y;
8570         break;
8571       }
8572       else if (IS_PLAYER(x, y))
8573         waiting_for_player = TRUE;
8574     }
8575
8576     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8577     {
8578       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8579       {
8580         Feld[ax][ay] = EL_AMOEBA_DEAD;
8581         TEST_DrawLevelField(ax, ay);
8582         AmoebaCnt[AmoebaNr[ax][ay]]--;
8583
8584         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8585         {
8586           if (element == EL_AMOEBA_FULL)
8587             AmoebeUmwandeln(ax, ay);
8588           else if (element == EL_BD_AMOEBA)
8589             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8590         }
8591       }
8592       return;
8593     }
8594     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8595     {
8596       /* amoeba gets larger by growing in some direction */
8597
8598       int new_group_nr = AmoebaNr[ax][ay];
8599
8600 #ifdef DEBUG
8601   if (new_group_nr == 0)
8602   {
8603     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8604     printf("AmoebeAbleger(): This should never happen!\n");
8605     return;
8606   }
8607 #endif
8608
8609       AmoebaNr[newax][neway] = new_group_nr;
8610       AmoebaCnt[new_group_nr]++;
8611       AmoebaCnt2[new_group_nr]++;
8612
8613       /* if amoeba touches other amoeba(s) after growing, unify them */
8614       AmoebenVereinigen(newax, neway);
8615
8616       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8617       {
8618         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8619         return;
8620       }
8621     }
8622   }
8623
8624   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8625       (neway == lev_fieldy - 1 && newax != ax))
8626   {
8627     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8628     Store[newax][neway] = element;
8629   }
8630   else if (neway == ay || element == EL_EMC_DRIPPER)
8631   {
8632     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8633
8634     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8635   }
8636   else
8637   {
8638     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8639     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8640     Store[ax][ay] = EL_AMOEBA_DROP;
8641     ContinueMoving(ax, ay);
8642     return;
8643   }
8644
8645   TEST_DrawLevelField(newax, neway);
8646 }
8647
8648 void Life(int ax, int ay)
8649 {
8650   int x1, y1, x2, y2;
8651   int life_time = 40;
8652   int element = Feld[ax][ay];
8653   int graphic = el2img(element);
8654   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8655                          level.biomaze);
8656   boolean changed = FALSE;
8657
8658   if (IS_ANIMATED(graphic))
8659     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8660
8661   if (Stop[ax][ay])
8662     return;
8663
8664   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8665     MovDelay[ax][ay] = life_time;
8666
8667   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8668   {
8669     MovDelay[ax][ay]--;
8670     if (MovDelay[ax][ay])
8671       return;
8672   }
8673
8674   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8675   {
8676     int xx = ax+x1, yy = ay+y1;
8677     int nachbarn = 0;
8678
8679     if (!IN_LEV_FIELD(xx, yy))
8680       continue;
8681
8682     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8683     {
8684       int x = xx+x2, y = yy+y2;
8685
8686       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8687         continue;
8688
8689       if (((Feld[x][y] == element ||
8690             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8691            !Stop[x][y]) ||
8692           (IS_FREE(x, y) && Stop[x][y]))
8693         nachbarn++;
8694     }
8695
8696     if (xx == ax && yy == ay)           /* field in the middle */
8697     {
8698       if (nachbarn < life_parameter[0] ||
8699           nachbarn > life_parameter[1])
8700       {
8701         Feld[xx][yy] = EL_EMPTY;
8702         if (!Stop[xx][yy])
8703           TEST_DrawLevelField(xx, yy);
8704         Stop[xx][yy] = TRUE;
8705         changed = TRUE;
8706       }
8707     }
8708     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8709     {                                   /* free border field */
8710       if (nachbarn >= life_parameter[2] &&
8711           nachbarn <= life_parameter[3])
8712       {
8713         Feld[xx][yy] = element;
8714         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8715         if (!Stop[xx][yy])
8716           TEST_DrawLevelField(xx, yy);
8717         Stop[xx][yy] = TRUE;
8718         changed = TRUE;
8719       }
8720     }
8721   }
8722
8723   if (changed)
8724     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8725                    SND_GAME_OF_LIFE_GROWING);
8726 }
8727
8728 static void InitRobotWheel(int x, int y)
8729 {
8730   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8731 }
8732
8733 static void RunRobotWheel(int x, int y)
8734 {
8735   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8736 }
8737
8738 static void StopRobotWheel(int x, int y)
8739 {
8740   if (ZX == x && ZY == y)
8741   {
8742     ZX = ZY = -1;
8743
8744     game.robot_wheel_active = FALSE;
8745   }
8746 }
8747
8748 static void InitTimegateWheel(int x, int y)
8749 {
8750   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8751 }
8752
8753 static void RunTimegateWheel(int x, int y)
8754 {
8755   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8756 }
8757
8758 static void InitMagicBallDelay(int x, int y)
8759 {
8760   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8761 }
8762
8763 static void ActivateMagicBall(int bx, int by)
8764 {
8765   int x, y;
8766
8767   if (level.ball_random)
8768   {
8769     int pos_border = RND(8);    /* select one of the eight border elements */
8770     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8771     int xx = pos_content % 3;
8772     int yy = pos_content / 3;
8773
8774     x = bx - 1 + xx;
8775     y = by - 1 + yy;
8776
8777     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8778       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8779   }
8780   else
8781   {
8782     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8783     {
8784       int xx = x - bx + 1;
8785       int yy = y - by + 1;
8786
8787       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8788         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8789     }
8790   }
8791
8792   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8793 }
8794
8795 void CheckExit(int x, int y)
8796 {
8797   if (local_player->gems_still_needed > 0 ||
8798       local_player->sokobanfields_still_needed > 0 ||
8799       local_player->lights_still_needed > 0)
8800   {
8801     int element = Feld[x][y];
8802     int graphic = el2img(element);
8803
8804     if (IS_ANIMATED(graphic))
8805       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8806
8807     return;
8808   }
8809
8810   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8811     return;
8812
8813   Feld[x][y] = EL_EXIT_OPENING;
8814
8815   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8816 }
8817
8818 void CheckExitEM(int x, int y)
8819 {
8820   if (local_player->gems_still_needed > 0 ||
8821       local_player->sokobanfields_still_needed > 0 ||
8822       local_player->lights_still_needed > 0)
8823   {
8824     int element = Feld[x][y];
8825     int graphic = el2img(element);
8826
8827     if (IS_ANIMATED(graphic))
8828       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8829
8830     return;
8831   }
8832
8833   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8834     return;
8835
8836   Feld[x][y] = EL_EM_EXIT_OPENING;
8837
8838   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8839 }
8840
8841 void CheckExitSteel(int x, int y)
8842 {
8843   if (local_player->gems_still_needed > 0 ||
8844       local_player->sokobanfields_still_needed > 0 ||
8845       local_player->lights_still_needed > 0)
8846   {
8847     int element = Feld[x][y];
8848     int graphic = el2img(element);
8849
8850     if (IS_ANIMATED(graphic))
8851       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8852
8853     return;
8854   }
8855
8856   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8857     return;
8858
8859   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8860
8861   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8862 }
8863
8864 void CheckExitSteelEM(int x, int y)
8865 {
8866   if (local_player->gems_still_needed > 0 ||
8867       local_player->sokobanfields_still_needed > 0 ||
8868       local_player->lights_still_needed > 0)
8869   {
8870     int element = Feld[x][y];
8871     int graphic = el2img(element);
8872
8873     if (IS_ANIMATED(graphic))
8874       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8875
8876     return;
8877   }
8878
8879   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8880     return;
8881
8882   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8883
8884   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8885 }
8886
8887 void CheckExitSP(int x, int y)
8888 {
8889   if (local_player->gems_still_needed > 0)
8890   {
8891     int element = Feld[x][y];
8892     int graphic = el2img(element);
8893
8894     if (IS_ANIMATED(graphic))
8895       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8896
8897     return;
8898   }
8899
8900   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8901     return;
8902
8903   Feld[x][y] = EL_SP_EXIT_OPENING;
8904
8905   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8906 }
8907
8908 static void CloseAllOpenTimegates()
8909 {
8910   int x, y;
8911
8912   SCAN_PLAYFIELD(x, y)
8913   {
8914     int element = Feld[x][y];
8915
8916     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8917     {
8918       Feld[x][y] = EL_TIMEGATE_CLOSING;
8919
8920       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8921     }
8922   }
8923 }
8924
8925 void DrawTwinkleOnField(int x, int y)
8926 {
8927   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8928     return;
8929
8930   if (Feld[x][y] == EL_BD_DIAMOND)
8931     return;
8932
8933   if (MovDelay[x][y] == 0)      /* next animation frame */
8934     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8935
8936   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8937   {
8938     MovDelay[x][y]--;
8939
8940     DrawLevelElementAnimation(x, y, Feld[x][y]);
8941
8942     if (MovDelay[x][y] != 0)
8943     {
8944       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8945                                            10 - MovDelay[x][y]);
8946
8947       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8948     }
8949   }
8950 }
8951
8952 void MauerWaechst(int x, int y)
8953 {
8954   int delay = 6;
8955
8956   if (!MovDelay[x][y])          /* next animation frame */
8957     MovDelay[x][y] = 3 * delay;
8958
8959   if (MovDelay[x][y])           /* wait some time before next frame */
8960   {
8961     MovDelay[x][y]--;
8962
8963     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8964     {
8965       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8966       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8967
8968       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8969     }
8970
8971     if (!MovDelay[x][y])
8972     {
8973       if (MovDir[x][y] == MV_LEFT)
8974       {
8975         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8976           TEST_DrawLevelField(x - 1, y);
8977       }
8978       else if (MovDir[x][y] == MV_RIGHT)
8979       {
8980         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8981           TEST_DrawLevelField(x + 1, y);
8982       }
8983       else if (MovDir[x][y] == MV_UP)
8984       {
8985         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8986           TEST_DrawLevelField(x, y - 1);
8987       }
8988       else
8989       {
8990         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8991           TEST_DrawLevelField(x, y + 1);
8992       }
8993
8994       Feld[x][y] = Store[x][y];
8995       Store[x][y] = 0;
8996       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8997       TEST_DrawLevelField(x, y);
8998     }
8999   }
9000 }
9001
9002 void MauerAbleger(int ax, int ay)
9003 {
9004   int element = Feld[ax][ay];
9005   int graphic = el2img(element);
9006   boolean oben_frei = FALSE, unten_frei = FALSE;
9007   boolean links_frei = FALSE, rechts_frei = FALSE;
9008   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9009   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9010   boolean new_wall = FALSE;
9011
9012   if (IS_ANIMATED(graphic))
9013     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9014
9015   if (!MovDelay[ax][ay])        /* start building new wall */
9016     MovDelay[ax][ay] = 6;
9017
9018   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9019   {
9020     MovDelay[ax][ay]--;
9021     if (MovDelay[ax][ay])
9022       return;
9023   }
9024
9025   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9026     oben_frei = TRUE;
9027   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9028     unten_frei = TRUE;
9029   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9030     links_frei = TRUE;
9031   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9032     rechts_frei = TRUE;
9033
9034   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9035       element == EL_EXPANDABLE_WALL_ANY)
9036   {
9037     if (oben_frei)
9038     {
9039       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9040       Store[ax][ay-1] = element;
9041       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9042       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9043         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9044                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9045       new_wall = TRUE;
9046     }
9047     if (unten_frei)
9048     {
9049       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9050       Store[ax][ay+1] = element;
9051       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9052       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9053         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9054                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9055       new_wall = TRUE;
9056     }
9057   }
9058
9059   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9060       element == EL_EXPANDABLE_WALL_ANY ||
9061       element == EL_EXPANDABLE_WALL ||
9062       element == EL_BD_EXPANDABLE_WALL)
9063   {
9064     if (links_frei)
9065     {
9066       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9067       Store[ax-1][ay] = element;
9068       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9069       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9070         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9071                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9072       new_wall = TRUE;
9073     }
9074
9075     if (rechts_frei)
9076     {
9077       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9078       Store[ax+1][ay] = element;
9079       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9080       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9081         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9082                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9083       new_wall = TRUE;
9084     }
9085   }
9086
9087   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9088     TEST_DrawLevelField(ax, ay);
9089
9090   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9091     oben_massiv = TRUE;
9092   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9093     unten_massiv = TRUE;
9094   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9095     links_massiv = TRUE;
9096   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9097     rechts_massiv = TRUE;
9098
9099   if (((oben_massiv && unten_massiv) ||
9100        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9101        element == EL_EXPANDABLE_WALL) &&
9102       ((links_massiv && rechts_massiv) ||
9103        element == EL_EXPANDABLE_WALL_VERTICAL))
9104     Feld[ax][ay] = EL_WALL;
9105
9106   if (new_wall)
9107     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9108 }
9109
9110 void MauerAblegerStahl(int ax, int ay)
9111 {
9112   int element = Feld[ax][ay];
9113   int graphic = el2img(element);
9114   boolean oben_frei = FALSE, unten_frei = FALSE;
9115   boolean links_frei = FALSE, rechts_frei = FALSE;
9116   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9117   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9118   boolean new_wall = FALSE;
9119
9120   if (IS_ANIMATED(graphic))
9121     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9122
9123   if (!MovDelay[ax][ay])        /* start building new wall */
9124     MovDelay[ax][ay] = 6;
9125
9126   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9127   {
9128     MovDelay[ax][ay]--;
9129     if (MovDelay[ax][ay])
9130       return;
9131   }
9132
9133   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9134     oben_frei = TRUE;
9135   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9136     unten_frei = TRUE;
9137   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9138     links_frei = TRUE;
9139   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9140     rechts_frei = TRUE;
9141
9142   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9143       element == EL_EXPANDABLE_STEELWALL_ANY)
9144   {
9145     if (oben_frei)
9146     {
9147       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9148       Store[ax][ay-1] = element;
9149       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9150       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9151         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9152                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9153       new_wall = TRUE;
9154     }
9155     if (unten_frei)
9156     {
9157       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9158       Store[ax][ay+1] = element;
9159       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9160       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9161         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9162                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9163       new_wall = TRUE;
9164     }
9165   }
9166
9167   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9168       element == EL_EXPANDABLE_STEELWALL_ANY)
9169   {
9170     if (links_frei)
9171     {
9172       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9173       Store[ax-1][ay] = element;
9174       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9175       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9176         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9177                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9178       new_wall = TRUE;
9179     }
9180
9181     if (rechts_frei)
9182     {
9183       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9184       Store[ax+1][ay] = element;
9185       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9186       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9187         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9188                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9189       new_wall = TRUE;
9190     }
9191   }
9192
9193   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9194     oben_massiv = TRUE;
9195   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9196     unten_massiv = TRUE;
9197   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9198     links_massiv = TRUE;
9199   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9200     rechts_massiv = TRUE;
9201
9202   if (((oben_massiv && unten_massiv) ||
9203        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9204       ((links_massiv && rechts_massiv) ||
9205        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9206     Feld[ax][ay] = EL_STEELWALL;
9207
9208   if (new_wall)
9209     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9210 }
9211
9212 void CheckForDragon(int x, int y)
9213 {
9214   int i, j;
9215   boolean dragon_found = FALSE;
9216   static int xy[4][2] =
9217   {
9218     { 0, -1 },
9219     { -1, 0 },
9220     { +1, 0 },
9221     { 0, +1 }
9222   };
9223
9224   for (i = 0; i < NUM_DIRECTIONS; i++)
9225   {
9226     for (j = 0; j < 4; j++)
9227     {
9228       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9229
9230       if (IN_LEV_FIELD(xx, yy) &&
9231           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9232       {
9233         if (Feld[xx][yy] == EL_DRAGON)
9234           dragon_found = TRUE;
9235       }
9236       else
9237         break;
9238     }
9239   }
9240
9241   if (!dragon_found)
9242   {
9243     for (i = 0; i < NUM_DIRECTIONS; i++)
9244     {
9245       for (j = 0; j < 3; j++)
9246       {
9247         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9248   
9249         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9250         {
9251           Feld[xx][yy] = EL_EMPTY;
9252           TEST_DrawLevelField(xx, yy);
9253         }
9254         else
9255           break;
9256       }
9257     }
9258   }
9259 }
9260
9261 static void InitBuggyBase(int x, int y)
9262 {
9263   int element = Feld[x][y];
9264   int activating_delay = FRAMES_PER_SECOND / 4;
9265
9266   ChangeDelay[x][y] =
9267     (element == EL_SP_BUGGY_BASE ?
9268      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9269      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9270      activating_delay :
9271      element == EL_SP_BUGGY_BASE_ACTIVE ?
9272      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9273 }
9274
9275 static void WarnBuggyBase(int x, int y)
9276 {
9277   int i;
9278   static int xy[4][2] =
9279   {
9280     { 0, -1 },
9281     { -1, 0 },
9282     { +1, 0 },
9283     { 0, +1 }
9284   };
9285
9286   for (i = 0; i < NUM_DIRECTIONS; i++)
9287   {
9288     int xx = x + xy[i][0];
9289     int yy = y + xy[i][1];
9290
9291     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9292     {
9293       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9294
9295       break;
9296     }
9297   }
9298 }
9299
9300 static void InitTrap(int x, int y)
9301 {
9302   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9303 }
9304
9305 static void ActivateTrap(int x, int y)
9306 {
9307   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9308 }
9309
9310 static void ChangeActiveTrap(int x, int y)
9311 {
9312   int graphic = IMG_TRAP_ACTIVE;
9313
9314   /* if new animation frame was drawn, correct crumbled sand border */
9315   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9316     TEST_DrawLevelFieldCrumbled(x, y);
9317 }
9318
9319 static int getSpecialActionElement(int element, int number, int base_element)
9320 {
9321   return (element != EL_EMPTY ? element :
9322           number != -1 ? base_element + number - 1 :
9323           EL_EMPTY);
9324 }
9325
9326 static int getModifiedActionNumber(int value_old, int operator, int operand,
9327                                    int value_min, int value_max)
9328 {
9329   int value_new = (operator == CA_MODE_SET      ? operand :
9330                    operator == CA_MODE_ADD      ? value_old + operand :
9331                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9332                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9333                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9334                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9335                    value_old);
9336
9337   return (value_new < value_min ? value_min :
9338           value_new > value_max ? value_max :
9339           value_new);
9340 }
9341
9342 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9343 {
9344   struct ElementInfo *ei = &element_info[element];
9345   struct ElementChangeInfo *change = &ei->change_page[page];
9346   int target_element = change->target_element;
9347   int action_type = change->action_type;
9348   int action_mode = change->action_mode;
9349   int action_arg = change->action_arg;
9350   int action_element = change->action_element;
9351   int i;
9352
9353   if (!change->has_action)
9354     return;
9355
9356   /* ---------- determine action paramater values -------------------------- */
9357
9358   int level_time_value =
9359     (level.time > 0 ? TimeLeft :
9360      TimePlayed);
9361
9362   int action_arg_element_raw =
9363     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9364      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9365      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9366      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9367      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9368      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9369      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9370      EL_EMPTY);
9371   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9372
9373   int action_arg_direction =
9374     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9375      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9376      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9377      change->actual_trigger_side :
9378      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9379      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9380      MV_NONE);
9381
9382   int action_arg_number_min =
9383     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9384      CA_ARG_MIN);
9385
9386   int action_arg_number_max =
9387     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9388      action_type == CA_SET_LEVEL_GEMS ? 999 :
9389      action_type == CA_SET_LEVEL_TIME ? 9999 :
9390      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9391      action_type == CA_SET_CE_VALUE ? 9999 :
9392      action_type == CA_SET_CE_SCORE ? 9999 :
9393      CA_ARG_MAX);
9394
9395   int action_arg_number_reset =
9396     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9397      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9398      action_type == CA_SET_LEVEL_TIME ? level.time :
9399      action_type == CA_SET_LEVEL_SCORE ? 0 :
9400      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9401      action_type == CA_SET_CE_SCORE ? 0 :
9402      0);
9403
9404   int action_arg_number =
9405     (action_arg <= CA_ARG_MAX ? action_arg :
9406      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9407      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9408      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9409      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9410      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9411      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9412      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9413      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9414      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9415      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9416      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9417      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9418      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9419      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9420      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9421      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9422      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9423      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9424      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9425      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9426      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9427      -1);
9428
9429   int action_arg_number_old =
9430     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9431      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9432      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9433      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9434      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9435      0);
9436
9437   int action_arg_number_new =
9438     getModifiedActionNumber(action_arg_number_old,
9439                             action_mode, action_arg_number,
9440                             action_arg_number_min, action_arg_number_max);
9441
9442   int trigger_player_bits =
9443     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9444      change->actual_trigger_player_bits : change->trigger_player);
9445
9446   int action_arg_player_bits =
9447     (action_arg >= CA_ARG_PLAYER_1 &&
9448      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9449      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9450      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9451      PLAYER_BITS_ANY);
9452
9453   /* ---------- execute action  -------------------------------------------- */
9454
9455   switch (action_type)
9456   {
9457     case CA_NO_ACTION:
9458     {
9459       return;
9460     }
9461
9462     /* ---------- level actions  ------------------------------------------- */
9463
9464     case CA_RESTART_LEVEL:
9465     {
9466       game.restart_level = TRUE;
9467
9468       break;
9469     }
9470
9471     case CA_SHOW_ENVELOPE:
9472     {
9473       int element = getSpecialActionElement(action_arg_element,
9474                                             action_arg_number, EL_ENVELOPE_1);
9475
9476       if (IS_ENVELOPE(element))
9477         local_player->show_envelope = element;
9478
9479       break;
9480     }
9481
9482     case CA_SET_LEVEL_TIME:
9483     {
9484       if (level.time > 0)       /* only modify limited time value */
9485       {
9486         TimeLeft = action_arg_number_new;
9487
9488         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9489
9490         DisplayGameControlValues();
9491
9492         if (!TimeLeft && setup.time_limit)
9493           for (i = 0; i < MAX_PLAYERS; i++)
9494             KillPlayer(&stored_player[i]);
9495       }
9496
9497       break;
9498     }
9499
9500     case CA_SET_LEVEL_SCORE:
9501     {
9502       local_player->score = action_arg_number_new;
9503
9504       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9505
9506       DisplayGameControlValues();
9507
9508       break;
9509     }
9510
9511     case CA_SET_LEVEL_GEMS:
9512     {
9513       local_player->gems_still_needed = action_arg_number_new;
9514
9515       game.snapshot.collected_item = TRUE;
9516
9517       game_panel_controls[GAME_PANEL_GEMS].value =
9518         local_player->gems_still_needed;
9519
9520       DisplayGameControlValues();
9521
9522       break;
9523     }
9524
9525     case CA_SET_LEVEL_WIND:
9526     {
9527       game.wind_direction = action_arg_direction;
9528
9529       break;
9530     }
9531
9532     case CA_SET_LEVEL_RANDOM_SEED:
9533     {
9534       /* ensure that setting a new random seed while playing is predictable */
9535       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9536
9537       break;
9538     }
9539
9540     /* ---------- player actions  ------------------------------------------ */
9541
9542     case CA_MOVE_PLAYER:
9543     {
9544       /* automatically move to the next field in specified direction */
9545       for (i = 0; i < MAX_PLAYERS; i++)
9546         if (trigger_player_bits & (1 << i))
9547           stored_player[i].programmed_action = action_arg_direction;
9548
9549       break;
9550     }
9551
9552     case CA_EXIT_PLAYER:
9553     {
9554       for (i = 0; i < MAX_PLAYERS; i++)
9555         if (action_arg_player_bits & (1 << i))
9556           PlayerWins(&stored_player[i]);
9557
9558       break;
9559     }
9560
9561     case CA_KILL_PLAYER:
9562     {
9563       for (i = 0; i < MAX_PLAYERS; i++)
9564         if (action_arg_player_bits & (1 << i))
9565           KillPlayer(&stored_player[i]);
9566
9567       break;
9568     }
9569
9570     case CA_SET_PLAYER_KEYS:
9571     {
9572       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9573       int element = getSpecialActionElement(action_arg_element,
9574                                             action_arg_number, EL_KEY_1);
9575
9576       if (IS_KEY(element))
9577       {
9578         for (i = 0; i < MAX_PLAYERS; i++)
9579         {
9580           if (trigger_player_bits & (1 << i))
9581           {
9582             stored_player[i].key[KEY_NR(element)] = key_state;
9583
9584             DrawGameDoorValues();
9585           }
9586         }
9587       }
9588
9589       break;
9590     }
9591
9592     case CA_SET_PLAYER_SPEED:
9593     {
9594       for (i = 0; i < MAX_PLAYERS; i++)
9595       {
9596         if (trigger_player_bits & (1 << i))
9597         {
9598           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9599
9600           if (action_arg == CA_ARG_SPEED_FASTER &&
9601               stored_player[i].cannot_move)
9602           {
9603             action_arg_number = STEPSIZE_VERY_SLOW;
9604           }
9605           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9606                    action_arg == CA_ARG_SPEED_FASTER)
9607           {
9608             action_arg_number = 2;
9609             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9610                            CA_MODE_MULTIPLY);
9611           }
9612           else if (action_arg == CA_ARG_NUMBER_RESET)
9613           {
9614             action_arg_number = level.initial_player_stepsize[i];
9615           }
9616
9617           move_stepsize =
9618             getModifiedActionNumber(move_stepsize,
9619                                     action_mode,
9620                                     action_arg_number,
9621                                     action_arg_number_min,
9622                                     action_arg_number_max);
9623
9624           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9625         }
9626       }
9627
9628       break;
9629     }
9630
9631     case CA_SET_PLAYER_SHIELD:
9632     {
9633       for (i = 0; i < MAX_PLAYERS; i++)
9634       {
9635         if (trigger_player_bits & (1 << i))
9636         {
9637           if (action_arg == CA_ARG_SHIELD_OFF)
9638           {
9639             stored_player[i].shield_normal_time_left = 0;
9640             stored_player[i].shield_deadly_time_left = 0;
9641           }
9642           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9643           {
9644             stored_player[i].shield_normal_time_left = 999999;
9645           }
9646           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9647           {
9648             stored_player[i].shield_normal_time_left = 999999;
9649             stored_player[i].shield_deadly_time_left = 999999;
9650           }
9651         }
9652       }
9653
9654       break;
9655     }
9656
9657     case CA_SET_PLAYER_GRAVITY:
9658     {
9659       for (i = 0; i < MAX_PLAYERS; i++)
9660       {
9661         if (trigger_player_bits & (1 << i))
9662         {
9663           stored_player[i].gravity =
9664             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9665              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9666              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9667              stored_player[i].gravity);
9668         }
9669       }
9670
9671       break;
9672     }
9673
9674     case CA_SET_PLAYER_ARTWORK:
9675     {
9676       for (i = 0; i < MAX_PLAYERS; i++)
9677       {
9678         if (trigger_player_bits & (1 << i))
9679         {
9680           int artwork_element = action_arg_element;
9681
9682           if (action_arg == CA_ARG_ELEMENT_RESET)
9683             artwork_element =
9684               (level.use_artwork_element[i] ? level.artwork_element[i] :
9685                stored_player[i].element_nr);
9686
9687           if (stored_player[i].artwork_element != artwork_element)
9688             stored_player[i].Frame = 0;
9689
9690           stored_player[i].artwork_element = artwork_element;
9691
9692           SetPlayerWaiting(&stored_player[i], FALSE);
9693
9694           /* set number of special actions for bored and sleeping animation */
9695           stored_player[i].num_special_action_bored =
9696             get_num_special_action(artwork_element,
9697                                    ACTION_BORING_1, ACTION_BORING_LAST);
9698           stored_player[i].num_special_action_sleeping =
9699             get_num_special_action(artwork_element,
9700                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9701         }
9702       }
9703
9704       break;
9705     }
9706
9707     case CA_SET_PLAYER_INVENTORY:
9708     {
9709       for (i = 0; i < MAX_PLAYERS; i++)
9710       {
9711         struct PlayerInfo *player = &stored_player[i];
9712         int j, k;
9713
9714         if (trigger_player_bits & (1 << i))
9715         {
9716           int inventory_element = action_arg_element;
9717
9718           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9719               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9720               action_arg == CA_ARG_ELEMENT_ACTION)
9721           {
9722             int element = inventory_element;
9723             int collect_count = element_info[element].collect_count_initial;
9724
9725             if (!IS_CUSTOM_ELEMENT(element))
9726               collect_count = 1;
9727
9728             if (collect_count == 0)
9729               player->inventory_infinite_element = element;
9730             else
9731               for (k = 0; k < collect_count; k++)
9732                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9733                   player->inventory_element[player->inventory_size++] =
9734                     element;
9735           }
9736           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9737                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9738                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9739           {
9740             if (player->inventory_infinite_element != EL_UNDEFINED &&
9741                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9742                                      action_arg_element_raw))
9743               player->inventory_infinite_element = EL_UNDEFINED;
9744
9745             for (k = 0, j = 0; j < player->inventory_size; j++)
9746             {
9747               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9748                                         action_arg_element_raw))
9749                 player->inventory_element[k++] = player->inventory_element[j];
9750             }
9751
9752             player->inventory_size = k;
9753           }
9754           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9755           {
9756             if (player->inventory_size > 0)
9757             {
9758               for (j = 0; j < player->inventory_size - 1; j++)
9759                 player->inventory_element[j] = player->inventory_element[j + 1];
9760
9761               player->inventory_size--;
9762             }
9763           }
9764           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9765           {
9766             if (player->inventory_size > 0)
9767               player->inventory_size--;
9768           }
9769           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9770           {
9771             player->inventory_infinite_element = EL_UNDEFINED;
9772             player->inventory_size = 0;
9773           }
9774           else if (action_arg == CA_ARG_INVENTORY_RESET)
9775           {
9776             player->inventory_infinite_element = EL_UNDEFINED;
9777             player->inventory_size = 0;
9778
9779             if (level.use_initial_inventory[i])
9780             {
9781               for (j = 0; j < level.initial_inventory_size[i]; j++)
9782               {
9783                 int element = level.initial_inventory_content[i][j];
9784                 int collect_count = element_info[element].collect_count_initial;
9785
9786                 if (!IS_CUSTOM_ELEMENT(element))
9787                   collect_count = 1;
9788
9789                 if (collect_count == 0)
9790                   player->inventory_infinite_element = element;
9791                 else
9792                   for (k = 0; k < collect_count; k++)
9793                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9794                       player->inventory_element[player->inventory_size++] =
9795                         element;
9796               }
9797             }
9798           }
9799         }
9800       }
9801
9802       break;
9803     }
9804
9805     /* ---------- CE actions  ---------------------------------------------- */
9806
9807     case CA_SET_CE_VALUE:
9808     {
9809       int last_ce_value = CustomValue[x][y];
9810
9811       CustomValue[x][y] = action_arg_number_new;
9812
9813       if (CustomValue[x][y] != last_ce_value)
9814       {
9815         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9816         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9817
9818         if (CustomValue[x][y] == 0)
9819         {
9820           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9821           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9822         }
9823       }
9824
9825       break;
9826     }
9827
9828     case CA_SET_CE_SCORE:
9829     {
9830       int last_ce_score = ei->collect_score;
9831
9832       ei->collect_score = action_arg_number_new;
9833
9834       if (ei->collect_score != last_ce_score)
9835       {
9836         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9837         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9838
9839         if (ei->collect_score == 0)
9840         {
9841           int xx, yy;
9842
9843           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9844           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9845
9846           /*
9847             This is a very special case that seems to be a mixture between
9848             CheckElementChange() and CheckTriggeredElementChange(): while
9849             the first one only affects single elements that are triggered
9850             directly, the second one affects multiple elements in the playfield
9851             that are triggered indirectly by another element. This is a third
9852             case: Changing the CE score always affects multiple identical CEs,
9853             so every affected CE must be checked, not only the single CE for
9854             which the CE score was changed in the first place (as every instance
9855             of that CE shares the same CE score, and therefore also can change)!
9856           */
9857           SCAN_PLAYFIELD(xx, yy)
9858           {
9859             if (Feld[xx][yy] == element)
9860               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9861                                  CE_SCORE_GETS_ZERO);
9862           }
9863         }
9864       }
9865
9866       break;
9867     }
9868
9869     case CA_SET_CE_ARTWORK:
9870     {
9871       int artwork_element = action_arg_element;
9872       boolean reset_frame = FALSE;
9873       int xx, yy;
9874
9875       if (action_arg == CA_ARG_ELEMENT_RESET)
9876         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9877                            element);
9878
9879       if (ei->gfx_element != artwork_element)
9880         reset_frame = TRUE;
9881
9882       ei->gfx_element = artwork_element;
9883
9884       SCAN_PLAYFIELD(xx, yy)
9885       {
9886         if (Feld[xx][yy] == element)
9887         {
9888           if (reset_frame)
9889           {
9890             ResetGfxAnimation(xx, yy);
9891             ResetRandomAnimationValue(xx, yy);
9892           }
9893
9894           TEST_DrawLevelField(xx, yy);
9895         }
9896       }
9897
9898       break;
9899     }
9900
9901     /* ---------- engine actions  ------------------------------------------ */
9902
9903     case CA_SET_ENGINE_SCAN_MODE:
9904     {
9905       InitPlayfieldScanMode(action_arg);
9906
9907       break;
9908     }
9909
9910     default:
9911       break;
9912   }
9913 }
9914
9915 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9916 {
9917   int old_element = Feld[x][y];
9918   int new_element = GetElementFromGroupElement(element);
9919   int previous_move_direction = MovDir[x][y];
9920   int last_ce_value = CustomValue[x][y];
9921   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9922   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9923   boolean add_player_onto_element = (new_element_is_player &&
9924                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9925                                      IS_WALKABLE(old_element));
9926
9927   if (!add_player_onto_element)
9928   {
9929     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9930       RemoveMovingField(x, y);
9931     else
9932       RemoveField(x, y);
9933
9934     Feld[x][y] = new_element;
9935
9936     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9937       MovDir[x][y] = previous_move_direction;
9938
9939     if (element_info[new_element].use_last_ce_value)
9940       CustomValue[x][y] = last_ce_value;
9941
9942     InitField_WithBug1(x, y, FALSE);
9943
9944     new_element = Feld[x][y];   /* element may have changed */
9945
9946     ResetGfxAnimation(x, y);
9947     ResetRandomAnimationValue(x, y);
9948
9949     TEST_DrawLevelField(x, y);
9950
9951     if (GFX_CRUMBLED(new_element))
9952       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9953   }
9954
9955   /* check if element under the player changes from accessible to unaccessible
9956      (needed for special case of dropping element which then changes) */
9957   /* (must be checked after creating new element for walkable group elements) */
9958   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9959       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9960   {
9961     Bang(x, y);
9962
9963     return;
9964   }
9965
9966   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9967   if (new_element_is_player)
9968     RelocatePlayer(x, y, new_element);
9969
9970   if (is_change)
9971     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9972
9973   TestIfBadThingTouchesPlayer(x, y);
9974   TestIfPlayerTouchesCustomElement(x, y);
9975   TestIfElementTouchesCustomElement(x, y);
9976 }
9977
9978 static void CreateField(int x, int y, int element)
9979 {
9980   CreateFieldExt(x, y, element, FALSE);
9981 }
9982
9983 static void CreateElementFromChange(int x, int y, int element)
9984 {
9985   element = GET_VALID_RUNTIME_ELEMENT(element);
9986
9987   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9988   {
9989     int old_element = Feld[x][y];
9990
9991     /* prevent changed element from moving in same engine frame
9992        unless both old and new element can either fall or move */
9993     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9994         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9995       Stop[x][y] = TRUE;
9996   }
9997
9998   CreateFieldExt(x, y, element, TRUE);
9999 }
10000
10001 static boolean ChangeElement(int x, int y, int element, int page)
10002 {
10003   struct ElementInfo *ei = &element_info[element];
10004   struct ElementChangeInfo *change = &ei->change_page[page];
10005   int ce_value = CustomValue[x][y];
10006   int ce_score = ei->collect_score;
10007   int target_element;
10008   int old_element = Feld[x][y];
10009
10010   /* always use default change event to prevent running into a loop */
10011   if (ChangeEvent[x][y] == -1)
10012     ChangeEvent[x][y] = CE_DELAY;
10013
10014   if (ChangeEvent[x][y] == CE_DELAY)
10015   {
10016     /* reset actual trigger element, trigger player and action element */
10017     change->actual_trigger_element = EL_EMPTY;
10018     change->actual_trigger_player = EL_EMPTY;
10019     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10020     change->actual_trigger_side = CH_SIDE_NONE;
10021     change->actual_trigger_ce_value = 0;
10022     change->actual_trigger_ce_score = 0;
10023   }
10024
10025   /* do not change elements more than a specified maximum number of changes */
10026   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10027     return FALSE;
10028
10029   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10030
10031   if (change->explode)
10032   {
10033     Bang(x, y);
10034
10035     return TRUE;
10036   }
10037
10038   if (change->use_target_content)
10039   {
10040     boolean complete_replace = TRUE;
10041     boolean can_replace[3][3];
10042     int xx, yy;
10043
10044     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10045     {
10046       boolean is_empty;
10047       boolean is_walkable;
10048       boolean is_diggable;
10049       boolean is_collectible;
10050       boolean is_removable;
10051       boolean is_destructible;
10052       int ex = x + xx - 1;
10053       int ey = y + yy - 1;
10054       int content_element = change->target_content.e[xx][yy];
10055       int e;
10056
10057       can_replace[xx][yy] = TRUE;
10058
10059       if (ex == x && ey == y)   /* do not check changing element itself */
10060         continue;
10061
10062       if (content_element == EL_EMPTY_SPACE)
10063       {
10064         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10065
10066         continue;
10067       }
10068
10069       if (!IN_LEV_FIELD(ex, ey))
10070       {
10071         can_replace[xx][yy] = FALSE;
10072         complete_replace = FALSE;
10073
10074         continue;
10075       }
10076
10077       e = Feld[ex][ey];
10078
10079       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10080         e = MovingOrBlocked2Element(ex, ey);
10081
10082       is_empty = (IS_FREE(ex, ey) ||
10083                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10084
10085       is_walkable     = (is_empty || IS_WALKABLE(e));
10086       is_diggable     = (is_empty || IS_DIGGABLE(e));
10087       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10088       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10089       is_removable    = (is_diggable || is_collectible);
10090
10091       can_replace[xx][yy] =
10092         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10093           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10094           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10095           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10096           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10097           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10098          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10099
10100       if (!can_replace[xx][yy])
10101         complete_replace = FALSE;
10102     }
10103
10104     if (!change->only_if_complete || complete_replace)
10105     {
10106       boolean something_has_changed = FALSE;
10107
10108       if (change->only_if_complete && change->use_random_replace &&
10109           RND(100) < change->random_percentage)
10110         return FALSE;
10111
10112       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10113       {
10114         int ex = x + xx - 1;
10115         int ey = y + yy - 1;
10116         int content_element;
10117
10118         if (can_replace[xx][yy] && (!change->use_random_replace ||
10119                                     RND(100) < change->random_percentage))
10120         {
10121           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10122             RemoveMovingField(ex, ey);
10123
10124           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10125
10126           content_element = change->target_content.e[xx][yy];
10127           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10128                                               ce_value, ce_score);
10129
10130           CreateElementFromChange(ex, ey, target_element);
10131
10132           something_has_changed = TRUE;
10133
10134           /* for symmetry reasons, freeze newly created border elements */
10135           if (ex != x || ey != y)
10136             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10137         }
10138       }
10139
10140       if (something_has_changed)
10141       {
10142         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10143         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10144       }
10145     }
10146   }
10147   else
10148   {
10149     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10150                                         ce_value, ce_score);
10151
10152     if (element == EL_DIAGONAL_GROWING ||
10153         element == EL_DIAGONAL_SHRINKING)
10154     {
10155       target_element = Store[x][y];
10156
10157       Store[x][y] = EL_EMPTY;
10158     }
10159
10160     CreateElementFromChange(x, y, target_element);
10161
10162     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10163     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10164   }
10165
10166   /* this uses direct change before indirect change */
10167   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10168
10169   return TRUE;
10170 }
10171
10172 static void HandleElementChange(int x, int y, int page)
10173 {
10174   int element = MovingOrBlocked2Element(x, y);
10175   struct ElementInfo *ei = &element_info[element];
10176   struct ElementChangeInfo *change = &ei->change_page[page];
10177   boolean handle_action_before_change = FALSE;
10178
10179 #ifdef DEBUG
10180   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10181       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10182   {
10183     printf("\n\n");
10184     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10185            x, y, element, element_info[element].token_name);
10186     printf("HandleElementChange(): This should never happen!\n");
10187     printf("\n\n");
10188   }
10189 #endif
10190
10191   /* this can happen with classic bombs on walkable, changing elements */
10192   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10193   {
10194     return;
10195   }
10196
10197   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10198   {
10199     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10200
10201     if (change->can_change)
10202     {
10203       /* !!! not clear why graphic animation should be reset at all here !!! */
10204       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10205       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10206
10207       /*
10208         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10209
10210         When using an animation frame delay of 1 (this only happens with
10211         "sp_zonk.moving.left/right" in the classic graphics), the default
10212         (non-moving) animation shows wrong animation frames (while the
10213         moving animation, like "sp_zonk.moving.left/right", is correct,
10214         so this graphical bug never shows up with the classic graphics).
10215         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10216         be drawn instead of the correct frames 0,1,2,3. This is caused by
10217         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10218         an element change: First when the change delay ("ChangeDelay[][]")
10219         counter has reached zero after decrementing, then a second time in
10220         the next frame (after "GfxFrame[][]" was already incremented) when
10221         "ChangeDelay[][]" is reset to the initial delay value again.
10222
10223         This causes frame 0 to be drawn twice, while the last frame won't
10224         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10225
10226         As some animations may already be cleverly designed around this bug
10227         (at least the "Snake Bite" snake tail animation does this), it cannot
10228         simply be fixed here without breaking such existing animations.
10229         Unfortunately, it cannot easily be detected if a graphics set was
10230         designed "before" or "after" the bug was fixed. As a workaround,
10231         a new graphics set option "game.graphics_engine_version" was added
10232         to be able to specify the game's major release version for which the
10233         graphics set was designed, which can then be used to decide if the
10234         bugfix should be used (version 4 and above) or not (version 3 or
10235         below, or if no version was specified at all, as with old sets).
10236
10237         (The wrong/fixed animation frames can be tested with the test level set
10238         "test_gfxframe" and level "000", which contains a specially prepared
10239         custom element at level position (x/y) == (11/9) which uses the zonk
10240         animation mentioned above. Using "game.graphics_engine_version: 4"
10241         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10242         This can also be seen from the debug output for this test element.)
10243       */
10244
10245       /* when a custom element is about to change (for example by change delay),
10246          do not reset graphic animation when the custom element is moving */
10247       if (game.graphics_engine_version < 4 &&
10248           !IS_MOVING(x, y))
10249       {
10250         ResetGfxAnimation(x, y);
10251         ResetRandomAnimationValue(x, y);
10252       }
10253
10254       if (change->pre_change_function)
10255         change->pre_change_function(x, y);
10256     }
10257   }
10258
10259   ChangeDelay[x][y]--;
10260
10261   if (ChangeDelay[x][y] != 0)           /* continue element change */
10262   {
10263     if (change->can_change)
10264     {
10265       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10266
10267       if (IS_ANIMATED(graphic))
10268         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10269
10270       if (change->change_function)
10271         change->change_function(x, y);
10272     }
10273   }
10274   else                                  /* finish element change */
10275   {
10276     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10277     {
10278       page = ChangePage[x][y];
10279       ChangePage[x][y] = -1;
10280
10281       change = &ei->change_page[page];
10282     }
10283
10284     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10285     {
10286       ChangeDelay[x][y] = 1;            /* try change after next move step */
10287       ChangePage[x][y] = page;          /* remember page to use for change */
10288
10289       return;
10290     }
10291
10292     /* special case: set new level random seed before changing element */
10293     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10294       handle_action_before_change = TRUE;
10295
10296     if (change->has_action && handle_action_before_change)
10297       ExecuteCustomElementAction(x, y, element, page);
10298
10299     if (change->can_change)
10300     {
10301       if (ChangeElement(x, y, element, page))
10302       {
10303         if (change->post_change_function)
10304           change->post_change_function(x, y);
10305       }
10306     }
10307
10308     if (change->has_action && !handle_action_before_change)
10309       ExecuteCustomElementAction(x, y, element, page);
10310   }
10311 }
10312
10313 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10314                                               int trigger_element,
10315                                               int trigger_event,
10316                                               int trigger_player,
10317                                               int trigger_side,
10318                                               int trigger_page)
10319 {
10320   boolean change_done_any = FALSE;
10321   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10322   int i;
10323
10324   if (!(trigger_events[trigger_element][trigger_event]))
10325     return FALSE;
10326
10327   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10328
10329   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10330   {
10331     int element = EL_CUSTOM_START + i;
10332     boolean change_done = FALSE;
10333     int p;
10334
10335     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10336         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10337       continue;
10338
10339     for (p = 0; p < element_info[element].num_change_pages; p++)
10340     {
10341       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10342
10343       if (change->can_change_or_has_action &&
10344           change->has_event[trigger_event] &&
10345           change->trigger_side & trigger_side &&
10346           change->trigger_player & trigger_player &&
10347           change->trigger_page & trigger_page_bits &&
10348           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10349       {
10350         change->actual_trigger_element = trigger_element;
10351         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10352         change->actual_trigger_player_bits = trigger_player;
10353         change->actual_trigger_side = trigger_side;
10354         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10355         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10356
10357         if ((change->can_change && !change_done) || change->has_action)
10358         {
10359           int x, y;
10360
10361           SCAN_PLAYFIELD(x, y)
10362           {
10363             if (Feld[x][y] == element)
10364             {
10365               if (change->can_change && !change_done)
10366               {
10367                 /* if element already changed in this frame, not only prevent
10368                    another element change (checked in ChangeElement()), but
10369                    also prevent additional element actions for this element */
10370
10371                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10372                     !level.use_action_after_change_bug)
10373                   continue;
10374
10375                 ChangeDelay[x][y] = 1;
10376                 ChangeEvent[x][y] = trigger_event;
10377
10378                 HandleElementChange(x, y, p);
10379               }
10380               else if (change->has_action)
10381               {
10382                 /* if element already changed in this frame, not only prevent
10383                    another element change (checked in ChangeElement()), but
10384                    also prevent additional element actions for this element */
10385
10386                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10387                     !level.use_action_after_change_bug)
10388                   continue;
10389
10390                 ExecuteCustomElementAction(x, y, element, p);
10391                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10392               }
10393             }
10394           }
10395
10396           if (change->can_change)
10397           {
10398             change_done = TRUE;
10399             change_done_any = TRUE;
10400           }
10401         }
10402       }
10403     }
10404   }
10405
10406   RECURSION_LOOP_DETECTION_END();
10407
10408   return change_done_any;
10409 }
10410
10411 static boolean CheckElementChangeExt(int x, int y,
10412                                      int element,
10413                                      int trigger_element,
10414                                      int trigger_event,
10415                                      int trigger_player,
10416                                      int trigger_side)
10417 {
10418   boolean change_done = FALSE;
10419   int p;
10420
10421   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10422       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10423     return FALSE;
10424
10425   if (Feld[x][y] == EL_BLOCKED)
10426   {
10427     Blocked2Moving(x, y, &x, &y);
10428     element = Feld[x][y];
10429   }
10430
10431   /* check if element has already changed or is about to change after moving */
10432   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10433        Feld[x][y] != element) ||
10434
10435       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10436        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10437         ChangePage[x][y] != -1)))
10438     return FALSE;
10439
10440   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10441
10442   for (p = 0; p < element_info[element].num_change_pages; p++)
10443   {
10444     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10445
10446     /* check trigger element for all events where the element that is checked
10447        for changing interacts with a directly adjacent element -- this is
10448        different to element changes that affect other elements to change on the
10449        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10450     boolean check_trigger_element =
10451       (trigger_event == CE_TOUCHING_X ||
10452        trigger_event == CE_HITTING_X ||
10453        trigger_event == CE_HIT_BY_X ||
10454        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10455
10456     if (change->can_change_or_has_action &&
10457         change->has_event[trigger_event] &&
10458         change->trigger_side & trigger_side &&
10459         change->trigger_player & trigger_player &&
10460         (!check_trigger_element ||
10461          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10462     {
10463       change->actual_trigger_element = trigger_element;
10464       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10465       change->actual_trigger_player_bits = trigger_player;
10466       change->actual_trigger_side = trigger_side;
10467       change->actual_trigger_ce_value = CustomValue[x][y];
10468       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10469
10470       /* special case: trigger element not at (x,y) position for some events */
10471       if (check_trigger_element)
10472       {
10473         static struct
10474         {
10475           int dx, dy;
10476         } move_xy[] =
10477           {
10478             {  0,  0 },
10479             { -1,  0 },
10480             { +1,  0 },
10481             {  0,  0 },
10482             {  0, -1 },
10483             {  0,  0 }, { 0, 0 }, { 0, 0 },
10484             {  0, +1 }
10485           };
10486
10487         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10488         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10489
10490         change->actual_trigger_ce_value = CustomValue[xx][yy];
10491         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10492       }
10493
10494       if (change->can_change && !change_done)
10495       {
10496         ChangeDelay[x][y] = 1;
10497         ChangeEvent[x][y] = trigger_event;
10498
10499         HandleElementChange(x, y, p);
10500
10501         change_done = TRUE;
10502       }
10503       else if (change->has_action)
10504       {
10505         ExecuteCustomElementAction(x, y, element, p);
10506         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10507       }
10508     }
10509   }
10510
10511   RECURSION_LOOP_DETECTION_END();
10512
10513   return change_done;
10514 }
10515
10516 static void PlayPlayerSound(struct PlayerInfo *player)
10517 {
10518   int jx = player->jx, jy = player->jy;
10519   int sound_element = player->artwork_element;
10520   int last_action = player->last_action_waiting;
10521   int action = player->action_waiting;
10522
10523   if (player->is_waiting)
10524   {
10525     if (action != last_action)
10526       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10527     else
10528       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10529   }
10530   else
10531   {
10532     if (action != last_action)
10533       StopSound(element_info[sound_element].sound[last_action]);
10534
10535     if (last_action == ACTION_SLEEPING)
10536       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10537   }
10538 }
10539
10540 static void PlayAllPlayersSound()
10541 {
10542   int i;
10543
10544   for (i = 0; i < MAX_PLAYERS; i++)
10545     if (stored_player[i].active)
10546       PlayPlayerSound(&stored_player[i]);
10547 }
10548
10549 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10550 {
10551   boolean last_waiting = player->is_waiting;
10552   int move_dir = player->MovDir;
10553
10554   player->dir_waiting = move_dir;
10555   player->last_action_waiting = player->action_waiting;
10556
10557   if (is_waiting)
10558   {
10559     if (!last_waiting)          /* not waiting -> waiting */
10560     {
10561       player->is_waiting = TRUE;
10562
10563       player->frame_counter_bored =
10564         FrameCounter +
10565         game.player_boring_delay_fixed +
10566         GetSimpleRandom(game.player_boring_delay_random);
10567       player->frame_counter_sleeping =
10568         FrameCounter +
10569         game.player_sleeping_delay_fixed +
10570         GetSimpleRandom(game.player_sleeping_delay_random);
10571
10572       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10573     }
10574
10575     if (game.player_sleeping_delay_fixed +
10576         game.player_sleeping_delay_random > 0 &&
10577         player->anim_delay_counter == 0 &&
10578         player->post_delay_counter == 0 &&
10579         FrameCounter >= player->frame_counter_sleeping)
10580       player->is_sleeping = TRUE;
10581     else if (game.player_boring_delay_fixed +
10582              game.player_boring_delay_random > 0 &&
10583              FrameCounter >= player->frame_counter_bored)
10584       player->is_bored = TRUE;
10585
10586     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10587                               player->is_bored ? ACTION_BORING :
10588                               ACTION_WAITING);
10589
10590     if (player->is_sleeping && player->use_murphy)
10591     {
10592       /* special case for sleeping Murphy when leaning against non-free tile */
10593
10594       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10595           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10596            !IS_MOVING(player->jx - 1, player->jy)))
10597         move_dir = MV_LEFT;
10598       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10599                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10600                 !IS_MOVING(player->jx + 1, player->jy)))
10601         move_dir = MV_RIGHT;
10602       else
10603         player->is_sleeping = FALSE;
10604
10605       player->dir_waiting = move_dir;
10606     }
10607
10608     if (player->is_sleeping)
10609     {
10610       if (player->num_special_action_sleeping > 0)
10611       {
10612         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10613         {
10614           int last_special_action = player->special_action_sleeping;
10615           int num_special_action = player->num_special_action_sleeping;
10616           int special_action =
10617             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10618              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10619              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10620              last_special_action + 1 : ACTION_SLEEPING);
10621           int special_graphic =
10622             el_act_dir2img(player->artwork_element, special_action, move_dir);
10623
10624           player->anim_delay_counter =
10625             graphic_info[special_graphic].anim_delay_fixed +
10626             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10627           player->post_delay_counter =
10628             graphic_info[special_graphic].post_delay_fixed +
10629             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10630
10631           player->special_action_sleeping = special_action;
10632         }
10633
10634         if (player->anim_delay_counter > 0)
10635         {
10636           player->action_waiting = player->special_action_sleeping;
10637           player->anim_delay_counter--;
10638         }
10639         else if (player->post_delay_counter > 0)
10640         {
10641           player->post_delay_counter--;
10642         }
10643       }
10644     }
10645     else if (player->is_bored)
10646     {
10647       if (player->num_special_action_bored > 0)
10648       {
10649         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10650         {
10651           int special_action =
10652             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10653           int special_graphic =
10654             el_act_dir2img(player->artwork_element, special_action, move_dir);
10655
10656           player->anim_delay_counter =
10657             graphic_info[special_graphic].anim_delay_fixed +
10658             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10659           player->post_delay_counter =
10660             graphic_info[special_graphic].post_delay_fixed +
10661             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10662
10663           player->special_action_bored = special_action;
10664         }
10665
10666         if (player->anim_delay_counter > 0)
10667         {
10668           player->action_waiting = player->special_action_bored;
10669           player->anim_delay_counter--;
10670         }
10671         else if (player->post_delay_counter > 0)
10672         {
10673           player->post_delay_counter--;
10674         }
10675       }
10676     }
10677   }
10678   else if (last_waiting)        /* waiting -> not waiting */
10679   {
10680     player->is_waiting = FALSE;
10681     player->is_bored = FALSE;
10682     player->is_sleeping = FALSE;
10683
10684     player->frame_counter_bored = -1;
10685     player->frame_counter_sleeping = -1;
10686
10687     player->anim_delay_counter = 0;
10688     player->post_delay_counter = 0;
10689
10690     player->dir_waiting = player->MovDir;
10691     player->action_waiting = ACTION_DEFAULT;
10692
10693     player->special_action_bored = ACTION_DEFAULT;
10694     player->special_action_sleeping = ACTION_DEFAULT;
10695   }
10696 }
10697
10698 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10699 {
10700   if ((!player->is_moving  && player->was_moving) ||
10701       (player->MovPos == 0 && player->was_moving) ||
10702       (player->is_snapping && !player->was_snapping) ||
10703       (player->is_dropping && !player->was_dropping))
10704   {
10705     if (!CheckSaveEngineSnapshotToList())
10706       return;
10707
10708     player->was_moving = FALSE;
10709     player->was_snapping = TRUE;
10710     player->was_dropping = TRUE;
10711   }
10712   else
10713   {
10714     if (player->is_moving)
10715       player->was_moving = TRUE;
10716
10717     if (!player->is_snapping)
10718       player->was_snapping = FALSE;
10719
10720     if (!player->is_dropping)
10721       player->was_dropping = FALSE;
10722   }
10723 }
10724
10725 static void CheckSingleStepMode(struct PlayerInfo *player)
10726 {
10727   if (tape.single_step && tape.recording && !tape.pausing)
10728   {
10729     /* as it is called "single step mode", just return to pause mode when the
10730        player stopped moving after one tile (or never starts moving at all) */
10731     if (!player->is_moving && !player->is_pushing)
10732     {
10733       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10734       SnapField(player, 0, 0);                  /* stop snapping */
10735     }
10736   }
10737
10738   CheckSaveEngineSnapshot(player);
10739 }
10740
10741 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10742 {
10743   int left      = player_action & JOY_LEFT;
10744   int right     = player_action & JOY_RIGHT;
10745   int up        = player_action & JOY_UP;
10746   int down      = player_action & JOY_DOWN;
10747   int button1   = player_action & JOY_BUTTON_1;
10748   int button2   = player_action & JOY_BUTTON_2;
10749   int dx        = (left ? -1 : right ? 1 : 0);
10750   int dy        = (up   ? -1 : down  ? 1 : 0);
10751
10752   if (!player->active || tape.pausing)
10753     return 0;
10754
10755   if (player_action)
10756   {
10757     if (button1)
10758       SnapField(player, dx, dy);
10759     else
10760     {
10761       if (button2)
10762         DropElement(player);
10763
10764       MovePlayer(player, dx, dy);
10765     }
10766
10767     CheckSingleStepMode(player);
10768
10769     SetPlayerWaiting(player, FALSE);
10770
10771     return player_action;
10772   }
10773   else
10774   {
10775     /* no actions for this player (no input at player's configured device) */
10776
10777     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10778     SnapField(player, 0, 0);
10779     CheckGravityMovementWhenNotMoving(player);
10780
10781     if (player->MovPos == 0)
10782       SetPlayerWaiting(player, TRUE);
10783
10784     if (player->MovPos == 0)    /* needed for tape.playing */
10785       player->is_moving = FALSE;
10786
10787     player->is_dropping = FALSE;
10788     player->is_dropping_pressed = FALSE;
10789     player->drop_pressed_delay = 0;
10790
10791     CheckSingleStepMode(player);
10792
10793     return 0;
10794   }
10795 }
10796
10797 static void CheckLevelTime()
10798 {
10799   int i;
10800
10801   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10802   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10803   {
10804     if (level.native_em_level->lev->home == 0)  /* all players at home */
10805     {
10806       PlayerWins(local_player);
10807
10808       AllPlayersGone = TRUE;
10809
10810       level.native_em_level->lev->home = -1;
10811     }
10812
10813     if (level.native_em_level->ply[0]->alive == 0 &&
10814         level.native_em_level->ply[1]->alive == 0 &&
10815         level.native_em_level->ply[2]->alive == 0 &&
10816         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10817       AllPlayersGone = TRUE;
10818   }
10819   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10820   {
10821     if (game_sp.LevelSolved &&
10822         !game_sp.GameOver)                              /* game won */
10823     {
10824       PlayerWins(local_player);
10825
10826       game_sp.GameOver = TRUE;
10827
10828       AllPlayersGone = TRUE;
10829     }
10830
10831     if (game_sp.GameOver)                               /* game lost */
10832       AllPlayersGone = TRUE;
10833   }
10834
10835   if (TimeFrames >= FRAMES_PER_SECOND)
10836   {
10837     TimeFrames = 0;
10838     TapeTime++;
10839
10840     for (i = 0; i < MAX_PLAYERS; i++)
10841     {
10842       struct PlayerInfo *player = &stored_player[i];
10843
10844       if (SHIELD_ON(player))
10845       {
10846         player->shield_normal_time_left--;
10847
10848         if (player->shield_deadly_time_left > 0)
10849           player->shield_deadly_time_left--;
10850       }
10851     }
10852
10853     if (!local_player->LevelSolved && !level.use_step_counter)
10854     {
10855       TimePlayed++;
10856
10857       if (TimeLeft > 0)
10858       {
10859         TimeLeft--;
10860
10861         if (TimeLeft <= 10 && setup.time_limit)
10862           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10863
10864         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10865            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10866
10867         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10868
10869         if (!TimeLeft && setup.time_limit)
10870         {
10871           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10872             level.native_em_level->lev->killed_out_of_time = TRUE;
10873           else
10874             for (i = 0; i < MAX_PLAYERS; i++)
10875               KillPlayer(&stored_player[i]);
10876         }
10877       }
10878       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10879       {
10880         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10881       }
10882
10883       level.native_em_level->lev->time =
10884         (game.no_time_limit ? TimePlayed : TimeLeft);
10885     }
10886
10887     if (tape.recording || tape.playing)
10888       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10889   }
10890
10891   if (tape.recording || tape.playing)
10892     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10893
10894   UpdateAndDisplayGameControlValues();
10895 }
10896
10897 void AdvanceFrameAndPlayerCounters(int player_nr)
10898 {
10899   int i;
10900
10901   /* advance frame counters (global frame counter and time frame counter) */
10902   FrameCounter++;
10903   TimeFrames++;
10904
10905   /* advance player counters (counters for move delay, move animation etc.) */
10906   for (i = 0; i < MAX_PLAYERS; i++)
10907   {
10908     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10909     int move_delay_value = stored_player[i].move_delay_value;
10910     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10911
10912     if (!advance_player_counters)       /* not all players may be affected */
10913       continue;
10914
10915     if (move_frames == 0)       /* less than one move per game frame */
10916     {
10917       int stepsize = TILEX / move_delay_value;
10918       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10919       int count = (stored_player[i].is_moving ?
10920                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10921
10922       if (count % delay == 0)
10923         move_frames = 1;
10924     }
10925
10926     stored_player[i].Frame += move_frames;
10927
10928     if (stored_player[i].MovPos != 0)
10929       stored_player[i].StepFrame += move_frames;
10930
10931     if (stored_player[i].move_delay > 0)
10932       stored_player[i].move_delay--;
10933
10934     /* due to bugs in previous versions, counter must count up, not down */
10935     if (stored_player[i].push_delay != -1)
10936       stored_player[i].push_delay++;
10937
10938     if (stored_player[i].drop_delay > 0)
10939       stored_player[i].drop_delay--;
10940
10941     if (stored_player[i].is_dropping_pressed)
10942       stored_player[i].drop_pressed_delay++;
10943   }
10944 }
10945
10946 void StartGameActions(boolean init_network_game, boolean record_tape,
10947                       int random_seed)
10948 {
10949   unsigned int new_random_seed = InitRND(random_seed);
10950
10951   if (record_tape)
10952     TapeStartRecording(new_random_seed);
10953
10954 #if defined(NETWORK_AVALIABLE)
10955   if (init_network_game)
10956   {
10957     SendToServer_StartPlaying();
10958
10959     return;
10960   }
10961 #endif
10962
10963   InitGame();
10964 }
10965
10966 void GameActionsExt()
10967 {
10968 #if 0
10969   static unsigned int game_frame_delay = 0;
10970 #endif
10971   unsigned int game_frame_delay_value;
10972   byte *recorded_player_action;
10973   byte summarized_player_action = 0;
10974   byte tape_action[MAX_PLAYERS];
10975   int i;
10976
10977   /* detect endless loops, caused by custom element programming */
10978   if (recursion_loop_detected && recursion_loop_depth == 0)
10979   {
10980     char *message = getStringCat3("Internal Error! Element ",
10981                                   EL_NAME(recursion_loop_element),
10982                                   " caused endless loop! Quit the game?");
10983
10984     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10985           EL_NAME(recursion_loop_element));
10986
10987     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10988
10989     recursion_loop_detected = FALSE;    /* if game should be continued */
10990
10991     free(message);
10992
10993     return;
10994   }
10995
10996   if (game.restart_level)
10997     StartGameActions(options.network, setup.autorecord, level.random_seed);
10998
10999   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11000   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11001   {
11002     if (level.native_em_level->lev->home == 0)  /* all players at home */
11003     {
11004       PlayerWins(local_player);
11005
11006       AllPlayersGone = TRUE;
11007
11008       level.native_em_level->lev->home = -1;
11009     }
11010
11011     if (level.native_em_level->ply[0]->alive == 0 &&
11012         level.native_em_level->ply[1]->alive == 0 &&
11013         level.native_em_level->ply[2]->alive == 0 &&
11014         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11015       AllPlayersGone = TRUE;
11016   }
11017   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11018   {
11019     if (game_sp.LevelSolved &&
11020         !game_sp.GameOver)                              /* game won */
11021     {
11022       PlayerWins(local_player);
11023
11024       game_sp.GameOver = TRUE;
11025
11026       AllPlayersGone = TRUE;
11027     }
11028
11029     if (game_sp.GameOver)                               /* game lost */
11030       AllPlayersGone = TRUE;
11031   }
11032
11033   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11034     GameWon();
11035
11036   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11037     TapeStop();
11038
11039   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11040     return;
11041
11042   game_frame_delay_value =
11043     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11044
11045   if (tape.playing && tape.warp_forward && !tape.pausing)
11046     game_frame_delay_value = 0;
11047
11048   SetVideoFrameDelay(game_frame_delay_value);
11049
11050 #if 0
11051 #if 0
11052   /* ---------- main game synchronization point ---------- */
11053
11054   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11055
11056   printf("::: skip == %d\n", skip);
11057
11058 #else
11059   /* ---------- main game synchronization point ---------- */
11060
11061   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11062 #endif
11063 #endif
11064
11065   if (network_playing && !network_player_action_received)
11066   {
11067     /* try to get network player actions in time */
11068
11069 #if defined(NETWORK_AVALIABLE)
11070     /* last chance to get network player actions without main loop delay */
11071     HandleNetworking();
11072 #endif
11073
11074     /* game was quit by network peer */
11075     if (game_status != GAME_MODE_PLAYING)
11076       return;
11077
11078     if (!network_player_action_received)
11079       return;           /* failed to get network player actions in time */
11080
11081     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11082   }
11083
11084   if (tape.pausing)
11085     return;
11086
11087   /* at this point we know that we really continue executing the game */
11088
11089   network_player_action_received = FALSE;
11090
11091   /* when playing tape, read previously recorded player input from tape data */
11092   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11093
11094   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11095   if (tape.pausing)
11096     return;
11097
11098   if (tape.set_centered_player)
11099   {
11100     game.centered_player_nr_next = tape.centered_player_nr_next;
11101     game.set_centered_player = TRUE;
11102   }
11103
11104   for (i = 0; i < MAX_PLAYERS; i++)
11105   {
11106     summarized_player_action |= stored_player[i].action;
11107
11108     if (!network_playing && (game.team_mode || tape.playing))
11109       stored_player[i].effective_action = stored_player[i].action;
11110   }
11111
11112 #if defined(NETWORK_AVALIABLE)
11113   if (network_playing)
11114     SendToServer_MovePlayer(summarized_player_action);
11115 #endif
11116
11117   // summarize all actions at local players mapped input device position
11118   // (this allows using different input devices in single player mode)
11119   if (!options.network && !game.team_mode)
11120     stored_player[map_player_action[local_player->index_nr]].effective_action =
11121       summarized_player_action;
11122
11123   if (tape.recording &&
11124       setup.team_mode &&
11125       setup.input_on_focus &&
11126       game.centered_player_nr != -1)
11127   {
11128     for (i = 0; i < MAX_PLAYERS; i++)
11129       stored_player[i].effective_action =
11130         (i == game.centered_player_nr ? summarized_player_action : 0);
11131   }
11132
11133   if (recorded_player_action != NULL)
11134     for (i = 0; i < MAX_PLAYERS; i++)
11135       stored_player[i].effective_action = recorded_player_action[i];
11136
11137   for (i = 0; i < MAX_PLAYERS; i++)
11138   {
11139     tape_action[i] = stored_player[i].effective_action;
11140
11141     /* (this may happen in the RND game engine if a player was not present on
11142        the playfield on level start, but appeared later from a custom element */
11143     if (setup.team_mode &&
11144         tape.recording &&
11145         tape_action[i] &&
11146         !tape.player_participates[i])
11147       tape.player_participates[i] = TRUE;
11148   }
11149
11150   /* only record actions from input devices, but not programmed actions */
11151   if (tape.recording)
11152     TapeRecordAction(tape_action);
11153
11154 #if USE_NEW_PLAYER_ASSIGNMENTS
11155   // !!! also map player actions in single player mode !!!
11156   // if (game.team_mode)
11157   if (1)
11158   {
11159     byte mapped_action[MAX_PLAYERS];
11160
11161 #if DEBUG_PLAYER_ACTIONS
11162     printf(":::");
11163     for (i = 0; i < MAX_PLAYERS; i++)
11164       printf(" %d, ", stored_player[i].effective_action);
11165 #endif
11166
11167     for (i = 0; i < MAX_PLAYERS; i++)
11168       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11169
11170     for (i = 0; i < MAX_PLAYERS; i++)
11171       stored_player[i].effective_action = mapped_action[i];
11172
11173 #if DEBUG_PLAYER_ACTIONS
11174     printf(" =>");
11175     for (i = 0; i < MAX_PLAYERS; i++)
11176       printf(" %d, ", stored_player[i].effective_action);
11177     printf("\n");
11178 #endif
11179   }
11180 #if DEBUG_PLAYER_ACTIONS
11181   else
11182   {
11183     printf(":::");
11184     for (i = 0; i < MAX_PLAYERS; i++)
11185       printf(" %d, ", stored_player[i].effective_action);
11186     printf("\n");
11187   }
11188 #endif
11189 #endif
11190
11191   for (i = 0; i < MAX_PLAYERS; i++)
11192   {
11193     // allow engine snapshot in case of changed movement attempt
11194     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11195         (stored_player[i].effective_action & KEY_MOTION))
11196       game.snapshot.changed_action = TRUE;
11197
11198     // allow engine snapshot in case of snapping/dropping attempt
11199     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11200         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11201       game.snapshot.changed_action = TRUE;
11202
11203     game.snapshot.last_action[i] = stored_player[i].effective_action;
11204   }
11205
11206   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11207   {
11208     GameActions_EM_Main();
11209   }
11210   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11211   {
11212     GameActions_SP_Main();
11213   }
11214   else
11215   {
11216     GameActions_RND_Main();
11217   }
11218
11219   BlitScreenToBitmap(backbuffer);
11220
11221   CheckLevelTime();
11222
11223   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11224
11225   if (options.debug)                    /* calculate frames per second */
11226   {
11227     static unsigned int fps_counter = 0;
11228     static int fps_frames = 0;
11229     unsigned int fps_delay_ms = Counter() - fps_counter;
11230
11231     fps_frames++;
11232
11233     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11234     {
11235       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11236
11237       fps_frames = 0;
11238       fps_counter = Counter();
11239     }
11240
11241     redraw_mask |= REDRAW_FPS;
11242   }
11243 }
11244
11245 static void GameActions_CheckSaveEngineSnapshot()
11246 {
11247   if (!game.snapshot.save_snapshot)
11248     return;
11249
11250   // clear flag for saving snapshot _before_ saving snapshot
11251   game.snapshot.save_snapshot = FALSE;
11252
11253   SaveEngineSnapshotToList();
11254 }
11255
11256 void GameActions()
11257 {
11258   GameActionsExt();
11259
11260   GameActions_CheckSaveEngineSnapshot();
11261 }
11262
11263 void GameActions_EM_Main()
11264 {
11265   byte effective_action[MAX_PLAYERS];
11266   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11267   int i;
11268
11269   for (i = 0; i < MAX_PLAYERS; i++)
11270     effective_action[i] = stored_player[i].effective_action;
11271
11272   GameActions_EM(effective_action, warp_mode);
11273 }
11274
11275 void GameActions_SP_Main()
11276 {
11277   byte effective_action[MAX_PLAYERS];
11278   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11279   int i;
11280
11281   for (i = 0; i < MAX_PLAYERS; i++)
11282     effective_action[i] = stored_player[i].effective_action;
11283
11284   GameActions_SP(effective_action, warp_mode);
11285 }
11286
11287 void GameActions_RND_Main()
11288 {
11289   GameActions_RND();
11290 }
11291
11292 void GameActions_RND()
11293 {
11294   int magic_wall_x = 0, magic_wall_y = 0;
11295   int i, x, y, element, graphic, last_gfx_frame;
11296
11297   InitPlayfieldScanModeVars();
11298
11299   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11300   {
11301     SCAN_PLAYFIELD(x, y)
11302     {
11303       ChangeCount[x][y] = 0;
11304       ChangeEvent[x][y] = -1;
11305     }
11306   }
11307
11308   if (game.set_centered_player)
11309   {
11310     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11311
11312     /* switching to "all players" only possible if all players fit to screen */
11313     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11314     {
11315       game.centered_player_nr_next = game.centered_player_nr;
11316       game.set_centered_player = FALSE;
11317     }
11318
11319     /* do not switch focus to non-existing (or non-active) player */
11320     if (game.centered_player_nr_next >= 0 &&
11321         !stored_player[game.centered_player_nr_next].active)
11322     {
11323       game.centered_player_nr_next = game.centered_player_nr;
11324       game.set_centered_player = FALSE;
11325     }
11326   }
11327
11328   if (game.set_centered_player &&
11329       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11330   {
11331     int sx, sy;
11332
11333     if (game.centered_player_nr_next == -1)
11334     {
11335       setScreenCenteredToAllPlayers(&sx, &sy);
11336     }
11337     else
11338     {
11339       sx = stored_player[game.centered_player_nr_next].jx;
11340       sy = stored_player[game.centered_player_nr_next].jy;
11341     }
11342
11343     game.centered_player_nr = game.centered_player_nr_next;
11344     game.set_centered_player = FALSE;
11345
11346     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11347     DrawGameDoorValues();
11348   }
11349
11350   for (i = 0; i < MAX_PLAYERS; i++)
11351   {
11352     int actual_player_action = stored_player[i].effective_action;
11353
11354 #if 1
11355     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11356        - rnd_equinox_tetrachloride 048
11357        - rnd_equinox_tetrachloride_ii 096
11358        - rnd_emanuel_schmieg 002
11359        - doctor_sloan_ww 001, 020
11360     */
11361     if (stored_player[i].MovPos == 0)
11362       CheckGravityMovement(&stored_player[i]);
11363 #endif
11364
11365     /* overwrite programmed action with tape action */
11366     if (stored_player[i].programmed_action)
11367       actual_player_action = stored_player[i].programmed_action;
11368
11369     PlayerActions(&stored_player[i], actual_player_action);
11370
11371     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11372   }
11373
11374   ScrollScreen(NULL, SCROLL_GO_ON);
11375
11376   /* for backwards compatibility, the following code emulates a fixed bug that
11377      occured when pushing elements (causing elements that just made their last
11378      pushing step to already (if possible) make their first falling step in the
11379      same game frame, which is bad); this code is also needed to use the famous
11380      "spring push bug" which is used in older levels and might be wanted to be
11381      used also in newer levels, but in this case the buggy pushing code is only
11382      affecting the "spring" element and no other elements */
11383
11384   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11385   {
11386     for (i = 0; i < MAX_PLAYERS; i++)
11387     {
11388       struct PlayerInfo *player = &stored_player[i];
11389       int x = player->jx;
11390       int y = player->jy;
11391
11392       if (player->active && player->is_pushing && player->is_moving &&
11393           IS_MOVING(x, y) &&
11394           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11395            Feld[x][y] == EL_SPRING))
11396       {
11397         ContinueMoving(x, y);
11398
11399         /* continue moving after pushing (this is actually a bug) */
11400         if (!IS_MOVING(x, y))
11401           Stop[x][y] = FALSE;
11402       }
11403     }
11404   }
11405
11406   SCAN_PLAYFIELD(x, y)
11407   {
11408     ChangeCount[x][y] = 0;
11409     ChangeEvent[x][y] = -1;
11410
11411     /* this must be handled before main playfield loop */
11412     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11413     {
11414       MovDelay[x][y]--;
11415       if (MovDelay[x][y] <= 0)
11416         RemoveField(x, y);
11417     }
11418
11419     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11420     {
11421       MovDelay[x][y]--;
11422       if (MovDelay[x][y] <= 0)
11423       {
11424         RemoveField(x, y);
11425         TEST_DrawLevelField(x, y);
11426
11427         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11428       }
11429     }
11430
11431 #if DEBUG
11432     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11433     {
11434       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11435       printf("GameActions(): This should never happen!\n");
11436
11437       ChangePage[x][y] = -1;
11438     }
11439 #endif
11440
11441     Stop[x][y] = FALSE;
11442     if (WasJustMoving[x][y] > 0)
11443       WasJustMoving[x][y]--;
11444     if (WasJustFalling[x][y] > 0)
11445       WasJustFalling[x][y]--;
11446     if (CheckCollision[x][y] > 0)
11447       CheckCollision[x][y]--;
11448     if (CheckImpact[x][y] > 0)
11449       CheckImpact[x][y]--;
11450
11451     GfxFrame[x][y]++;
11452
11453     /* reset finished pushing action (not done in ContinueMoving() to allow
11454        continuous pushing animation for elements with zero push delay) */
11455     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11456     {
11457       ResetGfxAnimation(x, y);
11458       TEST_DrawLevelField(x, y);
11459     }
11460
11461 #if DEBUG
11462     if (IS_BLOCKED(x, y))
11463     {
11464       int oldx, oldy;
11465
11466       Blocked2Moving(x, y, &oldx, &oldy);
11467       if (!IS_MOVING(oldx, oldy))
11468       {
11469         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11470         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11471         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11472         printf("GameActions(): This should never happen!\n");
11473       }
11474     }
11475 #endif
11476   }
11477
11478   SCAN_PLAYFIELD(x, y)
11479   {
11480     element = Feld[x][y];
11481     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11482     last_gfx_frame = GfxFrame[x][y];
11483
11484     ResetGfxFrame(x, y);
11485
11486     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11487       DrawLevelGraphicAnimation(x, y, graphic);
11488
11489     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11490         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11491       ResetRandomAnimationValue(x, y);
11492
11493     SetRandomAnimationValue(x, y);
11494
11495     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11496
11497     if (IS_INACTIVE(element))
11498     {
11499       if (IS_ANIMATED(graphic))
11500         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11501
11502       continue;
11503     }
11504
11505     /* this may take place after moving, so 'element' may have changed */
11506     if (IS_CHANGING(x, y) &&
11507         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11508     {
11509       int page = element_info[element].event_page_nr[CE_DELAY];
11510
11511       HandleElementChange(x, y, page);
11512
11513       element = Feld[x][y];
11514       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11515     }
11516
11517     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11518     {
11519       StartMoving(x, y);
11520
11521       element = Feld[x][y];
11522       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11523
11524       if (IS_ANIMATED(graphic) &&
11525           !IS_MOVING(x, y) &&
11526           !Stop[x][y])
11527         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11528
11529       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11530         TEST_DrawTwinkleOnField(x, y);
11531     }
11532     else if (element == EL_ACID)
11533     {
11534       if (!Stop[x][y])
11535         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11536     }
11537     else if ((element == EL_EXIT_OPEN ||
11538               element == EL_EM_EXIT_OPEN ||
11539               element == EL_SP_EXIT_OPEN ||
11540               element == EL_STEEL_EXIT_OPEN ||
11541               element == EL_EM_STEEL_EXIT_OPEN ||
11542               element == EL_SP_TERMINAL ||
11543               element == EL_SP_TERMINAL_ACTIVE ||
11544               element == EL_EXTRA_TIME ||
11545               element == EL_SHIELD_NORMAL ||
11546               element == EL_SHIELD_DEADLY) &&
11547              IS_ANIMATED(graphic))
11548       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11549     else if (IS_MOVING(x, y))
11550       ContinueMoving(x, y);
11551     else if (IS_ACTIVE_BOMB(element))
11552       CheckDynamite(x, y);
11553     else if (element == EL_AMOEBA_GROWING)
11554       AmoebeWaechst(x, y);
11555     else if (element == EL_AMOEBA_SHRINKING)
11556       AmoebaDisappearing(x, y);
11557
11558 #if !USE_NEW_AMOEBA_CODE
11559     else if (IS_AMOEBALIVE(element))
11560       AmoebeAbleger(x, y);
11561 #endif
11562
11563     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11564       Life(x, y);
11565     else if (element == EL_EXIT_CLOSED)
11566       CheckExit(x, y);
11567     else if (element == EL_EM_EXIT_CLOSED)
11568       CheckExitEM(x, y);
11569     else if (element == EL_STEEL_EXIT_CLOSED)
11570       CheckExitSteel(x, y);
11571     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11572       CheckExitSteelEM(x, y);
11573     else if (element == EL_SP_EXIT_CLOSED)
11574       CheckExitSP(x, y);
11575     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11576              element == EL_EXPANDABLE_STEELWALL_GROWING)
11577       MauerWaechst(x, y);
11578     else if (element == EL_EXPANDABLE_WALL ||
11579              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11580              element == EL_EXPANDABLE_WALL_VERTICAL ||
11581              element == EL_EXPANDABLE_WALL_ANY ||
11582              element == EL_BD_EXPANDABLE_WALL)
11583       MauerAbleger(x, y);
11584     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11585              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11586              element == EL_EXPANDABLE_STEELWALL_ANY)
11587       MauerAblegerStahl(x, y);
11588     else if (element == EL_FLAMES)
11589       CheckForDragon(x, y);
11590     else if (element == EL_EXPLOSION)
11591       ; /* drawing of correct explosion animation is handled separately */
11592     else if (element == EL_ELEMENT_SNAPPING ||
11593              element == EL_DIAGONAL_SHRINKING ||
11594              element == EL_DIAGONAL_GROWING)
11595     {
11596       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11597
11598       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11599     }
11600     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11601       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11602
11603     if (IS_BELT_ACTIVE(element))
11604       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11605
11606     if (game.magic_wall_active)
11607     {
11608       int jx = local_player->jx, jy = local_player->jy;
11609
11610       /* play the element sound at the position nearest to the player */
11611       if ((element == EL_MAGIC_WALL_FULL ||
11612            element == EL_MAGIC_WALL_ACTIVE ||
11613            element == EL_MAGIC_WALL_EMPTYING ||
11614            element == EL_BD_MAGIC_WALL_FULL ||
11615            element == EL_BD_MAGIC_WALL_ACTIVE ||
11616            element == EL_BD_MAGIC_WALL_EMPTYING ||
11617            element == EL_DC_MAGIC_WALL_FULL ||
11618            element == EL_DC_MAGIC_WALL_ACTIVE ||
11619            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11620           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11621       {
11622         magic_wall_x = x;
11623         magic_wall_y = y;
11624       }
11625     }
11626   }
11627
11628 #if USE_NEW_AMOEBA_CODE
11629   /* new experimental amoeba growth stuff */
11630   if (!(FrameCounter % 8))
11631   {
11632     static unsigned int random = 1684108901;
11633
11634     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11635     {
11636       x = RND(lev_fieldx);
11637       y = RND(lev_fieldy);
11638       element = Feld[x][y];
11639
11640       if (!IS_PLAYER(x,y) &&
11641           (element == EL_EMPTY ||
11642            CAN_GROW_INTO(element) ||
11643            element == EL_QUICKSAND_EMPTY ||
11644            element == EL_QUICKSAND_FAST_EMPTY ||
11645            element == EL_ACID_SPLASH_LEFT ||
11646            element == EL_ACID_SPLASH_RIGHT))
11647       {
11648         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11649             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11650             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11651             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11652           Feld[x][y] = EL_AMOEBA_DROP;
11653       }
11654
11655       random = random * 129 + 1;
11656     }
11657   }
11658 #endif
11659
11660   game.explosions_delayed = FALSE;
11661
11662   SCAN_PLAYFIELD(x, y)
11663   {
11664     element = Feld[x][y];
11665
11666     if (ExplodeField[x][y])
11667       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11668     else if (element == EL_EXPLOSION)
11669       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11670
11671     ExplodeField[x][y] = EX_TYPE_NONE;
11672   }
11673
11674   game.explosions_delayed = TRUE;
11675
11676   if (game.magic_wall_active)
11677   {
11678     if (!(game.magic_wall_time_left % 4))
11679     {
11680       int element = Feld[magic_wall_x][magic_wall_y];
11681
11682       if (element == EL_BD_MAGIC_WALL_FULL ||
11683           element == EL_BD_MAGIC_WALL_ACTIVE ||
11684           element == EL_BD_MAGIC_WALL_EMPTYING)
11685         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11686       else if (element == EL_DC_MAGIC_WALL_FULL ||
11687                element == EL_DC_MAGIC_WALL_ACTIVE ||
11688                element == EL_DC_MAGIC_WALL_EMPTYING)
11689         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11690       else
11691         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11692     }
11693
11694     if (game.magic_wall_time_left > 0)
11695     {
11696       game.magic_wall_time_left--;
11697
11698       if (!game.magic_wall_time_left)
11699       {
11700         SCAN_PLAYFIELD(x, y)
11701         {
11702           element = Feld[x][y];
11703
11704           if (element == EL_MAGIC_WALL_ACTIVE ||
11705               element == EL_MAGIC_WALL_FULL)
11706           {
11707             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11708             TEST_DrawLevelField(x, y);
11709           }
11710           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11711                    element == EL_BD_MAGIC_WALL_FULL)
11712           {
11713             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11714             TEST_DrawLevelField(x, y);
11715           }
11716           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11717                    element == EL_DC_MAGIC_WALL_FULL)
11718           {
11719             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11720             TEST_DrawLevelField(x, y);
11721           }
11722         }
11723
11724         game.magic_wall_active = FALSE;
11725       }
11726     }
11727   }
11728
11729   if (game.light_time_left > 0)
11730   {
11731     game.light_time_left--;
11732
11733     if (game.light_time_left == 0)
11734       RedrawAllLightSwitchesAndInvisibleElements();
11735   }
11736
11737   if (game.timegate_time_left > 0)
11738   {
11739     game.timegate_time_left--;
11740
11741     if (game.timegate_time_left == 0)
11742       CloseAllOpenTimegates();
11743   }
11744
11745   if (game.lenses_time_left > 0)
11746   {
11747     game.lenses_time_left--;
11748
11749     if (game.lenses_time_left == 0)
11750       RedrawAllInvisibleElementsForLenses();
11751   }
11752
11753   if (game.magnify_time_left > 0)
11754   {
11755     game.magnify_time_left--;
11756
11757     if (game.magnify_time_left == 0)
11758       RedrawAllInvisibleElementsForMagnifier();
11759   }
11760
11761   for (i = 0; i < MAX_PLAYERS; i++)
11762   {
11763     struct PlayerInfo *player = &stored_player[i];
11764
11765     if (SHIELD_ON(player))
11766     {
11767       if (player->shield_deadly_time_left)
11768         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11769       else if (player->shield_normal_time_left)
11770         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11771     }
11772   }
11773
11774 #if USE_DELAYED_GFX_REDRAW
11775   SCAN_PLAYFIELD(x, y)
11776   {
11777     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11778     {
11779       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11780          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11781
11782       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11783         DrawLevelField(x, y);
11784
11785       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11786         DrawLevelFieldCrumbled(x, y);
11787
11788       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11789         DrawLevelFieldCrumbledNeighbours(x, y);
11790
11791       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11792         DrawTwinkleOnField(x, y);
11793     }
11794
11795     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11796   }
11797 #endif
11798
11799   DrawAllPlayers();
11800   PlayAllPlayersSound();
11801
11802   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11803   {
11804     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11805
11806     local_player->show_envelope = 0;
11807   }
11808
11809   /* use random number generator in every frame to make it less predictable */
11810   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11811     RND(1);
11812 }
11813
11814 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11815 {
11816   int min_x = x, min_y = y, max_x = x, max_y = y;
11817   int i;
11818
11819   for (i = 0; i < MAX_PLAYERS; i++)
11820   {
11821     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11822
11823     if (!stored_player[i].active || &stored_player[i] == player)
11824       continue;
11825
11826     min_x = MIN(min_x, jx);
11827     min_y = MIN(min_y, jy);
11828     max_x = MAX(max_x, jx);
11829     max_y = MAX(max_y, jy);
11830   }
11831
11832   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11833 }
11834
11835 static boolean AllPlayersInVisibleScreen()
11836 {
11837   int i;
11838
11839   for (i = 0; i < MAX_PLAYERS; i++)
11840   {
11841     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11842
11843     if (!stored_player[i].active)
11844       continue;
11845
11846     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11847       return FALSE;
11848   }
11849
11850   return TRUE;
11851 }
11852
11853 void ScrollLevel(int dx, int dy)
11854 {
11855   int scroll_offset = 2 * TILEX_VAR;
11856   int x, y;
11857
11858   BlitBitmap(drawto_field, drawto_field,
11859              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11860              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11861              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11862              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11863              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11864              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11865
11866   if (dx != 0)
11867   {
11868     x = (dx == 1 ? BX1 : BX2);
11869     for (y = BY1; y <= BY2; y++)
11870       DrawScreenField(x, y);
11871   }
11872
11873   if (dy != 0)
11874   {
11875     y = (dy == 1 ? BY1 : BY2);
11876     for (x = BX1; x <= BX2; x++)
11877       DrawScreenField(x, y);
11878   }
11879
11880   redraw_mask |= REDRAW_FIELD;
11881 }
11882
11883 static boolean canFallDown(struct PlayerInfo *player)
11884 {
11885   int jx = player->jx, jy = player->jy;
11886
11887   return (IN_LEV_FIELD(jx, jy + 1) &&
11888           (IS_FREE(jx, jy + 1) ||
11889            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11890           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11891           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11892 }
11893
11894 static boolean canPassField(int x, int y, int move_dir)
11895 {
11896   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11897   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11898   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11899   int nextx = x + dx;
11900   int nexty = y + dy;
11901   int element = Feld[x][y];
11902
11903   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11904           !CAN_MOVE(element) &&
11905           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11906           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11907           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11908 }
11909
11910 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11911 {
11912   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11913   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11914   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11915   int newx = x + dx;
11916   int newy = y + dy;
11917
11918   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11919           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11920           (IS_DIGGABLE(Feld[newx][newy]) ||
11921            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11922            canPassField(newx, newy, move_dir)));
11923 }
11924
11925 static void CheckGravityMovement(struct PlayerInfo *player)
11926 {
11927   if (player->gravity && !player->programmed_action)
11928   {
11929     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11930     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11931     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11932     int jx = player->jx, jy = player->jy;
11933     boolean player_is_moving_to_valid_field =
11934       (!player_is_snapping &&
11935        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11936         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11937     boolean player_can_fall_down = canFallDown(player);
11938
11939     if (player_can_fall_down &&
11940         !player_is_moving_to_valid_field)
11941       player->programmed_action = MV_DOWN;
11942   }
11943 }
11944
11945 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11946 {
11947   return CheckGravityMovement(player);
11948
11949   if (player->gravity && !player->programmed_action)
11950   {
11951     int jx = player->jx, jy = player->jy;
11952     boolean field_under_player_is_free =
11953       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11954     boolean player_is_standing_on_valid_field =
11955       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11956        (IS_WALKABLE(Feld[jx][jy]) &&
11957         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11958
11959     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11960       player->programmed_action = MV_DOWN;
11961   }
11962 }
11963
11964 /*
11965   MovePlayerOneStep()
11966   -----------------------------------------------------------------------------
11967   dx, dy:               direction (non-diagonal) to try to move the player to
11968   real_dx, real_dy:     direction as read from input device (can be diagonal)
11969 */
11970
11971 boolean MovePlayerOneStep(struct PlayerInfo *player,
11972                           int dx, int dy, int real_dx, int real_dy)
11973 {
11974   int jx = player->jx, jy = player->jy;
11975   int new_jx = jx + dx, new_jy = jy + dy;
11976   int can_move;
11977   boolean player_can_move = !player->cannot_move;
11978
11979   if (!player->active || (!dx && !dy))
11980     return MP_NO_ACTION;
11981
11982   player->MovDir = (dx < 0 ? MV_LEFT :
11983                     dx > 0 ? MV_RIGHT :
11984                     dy < 0 ? MV_UP :
11985                     dy > 0 ? MV_DOWN :  MV_NONE);
11986
11987   if (!IN_LEV_FIELD(new_jx, new_jy))
11988     return MP_NO_ACTION;
11989
11990   if (!player_can_move)
11991   {
11992     if (player->MovPos == 0)
11993     {
11994       player->is_moving = FALSE;
11995       player->is_digging = FALSE;
11996       player->is_collecting = FALSE;
11997       player->is_snapping = FALSE;
11998       player->is_pushing = FALSE;
11999     }
12000   }
12001
12002   if (!options.network && game.centered_player_nr == -1 &&
12003       !AllPlayersInSight(player, new_jx, new_jy))
12004     return MP_NO_ACTION;
12005
12006   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12007   if (can_move != MP_MOVING)
12008     return can_move;
12009
12010   /* check if DigField() has caused relocation of the player */
12011   if (player->jx != jx || player->jy != jy)
12012     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12013
12014   StorePlayer[jx][jy] = 0;
12015   player->last_jx = jx;
12016   player->last_jy = jy;
12017   player->jx = new_jx;
12018   player->jy = new_jy;
12019   StorePlayer[new_jx][new_jy] = player->element_nr;
12020
12021   if (player->move_delay_value_next != -1)
12022   {
12023     player->move_delay_value = player->move_delay_value_next;
12024     player->move_delay_value_next = -1;
12025   }
12026
12027   player->MovPos =
12028     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12029
12030   player->step_counter++;
12031
12032   PlayerVisit[jx][jy] = FrameCounter;
12033
12034   player->is_moving = TRUE;
12035
12036 #if 1
12037   /* should better be called in MovePlayer(), but this breaks some tapes */
12038   ScrollPlayer(player, SCROLL_INIT);
12039 #endif
12040
12041   return MP_MOVING;
12042 }
12043
12044 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12045 {
12046   int jx = player->jx, jy = player->jy;
12047   int old_jx = jx, old_jy = jy;
12048   int moved = MP_NO_ACTION;
12049
12050   if (!player->active)
12051     return FALSE;
12052
12053   if (!dx && !dy)
12054   {
12055     if (player->MovPos == 0)
12056     {
12057       player->is_moving = FALSE;
12058       player->is_digging = FALSE;
12059       player->is_collecting = FALSE;
12060       player->is_snapping = FALSE;
12061       player->is_pushing = FALSE;
12062     }
12063
12064     return FALSE;
12065   }
12066
12067   if (player->move_delay > 0)
12068     return FALSE;
12069
12070   player->move_delay = -1;              /* set to "uninitialized" value */
12071
12072   /* store if player is automatically moved to next field */
12073   player->is_auto_moving = (player->programmed_action != MV_NONE);
12074
12075   /* remove the last programmed player action */
12076   player->programmed_action = 0;
12077
12078   if (player->MovPos)
12079   {
12080     /* should only happen if pre-1.2 tape recordings are played */
12081     /* this is only for backward compatibility */
12082
12083     int original_move_delay_value = player->move_delay_value;
12084
12085 #if DEBUG
12086     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12087            tape.counter);
12088 #endif
12089
12090     /* scroll remaining steps with finest movement resolution */
12091     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12092
12093     while (player->MovPos)
12094     {
12095       ScrollPlayer(player, SCROLL_GO_ON);
12096       ScrollScreen(NULL, SCROLL_GO_ON);
12097
12098       AdvanceFrameAndPlayerCounters(player->index_nr);
12099
12100       DrawAllPlayers();
12101       BackToFront_WithFrameDelay(0);
12102     }
12103
12104     player->move_delay_value = original_move_delay_value;
12105   }
12106
12107   player->is_active = FALSE;
12108
12109   if (player->last_move_dir & MV_HORIZONTAL)
12110   {
12111     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12112       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12113   }
12114   else
12115   {
12116     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12117       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12118   }
12119
12120   if (!moved && !player->is_active)
12121   {
12122     player->is_moving = FALSE;
12123     player->is_digging = FALSE;
12124     player->is_collecting = FALSE;
12125     player->is_snapping = FALSE;
12126     player->is_pushing = FALSE;
12127   }
12128
12129   jx = player->jx;
12130   jy = player->jy;
12131
12132   if (moved & MP_MOVING && !ScreenMovPos &&
12133       (player->index_nr == game.centered_player_nr ||
12134        game.centered_player_nr == -1))
12135   {
12136     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12137     int offset = game.scroll_delay_value;
12138
12139     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12140     {
12141       /* actual player has left the screen -- scroll in that direction */
12142       if (jx != old_jx)         /* player has moved horizontally */
12143         scroll_x += (jx - old_jx);
12144       else                      /* player has moved vertically */
12145         scroll_y += (jy - old_jy);
12146     }
12147     else
12148     {
12149       if (jx != old_jx)         /* player has moved horizontally */
12150       {
12151         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12152             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12153           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12154
12155         /* don't scroll over playfield boundaries */
12156         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12157           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12158
12159         /* don't scroll more than one field at a time */
12160         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12161
12162         /* don't scroll against the player's moving direction */
12163         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12164             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12165           scroll_x = old_scroll_x;
12166       }
12167       else                      /* player has moved vertically */
12168       {
12169         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12170             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12171           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12172
12173         /* don't scroll over playfield boundaries */
12174         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12175           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12176
12177         /* don't scroll more than one field at a time */
12178         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12179
12180         /* don't scroll against the player's moving direction */
12181         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12182             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12183           scroll_y = old_scroll_y;
12184       }
12185     }
12186
12187     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12188     {
12189       if (!options.network && game.centered_player_nr == -1 &&
12190           !AllPlayersInVisibleScreen())
12191       {
12192         scroll_x = old_scroll_x;
12193         scroll_y = old_scroll_y;
12194       }
12195       else
12196       {
12197         ScrollScreen(player, SCROLL_INIT);
12198         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12199       }
12200     }
12201   }
12202
12203   player->StepFrame = 0;
12204
12205   if (moved & MP_MOVING)
12206   {
12207     if (old_jx != jx && old_jy == jy)
12208       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12209     else if (old_jx == jx && old_jy != jy)
12210       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12211
12212     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12213
12214     player->last_move_dir = player->MovDir;
12215     player->is_moving = TRUE;
12216     player->is_snapping = FALSE;
12217     player->is_switching = FALSE;
12218     player->is_dropping = FALSE;
12219     player->is_dropping_pressed = FALSE;
12220     player->drop_pressed_delay = 0;
12221
12222 #if 0
12223     /* should better be called here than above, but this breaks some tapes */
12224     ScrollPlayer(player, SCROLL_INIT);
12225 #endif
12226   }
12227   else
12228   {
12229     CheckGravityMovementWhenNotMoving(player);
12230
12231     player->is_moving = FALSE;
12232
12233     /* at this point, the player is allowed to move, but cannot move right now
12234        (e.g. because of something blocking the way) -- ensure that the player
12235        is also allowed to move in the next frame (in old versions before 3.1.1,
12236        the player was forced to wait again for eight frames before next try) */
12237
12238     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12239       player->move_delay = 0;   /* allow direct movement in the next frame */
12240   }
12241
12242   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12243     player->move_delay = player->move_delay_value;
12244
12245   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12246   {
12247     TestIfPlayerTouchesBadThing(jx, jy);
12248     TestIfPlayerTouchesCustomElement(jx, jy);
12249   }
12250
12251   if (!player->active)
12252     RemovePlayer(player);
12253
12254   return moved;
12255 }
12256
12257 void ScrollPlayer(struct PlayerInfo *player, int mode)
12258 {
12259   int jx = player->jx, jy = player->jy;
12260   int last_jx = player->last_jx, last_jy = player->last_jy;
12261   int move_stepsize = TILEX / player->move_delay_value;
12262
12263   if (!player->active)
12264     return;
12265
12266   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12267     return;
12268
12269   if (mode == SCROLL_INIT)
12270   {
12271     player->actual_frame_counter = FrameCounter;
12272     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12273
12274     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12275         Feld[last_jx][last_jy] == EL_EMPTY)
12276     {
12277       int last_field_block_delay = 0;   /* start with no blocking at all */
12278       int block_delay_adjustment = player->block_delay_adjustment;
12279
12280       /* if player blocks last field, add delay for exactly one move */
12281       if (player->block_last_field)
12282       {
12283         last_field_block_delay += player->move_delay_value;
12284
12285         /* when blocking enabled, prevent moving up despite gravity */
12286         if (player->gravity && player->MovDir == MV_UP)
12287           block_delay_adjustment = -1;
12288       }
12289
12290       /* add block delay adjustment (also possible when not blocking) */
12291       last_field_block_delay += block_delay_adjustment;
12292
12293       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12294       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12295     }
12296
12297     if (player->MovPos != 0)    /* player has not yet reached destination */
12298       return;
12299   }
12300   else if (!FrameReached(&player->actual_frame_counter, 1))
12301     return;
12302
12303   if (player->MovPos != 0)
12304   {
12305     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12306     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12307
12308     /* before DrawPlayer() to draw correct player graphic for this case */
12309     if (player->MovPos == 0)
12310       CheckGravityMovement(player);
12311   }
12312
12313   if (player->MovPos == 0)      /* player reached destination field */
12314   {
12315     if (player->move_delay_reset_counter > 0)
12316     {
12317       player->move_delay_reset_counter--;
12318
12319       if (player->move_delay_reset_counter == 0)
12320       {
12321         /* continue with normal speed after quickly moving through gate */
12322         HALVE_PLAYER_SPEED(player);
12323
12324         /* be able to make the next move without delay */
12325         player->move_delay = 0;
12326       }
12327     }
12328
12329     player->last_jx = jx;
12330     player->last_jy = jy;
12331
12332     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12333         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12334         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12335         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12336         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12337         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12338         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12339         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12340     {
12341       DrawPlayer(player);       /* needed here only to cleanup last field */
12342       RemovePlayer(player);
12343
12344       if (local_player->friends_still_needed == 0 ||
12345           IS_SP_ELEMENT(Feld[jx][jy]))
12346         PlayerWins(player);
12347     }
12348
12349     /* this breaks one level: "machine", level 000 */
12350     {
12351       int move_direction = player->MovDir;
12352       int enter_side = MV_DIR_OPPOSITE(move_direction);
12353       int leave_side = move_direction;
12354       int old_jx = last_jx;
12355       int old_jy = last_jy;
12356       int old_element = Feld[old_jx][old_jy];
12357       int new_element = Feld[jx][jy];
12358
12359       if (IS_CUSTOM_ELEMENT(old_element))
12360         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12361                                    CE_LEFT_BY_PLAYER,
12362                                    player->index_bit, leave_side);
12363
12364       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12365                                           CE_PLAYER_LEAVES_X,
12366                                           player->index_bit, leave_side);
12367
12368       if (IS_CUSTOM_ELEMENT(new_element))
12369         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12370                                    player->index_bit, enter_side);
12371
12372       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12373                                           CE_PLAYER_ENTERS_X,
12374                                           player->index_bit, enter_side);
12375
12376       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12377                                         CE_MOVE_OF_X, move_direction);
12378     }
12379
12380     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12381     {
12382       TestIfPlayerTouchesBadThing(jx, jy);
12383       TestIfPlayerTouchesCustomElement(jx, jy);
12384
12385       /* needed because pushed element has not yet reached its destination,
12386          so it would trigger a change event at its previous field location */
12387       if (!player->is_pushing)
12388         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12389
12390       if (!player->active)
12391         RemovePlayer(player);
12392     }
12393
12394     if (!local_player->LevelSolved && level.use_step_counter)
12395     {
12396       int i;
12397
12398       TimePlayed++;
12399
12400       if (TimeLeft > 0)
12401       {
12402         TimeLeft--;
12403
12404         if (TimeLeft <= 10 && setup.time_limit)
12405           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12406
12407         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12408
12409         DisplayGameControlValues();
12410
12411         if (!TimeLeft && setup.time_limit)
12412           for (i = 0; i < MAX_PLAYERS; i++)
12413             KillPlayer(&stored_player[i]);
12414       }
12415       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12416       {
12417         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12418
12419         DisplayGameControlValues();
12420       }
12421     }
12422
12423     if (tape.single_step && tape.recording && !tape.pausing &&
12424         !player->programmed_action)
12425       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12426
12427     if (!player->programmed_action)
12428       CheckSaveEngineSnapshot(player);
12429   }
12430 }
12431
12432 void ScrollScreen(struct PlayerInfo *player, int mode)
12433 {
12434   static unsigned int screen_frame_counter = 0;
12435
12436   if (mode == SCROLL_INIT)
12437   {
12438     /* set scrolling step size according to actual player's moving speed */
12439     ScrollStepSize = TILEX / player->move_delay_value;
12440
12441     screen_frame_counter = FrameCounter;
12442     ScreenMovDir = player->MovDir;
12443     ScreenMovPos = player->MovPos;
12444     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12445     return;
12446   }
12447   else if (!FrameReached(&screen_frame_counter, 1))
12448     return;
12449
12450   if (ScreenMovPos)
12451   {
12452     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12453     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12454     redraw_mask |= REDRAW_FIELD;
12455   }
12456   else
12457     ScreenMovDir = MV_NONE;
12458 }
12459
12460 void TestIfPlayerTouchesCustomElement(int x, int y)
12461 {
12462   static int xy[4][2] =
12463   {
12464     { 0, -1 },
12465     { -1, 0 },
12466     { +1, 0 },
12467     { 0, +1 }
12468   };
12469   static int trigger_sides[4][2] =
12470   {
12471     /* center side       border side */
12472     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12473     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12474     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12475     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12476   };
12477   static int touch_dir[4] =
12478   {
12479     MV_LEFT | MV_RIGHT,
12480     MV_UP   | MV_DOWN,
12481     MV_UP   | MV_DOWN,
12482     MV_LEFT | MV_RIGHT
12483   };
12484   int center_element = Feld[x][y];      /* should always be non-moving! */
12485   int i;
12486
12487   for (i = 0; i < NUM_DIRECTIONS; i++)
12488   {
12489     int xx = x + xy[i][0];
12490     int yy = y + xy[i][1];
12491     int center_side = trigger_sides[i][0];
12492     int border_side = trigger_sides[i][1];
12493     int border_element;
12494
12495     if (!IN_LEV_FIELD(xx, yy))
12496       continue;
12497
12498     if (IS_PLAYER(x, y))                /* player found at center element */
12499     {
12500       struct PlayerInfo *player = PLAYERINFO(x, y);
12501
12502       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12503         border_element = Feld[xx][yy];          /* may be moving! */
12504       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12505         border_element = Feld[xx][yy];
12506       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12507         border_element = MovingOrBlocked2Element(xx, yy);
12508       else
12509         continue;               /* center and border element do not touch */
12510
12511       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12512                                  player->index_bit, border_side);
12513       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12514                                           CE_PLAYER_TOUCHES_X,
12515                                           player->index_bit, border_side);
12516
12517       {
12518         /* use player element that is initially defined in the level playfield,
12519            not the player element that corresponds to the runtime player number
12520            (example: a level that contains EL_PLAYER_3 as the only player would
12521            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12522         int player_element = PLAYERINFO(x, y)->initial_element;
12523
12524         CheckElementChangeBySide(xx, yy, border_element, player_element,
12525                                  CE_TOUCHING_X, border_side);
12526       }
12527     }
12528     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12529     {
12530       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12531
12532       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12533       {
12534         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12535           continue;             /* center and border element do not touch */
12536       }
12537
12538       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12539                                  player->index_bit, center_side);
12540       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12541                                           CE_PLAYER_TOUCHES_X,
12542                                           player->index_bit, center_side);
12543
12544       {
12545         /* use player element that is initially defined in the level playfield,
12546            not the player element that corresponds to the runtime player number
12547            (example: a level that contains EL_PLAYER_3 as the only player would
12548            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12549         int player_element = PLAYERINFO(xx, yy)->initial_element;
12550
12551         CheckElementChangeBySide(x, y, center_element, player_element,
12552                                  CE_TOUCHING_X, center_side);
12553       }
12554
12555       break;
12556     }
12557   }
12558 }
12559
12560 void TestIfElementTouchesCustomElement(int x, int y)
12561 {
12562   static int xy[4][2] =
12563   {
12564     { 0, -1 },
12565     { -1, 0 },
12566     { +1, 0 },
12567     { 0, +1 }
12568   };
12569   static int trigger_sides[4][2] =
12570   {
12571     /* center side      border side */
12572     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12573     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12574     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12575     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12576   };
12577   static int touch_dir[4] =
12578   {
12579     MV_LEFT | MV_RIGHT,
12580     MV_UP   | MV_DOWN,
12581     MV_UP   | MV_DOWN,
12582     MV_LEFT | MV_RIGHT
12583   };
12584   boolean change_center_element = FALSE;
12585   int center_element = Feld[x][y];      /* should always be non-moving! */
12586   int border_element_old[NUM_DIRECTIONS];
12587   int i;
12588
12589   for (i = 0; i < NUM_DIRECTIONS; i++)
12590   {
12591     int xx = x + xy[i][0];
12592     int yy = y + xy[i][1];
12593     int border_element;
12594
12595     border_element_old[i] = -1;
12596
12597     if (!IN_LEV_FIELD(xx, yy))
12598       continue;
12599
12600     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12601       border_element = Feld[xx][yy];    /* may be moving! */
12602     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12603       border_element = Feld[xx][yy];
12604     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12605       border_element = MovingOrBlocked2Element(xx, yy);
12606     else
12607       continue;                 /* center and border element do not touch */
12608
12609     border_element_old[i] = border_element;
12610   }
12611
12612   for (i = 0; i < NUM_DIRECTIONS; i++)
12613   {
12614     int xx = x + xy[i][0];
12615     int yy = y + xy[i][1];
12616     int center_side = trigger_sides[i][0];
12617     int border_element = border_element_old[i];
12618
12619     if (border_element == -1)
12620       continue;
12621
12622     /* check for change of border element */
12623     CheckElementChangeBySide(xx, yy, border_element, center_element,
12624                              CE_TOUCHING_X, center_side);
12625
12626     /* (center element cannot be player, so we dont have to check this here) */
12627   }
12628
12629   for (i = 0; i < NUM_DIRECTIONS; i++)
12630   {
12631     int xx = x + xy[i][0];
12632     int yy = y + xy[i][1];
12633     int border_side = trigger_sides[i][1];
12634     int border_element = border_element_old[i];
12635
12636     if (border_element == -1)
12637       continue;
12638
12639     /* check for change of center element (but change it only once) */
12640     if (!change_center_element)
12641       change_center_element =
12642         CheckElementChangeBySide(x, y, center_element, border_element,
12643                                  CE_TOUCHING_X, border_side);
12644
12645     if (IS_PLAYER(xx, yy))
12646     {
12647       /* use player element that is initially defined in the level playfield,
12648          not the player element that corresponds to the runtime player number
12649          (example: a level that contains EL_PLAYER_3 as the only player would
12650          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12651       int player_element = PLAYERINFO(xx, yy)->initial_element;
12652
12653       CheckElementChangeBySide(x, y, center_element, player_element,
12654                                CE_TOUCHING_X, border_side);
12655     }
12656   }
12657 }
12658
12659 void TestIfElementHitsCustomElement(int x, int y, int direction)
12660 {
12661   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12662   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12663   int hitx = x + dx, hity = y + dy;
12664   int hitting_element = Feld[x][y];
12665   int touched_element;
12666
12667   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12668     return;
12669
12670   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12671                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12672
12673   if (IN_LEV_FIELD(hitx, hity))
12674   {
12675     int opposite_direction = MV_DIR_OPPOSITE(direction);
12676     int hitting_side = direction;
12677     int touched_side = opposite_direction;
12678     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12679                           MovDir[hitx][hity] != direction ||
12680                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12681
12682     object_hit = TRUE;
12683
12684     if (object_hit)
12685     {
12686       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12687                                CE_HITTING_X, touched_side);
12688
12689       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12690                                CE_HIT_BY_X, hitting_side);
12691
12692       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12693                                CE_HIT_BY_SOMETHING, opposite_direction);
12694
12695       if (IS_PLAYER(hitx, hity))
12696       {
12697         /* use player element that is initially defined in the level playfield,
12698            not the player element that corresponds to the runtime player number
12699            (example: a level that contains EL_PLAYER_3 as the only player would
12700            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12701         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12702
12703         CheckElementChangeBySide(x, y, hitting_element, player_element,
12704                                  CE_HITTING_X, touched_side);
12705       }
12706     }
12707   }
12708
12709   /* "hitting something" is also true when hitting the playfield border */
12710   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12711                            CE_HITTING_SOMETHING, direction);
12712 }
12713
12714 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12715 {
12716   int i, kill_x = -1, kill_y = -1;
12717
12718   int bad_element = -1;
12719   static int test_xy[4][2] =
12720   {
12721     { 0, -1 },
12722     { -1, 0 },
12723     { +1, 0 },
12724     { 0, +1 }
12725   };
12726   static int test_dir[4] =
12727   {
12728     MV_UP,
12729     MV_LEFT,
12730     MV_RIGHT,
12731     MV_DOWN
12732   };
12733
12734   for (i = 0; i < NUM_DIRECTIONS; i++)
12735   {
12736     int test_x, test_y, test_move_dir, test_element;
12737
12738     test_x = good_x + test_xy[i][0];
12739     test_y = good_y + test_xy[i][1];
12740
12741     if (!IN_LEV_FIELD(test_x, test_y))
12742       continue;
12743
12744     test_move_dir =
12745       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12746
12747     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12748
12749     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12750        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12751     */
12752     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12753         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12754     {
12755       kill_x = test_x;
12756       kill_y = test_y;
12757       bad_element = test_element;
12758
12759       break;
12760     }
12761   }
12762
12763   if (kill_x != -1 || kill_y != -1)
12764   {
12765     if (IS_PLAYER(good_x, good_y))
12766     {
12767       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12768
12769       if (player->shield_deadly_time_left > 0 &&
12770           !IS_INDESTRUCTIBLE(bad_element))
12771         Bang(kill_x, kill_y);
12772       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12773         KillPlayer(player);
12774     }
12775     else
12776       Bang(good_x, good_y);
12777   }
12778 }
12779
12780 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12781 {
12782   int i, kill_x = -1, kill_y = -1;
12783   int bad_element = Feld[bad_x][bad_y];
12784   static int test_xy[4][2] =
12785   {
12786     { 0, -1 },
12787     { -1, 0 },
12788     { +1, 0 },
12789     { 0, +1 }
12790   };
12791   static int touch_dir[4] =
12792   {
12793     MV_LEFT | MV_RIGHT,
12794     MV_UP   | MV_DOWN,
12795     MV_UP   | MV_DOWN,
12796     MV_LEFT | MV_RIGHT
12797   };
12798   static int test_dir[4] =
12799   {
12800     MV_UP,
12801     MV_LEFT,
12802     MV_RIGHT,
12803     MV_DOWN
12804   };
12805
12806   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12807     return;
12808
12809   for (i = 0; i < NUM_DIRECTIONS; i++)
12810   {
12811     int test_x, test_y, test_move_dir, test_element;
12812
12813     test_x = bad_x + test_xy[i][0];
12814     test_y = bad_y + test_xy[i][1];
12815
12816     if (!IN_LEV_FIELD(test_x, test_y))
12817       continue;
12818
12819     test_move_dir =
12820       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12821
12822     test_element = Feld[test_x][test_y];
12823
12824     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12825        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12826     */
12827     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12828         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12829     {
12830       /* good thing is player or penguin that does not move away */
12831       if (IS_PLAYER(test_x, test_y))
12832       {
12833         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12834
12835         if (bad_element == EL_ROBOT && player->is_moving)
12836           continue;     /* robot does not kill player if he is moving */
12837
12838         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12839         {
12840           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12841             continue;           /* center and border element do not touch */
12842         }
12843
12844         kill_x = test_x;
12845         kill_y = test_y;
12846
12847         break;
12848       }
12849       else if (test_element == EL_PENGUIN)
12850       {
12851         kill_x = test_x;
12852         kill_y = test_y;
12853
12854         break;
12855       }
12856     }
12857   }
12858
12859   if (kill_x != -1 || kill_y != -1)
12860   {
12861     if (IS_PLAYER(kill_x, kill_y))
12862     {
12863       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12864
12865       if (player->shield_deadly_time_left > 0 &&
12866           !IS_INDESTRUCTIBLE(bad_element))
12867         Bang(bad_x, bad_y);
12868       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12869         KillPlayer(player);
12870     }
12871     else
12872       Bang(kill_x, kill_y);
12873   }
12874 }
12875
12876 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12877 {
12878   int bad_element = Feld[bad_x][bad_y];
12879   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12880   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12881   int test_x = bad_x + dx, test_y = bad_y + dy;
12882   int test_move_dir, test_element;
12883   int kill_x = -1, kill_y = -1;
12884
12885   if (!IN_LEV_FIELD(test_x, test_y))
12886     return;
12887
12888   test_move_dir =
12889     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12890
12891   test_element = Feld[test_x][test_y];
12892
12893   if (test_move_dir != bad_move_dir)
12894   {
12895     /* good thing can be player or penguin that does not move away */
12896     if (IS_PLAYER(test_x, test_y))
12897     {
12898       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12899
12900       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12901          player as being hit when he is moving towards the bad thing, because
12902          the "get hit by" condition would be lost after the player stops) */
12903       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12904         return;         /* player moves away from bad thing */
12905
12906       kill_x = test_x;
12907       kill_y = test_y;
12908     }
12909     else if (test_element == EL_PENGUIN)
12910     {
12911       kill_x = test_x;
12912       kill_y = test_y;
12913     }
12914   }
12915
12916   if (kill_x != -1 || kill_y != -1)
12917   {
12918     if (IS_PLAYER(kill_x, kill_y))
12919     {
12920       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12921
12922       if (player->shield_deadly_time_left > 0 &&
12923           !IS_INDESTRUCTIBLE(bad_element))
12924         Bang(bad_x, bad_y);
12925       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12926         KillPlayer(player);
12927     }
12928     else
12929       Bang(kill_x, kill_y);
12930   }
12931 }
12932
12933 void TestIfPlayerTouchesBadThing(int x, int y)
12934 {
12935   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12936 }
12937
12938 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12939 {
12940   TestIfGoodThingHitsBadThing(x, y, move_dir);
12941 }
12942
12943 void TestIfBadThingTouchesPlayer(int x, int y)
12944 {
12945   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12946 }
12947
12948 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12949 {
12950   TestIfBadThingHitsGoodThing(x, y, move_dir);
12951 }
12952
12953 void TestIfFriendTouchesBadThing(int x, int y)
12954 {
12955   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12956 }
12957
12958 void TestIfBadThingTouchesFriend(int x, int y)
12959 {
12960   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12961 }
12962
12963 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12964 {
12965   int i, kill_x = bad_x, kill_y = bad_y;
12966   static int xy[4][2] =
12967   {
12968     { 0, -1 },
12969     { -1, 0 },
12970     { +1, 0 },
12971     { 0, +1 }
12972   };
12973
12974   for (i = 0; i < NUM_DIRECTIONS; i++)
12975   {
12976     int x, y, element;
12977
12978     x = bad_x + xy[i][0];
12979     y = bad_y + xy[i][1];
12980     if (!IN_LEV_FIELD(x, y))
12981       continue;
12982
12983     element = Feld[x][y];
12984     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12985         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12986     {
12987       kill_x = x;
12988       kill_y = y;
12989       break;
12990     }
12991   }
12992
12993   if (kill_x != bad_x || kill_y != bad_y)
12994     Bang(bad_x, bad_y);
12995 }
12996
12997 void KillPlayer(struct PlayerInfo *player)
12998 {
12999   int jx = player->jx, jy = player->jy;
13000
13001   if (!player->active)
13002     return;
13003
13004 #if 0
13005   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13006          player->killed, player->active, player->reanimated);
13007 #endif
13008
13009   /* the following code was introduced to prevent an infinite loop when calling
13010      -> Bang()
13011      -> CheckTriggeredElementChangeExt()
13012      -> ExecuteCustomElementAction()
13013      -> KillPlayer()
13014      -> (infinitely repeating the above sequence of function calls)
13015      which occurs when killing the player while having a CE with the setting
13016      "kill player X when explosion of <player X>"; the solution using a new
13017      field "player->killed" was chosen for backwards compatibility, although
13018      clever use of the fields "player->active" etc. would probably also work */
13019 #if 1
13020   if (player->killed)
13021     return;
13022 #endif
13023
13024   player->killed = TRUE;
13025
13026   /* remove accessible field at the player's position */
13027   Feld[jx][jy] = EL_EMPTY;
13028
13029   /* deactivate shield (else Bang()/Explode() would not work right) */
13030   player->shield_normal_time_left = 0;
13031   player->shield_deadly_time_left = 0;
13032
13033 #if 0
13034   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13035          player->killed, player->active, player->reanimated);
13036 #endif
13037
13038   Bang(jx, jy);
13039
13040 #if 0
13041   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13042          player->killed, player->active, player->reanimated);
13043 #endif
13044
13045   if (player->reanimated)       /* killed player may have been reanimated */
13046     player->killed = player->reanimated = FALSE;
13047   else
13048     BuryPlayer(player);
13049 }
13050
13051 static void KillPlayerUnlessEnemyProtected(int x, int y)
13052 {
13053   if (!PLAYER_ENEMY_PROTECTED(x, y))
13054     KillPlayer(PLAYERINFO(x, y));
13055 }
13056
13057 static void KillPlayerUnlessExplosionProtected(int x, int y)
13058 {
13059   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13060     KillPlayer(PLAYERINFO(x, y));
13061 }
13062
13063 void BuryPlayer(struct PlayerInfo *player)
13064 {
13065   int jx = player->jx, jy = player->jy;
13066
13067   if (!player->active)
13068     return;
13069
13070   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13071   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13072
13073   player->GameOver = TRUE;
13074   RemovePlayer(player);
13075 }
13076
13077 void RemovePlayer(struct PlayerInfo *player)
13078 {
13079   int jx = player->jx, jy = player->jy;
13080   int i, found = FALSE;
13081
13082   player->present = FALSE;
13083   player->active = FALSE;
13084
13085   if (!ExplodeField[jx][jy])
13086     StorePlayer[jx][jy] = 0;
13087
13088   if (player->is_moving)
13089     TEST_DrawLevelField(player->last_jx, player->last_jy);
13090
13091   for (i = 0; i < MAX_PLAYERS; i++)
13092     if (stored_player[i].active)
13093       found = TRUE;
13094
13095   if (!found)
13096     AllPlayersGone = TRUE;
13097
13098   ExitX = ZX = jx;
13099   ExitY = ZY = jy;
13100 }
13101
13102 static void setFieldForSnapping(int x, int y, int element, int direction)
13103 {
13104   struct ElementInfo *ei = &element_info[element];
13105   int direction_bit = MV_DIR_TO_BIT(direction);
13106   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13107   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13108                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13109
13110   Feld[x][y] = EL_ELEMENT_SNAPPING;
13111   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13112
13113   ResetGfxAnimation(x, y);
13114
13115   GfxElement[x][y] = element;
13116   GfxAction[x][y] = action;
13117   GfxDir[x][y] = direction;
13118   GfxFrame[x][y] = -1;
13119 }
13120
13121 /*
13122   =============================================================================
13123   checkDiagonalPushing()
13124   -----------------------------------------------------------------------------
13125   check if diagonal input device direction results in pushing of object
13126   (by checking if the alternative direction is walkable, diggable, ...)
13127   =============================================================================
13128 */
13129
13130 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13131                                     int x, int y, int real_dx, int real_dy)
13132 {
13133   int jx, jy, dx, dy, xx, yy;
13134
13135   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13136     return TRUE;
13137
13138   /* diagonal direction: check alternative direction */
13139   jx = player->jx;
13140   jy = player->jy;
13141   dx = x - jx;
13142   dy = y - jy;
13143   xx = jx + (dx == 0 ? real_dx : 0);
13144   yy = jy + (dy == 0 ? real_dy : 0);
13145
13146   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13147 }
13148
13149 /*
13150   =============================================================================
13151   DigField()
13152   -----------------------------------------------------------------------------
13153   x, y:                 field next to player (non-diagonal) to try to dig to
13154   real_dx, real_dy:     direction as read from input device (can be diagonal)
13155   =============================================================================
13156 */
13157
13158 static int DigField(struct PlayerInfo *player,
13159                     int oldx, int oldy, int x, int y,
13160                     int real_dx, int real_dy, int mode)
13161 {
13162   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13163   boolean player_was_pushing = player->is_pushing;
13164   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13165   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13166   int jx = oldx, jy = oldy;
13167   int dx = x - jx, dy = y - jy;
13168   int nextx = x + dx, nexty = y + dy;
13169   int move_direction = (dx == -1 ? MV_LEFT  :
13170                         dx == +1 ? MV_RIGHT :
13171                         dy == -1 ? MV_UP    :
13172                         dy == +1 ? MV_DOWN  : MV_NONE);
13173   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13174   int dig_side = MV_DIR_OPPOSITE(move_direction);
13175   int old_element = Feld[jx][jy];
13176   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13177   int collect_count;
13178
13179   if (is_player)                /* function can also be called by EL_PENGUIN */
13180   {
13181     if (player->MovPos == 0)
13182     {
13183       player->is_digging = FALSE;
13184       player->is_collecting = FALSE;
13185     }
13186
13187     if (player->MovPos == 0)    /* last pushing move finished */
13188       player->is_pushing = FALSE;
13189
13190     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13191     {
13192       player->is_switching = FALSE;
13193       player->push_delay = -1;
13194
13195       return MP_NO_ACTION;
13196     }
13197   }
13198
13199   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13200     old_element = Back[jx][jy];
13201
13202   /* in case of element dropped at player position, check background */
13203   else if (Back[jx][jy] != EL_EMPTY &&
13204            game.engine_version >= VERSION_IDENT(2,2,0,0))
13205     old_element = Back[jx][jy];
13206
13207   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13208     return MP_NO_ACTION;        /* field has no opening in this direction */
13209
13210   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13211     return MP_NO_ACTION;        /* field has no opening in this direction */
13212
13213   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13214   {
13215     SplashAcid(x, y);
13216
13217     Feld[jx][jy] = player->artwork_element;
13218     InitMovingField(jx, jy, MV_DOWN);
13219     Store[jx][jy] = EL_ACID;
13220     ContinueMoving(jx, jy);
13221     BuryPlayer(player);
13222
13223     return MP_DONT_RUN_INTO;
13224   }
13225
13226   if (player_can_move && DONT_RUN_INTO(element))
13227   {
13228     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13229
13230     return MP_DONT_RUN_INTO;
13231   }
13232
13233   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13234     return MP_NO_ACTION;
13235
13236   collect_count = element_info[element].collect_count_initial;
13237
13238   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13239     return MP_NO_ACTION;
13240
13241   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13242     player_can_move = player_can_move_or_snap;
13243
13244   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13245       game.engine_version >= VERSION_IDENT(2,2,0,0))
13246   {
13247     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13248                                player->index_bit, dig_side);
13249     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13250                                         player->index_bit, dig_side);
13251
13252     if (element == EL_DC_LANDMINE)
13253       Bang(x, y);
13254
13255     if (Feld[x][y] != element)          /* field changed by snapping */
13256       return MP_ACTION;
13257
13258     return MP_NO_ACTION;
13259   }
13260
13261   if (player->gravity && is_player && !player->is_auto_moving &&
13262       canFallDown(player) && move_direction != MV_DOWN &&
13263       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13264     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13265
13266   if (player_can_move &&
13267       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13268   {
13269     int sound_element = SND_ELEMENT(element);
13270     int sound_action = ACTION_WALKING;
13271
13272     if (IS_RND_GATE(element))
13273     {
13274       if (!player->key[RND_GATE_NR(element)])
13275         return MP_NO_ACTION;
13276     }
13277     else if (IS_RND_GATE_GRAY(element))
13278     {
13279       if (!player->key[RND_GATE_GRAY_NR(element)])
13280         return MP_NO_ACTION;
13281     }
13282     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13283     {
13284       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13285         return MP_NO_ACTION;
13286     }
13287     else if (element == EL_EXIT_OPEN ||
13288              element == EL_EM_EXIT_OPEN ||
13289              element == EL_EM_EXIT_OPENING ||
13290              element == EL_STEEL_EXIT_OPEN ||
13291              element == EL_EM_STEEL_EXIT_OPEN ||
13292              element == EL_EM_STEEL_EXIT_OPENING ||
13293              element == EL_SP_EXIT_OPEN ||
13294              element == EL_SP_EXIT_OPENING)
13295     {
13296       sound_action = ACTION_PASSING;    /* player is passing exit */
13297     }
13298     else if (element == EL_EMPTY)
13299     {
13300       sound_action = ACTION_MOVING;             /* nothing to walk on */
13301     }
13302
13303     /* play sound from background or player, whatever is available */
13304     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13305       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13306     else
13307       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13308   }
13309   else if (player_can_move &&
13310            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13311   {
13312     if (!ACCESS_FROM(element, opposite_direction))
13313       return MP_NO_ACTION;      /* field not accessible from this direction */
13314
13315     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13316       return MP_NO_ACTION;
13317
13318     if (IS_EM_GATE(element))
13319     {
13320       if (!player->key[EM_GATE_NR(element)])
13321         return MP_NO_ACTION;
13322     }
13323     else if (IS_EM_GATE_GRAY(element))
13324     {
13325       if (!player->key[EM_GATE_GRAY_NR(element)])
13326         return MP_NO_ACTION;
13327     }
13328     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13329     {
13330       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13331         return MP_NO_ACTION;
13332     }
13333     else if (IS_EMC_GATE(element))
13334     {
13335       if (!player->key[EMC_GATE_NR(element)])
13336         return MP_NO_ACTION;
13337     }
13338     else if (IS_EMC_GATE_GRAY(element))
13339     {
13340       if (!player->key[EMC_GATE_GRAY_NR(element)])
13341         return MP_NO_ACTION;
13342     }
13343     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13344     {
13345       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13346         return MP_NO_ACTION;
13347     }
13348     else if (element == EL_DC_GATE_WHITE ||
13349              element == EL_DC_GATE_WHITE_GRAY ||
13350              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13351     {
13352       if (player->num_white_keys == 0)
13353         return MP_NO_ACTION;
13354
13355       player->num_white_keys--;
13356     }
13357     else if (IS_SP_PORT(element))
13358     {
13359       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13360           element == EL_SP_GRAVITY_PORT_RIGHT ||
13361           element == EL_SP_GRAVITY_PORT_UP ||
13362           element == EL_SP_GRAVITY_PORT_DOWN)
13363         player->gravity = !player->gravity;
13364       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13365                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13366                element == EL_SP_GRAVITY_ON_PORT_UP ||
13367                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13368         player->gravity = TRUE;
13369       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13370                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13371                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13372                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13373         player->gravity = FALSE;
13374     }
13375
13376     /* automatically move to the next field with double speed */
13377     player->programmed_action = move_direction;
13378
13379     if (player->move_delay_reset_counter == 0)
13380     {
13381       player->move_delay_reset_counter = 2;     /* two double speed steps */
13382
13383       DOUBLE_PLAYER_SPEED(player);
13384     }
13385
13386     PlayLevelSoundAction(x, y, ACTION_PASSING);
13387   }
13388   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13389   {
13390     RemoveField(x, y);
13391
13392     if (mode != DF_SNAP)
13393     {
13394       GfxElement[x][y] = GFX_ELEMENT(element);
13395       player->is_digging = TRUE;
13396     }
13397
13398     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13399
13400     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13401                                         player->index_bit, dig_side);
13402
13403     if (mode == DF_SNAP)
13404     {
13405       if (level.block_snap_field)
13406         setFieldForSnapping(x, y, element, move_direction);
13407       else
13408         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13409
13410       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13411                                           player->index_bit, dig_side);
13412     }
13413   }
13414   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13415   {
13416     RemoveField(x, y);
13417
13418     if (is_player && mode != DF_SNAP)
13419     {
13420       GfxElement[x][y] = element;
13421       player->is_collecting = TRUE;
13422     }
13423
13424     if (element == EL_SPEED_PILL)
13425     {
13426       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13427     }
13428     else if (element == EL_EXTRA_TIME && level.time > 0)
13429     {
13430       TimeLeft += level.extra_time;
13431
13432       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13433
13434       DisplayGameControlValues();
13435     }
13436     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13437     {
13438       player->shield_normal_time_left += level.shield_normal_time;
13439       if (element == EL_SHIELD_DEADLY)
13440         player->shield_deadly_time_left += level.shield_deadly_time;
13441     }
13442     else if (element == EL_DYNAMITE ||
13443              element == EL_EM_DYNAMITE ||
13444              element == EL_SP_DISK_RED)
13445     {
13446       if (player->inventory_size < MAX_INVENTORY_SIZE)
13447         player->inventory_element[player->inventory_size++] = element;
13448
13449       DrawGameDoorValues();
13450     }
13451     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13452     {
13453       player->dynabomb_count++;
13454       player->dynabombs_left++;
13455     }
13456     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13457     {
13458       player->dynabomb_size++;
13459     }
13460     else if (element == EL_DYNABOMB_INCREASE_POWER)
13461     {
13462       player->dynabomb_xl = TRUE;
13463     }
13464     else if (IS_KEY(element))
13465     {
13466       player->key[KEY_NR(element)] = TRUE;
13467
13468       DrawGameDoorValues();
13469     }
13470     else if (element == EL_DC_KEY_WHITE)
13471     {
13472       player->num_white_keys++;
13473
13474       /* display white keys? */
13475       /* DrawGameDoorValues(); */
13476     }
13477     else if (IS_ENVELOPE(element))
13478     {
13479       player->show_envelope = element;
13480     }
13481     else if (element == EL_EMC_LENSES)
13482     {
13483       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13484
13485       RedrawAllInvisibleElementsForLenses();
13486     }
13487     else if (element == EL_EMC_MAGNIFIER)
13488     {
13489       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13490
13491       RedrawAllInvisibleElementsForMagnifier();
13492     }
13493     else if (IS_DROPPABLE(element) ||
13494              IS_THROWABLE(element))     /* can be collected and dropped */
13495     {
13496       int i;
13497
13498       if (collect_count == 0)
13499         player->inventory_infinite_element = element;
13500       else
13501         for (i = 0; i < collect_count; i++)
13502           if (player->inventory_size < MAX_INVENTORY_SIZE)
13503             player->inventory_element[player->inventory_size++] = element;
13504
13505       DrawGameDoorValues();
13506     }
13507     else if (collect_count > 0)
13508     {
13509       local_player->gems_still_needed -= collect_count;
13510       if (local_player->gems_still_needed < 0)
13511         local_player->gems_still_needed = 0;
13512
13513       game.snapshot.collected_item = TRUE;
13514
13515       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13516
13517       DisplayGameControlValues();
13518     }
13519
13520     RaiseScoreElement(element);
13521     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13522
13523     if (is_player)
13524       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13525                                           player->index_bit, dig_side);
13526
13527     if (mode == DF_SNAP)
13528     {
13529       if (level.block_snap_field)
13530         setFieldForSnapping(x, y, element, move_direction);
13531       else
13532         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13533
13534       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13535                                           player->index_bit, dig_side);
13536     }
13537   }
13538   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13539   {
13540     if (mode == DF_SNAP && element != EL_BD_ROCK)
13541       return MP_NO_ACTION;
13542
13543     if (CAN_FALL(element) && dy)
13544       return MP_NO_ACTION;
13545
13546     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13547         !(element == EL_SPRING && level.use_spring_bug))
13548       return MP_NO_ACTION;
13549
13550     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13551         ((move_direction & MV_VERTICAL &&
13552           ((element_info[element].move_pattern & MV_LEFT &&
13553             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13554            (element_info[element].move_pattern & MV_RIGHT &&
13555             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13556          (move_direction & MV_HORIZONTAL &&
13557           ((element_info[element].move_pattern & MV_UP &&
13558             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13559            (element_info[element].move_pattern & MV_DOWN &&
13560             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13561       return MP_NO_ACTION;
13562
13563     /* do not push elements already moving away faster than player */
13564     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13565         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13566       return MP_NO_ACTION;
13567
13568     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13569     {
13570       if (player->push_delay_value == -1 || !player_was_pushing)
13571         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13572     }
13573     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13574     {
13575       if (player->push_delay_value == -1)
13576         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13577     }
13578     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13579     {
13580       if (!player->is_pushing)
13581         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13582     }
13583
13584     player->is_pushing = TRUE;
13585     player->is_active = TRUE;
13586
13587     if (!(IN_LEV_FIELD(nextx, nexty) &&
13588           (IS_FREE(nextx, nexty) ||
13589            (IS_SB_ELEMENT(element) &&
13590             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13591            (IS_CUSTOM_ELEMENT(element) &&
13592             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13593       return MP_NO_ACTION;
13594
13595     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13596       return MP_NO_ACTION;
13597
13598     if (player->push_delay == -1)       /* new pushing; restart delay */
13599       player->push_delay = 0;
13600
13601     if (player->push_delay < player->push_delay_value &&
13602         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13603         element != EL_SPRING && element != EL_BALLOON)
13604     {
13605       /* make sure that there is no move delay before next try to push */
13606       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13607         player->move_delay = 0;
13608
13609       return MP_NO_ACTION;
13610     }
13611
13612     if (IS_CUSTOM_ELEMENT(element) &&
13613         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13614     {
13615       if (!DigFieldByCE(nextx, nexty, element))
13616         return MP_NO_ACTION;
13617     }
13618
13619     if (IS_SB_ELEMENT(element))
13620     {
13621       if (element == EL_SOKOBAN_FIELD_FULL)
13622       {
13623         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13624         local_player->sokobanfields_still_needed++;
13625       }
13626
13627       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13628       {
13629         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13630         local_player->sokobanfields_still_needed--;
13631       }
13632
13633       Feld[x][y] = EL_SOKOBAN_OBJECT;
13634
13635       if (Back[x][y] == Back[nextx][nexty])
13636         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13637       else if (Back[x][y] != 0)
13638         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13639                                     ACTION_EMPTYING);
13640       else
13641         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13642                                     ACTION_FILLING);
13643
13644       if (local_player->sokobanfields_still_needed == 0 &&
13645           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13646       {
13647         PlayerWins(player);
13648
13649         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13650       }
13651     }
13652     else
13653       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13654
13655     InitMovingField(x, y, move_direction);
13656     GfxAction[x][y] = ACTION_PUSHING;
13657
13658     if (mode == DF_SNAP)
13659       ContinueMoving(x, y);
13660     else
13661       MovPos[x][y] = (dx != 0 ? dx : dy);
13662
13663     Pushed[x][y] = TRUE;
13664     Pushed[nextx][nexty] = TRUE;
13665
13666     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13667       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13668     else
13669       player->push_delay_value = -1;    /* get new value later */
13670
13671     /* check for element change _after_ element has been pushed */
13672     if (game.use_change_when_pushing_bug)
13673     {
13674       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13675                                  player->index_bit, dig_side);
13676       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13677                                           player->index_bit, dig_side);
13678     }
13679   }
13680   else if (IS_SWITCHABLE(element))
13681   {
13682     if (PLAYER_SWITCHING(player, x, y))
13683     {
13684       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13685                                           player->index_bit, dig_side);
13686
13687       return MP_ACTION;
13688     }
13689
13690     player->is_switching = TRUE;
13691     player->switch_x = x;
13692     player->switch_y = y;
13693
13694     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13695
13696     if (element == EL_ROBOT_WHEEL)
13697     {
13698       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13699       ZX = x;
13700       ZY = y;
13701
13702       game.robot_wheel_active = TRUE;
13703
13704       TEST_DrawLevelField(x, y);
13705     }
13706     else if (element == EL_SP_TERMINAL)
13707     {
13708       int xx, yy;
13709
13710       SCAN_PLAYFIELD(xx, yy)
13711       {
13712         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13713         {
13714           Bang(xx, yy);
13715         }
13716         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13717         {
13718           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13719
13720           ResetGfxAnimation(xx, yy);
13721           TEST_DrawLevelField(xx, yy);
13722         }
13723       }
13724     }
13725     else if (IS_BELT_SWITCH(element))
13726     {
13727       ToggleBeltSwitch(x, y);
13728     }
13729     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13730              element == EL_SWITCHGATE_SWITCH_DOWN ||
13731              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13732              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13733     {
13734       ToggleSwitchgateSwitch(x, y);
13735     }
13736     else if (element == EL_LIGHT_SWITCH ||
13737              element == EL_LIGHT_SWITCH_ACTIVE)
13738     {
13739       ToggleLightSwitch(x, y);
13740     }
13741     else if (element == EL_TIMEGATE_SWITCH ||
13742              element == EL_DC_TIMEGATE_SWITCH)
13743     {
13744       ActivateTimegateSwitch(x, y);
13745     }
13746     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13747              element == EL_BALLOON_SWITCH_RIGHT ||
13748              element == EL_BALLOON_SWITCH_UP    ||
13749              element == EL_BALLOON_SWITCH_DOWN  ||
13750              element == EL_BALLOON_SWITCH_NONE  ||
13751              element == EL_BALLOON_SWITCH_ANY)
13752     {
13753       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13754                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13755                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13756                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13757                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13758                              move_direction);
13759     }
13760     else if (element == EL_LAMP)
13761     {
13762       Feld[x][y] = EL_LAMP_ACTIVE;
13763       local_player->lights_still_needed--;
13764
13765       ResetGfxAnimation(x, y);
13766       TEST_DrawLevelField(x, y);
13767     }
13768     else if (element == EL_TIME_ORB_FULL)
13769     {
13770       Feld[x][y] = EL_TIME_ORB_EMPTY;
13771
13772       if (level.time > 0 || level.use_time_orb_bug)
13773       {
13774         TimeLeft += level.time_orb_time;
13775         game.no_time_limit = FALSE;
13776
13777         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13778
13779         DisplayGameControlValues();
13780       }
13781
13782       ResetGfxAnimation(x, y);
13783       TEST_DrawLevelField(x, y);
13784     }
13785     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13786              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13787     {
13788       int xx, yy;
13789
13790       game.ball_state = !game.ball_state;
13791
13792       SCAN_PLAYFIELD(xx, yy)
13793       {
13794         int e = Feld[xx][yy];
13795
13796         if (game.ball_state)
13797         {
13798           if (e == EL_EMC_MAGIC_BALL)
13799             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13800           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13801             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13802         }
13803         else
13804         {
13805           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13806             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13807           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13808             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13809         }
13810       }
13811     }
13812
13813     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13814                                         player->index_bit, dig_side);
13815
13816     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13817                                         player->index_bit, dig_side);
13818
13819     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13820                                         player->index_bit, dig_side);
13821
13822     return MP_ACTION;
13823   }
13824   else
13825   {
13826     if (!PLAYER_SWITCHING(player, x, y))
13827     {
13828       player->is_switching = TRUE;
13829       player->switch_x = x;
13830       player->switch_y = y;
13831
13832       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13833                                  player->index_bit, dig_side);
13834       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13835                                           player->index_bit, dig_side);
13836
13837       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13838                                  player->index_bit, dig_side);
13839       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13840                                           player->index_bit, dig_side);
13841     }
13842
13843     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13844                                player->index_bit, dig_side);
13845     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13846                                         player->index_bit, dig_side);
13847
13848     return MP_NO_ACTION;
13849   }
13850
13851   player->push_delay = -1;
13852
13853   if (is_player)                /* function can also be called by EL_PENGUIN */
13854   {
13855     if (Feld[x][y] != element)          /* really digged/collected something */
13856     {
13857       player->is_collecting = !player->is_digging;
13858       player->is_active = TRUE;
13859     }
13860   }
13861
13862   return MP_MOVING;
13863 }
13864
13865 static boolean DigFieldByCE(int x, int y, int digging_element)
13866 {
13867   int element = Feld[x][y];
13868
13869   if (!IS_FREE(x, y))
13870   {
13871     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13872                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13873                   ACTION_BREAKING);
13874
13875     /* no element can dig solid indestructible elements */
13876     if (IS_INDESTRUCTIBLE(element) &&
13877         !IS_DIGGABLE(element) &&
13878         !IS_COLLECTIBLE(element))
13879       return FALSE;
13880
13881     if (AmoebaNr[x][y] &&
13882         (element == EL_AMOEBA_FULL ||
13883          element == EL_BD_AMOEBA ||
13884          element == EL_AMOEBA_GROWING))
13885     {
13886       AmoebaCnt[AmoebaNr[x][y]]--;
13887       AmoebaCnt2[AmoebaNr[x][y]]--;
13888     }
13889
13890     if (IS_MOVING(x, y))
13891       RemoveMovingField(x, y);
13892     else
13893     {
13894       RemoveField(x, y);
13895       TEST_DrawLevelField(x, y);
13896     }
13897
13898     /* if digged element was about to explode, prevent the explosion */
13899     ExplodeField[x][y] = EX_TYPE_NONE;
13900
13901     PlayLevelSoundAction(x, y, action);
13902   }
13903
13904   Store[x][y] = EL_EMPTY;
13905
13906   /* this makes it possible to leave the removed element again */
13907   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13908     Store[x][y] = element;
13909
13910   return TRUE;
13911 }
13912
13913 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13914 {
13915   int jx = player->jx, jy = player->jy;
13916   int x = jx + dx, y = jy + dy;
13917   int snap_direction = (dx == -1 ? MV_LEFT  :
13918                         dx == +1 ? MV_RIGHT :
13919                         dy == -1 ? MV_UP    :
13920                         dy == +1 ? MV_DOWN  : MV_NONE);
13921   boolean can_continue_snapping = (level.continuous_snapping &&
13922                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13923
13924   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13925     return FALSE;
13926
13927   if (!player->active || !IN_LEV_FIELD(x, y))
13928     return FALSE;
13929
13930   if (dx && dy)
13931     return FALSE;
13932
13933   if (!dx && !dy)
13934   {
13935     if (player->MovPos == 0)
13936       player->is_pushing = FALSE;
13937
13938     player->is_snapping = FALSE;
13939
13940     if (player->MovPos == 0)
13941     {
13942       player->is_moving = FALSE;
13943       player->is_digging = FALSE;
13944       player->is_collecting = FALSE;
13945     }
13946
13947     return FALSE;
13948   }
13949
13950   /* prevent snapping with already pressed snap key when not allowed */
13951   if (player->is_snapping && !can_continue_snapping)
13952     return FALSE;
13953
13954   player->MovDir = snap_direction;
13955
13956   if (player->MovPos == 0)
13957   {
13958     player->is_moving = FALSE;
13959     player->is_digging = FALSE;
13960     player->is_collecting = FALSE;
13961   }
13962
13963   player->is_dropping = FALSE;
13964   player->is_dropping_pressed = FALSE;
13965   player->drop_pressed_delay = 0;
13966
13967   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13968     return FALSE;
13969
13970   player->is_snapping = TRUE;
13971   player->is_active = TRUE;
13972
13973   if (player->MovPos == 0)
13974   {
13975     player->is_moving = FALSE;
13976     player->is_digging = FALSE;
13977     player->is_collecting = FALSE;
13978   }
13979
13980   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13981     TEST_DrawLevelField(player->last_jx, player->last_jy);
13982
13983   TEST_DrawLevelField(x, y);
13984
13985   return TRUE;
13986 }
13987
13988 static boolean DropElement(struct PlayerInfo *player)
13989 {
13990   int old_element, new_element;
13991   int dropx = player->jx, dropy = player->jy;
13992   int drop_direction = player->MovDir;
13993   int drop_side = drop_direction;
13994   int drop_element = get_next_dropped_element(player);
13995
13996   player->is_dropping_pressed = TRUE;
13997
13998   /* do not drop an element on top of another element; when holding drop key
13999      pressed without moving, dropped element must move away before the next
14000      element can be dropped (this is especially important if the next element
14001      is dynamite, which can be placed on background for historical reasons) */
14002   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14003     return MP_ACTION;
14004
14005   if (IS_THROWABLE(drop_element))
14006   {
14007     dropx += GET_DX_FROM_DIR(drop_direction);
14008     dropy += GET_DY_FROM_DIR(drop_direction);
14009
14010     if (!IN_LEV_FIELD(dropx, dropy))
14011       return FALSE;
14012   }
14013
14014   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14015   new_element = drop_element;           /* default: no change when dropping */
14016
14017   /* check if player is active, not moving and ready to drop */
14018   if (!player->active || player->MovPos || player->drop_delay > 0)
14019     return FALSE;
14020
14021   /* check if player has anything that can be dropped */
14022   if (new_element == EL_UNDEFINED)
14023     return FALSE;
14024
14025   /* check if drop key was pressed long enough for EM style dynamite */
14026   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14027     return FALSE;
14028
14029   /* check if anything can be dropped at the current position */
14030   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14031     return FALSE;
14032
14033   /* collected custom elements can only be dropped on empty fields */
14034   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14035     return FALSE;
14036
14037   if (old_element != EL_EMPTY)
14038     Back[dropx][dropy] = old_element;   /* store old element on this field */
14039
14040   ResetGfxAnimation(dropx, dropy);
14041   ResetRandomAnimationValue(dropx, dropy);
14042
14043   if (player->inventory_size > 0 ||
14044       player->inventory_infinite_element != EL_UNDEFINED)
14045   {
14046     if (player->inventory_size > 0)
14047     {
14048       player->inventory_size--;
14049
14050       DrawGameDoorValues();
14051
14052       if (new_element == EL_DYNAMITE)
14053         new_element = EL_DYNAMITE_ACTIVE;
14054       else if (new_element == EL_EM_DYNAMITE)
14055         new_element = EL_EM_DYNAMITE_ACTIVE;
14056       else if (new_element == EL_SP_DISK_RED)
14057         new_element = EL_SP_DISK_RED_ACTIVE;
14058     }
14059
14060     Feld[dropx][dropy] = new_element;
14061
14062     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14063       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14064                           el2img(Feld[dropx][dropy]), 0);
14065
14066     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14067
14068     /* needed if previous element just changed to "empty" in the last frame */
14069     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14070
14071     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14072                                player->index_bit, drop_side);
14073     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14074                                         CE_PLAYER_DROPS_X,
14075                                         player->index_bit, drop_side);
14076
14077     TestIfElementTouchesCustomElement(dropx, dropy);
14078   }
14079   else          /* player is dropping a dyna bomb */
14080   {
14081     player->dynabombs_left--;
14082
14083     Feld[dropx][dropy] = new_element;
14084
14085     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14086       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14087                           el2img(Feld[dropx][dropy]), 0);
14088
14089     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14090   }
14091
14092   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14093     InitField_WithBug1(dropx, dropy, FALSE);
14094
14095   new_element = Feld[dropx][dropy];     /* element might have changed */
14096
14097   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14098       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14099   {
14100     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14101       MovDir[dropx][dropy] = drop_direction;
14102
14103     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14104
14105     /* do not cause impact style collision by dropping elements that can fall */
14106     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14107   }
14108
14109   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14110   player->is_dropping = TRUE;
14111
14112   player->drop_pressed_delay = 0;
14113   player->is_dropping_pressed = FALSE;
14114
14115   player->drop_x = dropx;
14116   player->drop_y = dropy;
14117
14118   return TRUE;
14119 }
14120
14121 /* ------------------------------------------------------------------------- */
14122 /* game sound playing functions                                              */
14123 /* ------------------------------------------------------------------------- */
14124
14125 static int *loop_sound_frame = NULL;
14126 static int *loop_sound_volume = NULL;
14127
14128 void InitPlayLevelSound()
14129 {
14130   int num_sounds = getSoundListSize();
14131
14132   checked_free(loop_sound_frame);
14133   checked_free(loop_sound_volume);
14134
14135   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14136   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14137 }
14138
14139 static void PlayLevelSound(int x, int y, int nr)
14140 {
14141   int sx = SCREENX(x), sy = SCREENY(y);
14142   int volume, stereo_position;
14143   int max_distance = 8;
14144   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14145
14146   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14147       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14148     return;
14149
14150   if (!IN_LEV_FIELD(x, y) ||
14151       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14152       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14153     return;
14154
14155   volume = SOUND_MAX_VOLUME;
14156
14157   if (!IN_SCR_FIELD(sx, sy))
14158   {
14159     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14160     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14161
14162     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14163   }
14164
14165   stereo_position = (SOUND_MAX_LEFT +
14166                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14167                      (SCR_FIELDX + 2 * max_distance));
14168
14169   if (IS_LOOP_SOUND(nr))
14170   {
14171     /* This assures that quieter loop sounds do not overwrite louder ones,
14172        while restarting sound volume comparison with each new game frame. */
14173
14174     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14175       return;
14176
14177     loop_sound_volume[nr] = volume;
14178     loop_sound_frame[nr] = FrameCounter;
14179   }
14180
14181   PlaySoundExt(nr, volume, stereo_position, type);
14182 }
14183
14184 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14185 {
14186   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14187                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14188                  y < LEVELY(BY1) ? LEVELY(BY1) :
14189                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14190                  sound_action);
14191 }
14192
14193 static void PlayLevelSoundAction(int x, int y, int action)
14194 {
14195   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14196 }
14197
14198 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14199 {
14200   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14201
14202   if (sound_effect != SND_UNDEFINED)
14203     PlayLevelSound(x, y, sound_effect);
14204 }
14205
14206 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14207                                               int action)
14208 {
14209   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14210
14211   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14212     PlayLevelSound(x, y, sound_effect);
14213 }
14214
14215 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14216 {
14217   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14218
14219   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14220     PlayLevelSound(x, y, sound_effect);
14221 }
14222
14223 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14224 {
14225   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14226
14227   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14228     StopSound(sound_effect);
14229 }
14230
14231 static void PlayLevelMusic()
14232 {
14233   if (levelset.music[level_nr] != MUS_UNDEFINED)
14234     PlayMusic(levelset.music[level_nr]);        /* from config file */
14235   else
14236     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14237 }
14238
14239 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14240 {
14241   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14242   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14243   int x = xx - 1 - offset;
14244   int y = yy - 1 - offset;
14245
14246   switch (sample)
14247   {
14248     case SAMPLE_blank:
14249       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14250       break;
14251
14252     case SAMPLE_roll:
14253       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14254       break;
14255
14256     case SAMPLE_stone:
14257       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14258       break;
14259
14260     case SAMPLE_nut:
14261       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14262       break;
14263
14264     case SAMPLE_crack:
14265       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14266       break;
14267
14268     case SAMPLE_bug:
14269       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14270       break;
14271
14272     case SAMPLE_tank:
14273       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14274       break;
14275
14276     case SAMPLE_android_clone:
14277       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14278       break;
14279
14280     case SAMPLE_android_move:
14281       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14282       break;
14283
14284     case SAMPLE_spring:
14285       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14286       break;
14287
14288     case SAMPLE_slurp:
14289       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14290       break;
14291
14292     case SAMPLE_eater:
14293       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14294       break;
14295
14296     case SAMPLE_eater_eat:
14297       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14298       break;
14299
14300     case SAMPLE_alien:
14301       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14302       break;
14303
14304     case SAMPLE_collect:
14305       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14306       break;
14307
14308     case SAMPLE_diamond:
14309       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14310       break;
14311
14312     case SAMPLE_squash:
14313       /* !!! CHECK THIS !!! */
14314 #if 1
14315       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14316 #else
14317       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14318 #endif
14319       break;
14320
14321     case SAMPLE_wonderfall:
14322       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14323       break;
14324
14325     case SAMPLE_drip:
14326       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14327       break;
14328
14329     case SAMPLE_push:
14330       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14331       break;
14332
14333     case SAMPLE_dirt:
14334       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14335       break;
14336
14337     case SAMPLE_acid:
14338       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14339       break;
14340
14341     case SAMPLE_ball:
14342       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14343       break;
14344
14345     case SAMPLE_grow:
14346       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14347       break;
14348
14349     case SAMPLE_wonder:
14350       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14351       break;
14352
14353     case SAMPLE_door:
14354       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14355       break;
14356
14357     case SAMPLE_exit_open:
14358       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14359       break;
14360
14361     case SAMPLE_exit_leave:
14362       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14363       break;
14364
14365     case SAMPLE_dynamite:
14366       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14367       break;
14368
14369     case SAMPLE_tick:
14370       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14371       break;
14372
14373     case SAMPLE_press:
14374       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14375       break;
14376
14377     case SAMPLE_wheel:
14378       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14379       break;
14380
14381     case SAMPLE_boom:
14382       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14383       break;
14384
14385     case SAMPLE_die:
14386       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14387       break;
14388
14389     case SAMPLE_time:
14390       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14391       break;
14392
14393     default:
14394       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14395       break;
14396   }
14397 }
14398
14399 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14400 {
14401   int element = map_element_SP_to_RND(element_sp);
14402   int action = map_action_SP_to_RND(action_sp);
14403   int offset = (setup.sp_show_border_elements ? 0 : 1);
14404   int x = xx - offset;
14405   int y = yy - offset;
14406
14407   PlayLevelSoundElementAction(x, y, element, action);
14408 }
14409
14410 void RaiseScore(int value)
14411 {
14412   local_player->score += value;
14413
14414   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14415
14416   DisplayGameControlValues();
14417 }
14418
14419 void RaiseScoreElement(int element)
14420 {
14421   switch (element)
14422   {
14423     case EL_EMERALD:
14424     case EL_BD_DIAMOND:
14425     case EL_EMERALD_YELLOW:
14426     case EL_EMERALD_RED:
14427     case EL_EMERALD_PURPLE:
14428     case EL_SP_INFOTRON:
14429       RaiseScore(level.score[SC_EMERALD]);
14430       break;
14431     case EL_DIAMOND:
14432       RaiseScore(level.score[SC_DIAMOND]);
14433       break;
14434     case EL_CRYSTAL:
14435       RaiseScore(level.score[SC_CRYSTAL]);
14436       break;
14437     case EL_PEARL:
14438       RaiseScore(level.score[SC_PEARL]);
14439       break;
14440     case EL_BUG:
14441     case EL_BD_BUTTERFLY:
14442     case EL_SP_ELECTRON:
14443       RaiseScore(level.score[SC_BUG]);
14444       break;
14445     case EL_SPACESHIP:
14446     case EL_BD_FIREFLY:
14447     case EL_SP_SNIKSNAK:
14448       RaiseScore(level.score[SC_SPACESHIP]);
14449       break;
14450     case EL_YAMYAM:
14451     case EL_DARK_YAMYAM:
14452       RaiseScore(level.score[SC_YAMYAM]);
14453       break;
14454     case EL_ROBOT:
14455       RaiseScore(level.score[SC_ROBOT]);
14456       break;
14457     case EL_PACMAN:
14458       RaiseScore(level.score[SC_PACMAN]);
14459       break;
14460     case EL_NUT:
14461       RaiseScore(level.score[SC_NUT]);
14462       break;
14463     case EL_DYNAMITE:
14464     case EL_EM_DYNAMITE:
14465     case EL_SP_DISK_RED:
14466     case EL_DYNABOMB_INCREASE_NUMBER:
14467     case EL_DYNABOMB_INCREASE_SIZE:
14468     case EL_DYNABOMB_INCREASE_POWER:
14469       RaiseScore(level.score[SC_DYNAMITE]);
14470       break;
14471     case EL_SHIELD_NORMAL:
14472     case EL_SHIELD_DEADLY:
14473       RaiseScore(level.score[SC_SHIELD]);
14474       break;
14475     case EL_EXTRA_TIME:
14476       RaiseScore(level.extra_time_score);
14477       break;
14478     case EL_KEY_1:
14479     case EL_KEY_2:
14480     case EL_KEY_3:
14481     case EL_KEY_4:
14482     case EL_EM_KEY_1:
14483     case EL_EM_KEY_2:
14484     case EL_EM_KEY_3:
14485     case EL_EM_KEY_4:
14486     case EL_EMC_KEY_5:
14487     case EL_EMC_KEY_6:
14488     case EL_EMC_KEY_7:
14489     case EL_EMC_KEY_8:
14490     case EL_DC_KEY_WHITE:
14491       RaiseScore(level.score[SC_KEY]);
14492       break;
14493     default:
14494       RaiseScore(element_info[element].collect_score);
14495       break;
14496   }
14497 }
14498
14499 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14500 {
14501   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14502   {
14503     /* closing door required in case of envelope style request dialogs */
14504     if (!skip_request)
14505       CloseDoor(DOOR_CLOSE_1);
14506
14507 #if defined(NETWORK_AVALIABLE)
14508     if (options.network)
14509       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14510     else
14511 #endif
14512     {
14513       if (quick_quit)
14514         FadeSkipNextFadeIn();
14515
14516       SetGameStatus(GAME_MODE_MAIN);
14517
14518       DrawMainMenu();
14519     }
14520   }
14521   else          /* continue playing the game */
14522   {
14523     if (tape.playing && tape.deactivate_display)
14524       TapeDeactivateDisplayOff(TRUE);
14525
14526     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14527
14528     if (tape.playing && tape.deactivate_display)
14529       TapeDeactivateDisplayOn();
14530   }
14531 }
14532
14533 void RequestQuitGame(boolean ask_if_really_quit)
14534 {
14535   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14536   boolean skip_request = AllPlayersGone || quick_quit;
14537
14538   RequestQuitGameExt(skip_request, quick_quit,
14539                      "Do you really want to quit the game?");
14540 }
14541
14542
14543 /* ------------------------------------------------------------------------- */
14544 /* random generator functions                                                */
14545 /* ------------------------------------------------------------------------- */
14546
14547 unsigned int InitEngineRandom_RND(int seed)
14548 {
14549   game.num_random_calls = 0;
14550
14551   return InitEngineRandom(seed);
14552 }
14553
14554 unsigned int RND(int max)
14555 {
14556   if (max > 0)
14557   {
14558     game.num_random_calls++;
14559
14560     return GetEngineRandom(max);
14561   }
14562
14563   return 0;
14564 }
14565
14566
14567 /* ------------------------------------------------------------------------- */
14568 /* game engine snapshot handling functions                                   */
14569 /* ------------------------------------------------------------------------- */
14570
14571 struct EngineSnapshotInfo
14572 {
14573   /* runtime values for custom element collect score */
14574   int collect_score[NUM_CUSTOM_ELEMENTS];
14575
14576   /* runtime values for group element choice position */
14577   int choice_pos[NUM_GROUP_ELEMENTS];
14578
14579   /* runtime values for belt position animations */
14580   int belt_graphic[4][NUM_BELT_PARTS];
14581   int belt_anim_mode[4][NUM_BELT_PARTS];
14582 };
14583
14584 static struct EngineSnapshotInfo engine_snapshot_rnd;
14585 static char *snapshot_level_identifier = NULL;
14586 static int snapshot_level_nr = -1;
14587
14588 static void SaveEngineSnapshotValues_RND()
14589 {
14590   static int belt_base_active_element[4] =
14591   {
14592     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14593     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14594     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14595     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14596   };
14597   int i, j;
14598
14599   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14600   {
14601     int element = EL_CUSTOM_START + i;
14602
14603     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14604   }
14605
14606   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14607   {
14608     int element = EL_GROUP_START + i;
14609
14610     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14611   }
14612
14613   for (i = 0; i < 4; i++)
14614   {
14615     for (j = 0; j < NUM_BELT_PARTS; j++)
14616     {
14617       int element = belt_base_active_element[i] + j;
14618       int graphic = el2img(element);
14619       int anim_mode = graphic_info[graphic].anim_mode;
14620
14621       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14622       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14623     }
14624   }
14625 }
14626
14627 static void LoadEngineSnapshotValues_RND()
14628 {
14629   unsigned int num_random_calls = game.num_random_calls;
14630   int i, j;
14631
14632   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14633   {
14634     int element = EL_CUSTOM_START + i;
14635
14636     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14637   }
14638
14639   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14640   {
14641     int element = EL_GROUP_START + i;
14642
14643     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14644   }
14645
14646   for (i = 0; i < 4; i++)
14647   {
14648     for (j = 0; j < NUM_BELT_PARTS; j++)
14649     {
14650       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14651       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14652
14653       graphic_info[graphic].anim_mode = anim_mode;
14654     }
14655   }
14656
14657   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14658   {
14659     InitRND(tape.random_seed);
14660     for (i = 0; i < num_random_calls; i++)
14661       RND(1);
14662   }
14663
14664   if (game.num_random_calls != num_random_calls)
14665   {
14666     Error(ERR_INFO, "number of random calls out of sync");
14667     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14668     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14669     Error(ERR_EXIT, "this should not happen -- please debug");
14670   }
14671 }
14672
14673 void FreeEngineSnapshotSingle()
14674 {
14675   FreeSnapshotSingle();
14676
14677   setString(&snapshot_level_identifier, NULL);
14678   snapshot_level_nr = -1;
14679 }
14680
14681 void FreeEngineSnapshotList()
14682 {
14683   FreeSnapshotList();
14684 }
14685
14686 ListNode *SaveEngineSnapshotBuffers()
14687 {
14688   ListNode *buffers = NULL;
14689
14690   /* copy some special values to a structure better suited for the snapshot */
14691
14692   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14693     SaveEngineSnapshotValues_RND();
14694   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14695     SaveEngineSnapshotValues_EM();
14696   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14697     SaveEngineSnapshotValues_SP(&buffers);
14698
14699   /* save values stored in special snapshot structure */
14700
14701   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14702     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14703   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14704     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14705   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14706     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14707
14708   /* save further RND engine values */
14709
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14713
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14718
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14724
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14728
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14730
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14732
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14735
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14754
14755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14757
14758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14761
14762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14764
14765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14770
14771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14773
14774 #if 0
14775   ListNode *node = engine_snapshot_list_rnd;
14776   int num_bytes = 0;
14777
14778   while (node != NULL)
14779   {
14780     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14781
14782     node = node->next;
14783   }
14784
14785   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14786 #endif
14787
14788   return buffers;
14789 }
14790
14791 void SaveEngineSnapshotSingle()
14792 {
14793   ListNode *buffers = SaveEngineSnapshotBuffers();
14794
14795   /* finally save all snapshot buffers to single snapshot */
14796   SaveSnapshotSingle(buffers);
14797
14798   /* save level identification information */
14799   setString(&snapshot_level_identifier, leveldir_current->identifier);
14800   snapshot_level_nr = level_nr;
14801 }
14802
14803 boolean CheckSaveEngineSnapshotToList()
14804 {
14805   boolean save_snapshot =
14806     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14807      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14808       game.snapshot.changed_action) ||
14809      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14810       game.snapshot.collected_item));
14811
14812   game.snapshot.changed_action = FALSE;
14813   game.snapshot.collected_item = FALSE;
14814   game.snapshot.save_snapshot = save_snapshot;
14815
14816   return save_snapshot;
14817 }
14818
14819 void SaveEngineSnapshotToList()
14820 {
14821   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14822       tape.quick_resume)
14823     return;
14824
14825   ListNode *buffers = SaveEngineSnapshotBuffers();
14826
14827   /* finally save all snapshot buffers to snapshot list */
14828   SaveSnapshotToList(buffers);
14829 }
14830
14831 void SaveEngineSnapshotToListInitial()
14832 {
14833   FreeEngineSnapshotList();
14834
14835   SaveEngineSnapshotToList();
14836 }
14837
14838 void LoadEngineSnapshotValues()
14839 {
14840   /* restore special values from snapshot structure */
14841
14842   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14843     LoadEngineSnapshotValues_RND();
14844   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14845     LoadEngineSnapshotValues_EM();
14846   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14847     LoadEngineSnapshotValues_SP();
14848 }
14849
14850 void LoadEngineSnapshotSingle()
14851 {
14852   LoadSnapshotSingle();
14853
14854   LoadEngineSnapshotValues();
14855 }
14856
14857 void LoadEngineSnapshot_Undo(int steps)
14858 {
14859   LoadSnapshotFromList_Older(steps);
14860
14861   LoadEngineSnapshotValues();
14862 }
14863
14864 void LoadEngineSnapshot_Redo(int steps)
14865 {
14866   LoadSnapshotFromList_Newer(steps);
14867
14868   LoadEngineSnapshotValues();
14869 }
14870
14871 boolean CheckEngineSnapshotSingle()
14872 {
14873   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14874           snapshot_level_nr == level_nr);
14875 }
14876
14877 boolean CheckEngineSnapshotList()
14878 {
14879   return CheckSnapshotList();
14880 }
14881
14882
14883 /* ---------- new game button stuff ---------------------------------------- */
14884
14885 static struct
14886 {
14887   int graphic;
14888   struct XY *pos;
14889   int gadget_id;
14890   char *infotext;
14891 } gamebutton_info[NUM_GAME_BUTTONS] =
14892 {
14893   {
14894     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14895     GAME_CTRL_ID_STOP,                  "stop game"
14896   },
14897   {
14898     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14899     GAME_CTRL_ID_PAUSE,                 "pause game"
14900   },
14901   {
14902     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14903     GAME_CTRL_ID_PLAY,                  "play game"
14904   },
14905   {
14906     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14907     GAME_CTRL_ID_UNDO,                  "undo step"
14908   },
14909   {
14910     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14911     GAME_CTRL_ID_REDO,                  "redo step"
14912   },
14913   {
14914     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14915     GAME_CTRL_ID_SAVE,                  "save game"
14916   },
14917   {
14918     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14919     GAME_CTRL_ID_PAUSE2,                "pause game"
14920   },
14921   {
14922     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14923     GAME_CTRL_ID_LOAD,                  "load game"
14924   },
14925   {
14926     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14927     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14928   },
14929   {
14930     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14931     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14932   },
14933   {
14934     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14935     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14936   }
14937 };
14938
14939 void CreateGameButtons()
14940 {
14941   int i;
14942
14943   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14944   {
14945     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14946     struct XY *pos = gamebutton_info[i].pos;
14947     struct GadgetInfo *gi;
14948     int button_type;
14949     boolean checked;
14950     unsigned int event_mask;
14951     int base_x = (tape.show_game_buttons ? VX : DX);
14952     int base_y = (tape.show_game_buttons ? VY : DY);
14953     int gd_x   = gfx->src_x;
14954     int gd_y   = gfx->src_y;
14955     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14956     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14957     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14958     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14959     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14960     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14961     int id = i;
14962
14963     if (gfx->bitmap == NULL)
14964     {
14965       game_gadget[id] = NULL;
14966
14967       continue;
14968     }
14969
14970     if (id == GAME_CTRL_ID_STOP ||
14971         id == GAME_CTRL_ID_PLAY ||
14972         id == GAME_CTRL_ID_SAVE ||
14973         id == GAME_CTRL_ID_LOAD)
14974     {
14975       button_type = GD_TYPE_NORMAL_BUTTON;
14976       checked = FALSE;
14977       event_mask = GD_EVENT_RELEASED;
14978     }
14979     else if (id == GAME_CTRL_ID_UNDO ||
14980              id == GAME_CTRL_ID_REDO)
14981     {
14982       button_type = GD_TYPE_NORMAL_BUTTON;
14983       checked = FALSE;
14984       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14985     }
14986     else
14987     {
14988       button_type = GD_TYPE_CHECK_BUTTON;
14989       checked =
14990         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14991          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14992          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14993       event_mask = GD_EVENT_PRESSED;
14994     }
14995
14996     gi = CreateGadget(GDI_CUSTOM_ID, id,
14997                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14998                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14999                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15000                       GDI_WIDTH, gfx->width,
15001                       GDI_HEIGHT, gfx->height,
15002                       GDI_TYPE, button_type,
15003                       GDI_STATE, GD_BUTTON_UNPRESSED,
15004                       GDI_CHECKED, checked,
15005                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15006                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15007                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15008                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15009                       GDI_DIRECT_DRAW, FALSE,
15010                       GDI_EVENT_MASK, event_mask,
15011                       GDI_CALLBACK_ACTION, HandleGameButtons,
15012                       GDI_END);
15013
15014     if (gi == NULL)
15015       Error(ERR_EXIT, "cannot create gadget");
15016
15017     game_gadget[id] = gi;
15018   }
15019 }
15020
15021 void FreeGameButtons()
15022 {
15023   int i;
15024
15025   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15026     FreeGadget(game_gadget[i]);
15027 }
15028
15029 static void UnmapGameButtonsAtSamePosition(int id)
15030 {
15031   int i;
15032
15033   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15034     if (i != id &&
15035         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15036         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15037       UnmapGadget(game_gadget[i]);
15038 }
15039
15040 static void UnmapGameButtonsAtSamePosition_All()
15041 {
15042   if (setup.show_snapshot_buttons)
15043   {
15044     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15045     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15046     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15047   }
15048   else
15049   {
15050     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15051     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15052     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15053   }
15054 }
15055
15056 static void MapGameButtonsAtSamePosition(int id)
15057 {
15058   int i;
15059
15060   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15061     if (i != id &&
15062         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15063         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15064       MapGadget(game_gadget[i]);
15065
15066   UnmapGameButtonsAtSamePosition_All();
15067 }
15068
15069 void MapUndoRedoButtons()
15070 {
15071   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15072   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15073
15074   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15075   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15076
15077   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15078 }
15079
15080 void UnmapUndoRedoButtons()
15081 {
15082   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15083   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15084
15085   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15086   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15087
15088   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15089 }
15090
15091 void MapGameButtons()
15092 {
15093   int i;
15094
15095   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15096     if (i != GAME_CTRL_ID_UNDO &&
15097         i != GAME_CTRL_ID_REDO)
15098       MapGadget(game_gadget[i]);
15099
15100   UnmapGameButtonsAtSamePosition_All();
15101
15102   RedrawGameButtons();
15103 }
15104
15105 void UnmapGameButtons()
15106 {
15107   int i;
15108
15109   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15110     UnmapGadget(game_gadget[i]);
15111 }
15112
15113 void RedrawGameButtons()
15114 {
15115   int i;
15116
15117   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15118     RedrawGadget(game_gadget[i]);
15119
15120   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15121   redraw_mask &= ~REDRAW_ALL;
15122 }
15123
15124 void GameUndoRedoExt()
15125 {
15126   ClearPlayerAction();
15127
15128   tape.pausing = TRUE;
15129
15130   RedrawPlayfield();
15131   UpdateAndDisplayGameControlValues();
15132
15133   DrawCompleteVideoDisplay();
15134   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15135   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15136   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15137
15138   BackToFront();
15139 }
15140
15141 void GameUndo(int steps)
15142 {
15143   if (!CheckEngineSnapshotList())
15144     return;
15145
15146   LoadEngineSnapshot_Undo(steps);
15147
15148   GameUndoRedoExt();
15149 }
15150
15151 void GameRedo(int steps)
15152 {
15153   if (!CheckEngineSnapshotList())
15154     return;
15155
15156   LoadEngineSnapshot_Redo(steps);
15157
15158   GameUndoRedoExt();
15159 }
15160
15161 static void HandleGameButtonsExt(int id, int button)
15162 {
15163   static boolean game_undo_executed = FALSE;
15164   int steps = BUTTON_STEPSIZE(button);
15165   boolean handle_game_buttons =
15166     (game_status == GAME_MODE_PLAYING ||
15167      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15168
15169   if (!handle_game_buttons)
15170     return;
15171
15172   switch (id)
15173   {
15174     case GAME_CTRL_ID_STOP:
15175       if (game_status == GAME_MODE_MAIN)
15176         break;
15177
15178       if (tape.playing)
15179         TapeStop();
15180       else
15181         RequestQuitGame(TRUE);
15182
15183       break;
15184
15185     case GAME_CTRL_ID_PAUSE:
15186     case GAME_CTRL_ID_PAUSE2:
15187       if (options.network && game_status == GAME_MODE_PLAYING)
15188       {
15189 #if defined(NETWORK_AVALIABLE)
15190         if (tape.pausing)
15191           SendToServer_ContinuePlaying();
15192         else
15193           SendToServer_PausePlaying();
15194 #endif
15195       }
15196       else
15197         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15198
15199       game_undo_executed = FALSE;
15200
15201       break;
15202
15203     case GAME_CTRL_ID_PLAY:
15204       if (game_status == GAME_MODE_MAIN)
15205       {
15206         StartGameActions(options.network, setup.autorecord, level.random_seed);
15207       }
15208       else if (tape.pausing)
15209       {
15210 #if defined(NETWORK_AVALIABLE)
15211         if (options.network)
15212           SendToServer_ContinuePlaying();
15213         else
15214 #endif
15215           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15216       }
15217       break;
15218
15219     case GAME_CTRL_ID_UNDO:
15220       // Important: When using "save snapshot when collecting an item" mode,
15221       // load last (current) snapshot for first "undo" after pressing "pause"
15222       // (else the last-but-one snapshot would be loaded, because the snapshot
15223       // pointer already points to the last snapshot when pressing "pause",
15224       // which is fine for "every step/move" mode, but not for "every collect")
15225       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15226           !game_undo_executed)
15227         steps--;
15228
15229       game_undo_executed = TRUE;
15230
15231       GameUndo(steps);
15232       break;
15233
15234     case GAME_CTRL_ID_REDO:
15235       GameRedo(steps);
15236       break;
15237
15238     case GAME_CTRL_ID_SAVE:
15239       TapeQuickSave();
15240       break;
15241
15242     case GAME_CTRL_ID_LOAD:
15243       TapeQuickLoad();
15244       break;
15245
15246     case SOUND_CTRL_ID_MUSIC:
15247       if (setup.sound_music)
15248       { 
15249         setup.sound_music = FALSE;
15250
15251         FadeMusic();
15252       }
15253       else if (audio.music_available)
15254       { 
15255         setup.sound = setup.sound_music = TRUE;
15256
15257         SetAudioMode(setup.sound);
15258
15259         PlayLevelMusic();
15260       }
15261       break;
15262
15263     case SOUND_CTRL_ID_LOOPS:
15264       if (setup.sound_loops)
15265         setup.sound_loops = FALSE;
15266       else if (audio.loops_available)
15267       {
15268         setup.sound = setup.sound_loops = TRUE;
15269
15270         SetAudioMode(setup.sound);
15271       }
15272       break;
15273
15274     case SOUND_CTRL_ID_SIMPLE:
15275       if (setup.sound_simple)
15276         setup.sound_simple = FALSE;
15277       else if (audio.sound_available)
15278       {
15279         setup.sound = setup.sound_simple = TRUE;
15280
15281         SetAudioMode(setup.sound);
15282       }
15283       break;
15284
15285     default:
15286       break;
15287   }
15288 }
15289
15290 static void HandleGameButtons(struct GadgetInfo *gi)
15291 {
15292   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15293 }
15294
15295 void HandleSoundButtonKeys(Key key)
15296 {
15297
15298   if (key == setup.shortcut.sound_simple)
15299     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15300   else if (key == setup.shortcut.sound_loops)
15301     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15302   else if (key == setup.shortcut.sound_music)
15303     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15304 }