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