6144f0d200d17a27026eb31425dd08e4e0ddf933
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_FRAME                        35
126 #define GAME_PANEL_SHIELD_NORMAL                36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
128 #define GAME_PANEL_SHIELD_DEADLY                38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
130 #define GAME_PANEL_EXIT                         40
131 #define GAME_PANEL_EMC_MAGIC_BALL               41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
133 #define GAME_PANEL_LIGHT_SWITCH                 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
135 #define GAME_PANEL_TIMEGATE_SWITCH              45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
137 #define GAME_PANEL_SWITCHGATE_SWITCH            47
138 #define GAME_PANEL_EMC_LENSES                   48
139 #define GAME_PANEL_EMC_LENSES_TIME              49
140 #define GAME_PANEL_EMC_MAGNIFIER                50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
142 #define GAME_PANEL_BALLOON_SWITCH               52
143 #define GAME_PANEL_DYNABOMB_NUMBER              53
144 #define GAME_PANEL_DYNABOMB_SIZE                54
145 #define GAME_PANEL_DYNABOMB_POWER               55
146 #define GAME_PANEL_PENGUINS                     56
147 #define GAME_PANEL_SOKOBAN_OBJECTS              57
148 #define GAME_PANEL_SOKOBAN_FIELDS               58
149 #define GAME_PANEL_ROBOT_WHEEL                  59
150 #define GAME_PANEL_CONVEYOR_BELT_1              60
151 #define GAME_PANEL_CONVEYOR_BELT_2              61
152 #define GAME_PANEL_CONVEYOR_BELT_3              62
153 #define GAME_PANEL_CONVEYOR_BELT_4              63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
158 #define GAME_PANEL_MAGIC_WALL                   68
159 #define GAME_PANEL_MAGIC_WALL_TIME              69
160 #define GAME_PANEL_GRAVITY_STATE                70
161 #define GAME_PANEL_GRAPHIC_1                    71
162 #define GAME_PANEL_GRAPHIC_2                    72
163 #define GAME_PANEL_GRAPHIC_3                    73
164 #define GAME_PANEL_GRAPHIC_4                    74
165 #define GAME_PANEL_GRAPHIC_5                    75
166 #define GAME_PANEL_GRAPHIC_6                    76
167 #define GAME_PANEL_GRAPHIC_7                    77
168 #define GAME_PANEL_GRAPHIC_8                    78
169 #define GAME_PANEL_ELEMENT_1                    79
170 #define GAME_PANEL_ELEMENT_2                    80
171 #define GAME_PANEL_ELEMENT_3                    81
172 #define GAME_PANEL_ELEMENT_4                    82
173 #define GAME_PANEL_ELEMENT_5                    83
174 #define GAME_PANEL_ELEMENT_6                    84
175 #define GAME_PANEL_ELEMENT_7                    85
176 #define GAME_PANEL_ELEMENT_8                    86
177 #define GAME_PANEL_ELEMENT_COUNT_1              87
178 #define GAME_PANEL_ELEMENT_COUNT_2              88
179 #define GAME_PANEL_ELEMENT_COUNT_3              89
180 #define GAME_PANEL_ELEMENT_COUNT_4              90
181 #define GAME_PANEL_ELEMENT_COUNT_5              91
182 #define GAME_PANEL_ELEMENT_COUNT_6              92
183 #define GAME_PANEL_ELEMENT_COUNT_7              93
184 #define GAME_PANEL_ELEMENT_COUNT_8              94
185 #define GAME_PANEL_CE_SCORE_1                   95
186 #define GAME_PANEL_CE_SCORE_2                   96
187 #define GAME_PANEL_CE_SCORE_3                   97
188 #define GAME_PANEL_CE_SCORE_4                   98
189 #define GAME_PANEL_CE_SCORE_5                   99
190 #define GAME_PANEL_CE_SCORE_6                   100
191 #define GAME_PANEL_CE_SCORE_7                   101
192 #define GAME_PANEL_CE_SCORE_8                   102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
201 #define GAME_PANEL_PLAYER_NAME                  111
202 #define GAME_PANEL_LEVEL_NAME                   112
203 #define GAME_PANEL_LEVEL_AUTHOR                 113
204
205 #define NUM_GAME_PANEL_CONTROLS                 114
206
207 struct GamePanelOrderInfo
208 {
209   int nr;
210   int sort_priority;
211 };
212
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
214
215 struct GamePanelControlInfo
216 {
217   int nr;
218
219   struct TextPosInfo *pos;
220   int type;
221
222   int value, last_value;
223   int frame, last_frame;
224   int gfx_frame;
225   int gfx_random;
226 };
227
228 static struct GamePanelControlInfo game_panel_controls[] =
229 {
230   {
231     GAME_PANEL_LEVEL_NUMBER,
232     &game.panel.level_number,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_PANEL_GEMS,
237     &game.panel.gems,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_INVENTORY_COUNT,
242     &game.panel.inventory_count,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_FIRST_1,
247     &game.panel.inventory_first[0],
248     TYPE_ELEMENT,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_2,
252     &game.panel.inventory_first[1],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_3,
257     &game.panel.inventory_first[2],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_4,
262     &game.panel.inventory_first[3],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_5,
267     &game.panel.inventory_first[4],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_6,
272     &game.panel.inventory_first[5],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_7,
277     &game.panel.inventory_first[6],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_8,
282     &game.panel.inventory_first[7],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_LAST_1,
287     &game.panel.inventory_last[0],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_2,
292     &game.panel.inventory_last[1],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_3,
297     &game.panel.inventory_last[2],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_4,
302     &game.panel.inventory_last[3],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_5,
307     &game.panel.inventory_last[4],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_6,
312     &game.panel.inventory_last[5],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_7,
317     &game.panel.inventory_last[6],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_8,
322     &game.panel.inventory_last[7],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_KEY_1,
327     &game.panel.key[0],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_2,
332     &game.panel.key[1],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_3,
337     &game.panel.key[2],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_4,
342     &game.panel.key[3],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_5,
347     &game.panel.key[4],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_6,
352     &game.panel.key[5],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_7,
357     &game.panel.key[6],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_8,
362     &game.panel.key[7],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_WHITE,
367     &game.panel.key_white,
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE_COUNT,
372     &game.panel.key_white_count,
373     TYPE_INTEGER,
374   },
375   {
376     GAME_PANEL_SCORE,
377     &game.panel.score,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_HIGHSCORE,
382     &game.panel.highscore,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_TIME,
387     &game.panel.time,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME_HH,
392     &game.panel.time_hh,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_MM,
397     &game.panel.time_mm,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_SS,
402     &game.panel.time_ss,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_FRAME,
407     &game.panel.frame,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_SHIELD_NORMAL,
412     &game.panel.shield_normal,
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_PANEL_SHIELD_NORMAL_TIME,
417     &game.panel.shield_normal_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_PANEL_SHIELD_DEADLY,
422     &game.panel.shield_deadly,
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_PANEL_SHIELD_DEADLY_TIME,
427     &game.panel.shield_deadly_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_PANEL_EXIT,
432     &game.panel.exit,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_PANEL_EMC_MAGIC_BALL,
437     &game.panel.emc_magic_ball,
438     TYPE_ELEMENT,
439   },
440   {
441     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442     &game.panel.emc_magic_ball_switch,
443     TYPE_ELEMENT,
444   },
445   {
446     GAME_PANEL_LIGHT_SWITCH,
447     &game.panel.light_switch,
448     TYPE_ELEMENT,
449   },
450   {
451     GAME_PANEL_LIGHT_SWITCH_TIME,
452     &game.panel.light_switch_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_PANEL_TIMEGATE_SWITCH,
457     &game.panel.timegate_switch,
458     TYPE_ELEMENT,
459   },
460   {
461     GAME_PANEL_TIMEGATE_SWITCH_TIME,
462     &game.panel.timegate_switch_time,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_PANEL_SWITCHGATE_SWITCH,
467     &game.panel.switchgate_switch,
468     TYPE_ELEMENT,
469   },
470   {
471     GAME_PANEL_EMC_LENSES,
472     &game.panel.emc_lenses,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_PANEL_EMC_LENSES_TIME,
477     &game.panel.emc_lenses_time,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_PANEL_EMC_MAGNIFIER,
482     &game.panel.emc_magnifier,
483     TYPE_ELEMENT,
484   },
485   {
486     GAME_PANEL_EMC_MAGNIFIER_TIME,
487     &game.panel.emc_magnifier_time,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_PANEL_BALLOON_SWITCH,
492     &game.panel.balloon_switch,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_PANEL_DYNABOMB_NUMBER,
497     &game.panel.dynabomb_number,
498     TYPE_INTEGER,
499   },
500   {
501     GAME_PANEL_DYNABOMB_SIZE,
502     &game.panel.dynabomb_size,
503     TYPE_INTEGER,
504   },
505   {
506     GAME_PANEL_DYNABOMB_POWER,
507     &game.panel.dynabomb_power,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_PANEL_PENGUINS,
512     &game.panel.penguins,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_PANEL_SOKOBAN_OBJECTS,
517     &game.panel.sokoban_objects,
518     TYPE_INTEGER,
519   },
520   {
521     GAME_PANEL_SOKOBAN_FIELDS,
522     &game.panel.sokoban_fields,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_PANEL_ROBOT_WHEEL,
527     &game.panel.robot_wheel,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_PANEL_CONVEYOR_BELT_1,
532     &game.panel.conveyor_belt[0],
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_PANEL_CONVEYOR_BELT_2,
537     &game.panel.conveyor_belt[1],
538     TYPE_ELEMENT,
539   },
540   {
541     GAME_PANEL_CONVEYOR_BELT_3,
542     &game.panel.conveyor_belt[2],
543     TYPE_ELEMENT,
544   },
545   {
546     GAME_PANEL_CONVEYOR_BELT_4,
547     &game.panel.conveyor_belt[3],
548     TYPE_ELEMENT,
549   },
550   {
551     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552     &game.panel.conveyor_belt_switch[0],
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557     &game.panel.conveyor_belt_switch[1],
558     TYPE_ELEMENT,
559   },
560   {
561     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562     &game.panel.conveyor_belt_switch[2],
563     TYPE_ELEMENT,
564   },
565   {
566     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567     &game.panel.conveyor_belt_switch[3],
568     TYPE_ELEMENT,
569   },
570   {
571     GAME_PANEL_MAGIC_WALL,
572     &game.panel.magic_wall,
573     TYPE_ELEMENT,
574   },
575   {
576     GAME_PANEL_MAGIC_WALL_TIME,
577     &game.panel.magic_wall_time,
578     TYPE_INTEGER,
579   },
580   {
581     GAME_PANEL_GRAVITY_STATE,
582     &game.panel.gravity_state,
583     TYPE_STRING,
584   },
585   {
586     GAME_PANEL_GRAPHIC_1,
587     &game.panel.graphic[0],
588     TYPE_ELEMENT,
589   },
590   {
591     GAME_PANEL_GRAPHIC_2,
592     &game.panel.graphic[1],
593     TYPE_ELEMENT,
594   },
595   {
596     GAME_PANEL_GRAPHIC_3,
597     &game.panel.graphic[2],
598     TYPE_ELEMENT,
599   },
600   {
601     GAME_PANEL_GRAPHIC_4,
602     &game.panel.graphic[3],
603     TYPE_ELEMENT,
604   },
605   {
606     GAME_PANEL_GRAPHIC_5,
607     &game.panel.graphic[4],
608     TYPE_ELEMENT,
609   },
610   {
611     GAME_PANEL_GRAPHIC_6,
612     &game.panel.graphic[5],
613     TYPE_ELEMENT,
614   },
615   {
616     GAME_PANEL_GRAPHIC_7,
617     &game.panel.graphic[6],
618     TYPE_ELEMENT,
619   },
620   {
621     GAME_PANEL_GRAPHIC_8,
622     &game.panel.graphic[7],
623     TYPE_ELEMENT,
624   },
625   {
626     GAME_PANEL_ELEMENT_1,
627     &game.panel.element[0],
628     TYPE_ELEMENT,
629   },
630   {
631     GAME_PANEL_ELEMENT_2,
632     &game.panel.element[1],
633     TYPE_ELEMENT,
634   },
635   {
636     GAME_PANEL_ELEMENT_3,
637     &game.panel.element[2],
638     TYPE_ELEMENT,
639   },
640   {
641     GAME_PANEL_ELEMENT_4,
642     &game.panel.element[3],
643     TYPE_ELEMENT,
644   },
645   {
646     GAME_PANEL_ELEMENT_5,
647     &game.panel.element[4],
648     TYPE_ELEMENT,
649   },
650   {
651     GAME_PANEL_ELEMENT_6,
652     &game.panel.element[5],
653     TYPE_ELEMENT,
654   },
655   {
656     GAME_PANEL_ELEMENT_7,
657     &game.panel.element[6],
658     TYPE_ELEMENT,
659   },
660   {
661     GAME_PANEL_ELEMENT_8,
662     &game.panel.element[7],
663     TYPE_ELEMENT,
664   },
665   {
666     GAME_PANEL_ELEMENT_COUNT_1,
667     &game.panel.element_count[0],
668     TYPE_INTEGER,
669   },
670   {
671     GAME_PANEL_ELEMENT_COUNT_2,
672     &game.panel.element_count[1],
673     TYPE_INTEGER,
674   },
675   {
676     GAME_PANEL_ELEMENT_COUNT_3,
677     &game.panel.element_count[2],
678     TYPE_INTEGER,
679   },
680   {
681     GAME_PANEL_ELEMENT_COUNT_4,
682     &game.panel.element_count[3],
683     TYPE_INTEGER,
684   },
685   {
686     GAME_PANEL_ELEMENT_COUNT_5,
687     &game.panel.element_count[4],
688     TYPE_INTEGER,
689   },
690   {
691     GAME_PANEL_ELEMENT_COUNT_6,
692     &game.panel.element_count[5],
693     TYPE_INTEGER,
694   },
695   {
696     GAME_PANEL_ELEMENT_COUNT_7,
697     &game.panel.element_count[6],
698     TYPE_INTEGER,
699   },
700   {
701     GAME_PANEL_ELEMENT_COUNT_8,
702     &game.panel.element_count[7],
703     TYPE_INTEGER,
704   },
705   {
706     GAME_PANEL_CE_SCORE_1,
707     &game.panel.ce_score[0],
708     TYPE_INTEGER,
709   },
710   {
711     GAME_PANEL_CE_SCORE_2,
712     &game.panel.ce_score[1],
713     TYPE_INTEGER,
714   },
715   {
716     GAME_PANEL_CE_SCORE_3,
717     &game.panel.ce_score[2],
718     TYPE_INTEGER,
719   },
720   {
721     GAME_PANEL_CE_SCORE_4,
722     &game.panel.ce_score[3],
723     TYPE_INTEGER,
724   },
725   {
726     GAME_PANEL_CE_SCORE_5,
727     &game.panel.ce_score[4],
728     TYPE_INTEGER,
729   },
730   {
731     GAME_PANEL_CE_SCORE_6,
732     &game.panel.ce_score[5],
733     TYPE_INTEGER,
734   },
735   {
736     GAME_PANEL_CE_SCORE_7,
737     &game.panel.ce_score[6],
738     TYPE_INTEGER,
739   },
740   {
741     GAME_PANEL_CE_SCORE_8,
742     &game.panel.ce_score[7],
743     TYPE_INTEGER,
744   },
745   {
746     GAME_PANEL_CE_SCORE_1_ELEMENT,
747     &game.panel.ce_score_element[0],
748     TYPE_ELEMENT,
749   },
750   {
751     GAME_PANEL_CE_SCORE_2_ELEMENT,
752     &game.panel.ce_score_element[1],
753     TYPE_ELEMENT,
754   },
755   {
756     GAME_PANEL_CE_SCORE_3_ELEMENT,
757     &game.panel.ce_score_element[2],
758     TYPE_ELEMENT,
759   },
760   {
761     GAME_PANEL_CE_SCORE_4_ELEMENT,
762     &game.panel.ce_score_element[3],
763     TYPE_ELEMENT,
764   },
765   {
766     GAME_PANEL_CE_SCORE_5_ELEMENT,
767     &game.panel.ce_score_element[4],
768     TYPE_ELEMENT,
769   },
770   {
771     GAME_PANEL_CE_SCORE_6_ELEMENT,
772     &game.panel.ce_score_element[5],
773     TYPE_ELEMENT,
774   },
775   {
776     GAME_PANEL_CE_SCORE_7_ELEMENT,
777     &game.panel.ce_score_element[6],
778     TYPE_ELEMENT,
779   },
780   {
781     GAME_PANEL_CE_SCORE_8_ELEMENT,
782     &game.panel.ce_score_element[7],
783     TYPE_ELEMENT,
784   },
785   {
786     GAME_PANEL_PLAYER_NAME,
787     &game.panel.player_name,
788     TYPE_STRING,
789   },
790   {
791     GAME_PANEL_LEVEL_NAME,
792     &game.panel.level_name,
793     TYPE_STRING,
794   },
795   {
796     GAME_PANEL_LEVEL_AUTHOR,
797     &game.panel.level_author,
798     TYPE_STRING,
799   },
800
801   {
802     -1,
803     NULL,
804     -1,
805   }
806 };
807
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING      3
810 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION   2
812 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
813
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF  -1
816 #define INITIAL_MOVE_DELAY_ON   0
817
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED    32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED   4
822 #define MOVE_DELAY_MAX_SPEED    1
823
824 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
826
827 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
829
830 /* values for other actions */
831 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
832 #define MOVE_STEPSIZE_MIN       (1)
833 #define MOVE_STEPSIZE_MAX       (TILEX)
834
835 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
836 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
837
838 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
839
840 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
841                                  RND(element_info[e].push_delay_random))
842 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
843                                  RND(element_info[e].drop_delay_random))
844 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
845                                  RND(element_info[e].move_delay_random))
846 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
847                                     (element_info[e].move_delay_random))
848 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
849                                  RND(element_info[e].ce_value_random_initial))
850 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
851 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
852                                  RND((c)->delay_random * (c)->delay_frames))
853 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
854                                  RND((c)->delay_random))
855
856
857 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
858          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
859
860 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
861         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
862          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
863          (be) + (e) - EL_SELF)
864
865 #define GET_PLAYER_FROM_BITS(p)                                         \
866         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
867
868 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
869         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
870          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
871          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
872          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
873          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
874          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
875          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
876          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
877          (e))
878
879 #define CAN_GROW_INTO(e)                                                \
880         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
881
882 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
883                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
884                                         (condition)))
885
886 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
887                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
888                                         (CAN_MOVE_INTO_ACID(e) &&       \
889                                          Feld[x][y] == EL_ACID) ||      \
890                                         (condition)))
891
892 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
893                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
894                                         (CAN_MOVE_INTO_ACID(e) &&       \
895                                          Feld[x][y] == EL_ACID) ||      \
896                                         (condition)))
897
898 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
899                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
900                                         (condition) ||                  \
901                                         (CAN_MOVE_INTO_ACID(e) &&       \
902                                          Feld[x][y] == EL_ACID) ||      \
903                                         (DONT_COLLIDE_WITH(e) &&        \
904                                          IS_PLAYER(x, y) &&             \
905                                          !PLAYER_ENEMY_PROTECTED(x, y))))
906
907 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
908         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
909
910 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
911         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
912
913 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
914         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
915
916 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
917         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
918                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
919
920 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
921         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
922
923 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
924         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
925
926 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
927         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
928
929 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
930         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
931
932 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
933         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
934
935 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
936         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
939                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
940                                                  IS_FOOD_PENGUIN(Feld[x][y])))
941 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
942         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
943
944 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
946
947 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
949
950 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
951         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
952                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
953
954 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
955
956 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
957                 (!IS_PLAYER(x, y) &&                                    \
958                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
959
960 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
961         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
962
963 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
964 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
965
966 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
967 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
969 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
970
971 /* game button identifiers */
972 #define GAME_CTRL_ID_STOP               0
973 #define GAME_CTRL_ID_PAUSE              1
974 #define GAME_CTRL_ID_PLAY               2
975 #define GAME_CTRL_ID_UNDO               3
976 #define GAME_CTRL_ID_REDO               4
977 #define GAME_CTRL_ID_SAVE               5
978 #define GAME_CTRL_ID_PAUSE2             6
979 #define GAME_CTRL_ID_LOAD               7
980 #define SOUND_CTRL_ID_MUSIC             8
981 #define SOUND_CTRL_ID_LOOPS             9
982 #define SOUND_CTRL_ID_SIMPLE            10
983
984 #define NUM_GAME_BUTTONS                11
985
986
987 /* forward declaration for internal use */
988
989 static void CreateField(int, int, int);
990
991 static void ResetGfxAnimation(int, int);
992
993 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
994 static void AdvanceFrameAndPlayerCounters(int);
995
996 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
997 static boolean MovePlayer(struct PlayerInfo *, int, int);
998 static void ScrollPlayer(struct PlayerInfo *, int);
999 static void ScrollScreen(struct PlayerInfo *, int);
1000
1001 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1002 static boolean DigFieldByCE(int, int, int);
1003 static boolean SnapField(struct PlayerInfo *, int, int);
1004 static boolean DropElement(struct PlayerInfo *);
1005
1006 static void InitBeltMovement(void);
1007 static void CloseAllOpenTimegates(void);
1008 static void CheckGravityMovement(struct PlayerInfo *);
1009 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1010 static void KillPlayerUnlessEnemyProtected(int, int);
1011 static void KillPlayerUnlessExplosionProtected(int, int);
1012
1013 static void TestIfPlayerTouchesCustomElement(int, int);
1014 static void TestIfElementTouchesCustomElement(int, int);
1015 static void TestIfElementHitsCustomElement(int, int, int);
1016
1017 static void HandleElementChange(int, int, int);
1018 static void ExecuteCustomElementAction(int, int, int, int);
1019 static boolean ChangeElement(int, int, int, int);
1020
1021 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1022 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1023         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1024 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1026 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1027         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1028 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1029         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1030
1031 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1032 #define CheckElementChange(x, y, e, te, ev)                             \
1033         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1034 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1035         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1036 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1037         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1038
1039 static void PlayLevelSound(int, int, int);
1040 static void PlayLevelSoundNearest(int, int, int);
1041 static void PlayLevelSoundAction(int, int, int);
1042 static void PlayLevelSoundElementAction(int, int, int, int);
1043 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1044 static void PlayLevelSoundActionIfLoop(int, int, int);
1045 static void StopLevelSoundActionIfLoop(int, int, int);
1046 static void PlayLevelMusic();
1047
1048 static void HandleGameButtons(struct GadgetInfo *);
1049
1050 int AmoebeNachbarNr(int, int);
1051 void AmoebeUmwandeln(int, int);
1052 void ContinueMoving(int, int);
1053 void Bang(int, int);
1054 void InitMovDir(int, int);
1055 void InitAmoebaNr(int, int);
1056 int NewHiScore(void);
1057
1058 void TestIfGoodThingHitsBadThing(int, int, int);
1059 void TestIfBadThingHitsGoodThing(int, int, int);
1060 void TestIfPlayerTouchesBadThing(int, int);
1061 void TestIfPlayerRunsIntoBadThing(int, int, int);
1062 void TestIfBadThingTouchesPlayer(int, int);
1063 void TestIfBadThingRunsIntoPlayer(int, int, int);
1064 void TestIfFriendTouchesBadThing(int, int);
1065 void TestIfBadThingTouchesFriend(int, int);
1066 void TestIfBadThingTouchesOtherBadThing(int, int);
1067 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1068
1069 void KillPlayer(struct PlayerInfo *);
1070 void BuryPlayer(struct PlayerInfo *);
1071 void RemovePlayer(struct PlayerInfo *);
1072
1073 static int getInvisibleActiveFromInvisibleElement(int);
1074 static int getInvisibleFromInvisibleActiveElement(int);
1075
1076 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1077
1078 /* for detection of endless loops, caused by custom element programming */
1079 /* (using maximal playfield width x 10 is just a rough approximation) */
1080 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1081
1082 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1083 {                                                                       \
1084   if (recursion_loop_detected)                                          \
1085     return (rc);                                                        \
1086                                                                         \
1087   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1088   {                                                                     \
1089     recursion_loop_detected = TRUE;                                     \
1090     recursion_loop_element = (e);                                       \
1091   }                                                                     \
1092                                                                         \
1093   recursion_loop_depth++;                                               \
1094 }
1095
1096 #define RECURSION_LOOP_DETECTION_END()                                  \
1097 {                                                                       \
1098   recursion_loop_depth--;                                               \
1099 }
1100
1101 static int recursion_loop_depth;
1102 static boolean recursion_loop_detected;
1103 static boolean recursion_loop_element;
1104
1105 static int map_player_action[MAX_PLAYERS];
1106
1107
1108 /* ------------------------------------------------------------------------- */
1109 /* definition of elements that automatically change to other elements after  */
1110 /* a specified time, eventually calling a function when changing             */
1111 /* ------------------------------------------------------------------------- */
1112
1113 /* forward declaration for changer functions */
1114 static void InitBuggyBase(int, int);
1115 static void WarnBuggyBase(int, int);
1116
1117 static void InitTrap(int, int);
1118 static void ActivateTrap(int, int);
1119 static void ChangeActiveTrap(int, int);
1120
1121 static void InitRobotWheel(int, int);
1122 static void RunRobotWheel(int, int);
1123 static void StopRobotWheel(int, int);
1124
1125 static void InitTimegateWheel(int, int);
1126 static void RunTimegateWheel(int, int);
1127
1128 static void InitMagicBallDelay(int, int);
1129 static void ActivateMagicBall(int, int);
1130
1131 struct ChangingElementInfo
1132 {
1133   int element;
1134   int target_element;
1135   int change_delay;
1136   void (*pre_change_function)(int x, int y);
1137   void (*change_function)(int x, int y);
1138   void (*post_change_function)(int x, int y);
1139 };
1140
1141 static struct ChangingElementInfo change_delay_list[] =
1142 {
1143   {
1144     EL_NUT_BREAKING,
1145     EL_EMERALD,
1146     6,
1147     NULL,
1148     NULL,
1149     NULL
1150   },
1151   {
1152     EL_PEARL_BREAKING,
1153     EL_EMPTY,
1154     8,
1155     NULL,
1156     NULL,
1157     NULL
1158   },
1159   {
1160     EL_EXIT_OPENING,
1161     EL_EXIT_OPEN,
1162     29,
1163     NULL,
1164     NULL,
1165     NULL
1166   },
1167   {
1168     EL_EXIT_CLOSING,
1169     EL_EXIT_CLOSED,
1170     29,
1171     NULL,
1172     NULL,
1173     NULL
1174   },
1175   {
1176     EL_STEEL_EXIT_OPENING,
1177     EL_STEEL_EXIT_OPEN,
1178     29,
1179     NULL,
1180     NULL,
1181     NULL
1182   },
1183   {
1184     EL_STEEL_EXIT_CLOSING,
1185     EL_STEEL_EXIT_CLOSED,
1186     29,
1187     NULL,
1188     NULL,
1189     NULL
1190   },
1191   {
1192     EL_EM_EXIT_OPENING,
1193     EL_EM_EXIT_OPEN,
1194     29,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_EM_EXIT_CLOSING,
1201     EL_EMPTY,
1202     29,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EM_STEEL_EXIT_OPENING,
1209     EL_EM_STEEL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EM_STEEL_EXIT_CLOSING,
1217     EL_STEELWALL,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_SP_EXIT_OPENING,
1225     EL_SP_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_SP_EXIT_CLOSING,
1233     EL_SP_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_SWITCHGATE_OPENING,
1241     EL_SWITCHGATE_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_SWITCHGATE_CLOSING,
1249     EL_SWITCHGATE_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_TIMEGATE_OPENING,
1257     EL_TIMEGATE_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_TIMEGATE_CLOSING,
1265     EL_TIMEGATE_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271
1272   {
1273     EL_ACID_SPLASH_LEFT,
1274     EL_EMPTY,
1275     8,
1276     NULL,
1277     NULL,
1278     NULL
1279   },
1280   {
1281     EL_ACID_SPLASH_RIGHT,
1282     EL_EMPTY,
1283     8,
1284     NULL,
1285     NULL,
1286     NULL
1287   },
1288   {
1289     EL_SP_BUGGY_BASE,
1290     EL_SP_BUGGY_BASE_ACTIVATING,
1291     0,
1292     InitBuggyBase,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_SP_BUGGY_BASE_ACTIVATING,
1298     EL_SP_BUGGY_BASE_ACTIVE,
1299     0,
1300     InitBuggyBase,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_BUGGY_BASE_ACTIVE,
1306     EL_SP_BUGGY_BASE,
1307     0,
1308     InitBuggyBase,
1309     WarnBuggyBase,
1310     NULL
1311   },
1312   {
1313     EL_TRAP,
1314     EL_TRAP_ACTIVE,
1315     0,
1316     InitTrap,
1317     NULL,
1318     ActivateTrap
1319   },
1320   {
1321     EL_TRAP_ACTIVE,
1322     EL_TRAP,
1323     31,
1324     NULL,
1325     ChangeActiveTrap,
1326     NULL
1327   },
1328   {
1329     EL_ROBOT_WHEEL_ACTIVE,
1330     EL_ROBOT_WHEEL,
1331     0,
1332     InitRobotWheel,
1333     RunRobotWheel,
1334     StopRobotWheel
1335   },
1336   {
1337     EL_TIMEGATE_SWITCH_ACTIVE,
1338     EL_TIMEGATE_SWITCH,
1339     0,
1340     InitTimegateWheel,
1341     RunTimegateWheel,
1342     NULL
1343   },
1344   {
1345     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1346     EL_DC_TIMEGATE_SWITCH,
1347     0,
1348     InitTimegateWheel,
1349     RunTimegateWheel,
1350     NULL
1351   },
1352   {
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     EL_EMC_MAGIC_BALL_ACTIVE,
1355     0,
1356     InitMagicBallDelay,
1357     NULL,
1358     ActivateMagicBall
1359   },
1360   {
1361     EL_EMC_SPRING_BUMPER_ACTIVE,
1362     EL_EMC_SPRING_BUMPER,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_DIAGONAL_SHRINKING,
1370     EL_UNDEFINED,
1371     0,
1372     NULL,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_DIAGONAL_GROWING,
1378     EL_UNDEFINED,
1379     0,
1380     NULL,
1381     NULL,
1382     NULL,
1383   },
1384
1385   {
1386     EL_UNDEFINED,
1387     EL_UNDEFINED,
1388     -1,
1389     NULL,
1390     NULL,
1391     NULL
1392   }
1393 };
1394
1395 struct
1396 {
1397   int element;
1398   int push_delay_fixed, push_delay_random;
1399 }
1400 push_delay_list[] =
1401 {
1402   { EL_SPRING,                  0, 0 },
1403   { EL_BALLOON,                 0, 0 },
1404
1405   { EL_SOKOBAN_OBJECT,          2, 0 },
1406   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1407   { EL_SATELLITE,               2, 0 },
1408   { EL_SP_DISK_YELLOW,          2, 0 },
1409
1410   { EL_UNDEFINED,               0, 0 },
1411 };
1412
1413 struct
1414 {
1415   int element;
1416   int move_stepsize;
1417 }
1418 move_stepsize_list[] =
1419 {
1420   { EL_AMOEBA_DROP,             2 },
1421   { EL_AMOEBA_DROPPING,         2 },
1422   { EL_QUICKSAND_FILLING,       1 },
1423   { EL_QUICKSAND_EMPTYING,      1 },
1424   { EL_QUICKSAND_FAST_FILLING,  2 },
1425   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1426   { EL_MAGIC_WALL_FILLING,      2 },
1427   { EL_MAGIC_WALL_EMPTYING,     2 },
1428   { EL_BD_MAGIC_WALL_FILLING,   2 },
1429   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1430   { EL_DC_MAGIC_WALL_FILLING,   2 },
1431   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1432
1433   { EL_UNDEFINED,               0 },
1434 };
1435
1436 struct
1437 {
1438   int element;
1439   int count;
1440 }
1441 collect_count_list[] =
1442 {
1443   { EL_EMERALD,                 1 },
1444   { EL_BD_DIAMOND,              1 },
1445   { EL_EMERALD_YELLOW,          1 },
1446   { EL_EMERALD_RED,             1 },
1447   { EL_EMERALD_PURPLE,          1 },
1448   { EL_DIAMOND,                 3 },
1449   { EL_SP_INFOTRON,             1 },
1450   { EL_PEARL,                   5 },
1451   { EL_CRYSTAL,                 8 },
1452
1453   { EL_UNDEFINED,               0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int direction;
1460 }
1461 access_direction_list[] =
1462 {
1463   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1464   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1465   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1466   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1467   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1468   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1469   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1470   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1471   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1472   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1473   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1474
1475   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1476   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1477   { EL_SP_PORT_UP,                                                   MV_DOWN },
1478   { EL_SP_PORT_DOWN,                                         MV_UP           },
1479   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1480   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1481   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1482   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1483   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1484   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1485   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1486   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1487   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1488   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1489   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1490   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1491   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1492   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1493   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1494
1495   { EL_UNDEFINED,                       MV_NONE                              }
1496 };
1497
1498 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1499
1500 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1501 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1502 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1503                                  IS_JUST_CHANGING(x, y))
1504
1505 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1506
1507 /* static variables for playfield scan mode (scanning forward or backward) */
1508 static int playfield_scan_start_x = 0;
1509 static int playfield_scan_start_y = 0;
1510 static int playfield_scan_delta_x = 1;
1511 static int playfield_scan_delta_y = 1;
1512
1513 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1514                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1515                                      (y) += playfield_scan_delta_y)     \
1516                                 for ((x) = playfield_scan_start_x;      \
1517                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1518                                      (x) += playfield_scan_delta_x)
1519
1520 #ifdef DEBUG
1521 void DEBUG_SetMaximumDynamite()
1522 {
1523   int i;
1524
1525   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1526     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1527       local_player->inventory_element[local_player->inventory_size++] =
1528         EL_DYNAMITE;
1529 }
1530 #endif
1531
1532 static void InitPlayfieldScanModeVars()
1533 {
1534   if (game.use_reverse_scan_direction)
1535   {
1536     playfield_scan_start_x = lev_fieldx - 1;
1537     playfield_scan_start_y = lev_fieldy - 1;
1538
1539     playfield_scan_delta_x = -1;
1540     playfield_scan_delta_y = -1;
1541   }
1542   else
1543   {
1544     playfield_scan_start_x = 0;
1545     playfield_scan_start_y = 0;
1546
1547     playfield_scan_delta_x = 1;
1548     playfield_scan_delta_y = 1;
1549   }
1550 }
1551
1552 static void InitPlayfieldScanMode(int mode)
1553 {
1554   game.use_reverse_scan_direction =
1555     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1556
1557   InitPlayfieldScanModeVars();
1558 }
1559
1560 static int get_move_delay_from_stepsize(int move_stepsize)
1561 {
1562   move_stepsize =
1563     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1564
1565   /* make sure that stepsize value is always a power of 2 */
1566   move_stepsize = (1 << log_2(move_stepsize));
1567
1568   return TILEX / move_stepsize;
1569 }
1570
1571 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1572                                boolean init_game)
1573 {
1574   int player_nr = player->index_nr;
1575   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1576   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1577
1578   /* do no immediately change move delay -- the player might just be moving */
1579   player->move_delay_value_next = move_delay;
1580
1581   /* information if player can move must be set separately */
1582   player->cannot_move = cannot_move;
1583
1584   if (init_game)
1585   {
1586     player->move_delay       = game.initial_move_delay[player_nr];
1587     player->move_delay_value = game.initial_move_delay_value[player_nr];
1588
1589     player->move_delay_value_next = -1;
1590
1591     player->move_delay_reset_counter = 0;
1592   }
1593 }
1594
1595 void GetPlayerConfig()
1596 {
1597   GameFrameDelay = setup.game_frame_delay;
1598
1599   if (!audio.sound_available)
1600     setup.sound_simple = FALSE;
1601
1602   if (!audio.loops_available)
1603     setup.sound_loops = FALSE;
1604
1605   if (!audio.music_available)
1606     setup.sound_music = FALSE;
1607
1608   if (!video.fullscreen_available)
1609     setup.fullscreen = FALSE;
1610
1611   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1612
1613   SetAudioMode(setup.sound);
1614   InitJoysticks();
1615 }
1616
1617 int GetElementFromGroupElement(int element)
1618 {
1619   if (IS_GROUP_ELEMENT(element))
1620   {
1621     struct ElementGroupInfo *group = element_info[element].group;
1622     int last_anim_random_frame = gfx.anim_random_frame;
1623     int element_pos;
1624
1625     if (group->choice_mode == ANIM_RANDOM)
1626       gfx.anim_random_frame = RND(group->num_elements_resolved);
1627
1628     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1629                                     group->choice_mode, 0,
1630                                     group->choice_pos);
1631
1632     if (group->choice_mode == ANIM_RANDOM)
1633       gfx.anim_random_frame = last_anim_random_frame;
1634
1635     group->choice_pos++;
1636
1637     element = group->element_resolved[element_pos];
1638   }
1639
1640   return element;
1641 }
1642
1643 static void InitPlayerField(int x, int y, int element, boolean init_game)
1644 {
1645   if (element == EL_SP_MURPHY)
1646   {
1647     if (init_game)
1648     {
1649       if (stored_player[0].present)
1650       {
1651         Feld[x][y] = EL_SP_MURPHY_CLONE;
1652
1653         return;
1654       }
1655       else
1656       {
1657         stored_player[0].initial_element = element;
1658         stored_player[0].use_murphy = TRUE;
1659
1660         if (!level.use_artwork_element[0])
1661           stored_player[0].artwork_element = EL_SP_MURPHY;
1662       }
1663
1664       Feld[x][y] = EL_PLAYER_1;
1665     }
1666   }
1667
1668   if (init_game)
1669   {
1670     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1671     int jx = player->jx, jy = player->jy;
1672
1673     player->present = TRUE;
1674
1675     player->block_last_field = (element == EL_SP_MURPHY ?
1676                                 level.sp_block_last_field :
1677                                 level.block_last_field);
1678
1679     /* ---------- initialize player's last field block delay --------------- */
1680
1681     /* always start with reliable default value (no adjustment needed) */
1682     player->block_delay_adjustment = 0;
1683
1684     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1685     if (player->block_last_field && element == EL_SP_MURPHY)
1686       player->block_delay_adjustment = 1;
1687
1688     /* special case 2: in game engines before 3.1.1, blocking was different */
1689     if (game.use_block_last_field_bug)
1690       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1691
1692     if (!options.network || player->connected)
1693     {
1694       player->active = TRUE;
1695
1696       /* remove potentially duplicate players */
1697       if (StorePlayer[jx][jy] == Feld[x][y])
1698         StorePlayer[jx][jy] = 0;
1699
1700       StorePlayer[x][y] = Feld[x][y];
1701
1702 #if DEBUG_INIT_PLAYER
1703       if (options.debug)
1704       {
1705         printf("- player element %d activated", player->element_nr);
1706         printf(" (local player is %d and currently %s)\n",
1707                local_player->element_nr,
1708                local_player->active ? "active" : "not active");
1709       }
1710     }
1711 #endif
1712
1713     Feld[x][y] = EL_EMPTY;
1714
1715     player->jx = player->last_jx = x;
1716     player->jy = player->last_jy = y;
1717   }
1718
1719   if (!init_game)
1720   {
1721     int player_nr = GET_PLAYER_NR(element);
1722     struct PlayerInfo *player = &stored_player[player_nr];
1723
1724     if (player->active && player->killed)
1725       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1726   }
1727 }
1728
1729 static void InitField(int x, int y, boolean init_game)
1730 {
1731   int element = Feld[x][y];
1732
1733   switch (element)
1734   {
1735     case EL_SP_MURPHY:
1736     case EL_PLAYER_1:
1737     case EL_PLAYER_2:
1738     case EL_PLAYER_3:
1739     case EL_PLAYER_4:
1740       InitPlayerField(x, y, element, init_game);
1741       break;
1742
1743     case EL_SOKOBAN_FIELD_PLAYER:
1744       element = Feld[x][y] = EL_PLAYER_1;
1745       InitField(x, y, init_game);
1746
1747       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1748       InitField(x, y, init_game);
1749       break;
1750
1751     case EL_SOKOBAN_FIELD_EMPTY:
1752       local_player->sokobanfields_still_needed++;
1753       break;
1754
1755     case EL_STONEBLOCK:
1756       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1758       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1759         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1764       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1765         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1766       break;
1767
1768     case EL_BUG:
1769     case EL_BUG_RIGHT:
1770     case EL_BUG_UP:
1771     case EL_BUG_LEFT:
1772     case EL_BUG_DOWN:
1773     case EL_SPACESHIP:
1774     case EL_SPACESHIP_RIGHT:
1775     case EL_SPACESHIP_UP:
1776     case EL_SPACESHIP_LEFT:
1777     case EL_SPACESHIP_DOWN:
1778     case EL_BD_BUTTERFLY:
1779     case EL_BD_BUTTERFLY_RIGHT:
1780     case EL_BD_BUTTERFLY_UP:
1781     case EL_BD_BUTTERFLY_LEFT:
1782     case EL_BD_BUTTERFLY_DOWN:
1783     case EL_BD_FIREFLY:
1784     case EL_BD_FIREFLY_RIGHT:
1785     case EL_BD_FIREFLY_UP:
1786     case EL_BD_FIREFLY_LEFT:
1787     case EL_BD_FIREFLY_DOWN:
1788     case EL_PACMAN_RIGHT:
1789     case EL_PACMAN_UP:
1790     case EL_PACMAN_LEFT:
1791     case EL_PACMAN_DOWN:
1792     case EL_YAMYAM:
1793     case EL_YAMYAM_LEFT:
1794     case EL_YAMYAM_RIGHT:
1795     case EL_YAMYAM_UP:
1796     case EL_YAMYAM_DOWN:
1797     case EL_DARK_YAMYAM:
1798     case EL_ROBOT:
1799     case EL_PACMAN:
1800     case EL_SP_SNIKSNAK:
1801     case EL_SP_ELECTRON:
1802     case EL_MOLE:
1803     case EL_MOLE_LEFT:
1804     case EL_MOLE_RIGHT:
1805     case EL_MOLE_UP:
1806     case EL_MOLE_DOWN:
1807       InitMovDir(x, y);
1808       break;
1809
1810     case EL_AMOEBA_FULL:
1811     case EL_BD_AMOEBA:
1812       InitAmoebaNr(x, y);
1813       break;
1814
1815     case EL_AMOEBA_DROP:
1816       if (y == lev_fieldy - 1)
1817       {
1818         Feld[x][y] = EL_AMOEBA_GROWING;
1819         Store[x][y] = EL_AMOEBA_WET;
1820       }
1821       break;
1822
1823     case EL_DYNAMITE_ACTIVE:
1824     case EL_SP_DISK_RED_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1828     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1829       MovDelay[x][y] = 96;
1830       break;
1831
1832     case EL_EM_DYNAMITE_ACTIVE:
1833       MovDelay[x][y] = 32;
1834       break;
1835
1836     case EL_LAMP:
1837       local_player->lights_still_needed++;
1838       break;
1839
1840     case EL_PENGUIN:
1841       local_player->friends_still_needed++;
1842       break;
1843
1844     case EL_PIG:
1845     case EL_DRAGON:
1846       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1847       break;
1848
1849     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1850     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1851     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1853     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1854     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1856     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1857     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1859     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1860     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1861       if (init_game)
1862       {
1863         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1865         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1866
1867         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1868         {
1869           game.belt_dir[belt_nr] = belt_dir;
1870           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1871         }
1872         else    /* more than one switch -- set it like the first switch */
1873         {
1874           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1875         }
1876       }
1877       break;
1878
1879     case EL_LIGHT_SWITCH_ACTIVE:
1880       if (init_game)
1881         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1882       break;
1883
1884     case EL_INVISIBLE_STEELWALL:
1885     case EL_INVISIBLE_WALL:
1886     case EL_INVISIBLE_SAND:
1887       if (game.light_time_left > 0 ||
1888           game.lenses_time_left > 0)
1889         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1890       break;
1891
1892     case EL_EMC_MAGIC_BALL:
1893       if (game.ball_state)
1894         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1895       break;
1896
1897     case EL_EMC_MAGIC_BALL_SWITCH:
1898       if (game.ball_state)
1899         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1900       break;
1901
1902     case EL_TRIGGER_PLAYER:
1903     case EL_TRIGGER_ELEMENT:
1904     case EL_TRIGGER_CE_VALUE:
1905     case EL_TRIGGER_CE_SCORE:
1906     case EL_SELF:
1907     case EL_ANY_ELEMENT:
1908     case EL_CURRENT_CE_VALUE:
1909     case EL_CURRENT_CE_SCORE:
1910     case EL_PREV_CE_1:
1911     case EL_PREV_CE_2:
1912     case EL_PREV_CE_3:
1913     case EL_PREV_CE_4:
1914     case EL_PREV_CE_5:
1915     case EL_PREV_CE_6:
1916     case EL_PREV_CE_7:
1917     case EL_PREV_CE_8:
1918     case EL_NEXT_CE_1:
1919     case EL_NEXT_CE_2:
1920     case EL_NEXT_CE_3:
1921     case EL_NEXT_CE_4:
1922     case EL_NEXT_CE_5:
1923     case EL_NEXT_CE_6:
1924     case EL_NEXT_CE_7:
1925     case EL_NEXT_CE_8:
1926       /* reference elements should not be used on the playfield */
1927       Feld[x][y] = EL_EMPTY;
1928       break;
1929
1930     default:
1931       if (IS_CUSTOM_ELEMENT(element))
1932       {
1933         if (CAN_MOVE(element))
1934           InitMovDir(x, y);
1935
1936         if (!element_info[element].use_last_ce_value || init_game)
1937           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1938       }
1939       else if (IS_GROUP_ELEMENT(element))
1940       {
1941         Feld[x][y] = GetElementFromGroupElement(element);
1942
1943         InitField(x, y, init_game);
1944       }
1945
1946       break;
1947   }
1948
1949   if (!init_game)
1950     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1951 }
1952
1953 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1954 {
1955   InitField(x, y, init_game);
1956
1957   /* not needed to call InitMovDir() -- already done by InitField()! */
1958   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1959       CAN_MOVE(Feld[x][y]))
1960     InitMovDir(x, y);
1961 }
1962
1963 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1964 {
1965   int old_element = Feld[x][y];
1966
1967   InitField(x, y, init_game);
1968
1969   /* not needed to call InitMovDir() -- already done by InitField()! */
1970   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971       CAN_MOVE(old_element) &&
1972       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1973     InitMovDir(x, y);
1974
1975   /* this case is in fact a combination of not less than three bugs:
1976      first, it calls InitMovDir() for elements that can move, although this is
1977      already done by InitField(); then, it checks the element that was at this
1978      field _before_ the call to InitField() (which can change it); lastly, it
1979      was not called for "mole with direction" elements, which were treated as
1980      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1981   */
1982 }
1983
1984 static int get_key_element_from_nr(int key_nr)
1985 {
1986   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1987                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1988                           EL_EM_KEY_1 : EL_KEY_1);
1989
1990   return key_base_element + key_nr;
1991 }
1992
1993 static int get_next_dropped_element(struct PlayerInfo *player)
1994 {
1995   return (player->inventory_size > 0 ?
1996           player->inventory_element[player->inventory_size - 1] :
1997           player->inventory_infinite_element != EL_UNDEFINED ?
1998           player->inventory_infinite_element :
1999           player->dynabombs_left > 0 ?
2000           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2001           EL_UNDEFINED);
2002 }
2003
2004 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2005 {
2006   /* pos >= 0: get element from bottom of the stack;
2007      pos <  0: get element from top of the stack */
2008
2009   if (pos < 0)
2010   {
2011     int min_inventory_size = -pos;
2012     int inventory_pos = player->inventory_size - min_inventory_size;
2013     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2014
2015     return (player->inventory_size >= min_inventory_size ?
2016             player->inventory_element[inventory_pos] :
2017             player->inventory_infinite_element != EL_UNDEFINED ?
2018             player->inventory_infinite_element :
2019             player->dynabombs_left >= min_dynabombs_left ?
2020             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2021             EL_UNDEFINED);
2022   }
2023   else
2024   {
2025     int min_dynabombs_left = pos + 1;
2026     int min_inventory_size = pos + 1 - player->dynabombs_left;
2027     int inventory_pos = pos - player->dynabombs_left;
2028
2029     return (player->inventory_infinite_element != EL_UNDEFINED ?
2030             player->inventory_infinite_element :
2031             player->dynabombs_left >= min_dynabombs_left ?
2032             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2033             player->inventory_size >= min_inventory_size ?
2034             player->inventory_element[inventory_pos] :
2035             EL_UNDEFINED);
2036   }
2037 }
2038
2039 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2040 {
2041   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2042   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2043   int compare_result;
2044
2045   if (gpo1->sort_priority != gpo2->sort_priority)
2046     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2047   else
2048     compare_result = gpo1->nr - gpo2->nr;
2049
2050   return compare_result;
2051 }
2052
2053 int getPlayerInventorySize(int player_nr)
2054 {
2055   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2056     return level.native_em_level->ply[player_nr]->dynamite;
2057   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2058     return level.native_sp_level->game_sp->red_disk_count;
2059   else
2060     return stored_player[player_nr].inventory_size;
2061 }
2062
2063 void InitGameControlValues()
2064 {
2065   int i;
2066
2067   for (i = 0; game_panel_controls[i].nr != -1; i++)
2068   {
2069     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2070     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2071     struct TextPosInfo *pos = gpc->pos;
2072     int nr = gpc->nr;
2073     int type = gpc->type;
2074
2075     if (nr != i)
2076     {
2077       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2078       Error(ERR_EXIT, "this should not happen -- please debug");
2079     }
2080
2081     /* force update of game controls after initialization */
2082     gpc->value = gpc->last_value = -1;
2083     gpc->frame = gpc->last_frame = -1;
2084     gpc->gfx_frame = -1;
2085
2086     /* determine panel value width for later calculation of alignment */
2087     if (type == TYPE_INTEGER || type == TYPE_STRING)
2088     {
2089       pos->width = pos->size * getFontWidth(pos->font);
2090       pos->height = getFontHeight(pos->font);
2091     }
2092     else if (type == TYPE_ELEMENT)
2093     {
2094       pos->width = pos->size;
2095       pos->height = pos->size;
2096     }
2097
2098     /* fill structure for game panel draw order */
2099     gpo->nr = gpc->nr;
2100     gpo->sort_priority = pos->sort_priority;
2101   }
2102
2103   /* sort game panel controls according to sort_priority and control number */
2104   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2105         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2106 }
2107
2108 void UpdatePlayfieldElementCount()
2109 {
2110   boolean use_element_count = FALSE;
2111   int i, j, x, y;
2112
2113   /* first check if it is needed at all to calculate playfield element count */
2114   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2115     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2116       use_element_count = TRUE;
2117
2118   if (!use_element_count)
2119     return;
2120
2121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2122     element_info[i].element_count = 0;
2123
2124   SCAN_PLAYFIELD(x, y)
2125   {
2126     element_info[Feld[x][y]].element_count++;
2127   }
2128
2129   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2130     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2131       if (IS_IN_GROUP(j, i))
2132         element_info[EL_GROUP_START + i].element_count +=
2133           element_info[j].element_count;
2134 }
2135
2136 void UpdateGameControlValues()
2137 {
2138   int i, k;
2139   int time = (local_player->LevelSolved ?
2140               local_player->LevelSolved_CountingTime :
2141               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142               level.native_em_level->lev->time :
2143               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144               level.native_sp_level->game_sp->time_played :
2145               game.no_time_limit ? TimePlayed : TimeLeft);
2146   int score = (local_player->LevelSolved ?
2147                local_player->LevelSolved_CountingScore :
2148                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149                level.native_em_level->lev->score :
2150                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151                level.native_sp_level->game_sp->score :
2152                local_player->score);
2153   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2154               level.native_em_level->lev->required :
2155               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2156               level.native_sp_level->game_sp->infotrons_still_needed :
2157               local_player->gems_still_needed);
2158   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2159                      level.native_em_level->lev->required > 0 :
2160                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2161                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2162                      local_player->gems_still_needed > 0 ||
2163                      local_player->sokobanfields_still_needed > 0 ||
2164                      local_player->lights_still_needed > 0);
2165
2166   UpdatePlayfieldElementCount();
2167
2168   /* update game panel control values */
2169
2170   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2171   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2172
2173   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2174   for (i = 0; i < MAX_NUM_KEYS; i++)
2175     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2176   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2177   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2178
2179   if (game.centered_player_nr == -1)
2180   {
2181     for (i = 0; i < MAX_PLAYERS; i++)
2182     {
2183       /* only one player in Supaplex game engine */
2184       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2185         break;
2186
2187       for (k = 0; k < MAX_NUM_KEYS; k++)
2188       {
2189         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2190         {
2191           if (level.native_em_level->ply[i]->keys & (1 << k))
2192             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2193               get_key_element_from_nr(k);
2194         }
2195         else if (stored_player[i].key[k])
2196           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2197             get_key_element_from_nr(k);
2198       }
2199
2200       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2201         getPlayerInventorySize(i);
2202
2203       if (stored_player[i].num_white_keys > 0)
2204         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2205           EL_DC_KEY_WHITE;
2206
2207       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2208         stored_player[i].num_white_keys;
2209     }
2210   }
2211   else
2212   {
2213     int player_nr = game.centered_player_nr;
2214
2215     for (k = 0; k < MAX_NUM_KEYS; k++)
2216     {
2217       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2218       {
2219         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2220           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221             get_key_element_from_nr(k);
2222       }
2223       else if (stored_player[player_nr].key[k])
2224         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2225           get_key_element_from_nr(k);
2226     }
2227
2228     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229       getPlayerInventorySize(player_nr);
2230
2231     if (stored_player[player_nr].num_white_keys > 0)
2232       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2233
2234     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2235       stored_player[player_nr].num_white_keys;
2236   }
2237
2238   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2239   {
2240     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2241       get_inventory_element_from_pos(local_player, i);
2242     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2243       get_inventory_element_from_pos(local_player, -i - 1);
2244   }
2245
2246   game_panel_controls[GAME_PANEL_SCORE].value = score;
2247   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2248
2249   game_panel_controls[GAME_PANEL_TIME].value = time;
2250
2251   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2252   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2253   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2254
2255   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2256
2257   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2258     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2259      EL_EMPTY);
2260   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2261     local_player->shield_normal_time_left;
2262   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2263     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2264      EL_EMPTY);
2265   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2266     local_player->shield_deadly_time_left;
2267
2268   game_panel_controls[GAME_PANEL_EXIT].value =
2269     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2270
2271   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2272     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2273   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2274     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2275      EL_EMC_MAGIC_BALL_SWITCH);
2276
2277   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2278     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2279   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2280     game.light_time_left;
2281
2282   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2283     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2284   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2285     game.timegate_time_left;
2286
2287   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2288     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2289
2290   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2291     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2292   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2293     game.lenses_time_left;
2294
2295   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2296     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2297   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2298     game.magnify_time_left;
2299
2300   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2301     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2302      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2303      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2304      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2305      EL_BALLOON_SWITCH_NONE);
2306
2307   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2308     local_player->dynabomb_count;
2309   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2310     local_player->dynabomb_size;
2311   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2312     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2313
2314   game_panel_controls[GAME_PANEL_PENGUINS].value =
2315     local_player->friends_still_needed;
2316
2317   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2318     local_player->sokobanfields_still_needed;
2319   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2320     local_player->sokobanfields_still_needed;
2321
2322   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2323     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2324
2325   for (i = 0; i < NUM_BELTS; i++)
2326   {
2327     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2328       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2329        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2330     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2331       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2332   }
2333
2334   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2335     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2336   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2337     game.magic_wall_time_left;
2338
2339   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2340     local_player->gravity;
2341
2342   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2343     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2344
2345   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2346     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2347       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2348        game.panel.element[i].id : EL_UNDEFINED);
2349
2350   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2351     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2352       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2353        element_info[game.panel.element_count[i].id].element_count : 0);
2354
2355   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2356     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2357       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2358        element_info[game.panel.ce_score[i].id].collect_score : 0);
2359
2360   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2361     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2362       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2363        element_info[game.panel.ce_score_element[i].id].collect_score :
2364        EL_UNDEFINED);
2365
2366   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2367   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2368   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2369
2370   /* update game panel control frames */
2371
2372   for (i = 0; game_panel_controls[i].nr != -1; i++)
2373   {
2374     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2375
2376     if (gpc->type == TYPE_ELEMENT)
2377     {
2378       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2379       {
2380         int last_anim_random_frame = gfx.anim_random_frame;
2381         int element = gpc->value;
2382         int graphic = el2panelimg(element);
2383
2384         if (gpc->value != gpc->last_value)
2385         {
2386           gpc->gfx_frame = 0;
2387           gpc->gfx_random = INIT_GFX_RANDOM();
2388         }
2389         else
2390         {
2391           gpc->gfx_frame++;
2392
2393           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2394               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2395             gpc->gfx_random = INIT_GFX_RANDOM();
2396         }
2397
2398         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2399           gfx.anim_random_frame = gpc->gfx_random;
2400
2401         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2402           gpc->gfx_frame = element_info[element].collect_score;
2403
2404         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2405                                               gpc->gfx_frame);
2406
2407         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2408           gfx.anim_random_frame = last_anim_random_frame;
2409       }
2410     }
2411   }
2412 }
2413
2414 void DisplayGameControlValues()
2415 {
2416   boolean redraw_panel = FALSE;
2417   int i;
2418
2419   for (i = 0; game_panel_controls[i].nr != -1; i++)
2420   {
2421     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2422
2423     if (PANEL_DEACTIVATED(gpc->pos))
2424       continue;
2425
2426     if (gpc->value == gpc->last_value &&
2427         gpc->frame == gpc->last_frame)
2428       continue;
2429
2430     redraw_panel = TRUE;
2431   }
2432
2433   if (!redraw_panel)
2434     return;
2435
2436   /* copy default game door content to main double buffer */
2437
2438   /* !!! CHECK AGAIN !!! */
2439   SetPanelBackground();
2440   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2441   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2442
2443   /* redraw game control buttons */
2444   RedrawGameButtons();
2445
2446   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2447
2448   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2449   {
2450     int nr = game_panel_order[i].nr;
2451     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2452     struct TextPosInfo *pos = gpc->pos;
2453     int type = gpc->type;
2454     int value = gpc->value;
2455     int frame = gpc->frame;
2456     int size = pos->size;
2457     int font = pos->font;
2458     boolean draw_masked = pos->draw_masked;
2459     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2460
2461     if (PANEL_DEACTIVATED(pos))
2462       continue;
2463
2464     gpc->last_value = value;
2465     gpc->last_frame = frame;
2466
2467     if (type == TYPE_INTEGER)
2468     {
2469       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2470           nr == GAME_PANEL_TIME)
2471       {
2472         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2473
2474         if (use_dynamic_size)           /* use dynamic number of digits */
2475         {
2476           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2477           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2478           int size2 = size1 + 1;
2479           int font1 = pos->font;
2480           int font2 = pos->font_alt;
2481
2482           size = (value < value_change ? size1 : size2);
2483           font = (value < value_change ? font1 : font2);
2484         }
2485       }
2486
2487       /* correct text size if "digits" is zero or less */
2488       if (size <= 0)
2489         size = strlen(int2str(value, size));
2490
2491       /* dynamically correct text alignment */
2492       pos->width = size * getFontWidth(font);
2493
2494       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2495                   int2str(value, size), font, mask_mode);
2496     }
2497     else if (type == TYPE_ELEMENT)
2498     {
2499       int element, graphic;
2500       Bitmap *src_bitmap;
2501       int src_x, src_y;
2502       int width, height;
2503       int dst_x = PANEL_XPOS(pos);
2504       int dst_y = PANEL_YPOS(pos);
2505
2506       if (value != EL_UNDEFINED && value != EL_EMPTY)
2507       {
2508         element = value;
2509         graphic = el2panelimg(value);
2510
2511         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2512
2513         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2514           size = TILESIZE;
2515
2516         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2517                               &src_x, &src_y);
2518
2519         width  = graphic_info[graphic].width  * size / TILESIZE;
2520         height = graphic_info[graphic].height * size / TILESIZE;
2521
2522         if (draw_masked)
2523           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2524                            dst_x, dst_y);
2525         else
2526           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2527                      dst_x, dst_y);
2528       }
2529     }
2530     else if (type == TYPE_STRING)
2531     {
2532       boolean active = (value != 0);
2533       char *state_normal = "off";
2534       char *state_active = "on";
2535       char *state = (active ? state_active : state_normal);
2536       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2537                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2538                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2539                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2540
2541       if (nr == GAME_PANEL_GRAVITY_STATE)
2542       {
2543         int font1 = pos->font;          /* (used for normal state) */
2544         int font2 = pos->font_alt;      /* (used for active state) */
2545
2546         font = (active ? font2 : font1);
2547       }
2548
2549       if (s != NULL)
2550       {
2551         char *s_cut;
2552
2553         if (size <= 0)
2554         {
2555           /* don't truncate output if "chars" is zero or less */
2556           size = strlen(s);
2557
2558           /* dynamically correct text alignment */
2559           pos->width = size * getFontWidth(font);
2560         }
2561
2562         s_cut = getStringCopyN(s, size);
2563
2564         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2565                     s_cut, font, mask_mode);
2566
2567         free(s_cut);
2568       }
2569     }
2570
2571     redraw_mask |= REDRAW_DOOR_1;
2572   }
2573
2574   SetGameStatus(GAME_MODE_PLAYING);
2575 }
2576
2577 void UpdateAndDisplayGameControlValues()
2578 {
2579   if (tape.deactivate_display)
2580     return;
2581
2582   UpdateGameControlValues();
2583   DisplayGameControlValues();
2584 }
2585
2586 void UpdateGameDoorValues()
2587 {
2588   UpdateGameControlValues();
2589 }
2590
2591 void DrawGameDoorValues()
2592 {
2593   DisplayGameControlValues();
2594 }
2595
2596
2597 /*
2598   =============================================================================
2599   InitGameEngine()
2600   -----------------------------------------------------------------------------
2601   initialize game engine due to level / tape version number
2602   =============================================================================
2603 */
2604
2605 static void InitGameEngine()
2606 {
2607   int i, j, k, l, x, y;
2608
2609   /* set game engine from tape file when re-playing, else from level file */
2610   game.engine_version = (tape.playing ? tape.engine_version :
2611                          level.game_version);
2612
2613   /* set single or multi-player game mode (needed for re-playing tapes) */
2614   game.team_mode = setup.team_mode;
2615
2616   if (tape.playing)
2617   {
2618     int num_players = 0;
2619
2620     for (i = 0; i < MAX_PLAYERS; i++)
2621       if (tape.player_participates[i])
2622         num_players++;
2623
2624     /* multi-player tapes contain input data for more than one player */
2625     game.team_mode = (num_players > 1);
2626   }
2627
2628   /* ---------------------------------------------------------------------- */
2629   /* set flags for bugs and changes according to active game engine version */
2630   /* ---------------------------------------------------------------------- */
2631
2632   /*
2633     Summary of bugfix/change:
2634     Fixed handling for custom elements that change when pushed by the player.
2635
2636     Fixed/changed in version:
2637     3.1.0
2638
2639     Description:
2640     Before 3.1.0, custom elements that "change when pushing" changed directly
2641     after the player started pushing them (until then handled in "DigField()").
2642     Since 3.1.0, these custom elements are not changed until the "pushing"
2643     move of the element is finished (now handled in "ContinueMoving()").
2644
2645     Affected levels/tapes:
2646     The first condition is generally needed for all levels/tapes before version
2647     3.1.0, which might use the old behaviour before it was changed; known tapes
2648     that are affected are some tapes from the level set "Walpurgis Gardens" by
2649     Jamie Cullen.
2650     The second condition is an exception from the above case and is needed for
2651     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2652     above (including some development versions of 3.1.0), but before it was
2653     known that this change would break tapes like the above and was fixed in
2654     3.1.1, so that the changed behaviour was active although the engine version
2655     while recording maybe was before 3.1.0. There is at least one tape that is
2656     affected by this exception, which is the tape for the one-level set "Bug
2657     Machine" by Juergen Bonhagen.
2658   */
2659
2660   game.use_change_when_pushing_bug =
2661     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2662      !(tape.playing &&
2663        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2664        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2665
2666   /*
2667     Summary of bugfix/change:
2668     Fixed handling for blocking the field the player leaves when moving.
2669
2670     Fixed/changed in version:
2671     3.1.1
2672
2673     Description:
2674     Before 3.1.1, when "block last field when moving" was enabled, the field
2675     the player is leaving when moving was blocked for the time of the move,
2676     and was directly unblocked afterwards. This resulted in the last field
2677     being blocked for exactly one less than the number of frames of one player
2678     move. Additionally, even when blocking was disabled, the last field was
2679     blocked for exactly one frame.
2680     Since 3.1.1, due to changes in player movement handling, the last field
2681     is not blocked at all when blocking is disabled. When blocking is enabled,
2682     the last field is blocked for exactly the number of frames of one player
2683     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2684     last field is blocked for exactly one more than the number of frames of
2685     one player move.
2686
2687     Affected levels/tapes:
2688     (!!! yet to be determined -- probably many !!!)
2689   */
2690
2691   game.use_block_last_field_bug =
2692     (game.engine_version < VERSION_IDENT(3,1,1,0));
2693
2694   /* ---------------------------------------------------------------------- */
2695
2696   /* set maximal allowed number of custom element changes per game frame */
2697   game.max_num_changes_per_frame = 1;
2698
2699   /* default scan direction: scan playfield from top/left to bottom/right */
2700   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2701
2702   /* dynamically adjust element properties according to game engine version */
2703   InitElementPropertiesEngine(game.engine_version);
2704
2705 #if 0
2706   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2707   printf("          tape version == %06d [%s] [file: %06d]\n",
2708          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2709          tape.file_version);
2710   printf("       => game.engine_version == %06d\n", game.engine_version);
2711 #endif
2712
2713   /* ---------- initialize player's initial move delay --------------------- */
2714
2715   /* dynamically adjust player properties according to level information */
2716   for (i = 0; i < MAX_PLAYERS; i++)
2717     game.initial_move_delay_value[i] =
2718       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2719
2720   /* dynamically adjust player properties according to game engine version */
2721   for (i = 0; i < MAX_PLAYERS; i++)
2722     game.initial_move_delay[i] =
2723       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2724        game.initial_move_delay_value[i] : 0);
2725
2726   /* ---------- initialize player's initial push delay --------------------- */
2727
2728   /* dynamically adjust player properties according to game engine version */
2729   game.initial_push_delay_value =
2730     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2731
2732   /* ---------- initialize changing elements ------------------------------- */
2733
2734   /* initialize changing elements information */
2735   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2736   {
2737     struct ElementInfo *ei = &element_info[i];
2738
2739     /* this pointer might have been changed in the level editor */
2740     ei->change = &ei->change_page[0];
2741
2742     if (!IS_CUSTOM_ELEMENT(i))
2743     {
2744       ei->change->target_element = EL_EMPTY_SPACE;
2745       ei->change->delay_fixed = 0;
2746       ei->change->delay_random = 0;
2747       ei->change->delay_frames = 1;
2748     }
2749
2750     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2751     {
2752       ei->has_change_event[j] = FALSE;
2753
2754       ei->event_page_nr[j] = 0;
2755       ei->event_page[j] = &ei->change_page[0];
2756     }
2757   }
2758
2759   /* add changing elements from pre-defined list */
2760   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2761   {
2762     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2763     struct ElementInfo *ei = &element_info[ch_delay->element];
2764
2765     ei->change->target_element       = ch_delay->target_element;
2766     ei->change->delay_fixed          = ch_delay->change_delay;
2767
2768     ei->change->pre_change_function  = ch_delay->pre_change_function;
2769     ei->change->change_function      = ch_delay->change_function;
2770     ei->change->post_change_function = ch_delay->post_change_function;
2771
2772     ei->change->can_change = TRUE;
2773     ei->change->can_change_or_has_action = TRUE;
2774
2775     ei->has_change_event[CE_DELAY] = TRUE;
2776
2777     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2778     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2779   }
2780
2781   /* ---------- initialize internal run-time variables --------------------- */
2782
2783   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2784   {
2785     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2786
2787     for (j = 0; j < ei->num_change_pages; j++)
2788     {
2789       ei->change_page[j].can_change_or_has_action =
2790         (ei->change_page[j].can_change |
2791          ei->change_page[j].has_action);
2792     }
2793   }
2794
2795   /* add change events from custom element configuration */
2796   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2797   {
2798     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2799
2800     for (j = 0; j < ei->num_change_pages; j++)
2801     {
2802       if (!ei->change_page[j].can_change_or_has_action)
2803         continue;
2804
2805       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2806       {
2807         /* only add event page for the first page found with this event */
2808         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2809         {
2810           ei->has_change_event[k] = TRUE;
2811
2812           ei->event_page_nr[k] = j;
2813           ei->event_page[k] = &ei->change_page[j];
2814         }
2815       }
2816     }
2817   }
2818
2819   /* ---------- initialize reference elements in change conditions --------- */
2820
2821   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2822   {
2823     int element = EL_CUSTOM_START + i;
2824     struct ElementInfo *ei = &element_info[element];
2825
2826     for (j = 0; j < ei->num_change_pages; j++)
2827     {
2828       int trigger_element = ei->change_page[j].initial_trigger_element;
2829
2830       if (trigger_element >= EL_PREV_CE_8 &&
2831           trigger_element <= EL_NEXT_CE_8)
2832         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2833
2834       ei->change_page[j].trigger_element = trigger_element;
2835     }
2836   }
2837
2838   /* ---------- initialize run-time trigger player and element ------------- */
2839
2840   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2841   {
2842     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2843
2844     for (j = 0; j < ei->num_change_pages; j++)
2845     {
2846       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2847       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2848       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2849       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2850       ei->change_page[j].actual_trigger_ce_value = 0;
2851       ei->change_page[j].actual_trigger_ce_score = 0;
2852     }
2853   }
2854
2855   /* ---------- initialize trigger events ---------------------------------- */
2856
2857   /* initialize trigger events information */
2858   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2859     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2860       trigger_events[i][j] = FALSE;
2861
2862   /* add trigger events from element change event properties */
2863   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2864   {
2865     struct ElementInfo *ei = &element_info[i];
2866
2867     for (j = 0; j < ei->num_change_pages; j++)
2868     {
2869       if (!ei->change_page[j].can_change_or_has_action)
2870         continue;
2871
2872       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2873       {
2874         int trigger_element = ei->change_page[j].trigger_element;
2875
2876         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2877         {
2878           if (ei->change_page[j].has_event[k])
2879           {
2880             if (IS_GROUP_ELEMENT(trigger_element))
2881             {
2882               struct ElementGroupInfo *group =
2883                 element_info[trigger_element].group;
2884
2885               for (l = 0; l < group->num_elements_resolved; l++)
2886                 trigger_events[group->element_resolved[l]][k] = TRUE;
2887             }
2888             else if (trigger_element == EL_ANY_ELEMENT)
2889               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2890                 trigger_events[l][k] = TRUE;
2891             else
2892               trigger_events[trigger_element][k] = TRUE;
2893           }
2894         }
2895       }
2896     }
2897   }
2898
2899   /* ---------- initialize push delay -------------------------------------- */
2900
2901   /* initialize push delay values to default */
2902   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2903   {
2904     if (!IS_CUSTOM_ELEMENT(i))
2905     {
2906       /* set default push delay values (corrected since version 3.0.7-1) */
2907       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2908       {
2909         element_info[i].push_delay_fixed = 2;
2910         element_info[i].push_delay_random = 8;
2911       }
2912       else
2913       {
2914         element_info[i].push_delay_fixed = 8;
2915         element_info[i].push_delay_random = 8;
2916       }
2917     }
2918   }
2919
2920   /* set push delay value for certain elements from pre-defined list */
2921   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2922   {
2923     int e = push_delay_list[i].element;
2924
2925     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2926     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2927   }
2928
2929   /* set push delay value for Supaplex elements for newer engine versions */
2930   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2931   {
2932     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2933     {
2934       if (IS_SP_ELEMENT(i))
2935       {
2936         /* set SP push delay to just enough to push under a falling zonk */
2937         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2938
2939         element_info[i].push_delay_fixed  = delay;
2940         element_info[i].push_delay_random = 0;
2941       }
2942     }
2943   }
2944
2945   /* ---------- initialize move stepsize ----------------------------------- */
2946
2947   /* initialize move stepsize values to default */
2948   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2949     if (!IS_CUSTOM_ELEMENT(i))
2950       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2951
2952   /* set move stepsize value for certain elements from pre-defined list */
2953   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2954   {
2955     int e = move_stepsize_list[i].element;
2956
2957     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2958   }
2959
2960   /* ---------- initialize collect score ----------------------------------- */
2961
2962   /* initialize collect score values for custom elements from initial value */
2963   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2964     if (IS_CUSTOM_ELEMENT(i))
2965       element_info[i].collect_score = element_info[i].collect_score_initial;
2966
2967   /* ---------- initialize collect count ----------------------------------- */
2968
2969   /* initialize collect count values for non-custom elements */
2970   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2971     if (!IS_CUSTOM_ELEMENT(i))
2972       element_info[i].collect_count_initial = 0;
2973
2974   /* add collect count values for all elements from pre-defined list */
2975   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2976     element_info[collect_count_list[i].element].collect_count_initial =
2977       collect_count_list[i].count;
2978
2979   /* ---------- initialize access direction -------------------------------- */
2980
2981   /* initialize access direction values to default (access from every side) */
2982   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2983     if (!IS_CUSTOM_ELEMENT(i))
2984       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2985
2986   /* set access direction value for certain elements from pre-defined list */
2987   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2988     element_info[access_direction_list[i].element].access_direction =
2989       access_direction_list[i].direction;
2990
2991   /* ---------- initialize explosion content ------------------------------- */
2992   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993   {
2994     if (IS_CUSTOM_ELEMENT(i))
2995       continue;
2996
2997     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2998     {
2999       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3000
3001       element_info[i].content.e[x][y] =
3002         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3003          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3004          i == EL_PLAYER_3 ? EL_EMERALD :
3005          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3006          i == EL_MOLE ? EL_EMERALD_RED :
3007          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3008          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3009          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3010          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3011          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3012          i == EL_WALL_EMERALD ? EL_EMERALD :
3013          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3014          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3015          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3016          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3017          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3018          i == EL_WALL_PEARL ? EL_PEARL :
3019          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3020          EL_EMPTY);
3021     }
3022   }
3023
3024   /* ---------- initialize recursion detection ------------------------------ */
3025   recursion_loop_depth = 0;
3026   recursion_loop_detected = FALSE;
3027   recursion_loop_element = EL_UNDEFINED;
3028
3029   /* ---------- initialize graphics engine ---------------------------------- */
3030   game.scroll_delay_value =
3031     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3032      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3033   game.scroll_delay_value =
3034     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3035
3036   /* ---------- initialize game engine snapshots ---------------------------- */
3037   for (i = 0; i < MAX_PLAYERS; i++)
3038     game.snapshot.last_action[i] = 0;
3039   game.snapshot.changed_action = FALSE;
3040   game.snapshot.collected_item = FALSE;
3041   game.snapshot.mode =
3042     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3043      SNAPSHOT_MODE_EVERY_STEP :
3044      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3045      SNAPSHOT_MODE_EVERY_MOVE :
3046      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3047      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3048   game.snapshot.save_snapshot = FALSE;
3049 }
3050
3051 int get_num_special_action(int element, int action_first, int action_last)
3052 {
3053   int num_special_action = 0;
3054   int i, j;
3055
3056   for (i = action_first; i <= action_last; i++)
3057   {
3058     boolean found = FALSE;
3059
3060     for (j = 0; j < NUM_DIRECTIONS; j++)
3061       if (el_act_dir2img(element, i, j) !=
3062           el_act_dir2img(element, ACTION_DEFAULT, j))
3063         found = TRUE;
3064
3065     if (found)
3066       num_special_action++;
3067     else
3068       break;
3069   }
3070
3071   return num_special_action;
3072 }
3073
3074
3075 /*
3076   =============================================================================
3077   InitGame()
3078   -----------------------------------------------------------------------------
3079   initialize and start new game
3080   =============================================================================
3081 */
3082
3083 void InitGame()
3084 {
3085   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3086   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3087   int fade_mask = REDRAW_FIELD;
3088
3089   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3090   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3091   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3092   int initial_move_dir = MV_DOWN;
3093   int i, j, x, y;
3094
3095   // required here to update video display before fading (FIX THIS)
3096   DrawMaskedBorder(REDRAW_DOOR_2);
3097
3098   if (!game.restart_level)
3099     CloseDoor(DOOR_CLOSE_1);
3100
3101   SetGameStatus(GAME_MODE_PLAYING);
3102
3103   if (level_editor_test_game)
3104     FadeSkipNextFadeIn();
3105   else
3106     FadeSetEnterScreen();
3107
3108   if (CheckIfGlobalBorderHasChanged())
3109     fade_mask = REDRAW_ALL;
3110
3111   FadeSoundsAndMusic();
3112
3113   ExpireSoundLoops(TRUE);
3114
3115   FadeOut(fade_mask);
3116
3117   /* needed if different viewport properties defined for playing */
3118   ChangeViewportPropertiesIfNeeded();
3119
3120   ClearField();
3121
3122   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3123
3124   DrawCompleteVideoDisplay();
3125
3126   InitGameEngine();
3127   InitGameControlValues();
3128
3129   /* don't play tapes over network */
3130   network_playing = (options.network && !tape.playing);
3131
3132   for (i = 0; i < MAX_PLAYERS; i++)
3133   {
3134     struct PlayerInfo *player = &stored_player[i];
3135
3136     player->index_nr = i;
3137     player->index_bit = (1 << i);
3138     player->element_nr = EL_PLAYER_1 + i;
3139
3140     player->present = FALSE;
3141     player->active = FALSE;
3142     player->mapped = FALSE;
3143
3144     player->killed = FALSE;
3145     player->reanimated = FALSE;
3146
3147     player->action = 0;
3148     player->effective_action = 0;
3149     player->programmed_action = 0;
3150
3151     player->score = 0;
3152     player->score_final = 0;
3153
3154     player->gems_still_needed = level.gems_needed;
3155     player->sokobanfields_still_needed = 0;
3156     player->lights_still_needed = 0;
3157     player->friends_still_needed = 0;
3158
3159     for (j = 0; j < MAX_NUM_KEYS; j++)
3160       player->key[j] = FALSE;
3161
3162     player->num_white_keys = 0;
3163
3164     player->dynabomb_count = 0;
3165     player->dynabomb_size = 1;
3166     player->dynabombs_left = 0;
3167     player->dynabomb_xl = FALSE;
3168
3169     player->MovDir = initial_move_dir;
3170     player->MovPos = 0;
3171     player->GfxPos = 0;
3172     player->GfxDir = initial_move_dir;
3173     player->GfxAction = ACTION_DEFAULT;
3174     player->Frame = 0;
3175     player->StepFrame = 0;
3176
3177     player->initial_element = player->element_nr;
3178     player->artwork_element =
3179       (level.use_artwork_element[i] ? level.artwork_element[i] :
3180        player->element_nr);
3181     player->use_murphy = FALSE;
3182
3183     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3184     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3185
3186     player->gravity = level.initial_player_gravity[i];
3187
3188     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3189
3190     player->actual_frame_counter = 0;
3191
3192     player->step_counter = 0;
3193
3194     player->last_move_dir = initial_move_dir;
3195
3196     player->is_active = FALSE;
3197
3198     player->is_waiting = FALSE;
3199     player->is_moving = FALSE;
3200     player->is_auto_moving = FALSE;
3201     player->is_digging = FALSE;
3202     player->is_snapping = FALSE;
3203     player->is_collecting = FALSE;
3204     player->is_pushing = FALSE;
3205     player->is_switching = FALSE;
3206     player->is_dropping = FALSE;
3207     player->is_dropping_pressed = FALSE;
3208
3209     player->is_bored = FALSE;
3210     player->is_sleeping = FALSE;
3211
3212     player->was_waiting = TRUE;
3213     player->was_moving = FALSE;
3214     player->was_snapping = FALSE;
3215     player->was_dropping = FALSE;
3216
3217     player->frame_counter_bored = -1;
3218     player->frame_counter_sleeping = -1;
3219
3220     player->anim_delay_counter = 0;
3221     player->post_delay_counter = 0;
3222
3223     player->dir_waiting = initial_move_dir;
3224     player->action_waiting = ACTION_DEFAULT;
3225     player->last_action_waiting = ACTION_DEFAULT;
3226     player->special_action_bored = ACTION_DEFAULT;
3227     player->special_action_sleeping = ACTION_DEFAULT;
3228
3229     player->switch_x = -1;
3230     player->switch_y = -1;
3231
3232     player->drop_x = -1;
3233     player->drop_y = -1;
3234
3235     player->show_envelope = 0;
3236
3237     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3238
3239     player->push_delay       = -1;      /* initialized when pushing starts */
3240     player->push_delay_value = game.initial_push_delay_value;
3241
3242     player->drop_delay = 0;
3243     player->drop_pressed_delay = 0;
3244
3245     player->last_jx = -1;
3246     player->last_jy = -1;
3247     player->jx = -1;
3248     player->jy = -1;
3249
3250     player->shield_normal_time_left = 0;
3251     player->shield_deadly_time_left = 0;
3252
3253     player->inventory_infinite_element = EL_UNDEFINED;
3254     player->inventory_size = 0;
3255
3256     if (level.use_initial_inventory[i])
3257     {
3258       for (j = 0; j < level.initial_inventory_size[i]; j++)
3259       {
3260         int element = level.initial_inventory_content[i][j];
3261         int collect_count = element_info[element].collect_count_initial;
3262         int k;
3263
3264         if (!IS_CUSTOM_ELEMENT(element))
3265           collect_count = 1;
3266
3267         if (collect_count == 0)
3268           player->inventory_infinite_element = element;
3269         else
3270           for (k = 0; k < collect_count; k++)
3271             if (player->inventory_size < MAX_INVENTORY_SIZE)
3272               player->inventory_element[player->inventory_size++] = element;
3273       }
3274     }
3275
3276     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3277     SnapField(player, 0, 0);
3278
3279     player->LevelSolved = FALSE;
3280     player->GameOver = FALSE;
3281
3282     player->LevelSolved_GameWon = FALSE;
3283     player->LevelSolved_GameEnd = FALSE;
3284     player->LevelSolved_PanelOff = FALSE;
3285     player->LevelSolved_SaveTape = FALSE;
3286     player->LevelSolved_SaveScore = FALSE;
3287     player->LevelSolved_CountingTime = 0;
3288     player->LevelSolved_CountingScore = 0;
3289
3290     map_player_action[i] = i;
3291   }
3292
3293   network_player_action_received = FALSE;
3294
3295 #if defined(NETWORK_AVALIABLE)
3296   /* initial null action */
3297   if (network_playing)
3298     SendToServer_MovePlayer(MV_NONE);
3299 #endif
3300
3301   ZX = ZY = -1;
3302   ExitX = ExitY = -1;
3303
3304   FrameCounter = 0;
3305   TimeFrames = 0;
3306   TimePlayed = 0;
3307   TimeLeft = level.time;
3308   TapeTime = 0;
3309
3310   ScreenMovDir = MV_NONE;
3311   ScreenMovPos = 0;
3312   ScreenGfxPos = 0;
3313
3314   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3315
3316   AllPlayersGone = FALSE;
3317
3318   game.no_time_limit = (level.time == 0);
3319
3320   game.yamyam_content_nr = 0;
3321   game.robot_wheel_active = FALSE;
3322   game.magic_wall_active = FALSE;
3323   game.magic_wall_time_left = 0;
3324   game.light_time_left = 0;
3325   game.timegate_time_left = 0;
3326   game.switchgate_pos = 0;
3327   game.wind_direction = level.wind_direction_initial;
3328
3329   game.lenses_time_left = 0;
3330   game.magnify_time_left = 0;
3331
3332   game.ball_state = level.ball_state_initial;
3333   game.ball_content_nr = 0;
3334
3335   game.envelope_active = FALSE;
3336
3337   /* set focus to local player for network games, else to all players */
3338   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3339   game.centered_player_nr_next = game.centered_player_nr;
3340   game.set_centered_player = FALSE;
3341
3342   if (network_playing && tape.recording)
3343   {
3344     /* store client dependent player focus when recording network games */
3345     tape.centered_player_nr_next = game.centered_player_nr_next;
3346     tape.set_centered_player = TRUE;
3347   }
3348
3349   for (i = 0; i < NUM_BELTS; i++)
3350   {
3351     game.belt_dir[i] = MV_NONE;
3352     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3353   }
3354
3355   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3356     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3357
3358 #if DEBUG_INIT_PLAYER
3359   if (options.debug)
3360   {
3361     printf("Player status at level initialization:\n");
3362   }
3363 #endif
3364
3365   SCAN_PLAYFIELD(x, y)
3366   {
3367     Feld[x][y] = level.field[x][y];
3368     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3369     ChangeDelay[x][y] = 0;
3370     ChangePage[x][y] = -1;
3371     CustomValue[x][y] = 0;              /* initialized in InitField() */
3372     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3373     AmoebaNr[x][y] = 0;
3374     WasJustMoving[x][y] = 0;
3375     WasJustFalling[x][y] = 0;
3376     CheckCollision[x][y] = 0;
3377     CheckImpact[x][y] = 0;
3378     Stop[x][y] = FALSE;
3379     Pushed[x][y] = FALSE;
3380
3381     ChangeCount[x][y] = 0;
3382     ChangeEvent[x][y] = -1;
3383
3384     ExplodePhase[x][y] = 0;
3385     ExplodeDelay[x][y] = 0;
3386     ExplodeField[x][y] = EX_TYPE_NONE;
3387
3388     RunnerVisit[x][y] = 0;
3389     PlayerVisit[x][y] = 0;
3390
3391     GfxFrame[x][y] = 0;
3392     GfxRandom[x][y] = INIT_GFX_RANDOM();
3393     GfxElement[x][y] = EL_UNDEFINED;
3394     GfxAction[x][y] = ACTION_DEFAULT;
3395     GfxDir[x][y] = MV_NONE;
3396     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3397   }
3398
3399   SCAN_PLAYFIELD(x, y)
3400   {
3401     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3402       emulate_bd = FALSE;
3403     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3404       emulate_sb = FALSE;
3405     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3406       emulate_sp = FALSE;
3407
3408     InitField(x, y, TRUE);
3409
3410     ResetGfxAnimation(x, y);
3411   }
3412
3413   InitBeltMovement();
3414
3415   for (i = 0; i < MAX_PLAYERS; i++)
3416   {
3417     struct PlayerInfo *player = &stored_player[i];
3418
3419     /* set number of special actions for bored and sleeping animation */
3420     player->num_special_action_bored =
3421       get_num_special_action(player->artwork_element,
3422                              ACTION_BORING_1, ACTION_BORING_LAST);
3423     player->num_special_action_sleeping =
3424       get_num_special_action(player->artwork_element,
3425                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3426   }
3427
3428   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3429                     emulate_sb ? EMU_SOKOBAN :
3430                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3431
3432   /* initialize type of slippery elements */
3433   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3434   {
3435     if (!IS_CUSTOM_ELEMENT(i))
3436     {
3437       /* default: elements slip down either to the left or right randomly */
3438       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3439
3440       /* SP style elements prefer to slip down on the left side */
3441       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3442         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3443
3444       /* BD style elements prefer to slip down on the left side */
3445       if (game.emulation == EMU_BOULDERDASH)
3446         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3447     }
3448   }
3449
3450   /* initialize explosion and ignition delay */
3451   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3452   {
3453     if (!IS_CUSTOM_ELEMENT(i))
3454     {
3455       int num_phase = 8;
3456       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3457                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3458                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3459       int last_phase = (num_phase + 1) * delay;
3460       int half_phase = (num_phase / 2) * delay;
3461
3462       element_info[i].explosion_delay = last_phase - 1;
3463       element_info[i].ignition_delay = half_phase;
3464
3465       if (i == EL_BLACK_ORB)
3466         element_info[i].ignition_delay = 1;
3467     }
3468   }
3469
3470   /* correct non-moving belts to start moving left */
3471   for (i = 0; i < NUM_BELTS; i++)
3472     if (game.belt_dir[i] == MV_NONE)
3473       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3474
3475 #if USE_NEW_PLAYER_ASSIGNMENTS
3476   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3477   /* choose default local player */
3478   local_player = &stored_player[0];
3479
3480   for (i = 0; i < MAX_PLAYERS; i++)
3481     stored_player[i].connected = FALSE;
3482
3483   local_player->connected = TRUE;
3484   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3485
3486   if (tape.playing)
3487   {
3488     for (i = 0; i < MAX_PLAYERS; i++)
3489       stored_player[i].connected = tape.player_participates[i];
3490   }
3491   else if (game.team_mode && !options.network)
3492   {
3493     /* try to guess locally connected team mode players (needed for correct
3494        assignment of player figures from level to locally playing players) */
3495
3496     for (i = 0; i < MAX_PLAYERS; i++)
3497       if (setup.input[i].use_joystick ||
3498           setup.input[i].key.left != KSYM_UNDEFINED)
3499         stored_player[i].connected = TRUE;
3500   }
3501
3502 #if DEBUG_INIT_PLAYER
3503   if (options.debug)
3504   {
3505     printf("Player status after level initialization:\n");
3506
3507     for (i = 0; i < MAX_PLAYERS; i++)
3508     {
3509       struct PlayerInfo *player = &stored_player[i];
3510
3511       printf("- player %d: present == %d, connected == %d, active == %d",
3512              i + 1,
3513              player->present,
3514              player->connected,
3515              player->active);
3516
3517       if (local_player == player)
3518         printf(" (local player)");
3519
3520       printf("\n");
3521     }
3522   }
3523 #endif
3524
3525 #if DEBUG_INIT_PLAYER
3526   if (options.debug)
3527     printf("Reassigning players ...\n");
3528 #endif
3529
3530   /* check if any connected player was not found in playfield */
3531   for (i = 0; i < MAX_PLAYERS; i++)
3532   {
3533     struct PlayerInfo *player = &stored_player[i];
3534
3535     if (player->connected && !player->present)
3536     {
3537       struct PlayerInfo *field_player = NULL;
3538
3539 #if DEBUG_INIT_PLAYER
3540       if (options.debug)
3541         printf("- looking for field player for player %d ...\n", i + 1);
3542 #endif
3543
3544       /* assign first free player found that is present in the playfield */
3545
3546       /* first try: look for unmapped playfield player that is not connected */
3547       for (j = 0; j < MAX_PLAYERS; j++)
3548         if (field_player == NULL &&
3549             stored_player[j].present &&
3550             !stored_player[j].mapped &&
3551             !stored_player[j].connected)
3552           field_player = &stored_player[j];
3553
3554       /* second try: look for *any* unmapped playfield player */
3555       for (j = 0; j < MAX_PLAYERS; j++)
3556         if (field_player == NULL &&
3557             stored_player[j].present &&
3558             !stored_player[j].mapped)
3559           field_player = &stored_player[j];
3560
3561       if (field_player != NULL)
3562       {
3563         int jx = field_player->jx, jy = field_player->jy;
3564
3565 #if DEBUG_INIT_PLAYER
3566         if (options.debug)
3567           printf("- found player %d\n", field_player->index_nr + 1);
3568 #endif
3569
3570         player->present = FALSE;
3571         player->active = FALSE;
3572
3573         field_player->present = TRUE;
3574         field_player->active = TRUE;
3575
3576         /*
3577         player->initial_element = field_player->initial_element;
3578         player->artwork_element = field_player->artwork_element;
3579
3580         player->block_last_field       = field_player->block_last_field;
3581         player->block_delay_adjustment = field_player->block_delay_adjustment;
3582         */
3583
3584         StorePlayer[jx][jy] = field_player->element_nr;
3585
3586         field_player->jx = field_player->last_jx = jx;
3587         field_player->jy = field_player->last_jy = jy;
3588
3589         if (local_player == player)
3590           local_player = field_player;
3591
3592         map_player_action[field_player->index_nr] = i;
3593
3594         field_player->mapped = TRUE;
3595
3596 #if DEBUG_INIT_PLAYER
3597         if (options.debug)
3598           printf("- map_player_action[%d] == %d\n",
3599                  field_player->index_nr + 1, i + 1);
3600 #endif
3601       }
3602     }
3603
3604     if (player->connected && player->present)
3605       player->mapped = TRUE;
3606   }
3607
3608 #if DEBUG_INIT_PLAYER
3609   if (options.debug)
3610   {
3611     printf("Player status after player assignment (first stage):\n");
3612
3613     for (i = 0; i < MAX_PLAYERS; i++)
3614     {
3615       struct PlayerInfo *player = &stored_player[i];
3616
3617       printf("- player %d: present == %d, connected == %d, active == %d",
3618              i + 1,
3619              player->present,
3620              player->connected,
3621              player->active);
3622
3623       if (local_player == player)
3624         printf(" (local player)");
3625
3626       printf("\n");
3627     }
3628   }
3629 #endif
3630
3631 #else
3632
3633   /* check if any connected player was not found in playfield */
3634   for (i = 0; i < MAX_PLAYERS; i++)
3635   {
3636     struct PlayerInfo *player = &stored_player[i];
3637
3638     if (player->connected && !player->present)
3639     {
3640       for (j = 0; j < MAX_PLAYERS; j++)
3641       {
3642         struct PlayerInfo *field_player = &stored_player[j];
3643         int jx = field_player->jx, jy = field_player->jy;
3644
3645         /* assign first free player found that is present in the playfield */
3646         if (field_player->present && !field_player->connected)
3647         {
3648           player->present = TRUE;
3649           player->active = TRUE;
3650
3651           field_player->present = FALSE;
3652           field_player->active = FALSE;
3653
3654           player->initial_element = field_player->initial_element;
3655           player->artwork_element = field_player->artwork_element;
3656
3657           player->block_last_field       = field_player->block_last_field;
3658           player->block_delay_adjustment = field_player->block_delay_adjustment;
3659
3660           StorePlayer[jx][jy] = player->element_nr;
3661
3662           player->jx = player->last_jx = jx;
3663           player->jy = player->last_jy = jy;
3664
3665           break;
3666         }
3667       }
3668     }
3669   }
3670 #endif
3671
3672 #if 0
3673   printf("::: local_player->present == %d\n", local_player->present);
3674 #endif
3675
3676   if (tape.playing)
3677   {
3678     /* when playing a tape, eliminate all players who do not participate */
3679
3680 #if USE_NEW_PLAYER_ASSIGNMENTS
3681
3682     if (!game.team_mode)
3683     {
3684       for (i = 0; i < MAX_PLAYERS; i++)
3685       {
3686         if (stored_player[i].active &&
3687             !tape.player_participates[map_player_action[i]])
3688         {
3689           struct PlayerInfo *player = &stored_player[i];
3690           int jx = player->jx, jy = player->jy;
3691
3692 #if DEBUG_INIT_PLAYER
3693           if (options.debug)
3694             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3695 #endif
3696
3697           player->active = FALSE;
3698           StorePlayer[jx][jy] = 0;
3699           Feld[jx][jy] = EL_EMPTY;
3700         }
3701       }
3702     }
3703
3704 #else
3705
3706     for (i = 0; i < MAX_PLAYERS; i++)
3707     {
3708       if (stored_player[i].active &&
3709           !tape.player_participates[i])
3710       {
3711         struct PlayerInfo *player = &stored_player[i];
3712         int jx = player->jx, jy = player->jy;
3713
3714         player->active = FALSE;
3715         StorePlayer[jx][jy] = 0;
3716         Feld[jx][jy] = EL_EMPTY;
3717       }
3718     }
3719 #endif
3720   }
3721   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3722   {
3723     /* when in single player mode, eliminate all but the first active player */
3724
3725     for (i = 0; i < MAX_PLAYERS; i++)
3726     {
3727       if (stored_player[i].active)
3728       {
3729         for (j = i + 1; j < MAX_PLAYERS; j++)
3730         {
3731           if (stored_player[j].active)
3732           {
3733             struct PlayerInfo *player = &stored_player[j];
3734             int jx = player->jx, jy = player->jy;
3735
3736             player->active = FALSE;
3737             player->present = FALSE;
3738
3739             StorePlayer[jx][jy] = 0;
3740             Feld[jx][jy] = EL_EMPTY;
3741           }
3742         }
3743       }
3744     }
3745   }
3746
3747   /* when recording the game, store which players take part in the game */
3748   if (tape.recording)
3749   {
3750 #if USE_NEW_PLAYER_ASSIGNMENTS
3751     for (i = 0; i < MAX_PLAYERS; i++)
3752       if (stored_player[i].connected)
3753         tape.player_participates[i] = TRUE;
3754 #else
3755     for (i = 0; i < MAX_PLAYERS; i++)
3756       if (stored_player[i].active)
3757         tape.player_participates[i] = TRUE;
3758 #endif
3759   }
3760
3761 #if DEBUG_INIT_PLAYER
3762   if (options.debug)
3763   {
3764     printf("Player status after player assignment (final stage):\n");
3765
3766     for (i = 0; i < MAX_PLAYERS; i++)
3767     {
3768       struct PlayerInfo *player = &stored_player[i];
3769
3770       printf("- player %d: present == %d, connected == %d, active == %d",
3771              i + 1,
3772              player->present,
3773              player->connected,
3774              player->active);
3775
3776       if (local_player == player)
3777         printf(" (local player)");
3778
3779       printf("\n");
3780     }
3781   }
3782 #endif
3783
3784   if (BorderElement == EL_EMPTY)
3785   {
3786     SBX_Left = 0;
3787     SBX_Right = lev_fieldx - SCR_FIELDX;
3788     SBY_Upper = 0;
3789     SBY_Lower = lev_fieldy - SCR_FIELDY;
3790   }
3791   else
3792   {
3793     SBX_Left = -1;
3794     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3795     SBY_Upper = -1;
3796     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3797   }
3798
3799   if (full_lev_fieldx <= SCR_FIELDX)
3800     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3801   if (full_lev_fieldy <= SCR_FIELDY)
3802     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3803
3804   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3805     SBX_Left--;
3806   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3807     SBY_Upper--;
3808
3809   /* if local player not found, look for custom element that might create
3810      the player (make some assumptions about the right custom element) */
3811   if (!local_player->present)
3812   {
3813     int start_x = 0, start_y = 0;
3814     int found_rating = 0;
3815     int found_element = EL_UNDEFINED;
3816     int player_nr = local_player->index_nr;
3817
3818     SCAN_PLAYFIELD(x, y)
3819     {
3820       int element = Feld[x][y];
3821       int content;
3822       int xx, yy;
3823       boolean is_player;
3824
3825       if (level.use_start_element[player_nr] &&
3826           level.start_element[player_nr] == element &&
3827           found_rating < 4)
3828       {
3829         start_x = x;
3830         start_y = y;
3831
3832         found_rating = 4;
3833         found_element = element;
3834       }
3835
3836       if (!IS_CUSTOM_ELEMENT(element))
3837         continue;
3838
3839       if (CAN_CHANGE(element))
3840       {
3841         for (i = 0; i < element_info[element].num_change_pages; i++)
3842         {
3843           /* check for player created from custom element as single target */
3844           content = element_info[element].change_page[i].target_element;
3845           is_player = ELEM_IS_PLAYER(content);
3846
3847           if (is_player && (found_rating < 3 ||
3848                             (found_rating == 3 && element < found_element)))
3849           {
3850             start_x = x;
3851             start_y = y;
3852
3853             found_rating = 3;
3854             found_element = element;
3855           }
3856         }
3857       }
3858
3859       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3860       {
3861         /* check for player created from custom element as explosion content */
3862         content = element_info[element].content.e[xx][yy];
3863         is_player = ELEM_IS_PLAYER(content);
3864
3865         if (is_player && (found_rating < 2 ||
3866                           (found_rating == 2 && element < found_element)))
3867         {
3868           start_x = x + xx - 1;
3869           start_y = y + yy - 1;
3870
3871           found_rating = 2;
3872           found_element = element;
3873         }
3874
3875         if (!CAN_CHANGE(element))
3876           continue;
3877
3878         for (i = 0; i < element_info[element].num_change_pages; i++)
3879         {
3880           /* check for player created from custom element as extended target */
3881           content =
3882             element_info[element].change_page[i].target_content.e[xx][yy];
3883
3884           is_player = ELEM_IS_PLAYER(content);
3885
3886           if (is_player && (found_rating < 1 ||
3887                             (found_rating == 1 && element < found_element)))
3888           {
3889             start_x = x + xx - 1;
3890             start_y = y + yy - 1;
3891
3892             found_rating = 1;
3893             found_element = element;
3894           }
3895         }
3896       }
3897     }
3898
3899     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3900                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3901                 start_x - MIDPOSX);
3902
3903     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3904                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3905                 start_y - MIDPOSY);
3906   }
3907   else
3908   {
3909     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3910                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3911                 local_player->jx - MIDPOSX);
3912
3913     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3914                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3915                 local_player->jy - MIDPOSY);
3916   }
3917
3918   /* !!! FIX THIS (START) !!! */
3919   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3920   {
3921     InitGameEngine_EM();
3922   }
3923   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3924   {
3925     InitGameEngine_SP();
3926   }
3927   else
3928   {
3929     DrawLevel(REDRAW_FIELD);
3930     DrawAllPlayers();
3931
3932     /* after drawing the level, correct some elements */
3933     if (game.timegate_time_left == 0)
3934       CloseAllOpenTimegates();
3935   }
3936
3937   /* blit playfield from scroll buffer to normal back buffer for fading in */
3938   BlitScreenToBitmap(backbuffer);
3939   /* !!! FIX THIS (END) !!! */
3940
3941   DrawMaskedBorder(fade_mask);
3942
3943   FadeIn(fade_mask);
3944
3945 #if 1
3946   // full screen redraw is required at this point in the following cases:
3947   // - special editor door undrawn when game was started from level editor
3948   // - drawing area (playfield) was changed and has to be removed completely
3949   redraw_mask = REDRAW_ALL;
3950   BackToFront();
3951 #endif
3952
3953   if (!game.restart_level)
3954   {
3955     /* copy default game door content to main double buffer */
3956
3957     /* !!! CHECK AGAIN !!! */
3958     SetPanelBackground();
3959     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3960     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3961   }
3962
3963   SetPanelBackground();
3964   SetDrawBackgroundMask(REDRAW_DOOR_1);
3965
3966   UpdateAndDisplayGameControlValues();
3967
3968   if (!game.restart_level)
3969   {
3970     UnmapGameButtons();
3971     UnmapTapeButtons();
3972
3973     FreeGameButtons();
3974     CreateGameButtons();
3975
3976     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3977     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3978     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3979
3980     MapGameButtons();
3981     MapTapeButtons();
3982
3983     /* copy actual game door content to door double buffer for OpenDoor() */
3984     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3985
3986     OpenDoor(DOOR_OPEN_ALL);
3987
3988     PlaySound(SND_GAME_STARTING);
3989
3990     if (setup.sound_music)
3991       PlayLevelMusic();
3992
3993     KeyboardAutoRepeatOffUnlessAutoplay();
3994
3995 #if DEBUG_INIT_PLAYER
3996     if (options.debug)
3997     {
3998       printf("Player status (final):\n");
3999
4000       for (i = 0; i < MAX_PLAYERS; i++)
4001       {
4002         struct PlayerInfo *player = &stored_player[i];
4003
4004         printf("- player %d: present == %d, connected == %d, active == %d",
4005                i + 1,
4006                player->present,
4007                player->connected,
4008                player->active);
4009
4010         if (local_player == player)
4011           printf(" (local player)");
4012
4013         printf("\n");
4014       }
4015     }
4016 #endif
4017   }
4018
4019   UnmapAllGadgets();
4020
4021   MapGameButtons();
4022   MapTapeButtons();
4023
4024   if (!game.restart_level && !tape.playing)
4025   {
4026     LevelStats_incPlayed(level_nr);
4027
4028     SaveLevelSetup_SeriesInfo();
4029   }
4030
4031   game.restart_level = FALSE;
4032
4033   SaveEngineSnapshotToListInitial();
4034 }
4035
4036 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4037                         int actual_player_x, int actual_player_y)
4038 {
4039   /* this is used for non-R'n'D game engines to update certain engine values */
4040
4041   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4042   {
4043     actual_player_x = correctLevelPosX_EM(actual_player_x);
4044     actual_player_y = correctLevelPosY_EM(actual_player_y);
4045   }
4046
4047   /* needed to determine if sounds are played within the visible screen area */
4048   scroll_x = actual_scroll_x;
4049   scroll_y = actual_scroll_y;
4050
4051   /* needed to get player position for "follow finger" playing input method */
4052   local_player->jx = actual_player_x;
4053   local_player->jy = actual_player_y;
4054 }
4055
4056 void InitMovDir(int x, int y)
4057 {
4058   int i, element = Feld[x][y];
4059   static int xy[4][2] =
4060   {
4061     {  0, +1 },
4062     { +1,  0 },
4063     {  0, -1 },
4064     { -1,  0 }
4065   };
4066   static int direction[3][4] =
4067   {
4068     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4069     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4070     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4071   };
4072
4073   switch (element)
4074   {
4075     case EL_BUG_RIGHT:
4076     case EL_BUG_UP:
4077     case EL_BUG_LEFT:
4078     case EL_BUG_DOWN:
4079       Feld[x][y] = EL_BUG;
4080       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4081       break;
4082
4083     case EL_SPACESHIP_RIGHT:
4084     case EL_SPACESHIP_UP:
4085     case EL_SPACESHIP_LEFT:
4086     case EL_SPACESHIP_DOWN:
4087       Feld[x][y] = EL_SPACESHIP;
4088       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4089       break;
4090
4091     case EL_BD_BUTTERFLY_RIGHT:
4092     case EL_BD_BUTTERFLY_UP:
4093     case EL_BD_BUTTERFLY_LEFT:
4094     case EL_BD_BUTTERFLY_DOWN:
4095       Feld[x][y] = EL_BD_BUTTERFLY;
4096       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4097       break;
4098
4099     case EL_BD_FIREFLY_RIGHT:
4100     case EL_BD_FIREFLY_UP:
4101     case EL_BD_FIREFLY_LEFT:
4102     case EL_BD_FIREFLY_DOWN:
4103       Feld[x][y] = EL_BD_FIREFLY;
4104       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4105       break;
4106
4107     case EL_PACMAN_RIGHT:
4108     case EL_PACMAN_UP:
4109     case EL_PACMAN_LEFT:
4110     case EL_PACMAN_DOWN:
4111       Feld[x][y] = EL_PACMAN;
4112       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4113       break;
4114
4115     case EL_YAMYAM_LEFT:
4116     case EL_YAMYAM_RIGHT:
4117     case EL_YAMYAM_UP:
4118     case EL_YAMYAM_DOWN:
4119       Feld[x][y] = EL_YAMYAM;
4120       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4121       break;
4122
4123     case EL_SP_SNIKSNAK:
4124       MovDir[x][y] = MV_UP;
4125       break;
4126
4127     case EL_SP_ELECTRON:
4128       MovDir[x][y] = MV_LEFT;
4129       break;
4130
4131     case EL_MOLE_LEFT:
4132     case EL_MOLE_RIGHT:
4133     case EL_MOLE_UP:
4134     case EL_MOLE_DOWN:
4135       Feld[x][y] = EL_MOLE;
4136       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4137       break;
4138
4139     default:
4140       if (IS_CUSTOM_ELEMENT(element))
4141       {
4142         struct ElementInfo *ei = &element_info[element];
4143         int move_direction_initial = ei->move_direction_initial;
4144         int move_pattern = ei->move_pattern;
4145
4146         if (move_direction_initial == MV_START_PREVIOUS)
4147         {
4148           if (MovDir[x][y] != MV_NONE)
4149             return;
4150
4151           move_direction_initial = MV_START_AUTOMATIC;
4152         }
4153
4154         if (move_direction_initial == MV_START_RANDOM)
4155           MovDir[x][y] = 1 << RND(4);
4156         else if (move_direction_initial & MV_ANY_DIRECTION)
4157           MovDir[x][y] = move_direction_initial;
4158         else if (move_pattern == MV_ALL_DIRECTIONS ||
4159                  move_pattern == MV_TURNING_LEFT ||
4160                  move_pattern == MV_TURNING_RIGHT ||
4161                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4162                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4163                  move_pattern == MV_TURNING_RANDOM)
4164           MovDir[x][y] = 1 << RND(4);
4165         else if (move_pattern == MV_HORIZONTAL)
4166           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4167         else if (move_pattern == MV_VERTICAL)
4168           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4169         else if (move_pattern & MV_ANY_DIRECTION)
4170           MovDir[x][y] = element_info[element].move_pattern;
4171         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4172                  move_pattern == MV_ALONG_RIGHT_SIDE)
4173         {
4174           /* use random direction as default start direction */
4175           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4176             MovDir[x][y] = 1 << RND(4);
4177
4178           for (i = 0; i < NUM_DIRECTIONS; i++)
4179           {
4180             int x1 = x + xy[i][0];
4181             int y1 = y + xy[i][1];
4182
4183             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4184             {
4185               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4186                 MovDir[x][y] = direction[0][i];
4187               else
4188                 MovDir[x][y] = direction[1][i];
4189
4190               break;
4191             }
4192           }
4193         }                
4194       }
4195       else
4196       {
4197         MovDir[x][y] = 1 << RND(4);
4198
4199         if (element != EL_BUG &&
4200             element != EL_SPACESHIP &&
4201             element != EL_BD_BUTTERFLY &&
4202             element != EL_BD_FIREFLY)
4203           break;
4204
4205         for (i = 0; i < NUM_DIRECTIONS; i++)
4206         {
4207           int x1 = x + xy[i][0];
4208           int y1 = y + xy[i][1];
4209
4210           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4211           {
4212             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4213             {
4214               MovDir[x][y] = direction[0][i];
4215               break;
4216             }
4217             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4218                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4219             {
4220               MovDir[x][y] = direction[1][i];
4221               break;
4222             }
4223           }
4224         }
4225       }
4226       break;
4227   }
4228
4229   GfxDir[x][y] = MovDir[x][y];
4230 }
4231
4232 void InitAmoebaNr(int x, int y)
4233 {
4234   int i;
4235   int group_nr = AmoebeNachbarNr(x, y);
4236
4237   if (group_nr == 0)
4238   {
4239     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4240     {
4241       if (AmoebaCnt[i] == 0)
4242       {
4243         group_nr = i;
4244         break;
4245       }
4246     }
4247   }
4248
4249   AmoebaNr[x][y] = group_nr;
4250   AmoebaCnt[group_nr]++;
4251   AmoebaCnt2[group_nr]++;
4252 }
4253
4254 static void PlayerWins(struct PlayerInfo *player)
4255 {
4256   player->LevelSolved = TRUE;
4257   player->GameOver = TRUE;
4258
4259   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4260                          level.native_em_level->lev->score : player->score);
4261
4262   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4263                                       TimeLeft);
4264   player->LevelSolved_CountingScore = player->score_final;
4265 }
4266
4267 void GameWon()
4268 {
4269   static int time, time_final;
4270   static int score, score_final;
4271   static int game_over_delay_1 = 0;
4272   static int game_over_delay_2 = 0;
4273   int game_over_delay_value_1 = 50;
4274   int game_over_delay_value_2 = 50;
4275
4276   if (!local_player->LevelSolved_GameWon)
4277   {
4278     int i;
4279
4280     /* do not start end game actions before the player stops moving (to exit) */
4281     if (local_player->MovPos)
4282       return;
4283
4284     local_player->LevelSolved_GameWon = TRUE;
4285     local_player->LevelSolved_SaveTape = tape.recording;
4286     local_player->LevelSolved_SaveScore = !tape.playing;
4287
4288     if (!tape.playing)
4289     {
4290       LevelStats_incSolved(level_nr);
4291
4292       SaveLevelSetup_SeriesInfo();
4293     }
4294
4295     if (tape.auto_play)         /* tape might already be stopped here */
4296       tape.auto_play_level_solved = TRUE;
4297
4298     TapeStop();
4299
4300     game_over_delay_1 = game_over_delay_value_1;
4301     game_over_delay_2 = game_over_delay_value_2;
4302
4303     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4304     score = score_final = local_player->score_final;
4305
4306     if (TimeLeft > 0)
4307     {
4308       time_final = 0;
4309       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4310     }
4311     else if (game.no_time_limit && TimePlayed < 999)
4312     {
4313       time_final = 999;
4314       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4315     }
4316
4317     local_player->score_final = score_final;
4318
4319     if (level_editor_test_game)
4320     {
4321       time = time_final;
4322       score = score_final;
4323
4324       local_player->LevelSolved_CountingTime = time;
4325       local_player->LevelSolved_CountingScore = score;
4326
4327       game_panel_controls[GAME_PANEL_TIME].value = time;
4328       game_panel_controls[GAME_PANEL_SCORE].value = score;
4329
4330       DisplayGameControlValues();
4331     }
4332
4333     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4334     {
4335       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4336       {
4337         /* close exit door after last player */
4338         if ((AllPlayersGone &&
4339              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4340               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4341               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4342             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4343             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4344         {
4345           int element = Feld[ExitX][ExitY];
4346
4347           Feld[ExitX][ExitY] =
4348             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4349              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4350              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4351              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4352              EL_EM_STEEL_EXIT_CLOSING);
4353
4354           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4355         }
4356
4357         /* player disappears */
4358         DrawLevelField(ExitX, ExitY);
4359       }
4360
4361       for (i = 0; i < MAX_PLAYERS; i++)
4362       {
4363         struct PlayerInfo *player = &stored_player[i];
4364
4365         if (player->present)
4366         {
4367           RemovePlayer(player);
4368
4369           /* player disappears */
4370           DrawLevelField(player->jx, player->jy);
4371         }
4372       }
4373     }
4374
4375     PlaySound(SND_GAME_WINNING);
4376   }
4377
4378   if (game_over_delay_1 > 0)
4379   {
4380     game_over_delay_1--;
4381
4382     return;
4383   }
4384
4385   if (time != time_final)
4386   {
4387     int time_to_go = ABS(time_final - time);
4388     int time_count_dir = (time < time_final ? +1 : -1);
4389     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4390
4391     time  += time_count_steps * time_count_dir;
4392     score += time_count_steps * level.score[SC_TIME_BONUS];
4393
4394     local_player->LevelSolved_CountingTime = time;
4395     local_player->LevelSolved_CountingScore = score;
4396
4397     game_panel_controls[GAME_PANEL_TIME].value = time;
4398     game_panel_controls[GAME_PANEL_SCORE].value = score;
4399
4400     DisplayGameControlValues();
4401
4402     if (time == time_final)
4403       StopSound(SND_GAME_LEVELTIME_BONUS);
4404     else if (setup.sound_loops)
4405       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4406     else
4407       PlaySound(SND_GAME_LEVELTIME_BONUS);
4408
4409     return;
4410   }
4411
4412   local_player->LevelSolved_PanelOff = TRUE;
4413
4414   if (game_over_delay_2 > 0)
4415   {
4416     game_over_delay_2--;
4417
4418     return;
4419   }
4420
4421   GameEnd();
4422 }
4423
4424 void GameEnd()
4425 {
4426   int hi_pos;
4427   boolean raise_level = FALSE;
4428
4429   local_player->LevelSolved_GameEnd = TRUE;
4430
4431   if (!global.use_envelope_request)
4432     CloseDoor(DOOR_CLOSE_1);
4433
4434   if (local_player->LevelSolved_SaveTape)
4435   {
4436     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4437   }
4438
4439   CloseDoor(DOOR_CLOSE_ALL);
4440
4441   if (level_editor_test_game)
4442   {
4443     SetGameStatus(GAME_MODE_MAIN);
4444
4445     DrawMainMenu();
4446
4447     return;
4448   }
4449
4450   if (!local_player->LevelSolved_SaveScore)
4451   {
4452     SetGameStatus(GAME_MODE_MAIN);
4453
4454     DrawMainMenu();
4455
4456     return;
4457   }
4458
4459   if (level_nr == leveldir_current->handicap_level)
4460   {
4461     leveldir_current->handicap_level++;
4462
4463     SaveLevelSetup_SeriesInfo();
4464   }
4465
4466   if (level_nr < leveldir_current->last_level)
4467     raise_level = TRUE;                 /* advance to next level */
4468
4469   if ((hi_pos = NewHiScore()) >= 0) 
4470   {
4471     SetGameStatus(GAME_MODE_SCORES);
4472
4473     DrawHallOfFame(hi_pos);
4474
4475     if (raise_level)
4476     {
4477       level_nr++;
4478       TapeErase();
4479     }
4480   }
4481   else
4482   {
4483     SetGameStatus(GAME_MODE_MAIN);
4484
4485     if (raise_level)
4486     {
4487       level_nr++;
4488       TapeErase();
4489     }
4490
4491     DrawMainMenu();
4492   }
4493 }
4494
4495 int NewHiScore()
4496 {
4497   int k, l;
4498   int position = -1;
4499
4500   LoadScore(level_nr);
4501
4502   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4503       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4504     return -1;
4505
4506   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4507   {
4508     if (local_player->score_final > highscore[k].Score)
4509     {
4510       /* player has made it to the hall of fame */
4511
4512       if (k < MAX_SCORE_ENTRIES - 1)
4513       {
4514         int m = MAX_SCORE_ENTRIES - 1;
4515
4516 #ifdef ONE_PER_NAME
4517         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4518           if (strEqual(setup.player_name, highscore[l].Name))
4519             m = l;
4520         if (m == k)     /* player's new highscore overwrites his old one */
4521           goto put_into_list;
4522 #endif
4523
4524         for (l = m; l > k; l--)
4525         {
4526           strcpy(highscore[l].Name, highscore[l - 1].Name);
4527           highscore[l].Score = highscore[l - 1].Score;
4528         }
4529       }
4530
4531 #ifdef ONE_PER_NAME
4532       put_into_list:
4533 #endif
4534       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4535       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4536       highscore[k].Score = local_player->score_final; 
4537       position = k;
4538       break;
4539     }
4540
4541 #ifdef ONE_PER_NAME
4542     else if (!strncmp(setup.player_name, highscore[k].Name,
4543                       MAX_PLAYER_NAME_LEN))
4544       break;    /* player already there with a higher score */
4545 #endif
4546
4547   }
4548
4549   if (position >= 0) 
4550     SaveScore(level_nr);
4551
4552   return position;
4553 }
4554
4555 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4556 {
4557   int element = Feld[x][y];
4558   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4559   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4560   int horiz_move = (dx != 0);
4561   int sign = (horiz_move ? dx : dy);
4562   int step = sign * element_info[element].move_stepsize;
4563
4564   /* special values for move stepsize for spring and things on conveyor belt */
4565   if (horiz_move)
4566   {
4567     if (CAN_FALL(element) &&
4568         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4569       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4570     else if (element == EL_SPRING)
4571       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4572   }
4573
4574   return step;
4575 }
4576
4577 inline static int getElementMoveStepsize(int x, int y)
4578 {
4579   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4580 }
4581
4582 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4583 {
4584   if (player->GfxAction != action || player->GfxDir != dir)
4585   {
4586     player->GfxAction = action;
4587     player->GfxDir = dir;
4588     player->Frame = 0;
4589     player->StepFrame = 0;
4590   }
4591 }
4592
4593 static void ResetGfxFrame(int x, int y, boolean redraw)
4594 {
4595   int element = Feld[x][y];
4596   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4597   int last_gfx_frame = GfxFrame[x][y];
4598
4599   if (graphic_info[graphic].anim_global_sync)
4600     GfxFrame[x][y] = FrameCounter;
4601   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4602     GfxFrame[x][y] = CustomValue[x][y];
4603   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4604     GfxFrame[x][y] = element_info[element].collect_score;
4605   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4606     GfxFrame[x][y] = ChangeDelay[x][y];
4607
4608   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4609     DrawLevelGraphicAnimation(x, y, graphic);
4610 }
4611
4612 static void ResetGfxAnimation(int x, int y)
4613 {
4614   GfxAction[x][y] = ACTION_DEFAULT;
4615   GfxDir[x][y] = MovDir[x][y];
4616   GfxFrame[x][y] = 0;
4617
4618   ResetGfxFrame(x, y, FALSE);
4619 }
4620
4621 static void ResetRandomAnimationValue(int x, int y)
4622 {
4623   GfxRandom[x][y] = INIT_GFX_RANDOM();
4624 }
4625
4626 void InitMovingField(int x, int y, int direction)
4627 {
4628   int element = Feld[x][y];
4629   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4630   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4631   int newx = x + dx;
4632   int newy = y + dy;
4633   boolean is_moving_before, is_moving_after;
4634
4635   /* check if element was/is moving or being moved before/after mode change */
4636   is_moving_before = (WasJustMoving[x][y] != 0);
4637   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4638
4639   /* reset animation only for moving elements which change direction of moving
4640      or which just started or stopped moving
4641      (else CEs with property "can move" / "not moving" are reset each frame) */
4642   if (is_moving_before != is_moving_after ||
4643       direction != MovDir[x][y])
4644     ResetGfxAnimation(x, y);
4645
4646   MovDir[x][y] = direction;
4647   GfxDir[x][y] = direction;
4648
4649   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4650                      direction == MV_DOWN && CAN_FALL(element) ?
4651                      ACTION_FALLING : ACTION_MOVING);
4652
4653   /* this is needed for CEs with property "can move" / "not moving" */
4654
4655   if (is_moving_after)
4656   {
4657     if (Feld[newx][newy] == EL_EMPTY)
4658       Feld[newx][newy] = EL_BLOCKED;
4659
4660     MovDir[newx][newy] = MovDir[x][y];
4661
4662     CustomValue[newx][newy] = CustomValue[x][y];
4663
4664     GfxFrame[newx][newy] = GfxFrame[x][y];
4665     GfxRandom[newx][newy] = GfxRandom[x][y];
4666     GfxAction[newx][newy] = GfxAction[x][y];
4667     GfxDir[newx][newy] = GfxDir[x][y];
4668   }
4669 }
4670
4671 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4672 {
4673   int direction = MovDir[x][y];
4674   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4675   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4676
4677   *goes_to_x = newx;
4678   *goes_to_y = newy;
4679 }
4680
4681 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4682 {
4683   int oldx = x, oldy = y;
4684   int direction = MovDir[x][y];
4685
4686   if (direction == MV_LEFT)
4687     oldx++;
4688   else if (direction == MV_RIGHT)
4689     oldx--;
4690   else if (direction == MV_UP)
4691     oldy++;
4692   else if (direction == MV_DOWN)
4693     oldy--;
4694
4695   *comes_from_x = oldx;
4696   *comes_from_y = oldy;
4697 }
4698
4699 int MovingOrBlocked2Element(int x, int y)
4700 {
4701   int element = Feld[x][y];
4702
4703   if (element == EL_BLOCKED)
4704   {
4705     int oldx, oldy;
4706
4707     Blocked2Moving(x, y, &oldx, &oldy);
4708     return Feld[oldx][oldy];
4709   }
4710   else
4711     return element;
4712 }
4713
4714 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4715 {
4716   /* like MovingOrBlocked2Element(), but if element is moving
4717      and (x,y) is the field the moving element is just leaving,
4718      return EL_BLOCKED instead of the element value */
4719   int element = Feld[x][y];
4720
4721   if (IS_MOVING(x, y))
4722   {
4723     if (element == EL_BLOCKED)
4724     {
4725       int oldx, oldy;
4726
4727       Blocked2Moving(x, y, &oldx, &oldy);
4728       return Feld[oldx][oldy];
4729     }
4730     else
4731       return EL_BLOCKED;
4732   }
4733   else
4734     return element;
4735 }
4736
4737 static void RemoveField(int x, int y)
4738 {
4739   Feld[x][y] = EL_EMPTY;
4740
4741   MovPos[x][y] = 0;
4742   MovDir[x][y] = 0;
4743   MovDelay[x][y] = 0;
4744
4745   CustomValue[x][y] = 0;
4746
4747   AmoebaNr[x][y] = 0;
4748   ChangeDelay[x][y] = 0;
4749   ChangePage[x][y] = -1;
4750   Pushed[x][y] = FALSE;
4751
4752   GfxElement[x][y] = EL_UNDEFINED;
4753   GfxAction[x][y] = ACTION_DEFAULT;
4754   GfxDir[x][y] = MV_NONE;
4755 }
4756
4757 void RemoveMovingField(int x, int y)
4758 {
4759   int oldx = x, oldy = y, newx = x, newy = y;
4760   int element = Feld[x][y];
4761   int next_element = EL_UNDEFINED;
4762
4763   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4764     return;
4765
4766   if (IS_MOVING(x, y))
4767   {
4768     Moving2Blocked(x, y, &newx, &newy);
4769
4770     if (Feld[newx][newy] != EL_BLOCKED)
4771     {
4772       /* element is moving, but target field is not free (blocked), but
4773          already occupied by something different (example: acid pool);
4774          in this case, only remove the moving field, but not the target */
4775
4776       RemoveField(oldx, oldy);
4777
4778       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4779
4780       TEST_DrawLevelField(oldx, oldy);
4781
4782       return;
4783     }
4784   }
4785   else if (element == EL_BLOCKED)
4786   {
4787     Blocked2Moving(x, y, &oldx, &oldy);
4788     if (!IS_MOVING(oldx, oldy))
4789       return;
4790   }
4791
4792   if (element == EL_BLOCKED &&
4793       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4794        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4795        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4796        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4797        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4798        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4799     next_element = get_next_element(Feld[oldx][oldy]);
4800
4801   RemoveField(oldx, oldy);
4802   RemoveField(newx, newy);
4803
4804   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4805
4806   if (next_element != EL_UNDEFINED)
4807     Feld[oldx][oldy] = next_element;
4808
4809   TEST_DrawLevelField(oldx, oldy);
4810   TEST_DrawLevelField(newx, newy);
4811 }
4812
4813 void DrawDynamite(int x, int y)
4814 {
4815   int sx = SCREENX(x), sy = SCREENY(y);
4816   int graphic = el2img(Feld[x][y]);
4817   int frame;
4818
4819   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4820     return;
4821
4822   if (IS_WALKABLE_INSIDE(Back[x][y]))
4823     return;
4824
4825   if (Back[x][y])
4826     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4827   else if (Store[x][y])
4828     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4829
4830   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4831
4832   if (Back[x][y] || Store[x][y])
4833     DrawGraphicThruMask(sx, sy, graphic, frame);
4834   else
4835     DrawGraphic(sx, sy, graphic, frame);
4836 }
4837
4838 void CheckDynamite(int x, int y)
4839 {
4840   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4841   {
4842     MovDelay[x][y]--;
4843
4844     if (MovDelay[x][y] != 0)
4845     {
4846       DrawDynamite(x, y);
4847       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4848
4849       return;
4850     }
4851   }
4852
4853   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4854
4855   Bang(x, y);
4856 }
4857
4858 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4859 {
4860   boolean num_checked_players = 0;
4861   int i;
4862
4863   for (i = 0; i < MAX_PLAYERS; i++)
4864   {
4865     if (stored_player[i].active)
4866     {
4867       int sx = stored_player[i].jx;
4868       int sy = stored_player[i].jy;
4869
4870       if (num_checked_players == 0)
4871       {
4872         *sx1 = *sx2 = sx;
4873         *sy1 = *sy2 = sy;
4874       }
4875       else
4876       {
4877         *sx1 = MIN(*sx1, sx);
4878         *sy1 = MIN(*sy1, sy);
4879         *sx2 = MAX(*sx2, sx);
4880         *sy2 = MAX(*sy2, sy);
4881       }
4882
4883       num_checked_players++;
4884     }
4885   }
4886 }
4887
4888 static boolean checkIfAllPlayersFitToScreen_RND()
4889 {
4890   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4891
4892   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4893
4894   return (sx2 - sx1 < SCR_FIELDX &&
4895           sy2 - sy1 < SCR_FIELDY);
4896 }
4897
4898 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4899 {
4900   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4901
4902   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4903
4904   *sx = (sx1 + sx2) / 2;
4905   *sy = (sy1 + sy2) / 2;
4906 }
4907
4908 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4909                         boolean center_screen, boolean quick_relocation)
4910 {
4911   unsigned int frame_delay_value_old = GetVideoFrameDelay();
4912   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4913   boolean no_delay = (tape.warp_forward);
4914   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4915   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4916   int new_scroll_x, new_scroll_y;
4917
4918   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4919   {
4920     /* case 1: quick relocation inside visible screen (without scrolling) */
4921
4922     RedrawPlayfield();
4923
4924     return;
4925   }
4926
4927   if (!level.shifted_relocation || center_screen)
4928   {
4929     /* relocation _with_ centering of screen */
4930
4931     new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4932                     x > SBX_Right + MIDPOSX ? SBX_Right :
4933                     x - MIDPOSX);
4934
4935     new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4936                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4937                     y - MIDPOSY);
4938   }
4939   else
4940   {
4941     /* relocation _without_ centering of screen */
4942
4943     int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4944                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
4945                            old_x - MIDPOSX);
4946
4947     int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4948                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4949                            old_y - MIDPOSY);
4950
4951     int offset_x = x + (scroll_x - center_scroll_x);
4952     int offset_y = y + (scroll_y - center_scroll_y);
4953
4954     new_scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4955                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4956                     offset_x - MIDPOSX);
4957
4958     new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4959                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4960                     offset_y - MIDPOSY);
4961   }
4962
4963   if (quick_relocation)
4964   {
4965     /* case 2: quick relocation (redraw without visible scrolling) */
4966
4967     scroll_x = new_scroll_x;
4968     scroll_y = new_scroll_y;
4969
4970     RedrawPlayfield();
4971
4972     return;
4973   }
4974
4975   /* case 3: visible relocation (with scrolling to new position) */
4976
4977   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4978
4979   SetVideoFrameDelay(wait_delay_value);
4980
4981   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4982   {
4983     int dx = 0, dy = 0;
4984     int fx = FX, fy = FY;
4985
4986     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4987     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4988
4989     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4990       break;
4991
4992     scroll_x -= dx;
4993     scroll_y -= dy;
4994
4995     fx += dx * TILEX / 2;
4996     fy += dy * TILEY / 2;
4997
4998     ScrollLevel(dx, dy);
4999     DrawAllPlayers();
5000
5001     /* scroll in two steps of half tile size to make things smoother */
5002     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5003
5004     /* scroll second step to align at full tile size */
5005     BlitScreenToBitmap(window);
5006   }
5007
5008   DrawAllPlayers();
5009   BackToFront();
5010
5011   SetVideoFrameDelay(frame_delay_value_old);
5012 }
5013
5014 void RelocatePlayer(int jx, int jy, int el_player_raw)
5015 {
5016   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5017   int player_nr = GET_PLAYER_NR(el_player);
5018   struct PlayerInfo *player = &stored_player[player_nr];
5019   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5020   boolean no_delay = (tape.warp_forward);
5021   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5022   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5023   int old_jx = player->jx;
5024   int old_jy = player->jy;
5025   int old_element = Feld[old_jx][old_jy];
5026   int element = Feld[jx][jy];
5027   boolean player_relocated = (old_jx != jx || old_jy != jy);
5028
5029   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5030   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5031   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5032   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5033   int leave_side_horiz = move_dir_horiz;
5034   int leave_side_vert  = move_dir_vert;
5035   int enter_side = enter_side_horiz | enter_side_vert;
5036   int leave_side = leave_side_horiz | leave_side_vert;
5037
5038   if (player->GameOver)         /* do not reanimate dead player */
5039     return;
5040
5041   if (!player_relocated)        /* no need to relocate the player */
5042     return;
5043
5044   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5045   {
5046     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5047     DrawLevelField(jx, jy);
5048   }
5049
5050   if (player->present)
5051   {
5052     while (player->MovPos)
5053     {
5054       ScrollPlayer(player, SCROLL_GO_ON);
5055       ScrollScreen(NULL, SCROLL_GO_ON);
5056
5057       AdvanceFrameAndPlayerCounters(player->index_nr);
5058
5059       DrawPlayer(player);
5060
5061       BackToFront_WithFrameDelay(wait_delay_value);
5062     }
5063
5064     DrawPlayer(player);         /* needed here only to cleanup last field */
5065     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5066
5067     player->is_moving = FALSE;
5068   }
5069
5070   if (IS_CUSTOM_ELEMENT(old_element))
5071     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5072                                CE_LEFT_BY_PLAYER,
5073                                player->index_bit, leave_side);
5074
5075   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5076                                       CE_PLAYER_LEAVES_X,
5077                                       player->index_bit, leave_side);
5078
5079   Feld[jx][jy] = el_player;
5080   InitPlayerField(jx, jy, el_player, TRUE);
5081
5082   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5083      possible that the relocation target field did not contain a player element,
5084      but a walkable element, to which the new player was relocated -- in this
5085      case, restore that (already initialized!) element on the player field */
5086   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5087   {
5088     Feld[jx][jy] = element;     /* restore previously existing element */
5089   }
5090
5091   /* only visually relocate centered player */
5092   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5093                      FALSE, level.instant_relocation);
5094
5095   TestIfPlayerTouchesBadThing(jx, jy);
5096   TestIfPlayerTouchesCustomElement(jx, jy);
5097
5098   if (IS_CUSTOM_ELEMENT(element))
5099     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5100                                player->index_bit, enter_side);
5101
5102   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5103                                       player->index_bit, enter_side);
5104
5105   if (player->is_switching)
5106   {
5107     /* ensure that relocation while still switching an element does not cause
5108        a new element to be treated as also switched directly after relocation
5109        (this is important for teleporter switches that teleport the player to
5110        a place where another teleporter switch is in the same direction, which
5111        would then incorrectly be treated as immediately switched before the
5112        direction key that caused the switch was released) */
5113
5114     player->switch_x += jx - old_jx;
5115     player->switch_y += jy - old_jy;
5116   }
5117 }
5118
5119 void Explode(int ex, int ey, int phase, int mode)
5120 {
5121   int x, y;
5122   int last_phase;
5123   int border_element;
5124
5125   /* !!! eliminate this variable !!! */
5126   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5127
5128   if (game.explosions_delayed)
5129   {
5130     ExplodeField[ex][ey] = mode;
5131     return;
5132   }
5133
5134   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5135   {
5136     int center_element = Feld[ex][ey];
5137     int artwork_element, explosion_element;     /* set these values later */
5138
5139     /* remove things displayed in background while burning dynamite */
5140     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5141       Back[ex][ey] = 0;
5142
5143     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5144     {
5145       /* put moving element to center field (and let it explode there) */
5146       center_element = MovingOrBlocked2Element(ex, ey);
5147       RemoveMovingField(ex, ey);
5148       Feld[ex][ey] = center_element;
5149     }
5150
5151     /* now "center_element" is finally determined -- set related values now */
5152     artwork_element = center_element;           /* for custom player artwork */
5153     explosion_element = center_element;         /* for custom player artwork */
5154
5155     if (IS_PLAYER(ex, ey))
5156     {
5157       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5158
5159       artwork_element = stored_player[player_nr].artwork_element;
5160
5161       if (level.use_explosion_element[player_nr])
5162       {
5163         explosion_element = level.explosion_element[player_nr];
5164         artwork_element = explosion_element;
5165       }
5166     }
5167
5168     if (mode == EX_TYPE_NORMAL ||
5169         mode == EX_TYPE_CENTER ||
5170         mode == EX_TYPE_CROSS)
5171       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5172
5173     last_phase = element_info[explosion_element].explosion_delay + 1;
5174
5175     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5176     {
5177       int xx = x - ex + 1;
5178       int yy = y - ey + 1;
5179       int element;
5180
5181       if (!IN_LEV_FIELD(x, y) ||
5182           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5183           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5184         continue;
5185
5186       element = Feld[x][y];
5187
5188       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5189       {
5190         element = MovingOrBlocked2Element(x, y);
5191
5192         if (!IS_EXPLOSION_PROOF(element))
5193           RemoveMovingField(x, y);
5194       }
5195
5196       /* indestructible elements can only explode in center (but not flames) */
5197       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5198                                            mode == EX_TYPE_BORDER)) ||
5199           element == EL_FLAMES)
5200         continue;
5201
5202       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5203          behaviour, for example when touching a yamyam that explodes to rocks
5204          with active deadly shield, a rock is created under the player !!! */
5205       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5206 #if 0
5207       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5208           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5209            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5210 #else
5211       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5212 #endif
5213       {
5214         if (IS_ACTIVE_BOMB(element))
5215         {
5216           /* re-activate things under the bomb like gate or penguin */
5217           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5218           Back[x][y] = 0;
5219         }
5220
5221         continue;
5222       }
5223
5224       /* save walkable background elements while explosion on same tile */
5225       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5226           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5227         Back[x][y] = element;
5228
5229       /* ignite explodable elements reached by other explosion */
5230       if (element == EL_EXPLOSION)
5231         element = Store2[x][y];
5232
5233       if (AmoebaNr[x][y] &&
5234           (element == EL_AMOEBA_FULL ||
5235            element == EL_BD_AMOEBA ||
5236            element == EL_AMOEBA_GROWING))
5237       {
5238         AmoebaCnt[AmoebaNr[x][y]]--;
5239         AmoebaCnt2[AmoebaNr[x][y]]--;
5240       }
5241
5242       RemoveField(x, y);
5243
5244       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5245       {
5246         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5247
5248         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5249
5250         if (PLAYERINFO(ex, ey)->use_murphy)
5251           Store[x][y] = EL_EMPTY;
5252       }
5253
5254       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5255          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5256       else if (ELEM_IS_PLAYER(center_element))
5257         Store[x][y] = EL_EMPTY;
5258       else if (center_element == EL_YAMYAM)
5259         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5260       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5261         Store[x][y] = element_info[center_element].content.e[xx][yy];
5262 #if 1
5263       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5264          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5265          otherwise) -- FIX THIS !!! */
5266       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5267         Store[x][y] = element_info[element].content.e[1][1];
5268 #else
5269       else if (!CAN_EXPLODE(element))
5270         Store[x][y] = element_info[element].content.e[1][1];
5271 #endif
5272       else
5273         Store[x][y] = EL_EMPTY;
5274
5275       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5276           center_element == EL_AMOEBA_TO_DIAMOND)
5277         Store2[x][y] = element;
5278
5279       Feld[x][y] = EL_EXPLOSION;
5280       GfxElement[x][y] = artwork_element;
5281
5282       ExplodePhase[x][y] = 1;
5283       ExplodeDelay[x][y] = last_phase;
5284
5285       Stop[x][y] = TRUE;
5286     }
5287
5288     if (center_element == EL_YAMYAM)
5289       game.yamyam_content_nr =
5290         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5291
5292     return;
5293   }
5294
5295   if (Stop[ex][ey])
5296     return;
5297
5298   x = ex;
5299   y = ey;
5300
5301   if (phase == 1)
5302     GfxFrame[x][y] = 0;         /* restart explosion animation */
5303
5304   last_phase = ExplodeDelay[x][y];
5305
5306   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5307
5308   /* this can happen if the player leaves an explosion just in time */
5309   if (GfxElement[x][y] == EL_UNDEFINED)
5310     GfxElement[x][y] = EL_EMPTY;
5311
5312   border_element = Store2[x][y];
5313   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5314     border_element = StorePlayer[x][y];
5315
5316   if (phase == element_info[border_element].ignition_delay ||
5317       phase == last_phase)
5318   {
5319     boolean border_explosion = FALSE;
5320
5321     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5322         !PLAYER_EXPLOSION_PROTECTED(x, y))
5323     {
5324       KillPlayerUnlessExplosionProtected(x, y);
5325       border_explosion = TRUE;
5326     }
5327     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5328     {
5329       Feld[x][y] = Store2[x][y];
5330       Store2[x][y] = 0;
5331       Bang(x, y);
5332       border_explosion = TRUE;
5333     }
5334     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5335     {
5336       AmoebeUmwandeln(x, y);
5337       Store2[x][y] = 0;
5338       border_explosion = TRUE;
5339     }
5340
5341     /* if an element just explodes due to another explosion (chain-reaction),
5342        do not immediately end the new explosion when it was the last frame of
5343        the explosion (as it would be done in the following "if"-statement!) */
5344     if (border_explosion && phase == last_phase)
5345       return;
5346   }
5347
5348   if (phase == last_phase)
5349   {
5350     int element;
5351
5352     element = Feld[x][y] = Store[x][y];
5353     Store[x][y] = Store2[x][y] = 0;
5354     GfxElement[x][y] = EL_UNDEFINED;
5355
5356     /* player can escape from explosions and might therefore be still alive */
5357     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5358         element <= EL_PLAYER_IS_EXPLODING_4)
5359     {
5360       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5361       int explosion_element = EL_PLAYER_1 + player_nr;
5362       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5363       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5364
5365       if (level.use_explosion_element[player_nr])
5366         explosion_element = level.explosion_element[player_nr];
5367
5368       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5369                     element_info[explosion_element].content.e[xx][yy]);
5370     }
5371
5372     /* restore probably existing indestructible background element */
5373     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5374       element = Feld[x][y] = Back[x][y];
5375     Back[x][y] = 0;
5376
5377     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5378     GfxDir[x][y] = MV_NONE;
5379     ChangeDelay[x][y] = 0;
5380     ChangePage[x][y] = -1;
5381
5382     CustomValue[x][y] = 0;
5383
5384     InitField_WithBug2(x, y, FALSE);
5385
5386     TEST_DrawLevelField(x, y);
5387
5388     TestIfElementTouchesCustomElement(x, y);
5389
5390     if (GFX_CRUMBLED(element))
5391       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5392
5393     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5394       StorePlayer[x][y] = 0;
5395
5396     if (ELEM_IS_PLAYER(element))
5397       RelocatePlayer(x, y, element);
5398   }
5399   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5400   {
5401     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5402     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5403
5404     if (phase == delay)
5405       TEST_DrawLevelFieldCrumbled(x, y);
5406
5407     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5408     {
5409       DrawLevelElement(x, y, Back[x][y]);
5410       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5411     }
5412     else if (IS_WALKABLE_UNDER(Back[x][y]))
5413     {
5414       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5415       DrawLevelElementThruMask(x, y, Back[x][y]);
5416     }
5417     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5418       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5419   }
5420 }
5421
5422 void DynaExplode(int ex, int ey)
5423 {
5424   int i, j;
5425   int dynabomb_element = Feld[ex][ey];
5426   int dynabomb_size = 1;
5427   boolean dynabomb_xl = FALSE;
5428   struct PlayerInfo *player;
5429   static int xy[4][2] =
5430   {
5431     { 0, -1 },
5432     { -1, 0 },
5433     { +1, 0 },
5434     { 0, +1 }
5435   };
5436
5437   if (IS_ACTIVE_BOMB(dynabomb_element))
5438   {
5439     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5440     dynabomb_size = player->dynabomb_size;
5441     dynabomb_xl = player->dynabomb_xl;
5442     player->dynabombs_left++;
5443   }
5444
5445   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5446
5447   for (i = 0; i < NUM_DIRECTIONS; i++)
5448   {
5449     for (j = 1; j <= dynabomb_size; j++)
5450     {
5451       int x = ex + j * xy[i][0];
5452       int y = ey + j * xy[i][1];
5453       int element;
5454
5455       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5456         break;
5457
5458       element = Feld[x][y];
5459
5460       /* do not restart explosions of fields with active bombs */
5461       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5462         continue;
5463
5464       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5465
5466       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5467           !IS_DIGGABLE(element) && !dynabomb_xl)
5468         break;
5469     }
5470   }
5471 }
5472
5473 void Bang(int x, int y)
5474 {
5475   int element = MovingOrBlocked2Element(x, y);
5476   int explosion_type = EX_TYPE_NORMAL;
5477
5478   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5479   {
5480     struct PlayerInfo *player = PLAYERINFO(x, y);
5481
5482     element = Feld[x][y] = player->initial_element;
5483
5484     if (level.use_explosion_element[player->index_nr])
5485     {
5486       int explosion_element = level.explosion_element[player->index_nr];
5487
5488       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5489         explosion_type = EX_TYPE_CROSS;
5490       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5491         explosion_type = EX_TYPE_CENTER;
5492     }
5493   }
5494
5495   switch (element)
5496   {
5497     case EL_BUG:
5498     case EL_SPACESHIP:
5499     case EL_BD_BUTTERFLY:
5500     case EL_BD_FIREFLY:
5501     case EL_YAMYAM:
5502     case EL_DARK_YAMYAM:
5503     case EL_ROBOT:
5504     case EL_PACMAN:
5505     case EL_MOLE:
5506       RaiseScoreElement(element);
5507       break;
5508
5509     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5510     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5511     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5512     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5513     case EL_DYNABOMB_INCREASE_NUMBER:
5514     case EL_DYNABOMB_INCREASE_SIZE:
5515     case EL_DYNABOMB_INCREASE_POWER:
5516       explosion_type = EX_TYPE_DYNA;
5517       break;
5518
5519     case EL_DC_LANDMINE:
5520       explosion_type = EX_TYPE_CENTER;
5521       break;
5522
5523     case EL_PENGUIN:
5524     case EL_LAMP:
5525     case EL_LAMP_ACTIVE:
5526     case EL_AMOEBA_TO_DIAMOND:
5527       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5528         explosion_type = EX_TYPE_CENTER;
5529       break;
5530
5531     default:
5532       if (element_info[element].explosion_type == EXPLODES_CROSS)
5533         explosion_type = EX_TYPE_CROSS;
5534       else if (element_info[element].explosion_type == EXPLODES_1X1)
5535         explosion_type = EX_TYPE_CENTER;
5536       break;
5537   }
5538
5539   if (explosion_type == EX_TYPE_DYNA)
5540     DynaExplode(x, y);
5541   else
5542     Explode(x, y, EX_PHASE_START, explosion_type);
5543
5544   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5545 }
5546
5547 void SplashAcid(int x, int y)
5548 {
5549   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5550       (!IN_LEV_FIELD(x - 1, y - 2) ||
5551        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5552     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5553
5554   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5555       (!IN_LEV_FIELD(x + 1, y - 2) ||
5556        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5557     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5558
5559   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5560 }
5561
5562 static void InitBeltMovement()
5563 {
5564   static int belt_base_element[4] =
5565   {
5566     EL_CONVEYOR_BELT_1_LEFT,
5567     EL_CONVEYOR_BELT_2_LEFT,
5568     EL_CONVEYOR_BELT_3_LEFT,
5569     EL_CONVEYOR_BELT_4_LEFT
5570   };
5571   static int belt_base_active_element[4] =
5572   {
5573     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5574     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5575     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5576     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5577   };
5578
5579   int x, y, i, j;
5580
5581   /* set frame order for belt animation graphic according to belt direction */
5582   for (i = 0; i < NUM_BELTS; i++)
5583   {
5584     int belt_nr = i;
5585
5586     for (j = 0; j < NUM_BELT_PARTS; j++)
5587     {
5588       int element = belt_base_active_element[belt_nr] + j;
5589       int graphic_1 = el2img(element);
5590       int graphic_2 = el2panelimg(element);
5591
5592       if (game.belt_dir[i] == MV_LEFT)
5593       {
5594         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5595         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5596       }
5597       else
5598       {
5599         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5600         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5601       }
5602     }
5603   }
5604
5605   SCAN_PLAYFIELD(x, y)
5606   {
5607     int element = Feld[x][y];
5608
5609     for (i = 0; i < NUM_BELTS; i++)
5610     {
5611       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5612       {
5613         int e_belt_nr = getBeltNrFromBeltElement(element);
5614         int belt_nr = i;
5615
5616         if (e_belt_nr == belt_nr)
5617         {
5618           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5619
5620           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5621         }
5622       }
5623     }
5624   }
5625 }
5626
5627 static void ToggleBeltSwitch(int x, int y)
5628 {
5629   static int belt_base_element[4] =
5630   {
5631     EL_CONVEYOR_BELT_1_LEFT,
5632     EL_CONVEYOR_BELT_2_LEFT,
5633     EL_CONVEYOR_BELT_3_LEFT,
5634     EL_CONVEYOR_BELT_4_LEFT
5635   };
5636   static int belt_base_active_element[4] =
5637   {
5638     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5639     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5640     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5641     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5642   };
5643   static int belt_base_switch_element[4] =
5644   {
5645     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5646     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5647     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5648     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5649   };
5650   static int belt_move_dir[4] =
5651   {
5652     MV_LEFT,
5653     MV_NONE,
5654     MV_RIGHT,
5655     MV_NONE,
5656   };
5657
5658   int element = Feld[x][y];
5659   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5660   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5661   int belt_dir = belt_move_dir[belt_dir_nr];
5662   int xx, yy, i;
5663
5664   if (!IS_BELT_SWITCH(element))
5665     return;
5666
5667   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5668   game.belt_dir[belt_nr] = belt_dir;
5669
5670   if (belt_dir_nr == 3)
5671     belt_dir_nr = 1;
5672
5673   /* set frame order for belt animation graphic according to belt direction */
5674   for (i = 0; i < NUM_BELT_PARTS; i++)
5675   {
5676     int element = belt_base_active_element[belt_nr] + i;
5677     int graphic_1 = el2img(element);
5678     int graphic_2 = el2panelimg(element);
5679
5680     if (belt_dir == MV_LEFT)
5681     {
5682       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5683       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5684     }
5685     else
5686     {
5687       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5688       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5689     }
5690   }
5691
5692   SCAN_PLAYFIELD(xx, yy)
5693   {
5694     int element = Feld[xx][yy];
5695
5696     if (IS_BELT_SWITCH(element))
5697     {
5698       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5699
5700       if (e_belt_nr == belt_nr)
5701       {
5702         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5703         TEST_DrawLevelField(xx, yy);
5704       }
5705     }
5706     else if (IS_BELT(element) && belt_dir != MV_NONE)
5707     {
5708       int e_belt_nr = getBeltNrFromBeltElement(element);
5709
5710       if (e_belt_nr == belt_nr)
5711       {
5712         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5713
5714         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5715         TEST_DrawLevelField(xx, yy);
5716       }
5717     }
5718     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5719     {
5720       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5721
5722       if (e_belt_nr == belt_nr)
5723       {
5724         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5725
5726         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5727         TEST_DrawLevelField(xx, yy);
5728       }
5729     }
5730   }
5731 }
5732
5733 static void ToggleSwitchgateSwitch(int x, int y)
5734 {
5735   int xx, yy;
5736
5737   game.switchgate_pos = !game.switchgate_pos;
5738
5739   SCAN_PLAYFIELD(xx, yy)
5740   {
5741     int element = Feld[xx][yy];
5742
5743     if (element == EL_SWITCHGATE_SWITCH_UP)
5744     {
5745       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5746       TEST_DrawLevelField(xx, yy);
5747     }
5748     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5749     {
5750       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5751       TEST_DrawLevelField(xx, yy);
5752     }
5753     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5754     {
5755       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5756       TEST_DrawLevelField(xx, yy);
5757     }
5758     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5759     {
5760       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5761       TEST_DrawLevelField(xx, yy);
5762     }
5763     else if (element == EL_SWITCHGATE_OPEN ||
5764              element == EL_SWITCHGATE_OPENING)
5765     {
5766       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5767
5768       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5769     }
5770     else if (element == EL_SWITCHGATE_CLOSED ||
5771              element == EL_SWITCHGATE_CLOSING)
5772     {
5773       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5774
5775       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5776     }
5777   }
5778 }
5779
5780 static int getInvisibleActiveFromInvisibleElement(int element)
5781 {
5782   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5783           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5784           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5785           element);
5786 }
5787
5788 static int getInvisibleFromInvisibleActiveElement(int element)
5789 {
5790   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5791           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5792           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5793           element);
5794 }
5795
5796 static void RedrawAllLightSwitchesAndInvisibleElements()
5797 {
5798   int x, y;
5799
5800   SCAN_PLAYFIELD(x, y)
5801   {
5802     int element = Feld[x][y];
5803
5804     if (element == EL_LIGHT_SWITCH &&
5805         game.light_time_left > 0)
5806     {
5807       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5808       TEST_DrawLevelField(x, y);
5809     }
5810     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5811              game.light_time_left == 0)
5812     {
5813       Feld[x][y] = EL_LIGHT_SWITCH;
5814       TEST_DrawLevelField(x, y);
5815     }
5816     else if (element == EL_EMC_DRIPPER &&
5817              game.light_time_left > 0)
5818     {
5819       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5820       TEST_DrawLevelField(x, y);
5821     }
5822     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5823              game.light_time_left == 0)
5824     {
5825       Feld[x][y] = EL_EMC_DRIPPER;
5826       TEST_DrawLevelField(x, y);
5827     }
5828     else if (element == EL_INVISIBLE_STEELWALL ||
5829              element == EL_INVISIBLE_WALL ||
5830              element == EL_INVISIBLE_SAND)
5831     {
5832       if (game.light_time_left > 0)
5833         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5834
5835       TEST_DrawLevelField(x, y);
5836
5837       /* uncrumble neighbour fields, if needed */
5838       if (element == EL_INVISIBLE_SAND)
5839         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5840     }
5841     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5842              element == EL_INVISIBLE_WALL_ACTIVE ||
5843              element == EL_INVISIBLE_SAND_ACTIVE)
5844     {
5845       if (game.light_time_left == 0)
5846         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5847
5848       TEST_DrawLevelField(x, y);
5849
5850       /* re-crumble neighbour fields, if needed */
5851       if (element == EL_INVISIBLE_SAND)
5852         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5853     }
5854   }
5855 }
5856
5857 static void RedrawAllInvisibleElementsForLenses()
5858 {
5859   int x, y;
5860
5861   SCAN_PLAYFIELD(x, y)
5862   {
5863     int element = Feld[x][y];
5864
5865     if (element == EL_EMC_DRIPPER &&
5866         game.lenses_time_left > 0)
5867     {
5868       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5869       TEST_DrawLevelField(x, y);
5870     }
5871     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5872              game.lenses_time_left == 0)
5873     {
5874       Feld[x][y] = EL_EMC_DRIPPER;
5875       TEST_DrawLevelField(x, y);
5876     }
5877     else if (element == EL_INVISIBLE_STEELWALL ||
5878              element == EL_INVISIBLE_WALL ||
5879              element == EL_INVISIBLE_SAND)
5880     {
5881       if (game.lenses_time_left > 0)
5882         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5883
5884       TEST_DrawLevelField(x, y);
5885
5886       /* uncrumble neighbour fields, if needed */
5887       if (element == EL_INVISIBLE_SAND)
5888         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5889     }
5890     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5891              element == EL_INVISIBLE_WALL_ACTIVE ||
5892              element == EL_INVISIBLE_SAND_ACTIVE)
5893     {
5894       if (game.lenses_time_left == 0)
5895         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5896
5897       TEST_DrawLevelField(x, y);
5898
5899       /* re-crumble neighbour fields, if needed */
5900       if (element == EL_INVISIBLE_SAND)
5901         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5902     }
5903   }
5904 }
5905
5906 static void RedrawAllInvisibleElementsForMagnifier()
5907 {
5908   int x, y;
5909
5910   SCAN_PLAYFIELD(x, y)
5911   {
5912     int element = Feld[x][y];
5913
5914     if (element == EL_EMC_FAKE_GRASS &&
5915         game.magnify_time_left > 0)
5916     {
5917       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5918       TEST_DrawLevelField(x, y);
5919     }
5920     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5921              game.magnify_time_left == 0)
5922     {
5923       Feld[x][y] = EL_EMC_FAKE_GRASS;
5924       TEST_DrawLevelField(x, y);
5925     }
5926     else if (IS_GATE_GRAY(element) &&
5927              game.magnify_time_left > 0)
5928     {
5929       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5930                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5931                     IS_EM_GATE_GRAY(element) ?
5932                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5933                     IS_EMC_GATE_GRAY(element) ?
5934                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5935                     IS_DC_GATE_GRAY(element) ?
5936                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5937                     element);
5938       TEST_DrawLevelField(x, y);
5939     }
5940     else if (IS_GATE_GRAY_ACTIVE(element) &&
5941              game.magnify_time_left == 0)
5942     {
5943       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5944                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5945                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5946                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5947                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5948                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5949                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5950                     EL_DC_GATE_WHITE_GRAY :
5951                     element);
5952       TEST_DrawLevelField(x, y);
5953     }
5954   }
5955 }
5956
5957 static void ToggleLightSwitch(int x, int y)
5958 {
5959   int element = Feld[x][y];
5960
5961   game.light_time_left =
5962     (element == EL_LIGHT_SWITCH ?
5963      level.time_light * FRAMES_PER_SECOND : 0);
5964
5965   RedrawAllLightSwitchesAndInvisibleElements();
5966 }
5967
5968 static void ActivateTimegateSwitch(int x, int y)
5969 {
5970   int xx, yy;
5971
5972   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5973
5974   SCAN_PLAYFIELD(xx, yy)
5975   {
5976     int element = Feld[xx][yy];
5977
5978     if (element == EL_TIMEGATE_CLOSED ||
5979         element == EL_TIMEGATE_CLOSING)
5980     {
5981       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5982       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5983     }
5984
5985     /*
5986     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5987     {
5988       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5989       TEST_DrawLevelField(xx, yy);
5990     }
5991     */
5992
5993   }
5994
5995   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5996                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5997 }
5998
5999 void Impact(int x, int y)
6000 {
6001   boolean last_line = (y == lev_fieldy - 1);
6002   boolean object_hit = FALSE;
6003   boolean impact = (last_line || object_hit);
6004   int element = Feld[x][y];
6005   int smashed = EL_STEELWALL;
6006
6007   if (!last_line)       /* check if element below was hit */
6008   {
6009     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6010       return;
6011
6012     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6013                                          MovDir[x][y + 1] != MV_DOWN ||
6014                                          MovPos[x][y + 1] <= TILEY / 2));
6015
6016     /* do not smash moving elements that left the smashed field in time */
6017     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6018         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6019       object_hit = FALSE;
6020
6021 #if USE_QUICKSAND_IMPACT_BUGFIX
6022     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6023     {
6024       RemoveMovingField(x, y + 1);
6025       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6026       Feld[x][y + 2] = EL_ROCK;
6027       TEST_DrawLevelField(x, y + 2);
6028
6029       object_hit = TRUE;
6030     }
6031
6032     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6033     {
6034       RemoveMovingField(x, y + 1);
6035       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6036       Feld[x][y + 2] = EL_ROCK;
6037       TEST_DrawLevelField(x, y + 2);
6038
6039       object_hit = TRUE;
6040     }
6041 #endif
6042
6043     if (object_hit)
6044       smashed = MovingOrBlocked2Element(x, y + 1);
6045
6046     impact = (last_line || object_hit);
6047   }
6048
6049   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6050   {
6051     SplashAcid(x, y + 1);
6052     return;
6053   }
6054
6055   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6056   /* only reset graphic animation if graphic really changes after impact */
6057   if (impact &&
6058       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6059   {
6060     ResetGfxAnimation(x, y);
6061     TEST_DrawLevelField(x, y);
6062   }
6063
6064   if (impact && CAN_EXPLODE_IMPACT(element))
6065   {
6066     Bang(x, y);
6067     return;
6068   }
6069   else if (impact && element == EL_PEARL &&
6070            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6071   {
6072     ResetGfxAnimation(x, y);
6073
6074     Feld[x][y] = EL_PEARL_BREAKING;
6075     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6076     return;
6077   }
6078   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6079   {
6080     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6081
6082     return;
6083   }
6084
6085   if (impact && element == EL_AMOEBA_DROP)
6086   {
6087     if (object_hit && IS_PLAYER(x, y + 1))
6088       KillPlayerUnlessEnemyProtected(x, y + 1);
6089     else if (object_hit && smashed == EL_PENGUIN)
6090       Bang(x, y + 1);
6091     else
6092     {
6093       Feld[x][y] = EL_AMOEBA_GROWING;
6094       Store[x][y] = EL_AMOEBA_WET;
6095
6096       ResetRandomAnimationValue(x, y);
6097     }
6098     return;
6099   }
6100
6101   if (object_hit)               /* check which object was hit */
6102   {
6103     if ((CAN_PASS_MAGIC_WALL(element) && 
6104          (smashed == EL_MAGIC_WALL ||
6105           smashed == EL_BD_MAGIC_WALL)) ||
6106         (CAN_PASS_DC_MAGIC_WALL(element) &&
6107          smashed == EL_DC_MAGIC_WALL))
6108     {
6109       int xx, yy;
6110       int activated_magic_wall =
6111         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6112          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6113          EL_DC_MAGIC_WALL_ACTIVE);
6114
6115       /* activate magic wall / mill */
6116       SCAN_PLAYFIELD(xx, yy)
6117       {
6118         if (Feld[xx][yy] == smashed)
6119           Feld[xx][yy] = activated_magic_wall;
6120       }
6121
6122       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6123       game.magic_wall_active = TRUE;
6124
6125       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6126                             SND_MAGIC_WALL_ACTIVATING :
6127                             smashed == EL_BD_MAGIC_WALL ?
6128                             SND_BD_MAGIC_WALL_ACTIVATING :
6129                             SND_DC_MAGIC_WALL_ACTIVATING));
6130     }
6131
6132     if (IS_PLAYER(x, y + 1))
6133     {
6134       if (CAN_SMASH_PLAYER(element))
6135       {
6136         KillPlayerUnlessEnemyProtected(x, y + 1);
6137         return;
6138       }
6139     }
6140     else if (smashed == EL_PENGUIN)
6141     {
6142       if (CAN_SMASH_PLAYER(element))
6143       {
6144         Bang(x, y + 1);
6145         return;
6146       }
6147     }
6148     else if (element == EL_BD_DIAMOND)
6149     {
6150       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6151       {
6152         Bang(x, y + 1);
6153         return;
6154       }
6155     }
6156     else if (((element == EL_SP_INFOTRON ||
6157                element == EL_SP_ZONK) &&
6158               (smashed == EL_SP_SNIKSNAK ||
6159                smashed == EL_SP_ELECTRON ||
6160                smashed == EL_SP_DISK_ORANGE)) ||
6161              (element == EL_SP_INFOTRON &&
6162               smashed == EL_SP_DISK_YELLOW))
6163     {
6164       Bang(x, y + 1);
6165       return;
6166     }
6167     else if (CAN_SMASH_EVERYTHING(element))
6168     {
6169       if (IS_CLASSIC_ENEMY(smashed) ||
6170           CAN_EXPLODE_SMASHED(smashed))
6171       {
6172         Bang(x, y + 1);
6173         return;
6174       }
6175       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6176       {
6177         if (smashed == EL_LAMP ||
6178             smashed == EL_LAMP_ACTIVE)
6179         {
6180           Bang(x, y + 1);
6181           return;
6182         }
6183         else if (smashed == EL_NUT)
6184         {
6185           Feld[x][y + 1] = EL_NUT_BREAKING;
6186           PlayLevelSound(x, y, SND_NUT_BREAKING);
6187           RaiseScoreElement(EL_NUT);
6188           return;
6189         }
6190         else if (smashed == EL_PEARL)
6191         {
6192           ResetGfxAnimation(x, y);
6193
6194           Feld[x][y + 1] = EL_PEARL_BREAKING;
6195           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6196           return;
6197         }
6198         else if (smashed == EL_DIAMOND)
6199         {
6200           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6201           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6202           return;
6203         }
6204         else if (IS_BELT_SWITCH(smashed))
6205         {
6206           ToggleBeltSwitch(x, y + 1);
6207         }
6208         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6209                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6210                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6211                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6212         {
6213           ToggleSwitchgateSwitch(x, y + 1);
6214         }
6215         else if (smashed == EL_LIGHT_SWITCH ||
6216                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6217         {
6218           ToggleLightSwitch(x, y + 1);
6219         }
6220         else
6221         {
6222           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6223
6224           CheckElementChangeBySide(x, y + 1, smashed, element,
6225                                    CE_SWITCHED, CH_SIDE_TOP);
6226           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6227                                             CH_SIDE_TOP);
6228         }
6229       }
6230       else
6231       {
6232         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6233       }
6234     }
6235   }
6236
6237   /* play sound of magic wall / mill */
6238   if (!last_line &&
6239       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6240        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6241        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6242   {
6243     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6244       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6245     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6246       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6247     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6248       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6249
6250     return;
6251   }
6252
6253   /* play sound of object that hits the ground */
6254   if (last_line || object_hit)
6255     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6256 }
6257
6258 inline static void TurnRoundExt(int x, int y)
6259 {
6260   static struct
6261   {
6262     int dx, dy;
6263   } move_xy[] =
6264   {
6265     {  0,  0 },
6266     { -1,  0 },
6267     { +1,  0 },
6268     {  0,  0 },
6269     {  0, -1 },
6270     {  0,  0 }, { 0, 0 }, { 0, 0 },
6271     {  0, +1 }
6272   };
6273   static struct
6274   {
6275     int left, right, back;
6276   } turn[] =
6277   {
6278     { 0,        0,              0        },
6279     { MV_DOWN,  MV_UP,          MV_RIGHT },
6280     { MV_UP,    MV_DOWN,        MV_LEFT  },
6281     { 0,        0,              0        },
6282     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6283     { 0,        0,              0        },
6284     { 0,        0,              0        },
6285     { 0,        0,              0        },
6286     { MV_RIGHT, MV_LEFT,        MV_UP    }
6287   };
6288
6289   int element = Feld[x][y];
6290   int move_pattern = element_info[element].move_pattern;
6291
6292   int old_move_dir = MovDir[x][y];
6293   int left_dir  = turn[old_move_dir].left;
6294   int right_dir = turn[old_move_dir].right;
6295   int back_dir  = turn[old_move_dir].back;
6296
6297   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6298   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6299   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6300   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6301
6302   int left_x  = x + left_dx,  left_y  = y + left_dy;
6303   int right_x = x + right_dx, right_y = y + right_dy;
6304   int move_x  = x + move_dx,  move_y  = y + move_dy;
6305
6306   int xx, yy;
6307
6308   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6309   {
6310     TestIfBadThingTouchesOtherBadThing(x, y);
6311
6312     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6313       MovDir[x][y] = right_dir;
6314     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6315       MovDir[x][y] = left_dir;
6316
6317     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6318       MovDelay[x][y] = 9;
6319     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6320       MovDelay[x][y] = 1;
6321   }
6322   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6323   {
6324     TestIfBadThingTouchesOtherBadThing(x, y);
6325
6326     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6327       MovDir[x][y] = left_dir;
6328     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6329       MovDir[x][y] = right_dir;
6330
6331     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6332       MovDelay[x][y] = 9;
6333     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6334       MovDelay[x][y] = 1;
6335   }
6336   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6337   {
6338     TestIfBadThingTouchesOtherBadThing(x, y);
6339
6340     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6341       MovDir[x][y] = left_dir;
6342     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6343       MovDir[x][y] = right_dir;
6344
6345     if (MovDir[x][y] != old_move_dir)
6346       MovDelay[x][y] = 9;
6347   }
6348   else if (element == EL_YAMYAM)
6349   {
6350     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6351     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6352
6353     if (can_turn_left && can_turn_right)
6354       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6355     else if (can_turn_left)
6356       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6357     else if (can_turn_right)
6358       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6359     else
6360       MovDir[x][y] = back_dir;
6361
6362     MovDelay[x][y] = 16 + 16 * RND(3);
6363   }
6364   else if (element == EL_DARK_YAMYAM)
6365   {
6366     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6367                                                          left_x, left_y);
6368     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6369                                                          right_x, right_y);
6370
6371     if (can_turn_left && can_turn_right)
6372       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6373     else if (can_turn_left)
6374       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6375     else if (can_turn_right)
6376       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6377     else
6378       MovDir[x][y] = back_dir;
6379
6380     MovDelay[x][y] = 16 + 16 * RND(3);
6381   }
6382   else if (element == EL_PACMAN)
6383   {
6384     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6385     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6386
6387     if (can_turn_left && can_turn_right)
6388       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6389     else if (can_turn_left)
6390       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6391     else if (can_turn_right)
6392       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6393     else
6394       MovDir[x][y] = back_dir;
6395
6396     MovDelay[x][y] = 6 + RND(40);
6397   }
6398   else if (element == EL_PIG)
6399   {
6400     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6401     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6402     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6403     boolean should_turn_left, should_turn_right, should_move_on;
6404     int rnd_value = 24;
6405     int rnd = RND(rnd_value);
6406
6407     should_turn_left = (can_turn_left &&
6408                         (!can_move_on ||
6409                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6410                                                    y + back_dy + left_dy)));
6411     should_turn_right = (can_turn_right &&
6412                          (!can_move_on ||
6413                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6414                                                     y + back_dy + right_dy)));
6415     should_move_on = (can_move_on &&
6416                       (!can_turn_left ||
6417                        !can_turn_right ||
6418                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6419                                                  y + move_dy + left_dy) ||
6420                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6421                                                  y + move_dy + right_dy)));
6422
6423     if (should_turn_left || should_turn_right || should_move_on)
6424     {
6425       if (should_turn_left && should_turn_right && should_move_on)
6426         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6427                         rnd < 2 * rnd_value / 3 ? right_dir :
6428                         old_move_dir);
6429       else if (should_turn_left && should_turn_right)
6430         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6431       else if (should_turn_left && should_move_on)
6432         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6433       else if (should_turn_right && should_move_on)
6434         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6435       else if (should_turn_left)
6436         MovDir[x][y] = left_dir;
6437       else if (should_turn_right)
6438         MovDir[x][y] = right_dir;
6439       else if (should_move_on)
6440         MovDir[x][y] = old_move_dir;
6441     }
6442     else if (can_move_on && rnd > rnd_value / 8)
6443       MovDir[x][y] = old_move_dir;
6444     else if (can_turn_left && can_turn_right)
6445       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6446     else if (can_turn_left && rnd > rnd_value / 8)
6447       MovDir[x][y] = left_dir;
6448     else if (can_turn_right && rnd > rnd_value/8)
6449       MovDir[x][y] = right_dir;
6450     else
6451       MovDir[x][y] = back_dir;
6452
6453     xx = x + move_xy[MovDir[x][y]].dx;
6454     yy = y + move_xy[MovDir[x][y]].dy;
6455
6456     if (!IN_LEV_FIELD(xx, yy) ||
6457         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6458       MovDir[x][y] = old_move_dir;
6459
6460     MovDelay[x][y] = 0;
6461   }
6462   else if (element == EL_DRAGON)
6463   {
6464     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6465     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6466     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6467     int rnd_value = 24;
6468     int rnd = RND(rnd_value);
6469
6470     if (can_move_on && rnd > rnd_value / 8)
6471       MovDir[x][y] = old_move_dir;
6472     else if (can_turn_left && can_turn_right)
6473       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6474     else if (can_turn_left && rnd > rnd_value / 8)
6475       MovDir[x][y] = left_dir;
6476     else if (can_turn_right && rnd > rnd_value / 8)
6477       MovDir[x][y] = right_dir;
6478     else
6479       MovDir[x][y] = back_dir;
6480
6481     xx = x + move_xy[MovDir[x][y]].dx;
6482     yy = y + move_xy[MovDir[x][y]].dy;
6483
6484     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6485       MovDir[x][y] = old_move_dir;
6486
6487     MovDelay[x][y] = 0;
6488   }
6489   else if (element == EL_MOLE)
6490   {
6491     boolean can_move_on =
6492       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6493                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6494                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6495     if (!can_move_on)
6496     {
6497       boolean can_turn_left =
6498         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6499                               IS_AMOEBOID(Feld[left_x][left_y])));
6500
6501       boolean can_turn_right =
6502         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6503                               IS_AMOEBOID(Feld[right_x][right_y])));
6504
6505       if (can_turn_left && can_turn_right)
6506         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6507       else if (can_turn_left)
6508         MovDir[x][y] = left_dir;
6509       else
6510         MovDir[x][y] = right_dir;
6511     }
6512
6513     if (MovDir[x][y] != old_move_dir)
6514       MovDelay[x][y] = 9;
6515   }
6516   else if (element == EL_BALLOON)
6517   {
6518     MovDir[x][y] = game.wind_direction;
6519     MovDelay[x][y] = 0;
6520   }
6521   else if (element == EL_SPRING)
6522   {
6523     if (MovDir[x][y] & MV_HORIZONTAL)
6524     {
6525       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6526           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6527       {
6528         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6529         ResetGfxAnimation(move_x, move_y);
6530         TEST_DrawLevelField(move_x, move_y);
6531
6532         MovDir[x][y] = back_dir;
6533       }
6534       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6535                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6536         MovDir[x][y] = MV_NONE;
6537     }
6538
6539     MovDelay[x][y] = 0;
6540   }
6541   else if (element == EL_ROBOT ||
6542            element == EL_SATELLITE ||
6543            element == EL_PENGUIN ||
6544            element == EL_EMC_ANDROID)
6545   {
6546     int attr_x = -1, attr_y = -1;
6547
6548     if (AllPlayersGone)
6549     {
6550       attr_x = ExitX;
6551       attr_y = ExitY;
6552     }
6553     else
6554     {
6555       int i;
6556
6557       for (i = 0; i < MAX_PLAYERS; i++)
6558       {
6559         struct PlayerInfo *player = &stored_player[i];
6560         int jx = player->jx, jy = player->jy;
6561
6562         if (!player->active)
6563           continue;
6564
6565         if (attr_x == -1 ||
6566             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6567         {
6568           attr_x = jx;
6569           attr_y = jy;
6570         }
6571       }
6572     }
6573
6574     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6575         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6576          game.engine_version < VERSION_IDENT(3,1,0,0)))
6577     {
6578       attr_x = ZX;
6579       attr_y = ZY;
6580     }
6581
6582     if (element == EL_PENGUIN)
6583     {
6584       int i;
6585       static int xy[4][2] =
6586       {
6587         { 0, -1 },
6588         { -1, 0 },
6589         { +1, 0 },
6590         { 0, +1 }
6591       };
6592
6593       for (i = 0; i < NUM_DIRECTIONS; i++)
6594       {
6595         int ex = x + xy[i][0];
6596         int ey = y + xy[i][1];
6597
6598         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6599                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6600                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6601                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6602         {
6603           attr_x = ex;
6604           attr_y = ey;
6605           break;
6606         }
6607       }
6608     }
6609
6610     MovDir[x][y] = MV_NONE;
6611     if (attr_x < x)
6612       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6613     else if (attr_x > x)
6614       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6615     if (attr_y < y)
6616       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6617     else if (attr_y > y)
6618       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6619
6620     if (element == EL_ROBOT)
6621     {
6622       int newx, newy;
6623
6624       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6625         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6626       Moving2Blocked(x, y, &newx, &newy);
6627
6628       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6629         MovDelay[x][y] = 8 + 8 * !RND(3);
6630       else
6631         MovDelay[x][y] = 16;
6632     }
6633     else if (element == EL_PENGUIN)
6634     {
6635       int newx, newy;
6636
6637       MovDelay[x][y] = 1;
6638
6639       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6640       {
6641         boolean first_horiz = RND(2);
6642         int new_move_dir = MovDir[x][y];
6643
6644         MovDir[x][y] =
6645           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6646         Moving2Blocked(x, y, &newx, &newy);
6647
6648         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6649           return;
6650
6651         MovDir[x][y] =
6652           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6653         Moving2Blocked(x, y, &newx, &newy);
6654
6655         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6656           return;
6657
6658         MovDir[x][y] = old_move_dir;
6659         return;
6660       }
6661     }
6662     else if (element == EL_SATELLITE)
6663     {
6664       int newx, newy;
6665
6666       MovDelay[x][y] = 1;
6667
6668       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6669       {
6670         boolean first_horiz = RND(2);
6671         int new_move_dir = MovDir[x][y];
6672
6673         MovDir[x][y] =
6674           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6675         Moving2Blocked(x, y, &newx, &newy);
6676
6677         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6678           return;
6679
6680         MovDir[x][y] =
6681           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6682         Moving2Blocked(x, y, &newx, &newy);
6683
6684         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6685           return;
6686
6687         MovDir[x][y] = old_move_dir;
6688         return;
6689       }
6690     }
6691     else if (element == EL_EMC_ANDROID)
6692     {
6693       static int check_pos[16] =
6694       {
6695         -1,             /*  0 => (invalid)          */
6696         7,              /*  1 => MV_LEFT            */
6697         3,              /*  2 => MV_RIGHT           */
6698         -1,             /*  3 => (invalid)          */
6699         1,              /*  4 =>            MV_UP   */
6700         0,              /*  5 => MV_LEFT  | MV_UP   */
6701         2,              /*  6 => MV_RIGHT | MV_UP   */
6702         -1,             /*  7 => (invalid)          */
6703         5,              /*  8 =>            MV_DOWN */
6704         6,              /*  9 => MV_LEFT  | MV_DOWN */
6705         4,              /* 10 => MV_RIGHT | MV_DOWN */
6706         -1,             /* 11 => (invalid)          */
6707         -1,             /* 12 => (invalid)          */
6708         -1,             /* 13 => (invalid)          */
6709         -1,             /* 14 => (invalid)          */
6710         -1,             /* 15 => (invalid)          */
6711       };
6712       static struct
6713       {
6714         int dx, dy;
6715         int dir;
6716       } check_xy[8] =
6717       {
6718         { -1, -1,       MV_LEFT  | MV_UP   },
6719         {  0, -1,                  MV_UP   },
6720         { +1, -1,       MV_RIGHT | MV_UP   },
6721         { +1,  0,       MV_RIGHT           },
6722         { +1, +1,       MV_RIGHT | MV_DOWN },
6723         {  0, +1,                  MV_DOWN },
6724         { -1, +1,       MV_LEFT  | MV_DOWN },
6725         { -1,  0,       MV_LEFT            },
6726       };
6727       int start_pos, check_order;
6728       boolean can_clone = FALSE;
6729       int i;
6730
6731       /* check if there is any free field around current position */
6732       for (i = 0; i < 8; i++)
6733       {
6734         int newx = x + check_xy[i].dx;
6735         int newy = y + check_xy[i].dy;
6736
6737         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6738         {
6739           can_clone = TRUE;
6740
6741           break;
6742         }
6743       }
6744
6745       if (can_clone)            /* randomly find an element to clone */
6746       {
6747         can_clone = FALSE;
6748
6749         start_pos = check_pos[RND(8)];
6750         check_order = (RND(2) ? -1 : +1);
6751
6752         for (i = 0; i < 8; i++)
6753         {
6754           int pos_raw = start_pos + i * check_order;
6755           int pos = (pos_raw + 8) % 8;
6756           int newx = x + check_xy[pos].dx;
6757           int newy = y + check_xy[pos].dy;
6758
6759           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6760           {
6761             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6762             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6763
6764             Store[x][y] = Feld[newx][newy];
6765
6766             can_clone = TRUE;
6767
6768             break;
6769           }
6770         }
6771       }
6772
6773       if (can_clone)            /* randomly find a direction to move */
6774       {
6775         can_clone = FALSE;
6776
6777         start_pos = check_pos[RND(8)];
6778         check_order = (RND(2) ? -1 : +1);
6779
6780         for (i = 0; i < 8; i++)
6781         {
6782           int pos_raw = start_pos + i * check_order;
6783           int pos = (pos_raw + 8) % 8;
6784           int newx = x + check_xy[pos].dx;
6785           int newy = y + check_xy[pos].dy;
6786           int new_move_dir = check_xy[pos].dir;
6787
6788           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6789           {
6790             MovDir[x][y] = new_move_dir;
6791             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6792
6793             can_clone = TRUE;
6794
6795             break;
6796           }
6797         }
6798       }
6799
6800       if (can_clone)            /* cloning and moving successful */
6801         return;
6802
6803       /* cannot clone -- try to move towards player */
6804
6805       start_pos = check_pos[MovDir[x][y] & 0x0f];
6806       check_order = (RND(2) ? -1 : +1);
6807
6808       for (i = 0; i < 3; i++)
6809       {
6810         /* first check start_pos, then previous/next or (next/previous) pos */
6811         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6812         int pos = (pos_raw + 8) % 8;
6813         int newx = x + check_xy[pos].dx;
6814         int newy = y + check_xy[pos].dy;
6815         int new_move_dir = check_xy[pos].dir;
6816
6817         if (IS_PLAYER(newx, newy))
6818           break;
6819
6820         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6821         {
6822           MovDir[x][y] = new_move_dir;
6823           MovDelay[x][y] = level.android_move_time * 8 + 1;
6824
6825           break;
6826         }
6827       }
6828     }
6829   }
6830   else if (move_pattern == MV_TURNING_LEFT ||
6831            move_pattern == MV_TURNING_RIGHT ||
6832            move_pattern == MV_TURNING_LEFT_RIGHT ||
6833            move_pattern == MV_TURNING_RIGHT_LEFT ||
6834            move_pattern == MV_TURNING_RANDOM ||
6835            move_pattern == MV_ALL_DIRECTIONS)
6836   {
6837     boolean can_turn_left =
6838       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6839     boolean can_turn_right =
6840       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6841
6842     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6843       return;
6844
6845     if (move_pattern == MV_TURNING_LEFT)
6846       MovDir[x][y] = left_dir;
6847     else if (move_pattern == MV_TURNING_RIGHT)
6848       MovDir[x][y] = right_dir;
6849     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6850       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6851     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6852       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6853     else if (move_pattern == MV_TURNING_RANDOM)
6854       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6855                       can_turn_right && !can_turn_left ? right_dir :
6856                       RND(2) ? left_dir : right_dir);
6857     else if (can_turn_left && can_turn_right)
6858       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6859     else if (can_turn_left)
6860       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6861     else if (can_turn_right)
6862       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6863     else
6864       MovDir[x][y] = back_dir;
6865
6866     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6867   }
6868   else if (move_pattern == MV_HORIZONTAL ||
6869            move_pattern == MV_VERTICAL)
6870   {
6871     if (move_pattern & old_move_dir)
6872       MovDir[x][y] = back_dir;
6873     else if (move_pattern == MV_HORIZONTAL)
6874       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6875     else if (move_pattern == MV_VERTICAL)
6876       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6877
6878     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6879   }
6880   else if (move_pattern & MV_ANY_DIRECTION)
6881   {
6882     MovDir[x][y] = move_pattern;
6883     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6884   }
6885   else if (move_pattern & MV_WIND_DIRECTION)
6886   {
6887     MovDir[x][y] = game.wind_direction;
6888     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6889   }
6890   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6891   {
6892     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6893       MovDir[x][y] = left_dir;
6894     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6895       MovDir[x][y] = right_dir;
6896
6897     if (MovDir[x][y] != old_move_dir)
6898       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6899   }
6900   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6901   {
6902     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6903       MovDir[x][y] = right_dir;
6904     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6905       MovDir[x][y] = left_dir;
6906
6907     if (MovDir[x][y] != old_move_dir)
6908       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6909   }
6910   else if (move_pattern == MV_TOWARDS_PLAYER ||
6911            move_pattern == MV_AWAY_FROM_PLAYER)
6912   {
6913     int attr_x = -1, attr_y = -1;
6914     int newx, newy;
6915     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6916
6917     if (AllPlayersGone)
6918     {
6919       attr_x = ExitX;
6920       attr_y = ExitY;
6921     }
6922     else
6923     {
6924       int i;
6925
6926       for (i = 0; i < MAX_PLAYERS; i++)
6927       {
6928         struct PlayerInfo *player = &stored_player[i];
6929         int jx = player->jx, jy = player->jy;
6930
6931         if (!player->active)
6932           continue;
6933
6934         if (attr_x == -1 ||
6935             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6936         {
6937           attr_x = jx;
6938           attr_y = jy;
6939         }
6940       }
6941     }
6942
6943     MovDir[x][y] = MV_NONE;
6944     if (attr_x < x)
6945       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6946     else if (attr_x > x)
6947       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6948     if (attr_y < y)
6949       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6950     else if (attr_y > y)
6951       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6952
6953     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6954
6955     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6956     {
6957       boolean first_horiz = RND(2);
6958       int new_move_dir = MovDir[x][y];
6959
6960       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6961       {
6962         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6963         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6964
6965         return;
6966       }
6967
6968       MovDir[x][y] =
6969         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6970       Moving2Blocked(x, y, &newx, &newy);
6971
6972       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6973         return;
6974
6975       MovDir[x][y] =
6976         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6977       Moving2Blocked(x, y, &newx, &newy);
6978
6979       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6980         return;
6981
6982       MovDir[x][y] = old_move_dir;
6983     }
6984   }
6985   else if (move_pattern == MV_WHEN_PUSHED ||
6986            move_pattern == MV_WHEN_DROPPED)
6987   {
6988     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6989       MovDir[x][y] = MV_NONE;
6990
6991     MovDelay[x][y] = 0;
6992   }
6993   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6994   {
6995     static int test_xy[7][2] =
6996     {
6997       { 0, -1 },
6998       { -1, 0 },
6999       { +1, 0 },
7000       { 0, +1 },
7001       { 0, -1 },
7002       { -1, 0 },
7003       { +1, 0 },
7004     };
7005     static int test_dir[7] =
7006     {
7007       MV_UP,
7008       MV_LEFT,
7009       MV_RIGHT,
7010       MV_DOWN,
7011       MV_UP,
7012       MV_LEFT,
7013       MV_RIGHT,
7014     };
7015     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7016     int move_preference = -1000000;     /* start with very low preference */
7017     int new_move_dir = MV_NONE;
7018     int start_test = RND(4);
7019     int i;
7020
7021     for (i = 0; i < NUM_DIRECTIONS; i++)
7022     {
7023       int move_dir = test_dir[start_test + i];
7024       int move_dir_preference;
7025
7026       xx = x + test_xy[start_test + i][0];
7027       yy = y + test_xy[start_test + i][1];
7028
7029       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7030           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7031       {
7032         new_move_dir = move_dir;
7033
7034         break;
7035       }
7036
7037       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7038         continue;
7039
7040       move_dir_preference = -1 * RunnerVisit[xx][yy];
7041       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7042         move_dir_preference = PlayerVisit[xx][yy];
7043
7044       if (move_dir_preference > move_preference)
7045       {
7046         /* prefer field that has not been visited for the longest time */
7047         move_preference = move_dir_preference;
7048         new_move_dir = move_dir;
7049       }
7050       else if (move_dir_preference == move_preference &&
7051                move_dir == old_move_dir)
7052       {
7053         /* prefer last direction when all directions are preferred equally */
7054         move_preference = move_dir_preference;
7055         new_move_dir = move_dir;
7056       }
7057     }
7058
7059     MovDir[x][y] = new_move_dir;
7060     if (old_move_dir != new_move_dir)
7061       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7062   }
7063 }
7064
7065 static void TurnRound(int x, int y)
7066 {
7067   int direction = MovDir[x][y];
7068
7069   TurnRoundExt(x, y);
7070
7071   GfxDir[x][y] = MovDir[x][y];
7072
7073   if (direction != MovDir[x][y])
7074     GfxFrame[x][y] = 0;
7075
7076   if (MovDelay[x][y])
7077     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7078
7079   ResetGfxFrame(x, y, FALSE);
7080 }
7081
7082 static boolean JustBeingPushed(int x, int y)
7083 {
7084   int i;
7085
7086   for (i = 0; i < MAX_PLAYERS; i++)
7087   {
7088     struct PlayerInfo *player = &stored_player[i];
7089
7090     if (player->active && player->is_pushing && player->MovPos)
7091     {
7092       int next_jx = player->jx + (player->jx - player->last_jx);
7093       int next_jy = player->jy + (player->jy - player->last_jy);
7094
7095       if (x == next_jx && y == next_jy)
7096         return TRUE;
7097     }
7098   }
7099
7100   return FALSE;
7101 }
7102
7103 void StartMoving(int x, int y)
7104 {
7105   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7106   int element = Feld[x][y];
7107
7108   if (Stop[x][y])
7109     return;
7110
7111   if (MovDelay[x][y] == 0)
7112     GfxAction[x][y] = ACTION_DEFAULT;
7113
7114   if (CAN_FALL(element) && y < lev_fieldy - 1)
7115   {
7116     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7117         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7118       if (JustBeingPushed(x, y))
7119         return;
7120
7121     if (element == EL_QUICKSAND_FULL)
7122     {
7123       if (IS_FREE(x, y + 1))
7124       {
7125         InitMovingField(x, y, MV_DOWN);
7126         started_moving = TRUE;
7127
7128         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7129 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7130         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7131           Store[x][y] = EL_ROCK;
7132 #else
7133         Store[x][y] = EL_ROCK;
7134 #endif
7135
7136         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7137       }
7138       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7139       {
7140         if (!MovDelay[x][y])
7141         {
7142           MovDelay[x][y] = TILEY + 1;
7143
7144           ResetGfxAnimation(x, y);
7145           ResetGfxAnimation(x, y + 1);
7146         }
7147
7148         if (MovDelay[x][y])
7149         {
7150           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7151           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7152
7153           MovDelay[x][y]--;
7154           if (MovDelay[x][y])
7155             return;
7156         }
7157
7158         Feld[x][y] = EL_QUICKSAND_EMPTY;
7159         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7160         Store[x][y + 1] = Store[x][y];
7161         Store[x][y] = 0;
7162
7163         PlayLevelSoundAction(x, y, ACTION_FILLING);
7164       }
7165       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7166       {
7167         if (!MovDelay[x][y])
7168         {
7169           MovDelay[x][y] = TILEY + 1;
7170
7171           ResetGfxAnimation(x, y);
7172           ResetGfxAnimation(x, y + 1);
7173         }
7174
7175         if (MovDelay[x][y])
7176         {
7177           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7178           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7179
7180           MovDelay[x][y]--;
7181           if (MovDelay[x][y])
7182             return;
7183         }
7184
7185         Feld[x][y] = EL_QUICKSAND_EMPTY;
7186         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7187         Store[x][y + 1] = Store[x][y];
7188         Store[x][y] = 0;
7189
7190         PlayLevelSoundAction(x, y, ACTION_FILLING);
7191       }
7192     }
7193     else if (element == EL_QUICKSAND_FAST_FULL)
7194     {
7195       if (IS_FREE(x, y + 1))
7196       {
7197         InitMovingField(x, y, MV_DOWN);
7198         started_moving = TRUE;
7199
7200         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7201 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7202         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7203           Store[x][y] = EL_ROCK;
7204 #else
7205         Store[x][y] = EL_ROCK;
7206 #endif
7207
7208         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7209       }
7210       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7211       {
7212         if (!MovDelay[x][y])
7213         {
7214           MovDelay[x][y] = TILEY + 1;
7215
7216           ResetGfxAnimation(x, y);
7217           ResetGfxAnimation(x, y + 1);
7218         }
7219
7220         if (MovDelay[x][y])
7221         {
7222           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7223           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7224
7225           MovDelay[x][y]--;
7226           if (MovDelay[x][y])
7227             return;
7228         }
7229
7230         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7231         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7232         Store[x][y + 1] = Store[x][y];
7233         Store[x][y] = 0;
7234
7235         PlayLevelSoundAction(x, y, ACTION_FILLING);
7236       }
7237       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7238       {
7239         if (!MovDelay[x][y])
7240         {
7241           MovDelay[x][y] = TILEY + 1;
7242
7243           ResetGfxAnimation(x, y);
7244           ResetGfxAnimation(x, y + 1);
7245         }
7246
7247         if (MovDelay[x][y])
7248         {
7249           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7250           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7251
7252           MovDelay[x][y]--;
7253           if (MovDelay[x][y])
7254             return;
7255         }
7256
7257         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7258         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7259         Store[x][y + 1] = Store[x][y];
7260         Store[x][y] = 0;
7261
7262         PlayLevelSoundAction(x, y, ACTION_FILLING);
7263       }
7264     }
7265     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7266              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7267     {
7268       InitMovingField(x, y, MV_DOWN);
7269       started_moving = TRUE;
7270
7271       Feld[x][y] = EL_QUICKSAND_FILLING;
7272       Store[x][y] = element;
7273
7274       PlayLevelSoundAction(x, y, ACTION_FILLING);
7275     }
7276     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7277              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7278     {
7279       InitMovingField(x, y, MV_DOWN);
7280       started_moving = TRUE;
7281
7282       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7283       Store[x][y] = element;
7284
7285       PlayLevelSoundAction(x, y, ACTION_FILLING);
7286     }
7287     else if (element == EL_MAGIC_WALL_FULL)
7288     {
7289       if (IS_FREE(x, y + 1))
7290       {
7291         InitMovingField(x, y, MV_DOWN);
7292         started_moving = TRUE;
7293
7294         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7295         Store[x][y] = EL_CHANGED(Store[x][y]);
7296       }
7297       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7298       {
7299         if (!MovDelay[x][y])
7300           MovDelay[x][y] = TILEY / 4 + 1;
7301
7302         if (MovDelay[x][y])
7303         {
7304           MovDelay[x][y]--;
7305           if (MovDelay[x][y])
7306             return;
7307         }
7308
7309         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7310         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7311         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7312         Store[x][y] = 0;
7313       }
7314     }
7315     else if (element == EL_BD_MAGIC_WALL_FULL)
7316     {
7317       if (IS_FREE(x, y + 1))
7318       {
7319         InitMovingField(x, y, MV_DOWN);
7320         started_moving = TRUE;
7321
7322         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7323         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7324       }
7325       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7326       {
7327         if (!MovDelay[x][y])
7328           MovDelay[x][y] = TILEY / 4 + 1;
7329
7330         if (MovDelay[x][y])
7331         {
7332           MovDelay[x][y]--;
7333           if (MovDelay[x][y])
7334             return;
7335         }
7336
7337         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7338         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7339         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7340         Store[x][y] = 0;
7341       }
7342     }
7343     else if (element == EL_DC_MAGIC_WALL_FULL)
7344     {
7345       if (IS_FREE(x, y + 1))
7346       {
7347         InitMovingField(x, y, MV_DOWN);
7348         started_moving = TRUE;
7349
7350         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7351         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7352       }
7353       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7354       {
7355         if (!MovDelay[x][y])
7356           MovDelay[x][y] = TILEY / 4 + 1;
7357
7358         if (MovDelay[x][y])
7359         {
7360           MovDelay[x][y]--;
7361           if (MovDelay[x][y])
7362             return;
7363         }
7364
7365         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7366         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7367         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7368         Store[x][y] = 0;
7369       }
7370     }
7371     else if ((CAN_PASS_MAGIC_WALL(element) &&
7372               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7373                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7374              (CAN_PASS_DC_MAGIC_WALL(element) &&
7375               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7376
7377     {
7378       InitMovingField(x, y, MV_DOWN);
7379       started_moving = TRUE;
7380
7381       Feld[x][y] =
7382         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7383          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7384          EL_DC_MAGIC_WALL_FILLING);
7385       Store[x][y] = element;
7386     }
7387     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7388     {
7389       SplashAcid(x, y + 1);
7390
7391       InitMovingField(x, y, MV_DOWN);
7392       started_moving = TRUE;
7393
7394       Store[x][y] = EL_ACID;
7395     }
7396     else if (
7397              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7398               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7399              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7400               CAN_FALL(element) && WasJustFalling[x][y] &&
7401               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7402
7403              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7404               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7405               (Feld[x][y + 1] == EL_BLOCKED)))
7406     {
7407       /* this is needed for a special case not covered by calling "Impact()"
7408          from "ContinueMoving()": if an element moves to a tile directly below
7409          another element which was just falling on that tile (which was empty
7410          in the previous frame), the falling element above would just stop
7411          instead of smashing the element below (in previous version, the above
7412          element was just checked for "moving" instead of "falling", resulting
7413          in incorrect smashes caused by horizontal movement of the above
7414          element; also, the case of the player being the element to smash was
7415          simply not covered here... :-/ ) */
7416
7417       CheckCollision[x][y] = 0;
7418       CheckImpact[x][y] = 0;
7419
7420       Impact(x, y);
7421     }
7422     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7423     {
7424       if (MovDir[x][y] == MV_NONE)
7425       {
7426         InitMovingField(x, y, MV_DOWN);
7427         started_moving = TRUE;
7428       }
7429     }
7430     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7431     {
7432       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7433         MovDir[x][y] = MV_DOWN;
7434
7435       InitMovingField(x, y, MV_DOWN);
7436       started_moving = TRUE;
7437     }
7438     else if (element == EL_AMOEBA_DROP)
7439     {
7440       Feld[x][y] = EL_AMOEBA_GROWING;
7441       Store[x][y] = EL_AMOEBA_WET;
7442     }
7443     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7444               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7445              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7446              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7447     {
7448       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7449                                 (IS_FREE(x - 1, y + 1) ||
7450                                  Feld[x - 1][y + 1] == EL_ACID));
7451       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7452                                 (IS_FREE(x + 1, y + 1) ||
7453                                  Feld[x + 1][y + 1] == EL_ACID));
7454       boolean can_fall_any  = (can_fall_left || can_fall_right);
7455       boolean can_fall_both = (can_fall_left && can_fall_right);
7456       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7457
7458       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7459       {
7460         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7461           can_fall_right = FALSE;
7462         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7463           can_fall_left = FALSE;
7464         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7465           can_fall_right = FALSE;
7466         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7467           can_fall_left = FALSE;
7468
7469         can_fall_any  = (can_fall_left || can_fall_right);
7470         can_fall_both = FALSE;
7471       }
7472
7473       if (can_fall_both)
7474       {
7475         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7476           can_fall_right = FALSE;       /* slip down on left side */
7477         else
7478           can_fall_left = !(can_fall_right = RND(2));
7479
7480         can_fall_both = FALSE;
7481       }
7482
7483       if (can_fall_any)
7484       {
7485         /* if not determined otherwise, prefer left side for slipping down */
7486         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7487         started_moving = TRUE;
7488       }
7489     }
7490     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7491     {
7492       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7493       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7494       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7495       int belt_dir = game.belt_dir[belt_nr];
7496
7497       if ((belt_dir == MV_LEFT  && left_is_free) ||
7498           (belt_dir == MV_RIGHT && right_is_free))
7499       {
7500         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7501
7502         InitMovingField(x, y, belt_dir);
7503         started_moving = TRUE;
7504
7505         Pushed[x][y] = TRUE;
7506         Pushed[nextx][y] = TRUE;
7507
7508         GfxAction[x][y] = ACTION_DEFAULT;
7509       }
7510       else
7511       {
7512         MovDir[x][y] = 0;       /* if element was moving, stop it */
7513       }
7514     }
7515   }
7516
7517   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7518   if (CAN_MOVE(element) && !started_moving)
7519   {
7520     int move_pattern = element_info[element].move_pattern;
7521     int newx, newy;
7522
7523     Moving2Blocked(x, y, &newx, &newy);
7524
7525     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7526       return;
7527
7528     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7529         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7530     {
7531       WasJustMoving[x][y] = 0;
7532       CheckCollision[x][y] = 0;
7533
7534       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7535
7536       if (Feld[x][y] != element)        /* element has changed */
7537         return;
7538     }
7539
7540     if (!MovDelay[x][y])        /* start new movement phase */
7541     {
7542       /* all objects that can change their move direction after each step
7543          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7544
7545       if (element != EL_YAMYAM &&
7546           element != EL_DARK_YAMYAM &&
7547           element != EL_PACMAN &&
7548           !(move_pattern & MV_ANY_DIRECTION) &&
7549           move_pattern != MV_TURNING_LEFT &&
7550           move_pattern != MV_TURNING_RIGHT &&
7551           move_pattern != MV_TURNING_LEFT_RIGHT &&
7552           move_pattern != MV_TURNING_RIGHT_LEFT &&
7553           move_pattern != MV_TURNING_RANDOM)
7554       {
7555         TurnRound(x, y);
7556
7557         if (MovDelay[x][y] && (element == EL_BUG ||
7558                                element == EL_SPACESHIP ||
7559                                element == EL_SP_SNIKSNAK ||
7560                                element == EL_SP_ELECTRON ||
7561                                element == EL_MOLE))
7562           TEST_DrawLevelField(x, y);
7563       }
7564     }
7565
7566     if (MovDelay[x][y])         /* wait some time before next movement */
7567     {
7568       MovDelay[x][y]--;
7569
7570       if (element == EL_ROBOT ||
7571           element == EL_YAMYAM ||
7572           element == EL_DARK_YAMYAM)
7573       {
7574         DrawLevelElementAnimationIfNeeded(x, y, element);
7575         PlayLevelSoundAction(x, y, ACTION_WAITING);
7576       }
7577       else if (element == EL_SP_ELECTRON)
7578         DrawLevelElementAnimationIfNeeded(x, y, element);
7579       else if (element == EL_DRAGON)
7580       {
7581         int i;
7582         int dir = MovDir[x][y];
7583         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7584         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7585         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7586                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7587                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7588                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7589         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7590
7591         GfxAction[x][y] = ACTION_ATTACKING;
7592
7593         if (IS_PLAYER(x, y))
7594           DrawPlayerField(x, y);
7595         else
7596           TEST_DrawLevelField(x, y);
7597
7598         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7599
7600         for (i = 1; i <= 3; i++)
7601         {
7602           int xx = x + i * dx;
7603           int yy = y + i * dy;
7604           int sx = SCREENX(xx);
7605           int sy = SCREENY(yy);
7606           int flame_graphic = graphic + (i - 1);
7607
7608           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7609             break;
7610
7611           if (MovDelay[x][y])
7612           {
7613             int flamed = MovingOrBlocked2Element(xx, yy);
7614
7615             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7616               Bang(xx, yy);
7617             else
7618               RemoveMovingField(xx, yy);
7619
7620             ChangeDelay[xx][yy] = 0;
7621
7622             Feld[xx][yy] = EL_FLAMES;
7623
7624             if (IN_SCR_FIELD(sx, sy))
7625             {
7626               TEST_DrawLevelFieldCrumbled(xx, yy);
7627               DrawGraphic(sx, sy, flame_graphic, frame);
7628             }
7629           }
7630           else
7631           {
7632             if (Feld[xx][yy] == EL_FLAMES)
7633               Feld[xx][yy] = EL_EMPTY;
7634             TEST_DrawLevelField(xx, yy);
7635           }
7636         }
7637       }
7638
7639       if (MovDelay[x][y])       /* element still has to wait some time */
7640       {
7641         PlayLevelSoundAction(x, y, ACTION_WAITING);
7642
7643         return;
7644       }
7645     }
7646
7647     /* now make next step */
7648
7649     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7650
7651     if (DONT_COLLIDE_WITH(element) &&
7652         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7653         !PLAYER_ENEMY_PROTECTED(newx, newy))
7654     {
7655       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7656
7657       return;
7658     }
7659
7660     else if (CAN_MOVE_INTO_ACID(element) &&
7661              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7662              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7663              (MovDir[x][y] == MV_DOWN ||
7664               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7665     {
7666       SplashAcid(newx, newy);
7667       Store[x][y] = EL_ACID;
7668     }
7669     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7670     {
7671       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7672           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7673           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7674           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7675       {
7676         RemoveField(x, y);
7677         TEST_DrawLevelField(x, y);
7678
7679         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7680         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7681           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7682
7683         local_player->friends_still_needed--;
7684         if (!local_player->friends_still_needed &&
7685             !local_player->GameOver && AllPlayersGone)
7686           PlayerWins(local_player);
7687
7688         return;
7689       }
7690       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7691       {
7692         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7693           TEST_DrawLevelField(newx, newy);
7694         else
7695           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7696       }
7697       else if (!IS_FREE(newx, newy))
7698       {
7699         GfxAction[x][y] = ACTION_WAITING;
7700
7701         if (IS_PLAYER(x, y))
7702           DrawPlayerField(x, y);
7703         else
7704           TEST_DrawLevelField(x, y);
7705
7706         return;
7707       }
7708     }
7709     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7710     {
7711       if (IS_FOOD_PIG(Feld[newx][newy]))
7712       {
7713         if (IS_MOVING(newx, newy))
7714           RemoveMovingField(newx, newy);
7715         else
7716         {
7717           Feld[newx][newy] = EL_EMPTY;
7718           TEST_DrawLevelField(newx, newy);
7719         }
7720
7721         PlayLevelSound(x, y, SND_PIG_DIGGING);
7722       }
7723       else if (!IS_FREE(newx, newy))
7724       {
7725         if (IS_PLAYER(x, y))
7726           DrawPlayerField(x, y);
7727         else
7728           TEST_DrawLevelField(x, y);
7729
7730         return;
7731       }
7732     }
7733     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7734     {
7735       if (Store[x][y] != EL_EMPTY)
7736       {
7737         boolean can_clone = FALSE;
7738         int xx, yy;
7739
7740         /* check if element to clone is still there */
7741         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7742         {
7743           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7744           {
7745             can_clone = TRUE;
7746
7747             break;
7748           }
7749         }
7750
7751         /* cannot clone or target field not free anymore -- do not clone */
7752         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7753           Store[x][y] = EL_EMPTY;
7754       }
7755
7756       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7757       {
7758         if (IS_MV_DIAGONAL(MovDir[x][y]))
7759         {
7760           int diagonal_move_dir = MovDir[x][y];
7761           int stored = Store[x][y];
7762           int change_delay = 8;
7763           int graphic;
7764
7765           /* android is moving diagonally */
7766
7767           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7768
7769           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7770           GfxElement[x][y] = EL_EMC_ANDROID;
7771           GfxAction[x][y] = ACTION_SHRINKING;
7772           GfxDir[x][y] = diagonal_move_dir;
7773           ChangeDelay[x][y] = change_delay;
7774
7775           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7776                                    GfxDir[x][y]);
7777
7778           DrawLevelGraphicAnimation(x, y, graphic);
7779           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7780
7781           if (Feld[newx][newy] == EL_ACID)
7782           {
7783             SplashAcid(newx, newy);
7784
7785             return;
7786           }
7787
7788           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7789
7790           Store[newx][newy] = EL_EMC_ANDROID;
7791           GfxElement[newx][newy] = EL_EMC_ANDROID;
7792           GfxAction[newx][newy] = ACTION_GROWING;
7793           GfxDir[newx][newy] = diagonal_move_dir;
7794           ChangeDelay[newx][newy] = change_delay;
7795
7796           graphic = el_act_dir2img(GfxElement[newx][newy],
7797                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7798
7799           DrawLevelGraphicAnimation(newx, newy, graphic);
7800           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7801
7802           return;
7803         }
7804         else
7805         {
7806           Feld[newx][newy] = EL_EMPTY;
7807           TEST_DrawLevelField(newx, newy);
7808
7809           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7810         }
7811       }
7812       else if (!IS_FREE(newx, newy))
7813       {
7814         return;
7815       }
7816     }
7817     else if (IS_CUSTOM_ELEMENT(element) &&
7818              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7819     {
7820       if (!DigFieldByCE(newx, newy, element))
7821         return;
7822
7823       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7824       {
7825         RunnerVisit[x][y] = FrameCounter;
7826         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7827       }
7828     }
7829     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7830     {
7831       if (!IS_FREE(newx, newy))
7832       {
7833         if (IS_PLAYER(x, y))
7834           DrawPlayerField(x, y);
7835         else
7836           TEST_DrawLevelField(x, y);
7837
7838         return;
7839       }
7840       else
7841       {
7842         boolean wanna_flame = !RND(10);
7843         int dx = newx - x, dy = newy - y;
7844         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7845         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7846         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7847                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7848         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7849                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7850
7851         if ((wanna_flame ||
7852              IS_CLASSIC_ENEMY(element1) ||
7853              IS_CLASSIC_ENEMY(element2)) &&
7854             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7855             element1 != EL_FLAMES && element2 != EL_FLAMES)
7856         {
7857           ResetGfxAnimation(x, y);
7858           GfxAction[x][y] = ACTION_ATTACKING;
7859
7860           if (IS_PLAYER(x, y))
7861             DrawPlayerField(x, y);
7862           else
7863             TEST_DrawLevelField(x, y);
7864
7865           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7866
7867           MovDelay[x][y] = 50;
7868
7869           Feld[newx][newy] = EL_FLAMES;
7870           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7871             Feld[newx1][newy1] = EL_FLAMES;
7872           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7873             Feld[newx2][newy2] = EL_FLAMES;
7874
7875           return;
7876         }
7877       }
7878     }
7879     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7880              Feld[newx][newy] == EL_DIAMOND)
7881     {
7882       if (IS_MOVING(newx, newy))
7883         RemoveMovingField(newx, newy);
7884       else
7885       {
7886         Feld[newx][newy] = EL_EMPTY;
7887         TEST_DrawLevelField(newx, newy);
7888       }
7889
7890       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7891     }
7892     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7893              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7894     {
7895       if (AmoebaNr[newx][newy])
7896       {
7897         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7898         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7899             Feld[newx][newy] == EL_BD_AMOEBA)
7900           AmoebaCnt[AmoebaNr[newx][newy]]--;
7901       }
7902
7903       if (IS_MOVING(newx, newy))
7904       {
7905         RemoveMovingField(newx, newy);
7906       }
7907       else
7908       {
7909         Feld[newx][newy] = EL_EMPTY;
7910         TEST_DrawLevelField(newx, newy);
7911       }
7912
7913       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7914     }
7915     else if ((element == EL_PACMAN || element == EL_MOLE)
7916              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7917     {
7918       if (AmoebaNr[newx][newy])
7919       {
7920         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7921         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7922             Feld[newx][newy] == EL_BD_AMOEBA)
7923           AmoebaCnt[AmoebaNr[newx][newy]]--;
7924       }
7925
7926       if (element == EL_MOLE)
7927       {
7928         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7929         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7930
7931         ResetGfxAnimation(x, y);
7932         GfxAction[x][y] = ACTION_DIGGING;
7933         TEST_DrawLevelField(x, y);
7934
7935         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7936
7937         return;                         /* wait for shrinking amoeba */
7938       }
7939       else      /* element == EL_PACMAN */
7940       {
7941         Feld[newx][newy] = EL_EMPTY;
7942         TEST_DrawLevelField(newx, newy);
7943         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7944       }
7945     }
7946     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7947              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7948               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7949     {
7950       /* wait for shrinking amoeba to completely disappear */
7951       return;
7952     }
7953     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7954     {
7955       /* object was running against a wall */
7956
7957       TurnRound(x, y);
7958
7959       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7960         DrawLevelElementAnimation(x, y, element);
7961
7962       if (DONT_TOUCH(element))
7963         TestIfBadThingTouchesPlayer(x, y);
7964
7965       return;
7966     }
7967
7968     InitMovingField(x, y, MovDir[x][y]);
7969
7970     PlayLevelSoundAction(x, y, ACTION_MOVING);
7971   }
7972
7973   if (MovDir[x][y])
7974     ContinueMoving(x, y);
7975 }
7976
7977 void ContinueMoving(int x, int y)
7978 {
7979   int element = Feld[x][y];
7980   struct ElementInfo *ei = &element_info[element];
7981   int direction = MovDir[x][y];
7982   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7983   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7984   int newx = x + dx, newy = y + dy;
7985   int stored = Store[x][y];
7986   int stored_new = Store[newx][newy];
7987   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7988   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7989   boolean last_line = (newy == lev_fieldy - 1);
7990
7991   MovPos[x][y] += getElementMoveStepsize(x, y);
7992
7993   if (pushed_by_player) /* special case: moving object pushed by player */
7994     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7995
7996   if (ABS(MovPos[x][y]) < TILEX)
7997   {
7998     TEST_DrawLevelField(x, y);
7999
8000     return;     /* element is still moving */
8001   }
8002
8003   /* element reached destination field */
8004
8005   Feld[x][y] = EL_EMPTY;
8006   Feld[newx][newy] = element;
8007   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8008
8009   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8010   {
8011     element = Feld[newx][newy] = EL_ACID;
8012   }
8013   else if (element == EL_MOLE)
8014   {
8015     Feld[x][y] = EL_SAND;
8016
8017     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8018   }
8019   else if (element == EL_QUICKSAND_FILLING)
8020   {
8021     element = Feld[newx][newy] = get_next_element(element);
8022     Store[newx][newy] = Store[x][y];
8023   }
8024   else if (element == EL_QUICKSAND_EMPTYING)
8025   {
8026     Feld[x][y] = get_next_element(element);
8027     element = Feld[newx][newy] = Store[x][y];
8028   }
8029   else if (element == EL_QUICKSAND_FAST_FILLING)
8030   {
8031     element = Feld[newx][newy] = get_next_element(element);
8032     Store[newx][newy] = Store[x][y];
8033   }
8034   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8035   {
8036     Feld[x][y] = get_next_element(element);
8037     element = Feld[newx][newy] = Store[x][y];
8038   }
8039   else if (element == EL_MAGIC_WALL_FILLING)
8040   {
8041     element = Feld[newx][newy] = get_next_element(element);
8042     if (!game.magic_wall_active)
8043       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8044     Store[newx][newy] = Store[x][y];
8045   }
8046   else if (element == EL_MAGIC_WALL_EMPTYING)
8047   {
8048     Feld[x][y] = get_next_element(element);
8049     if (!game.magic_wall_active)
8050       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8051     element = Feld[newx][newy] = Store[x][y];
8052
8053     InitField(newx, newy, FALSE);
8054   }
8055   else if (element == EL_BD_MAGIC_WALL_FILLING)
8056   {
8057     element = Feld[newx][newy] = get_next_element(element);
8058     if (!game.magic_wall_active)
8059       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8060     Store[newx][newy] = Store[x][y];
8061   }
8062   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8063   {
8064     Feld[x][y] = get_next_element(element);
8065     if (!game.magic_wall_active)
8066       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8067     element = Feld[newx][newy] = Store[x][y];
8068
8069     InitField(newx, newy, FALSE);
8070   }
8071   else if (element == EL_DC_MAGIC_WALL_FILLING)
8072   {
8073     element = Feld[newx][newy] = get_next_element(element);
8074     if (!game.magic_wall_active)
8075       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8076     Store[newx][newy] = Store[x][y];
8077   }
8078   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8079   {
8080     Feld[x][y] = get_next_element(element);
8081     if (!game.magic_wall_active)
8082       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8083     element = Feld[newx][newy] = Store[x][y];
8084
8085     InitField(newx, newy, FALSE);
8086   }
8087   else if (element == EL_AMOEBA_DROPPING)
8088   {
8089     Feld[x][y] = get_next_element(element);
8090     element = Feld[newx][newy] = Store[x][y];
8091   }
8092   else if (element == EL_SOKOBAN_OBJECT)
8093   {
8094     if (Back[x][y])
8095       Feld[x][y] = Back[x][y];
8096
8097     if (Back[newx][newy])
8098       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8099
8100     Back[x][y] = Back[newx][newy] = 0;
8101   }
8102
8103   Store[x][y] = EL_EMPTY;
8104   MovPos[x][y] = 0;
8105   MovDir[x][y] = 0;
8106   MovDelay[x][y] = 0;
8107
8108   MovDelay[newx][newy] = 0;
8109
8110   if (CAN_CHANGE_OR_HAS_ACTION(element))
8111   {
8112     /* copy element change control values to new field */
8113     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8114     ChangePage[newx][newy]  = ChangePage[x][y];
8115     ChangeCount[newx][newy] = ChangeCount[x][y];
8116     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8117   }
8118
8119   CustomValue[newx][newy] = CustomValue[x][y];
8120
8121   ChangeDelay[x][y] = 0;
8122   ChangePage[x][y] = -1;
8123   ChangeCount[x][y] = 0;
8124   ChangeEvent[x][y] = -1;
8125
8126   CustomValue[x][y] = 0;
8127
8128   /* copy animation control values to new field */
8129   GfxFrame[newx][newy]  = GfxFrame[x][y];
8130   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8131   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8132   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8133
8134   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8135
8136   /* some elements can leave other elements behind after moving */
8137   if (ei->move_leave_element != EL_EMPTY &&
8138       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8139       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8140   {
8141     int move_leave_element = ei->move_leave_element;
8142
8143     /* this makes it possible to leave the removed element again */
8144     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8145       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8146
8147     Feld[x][y] = move_leave_element;
8148
8149     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8150       MovDir[x][y] = direction;
8151
8152     InitField(x, y, FALSE);
8153
8154     if (GFX_CRUMBLED(Feld[x][y]))
8155       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8156
8157     if (ELEM_IS_PLAYER(move_leave_element))
8158       RelocatePlayer(x, y, move_leave_element);
8159   }
8160
8161   /* do this after checking for left-behind element */
8162   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8163
8164   if (!CAN_MOVE(element) ||
8165       (CAN_FALL(element) && direction == MV_DOWN &&
8166        (element == EL_SPRING ||
8167         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8168         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8169     GfxDir[x][y] = MovDir[newx][newy] = 0;
8170
8171   TEST_DrawLevelField(x, y);
8172   TEST_DrawLevelField(newx, newy);
8173
8174   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8175
8176   /* prevent pushed element from moving on in pushed direction */
8177   if (pushed_by_player && CAN_MOVE(element) &&
8178       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8179       !(element_info[element].move_pattern & direction))
8180     TurnRound(newx, newy);
8181
8182   /* prevent elements on conveyor belt from moving on in last direction */
8183   if (pushed_by_conveyor && CAN_FALL(element) &&
8184       direction & MV_HORIZONTAL)
8185     MovDir[newx][newy] = 0;
8186
8187   if (!pushed_by_player)
8188   {
8189     int nextx = newx + dx, nexty = newy + dy;
8190     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8191
8192     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8193
8194     if (CAN_FALL(element) && direction == MV_DOWN)
8195       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8196
8197     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8198       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8199
8200     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8201       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8202   }
8203
8204   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8205   {
8206     TestIfBadThingTouchesPlayer(newx, newy);
8207     TestIfBadThingTouchesFriend(newx, newy);
8208
8209     if (!IS_CUSTOM_ELEMENT(element))
8210       TestIfBadThingTouchesOtherBadThing(newx, newy);
8211   }
8212   else if (element == EL_PENGUIN)
8213     TestIfFriendTouchesBadThing(newx, newy);
8214
8215   if (DONT_GET_HIT_BY(element))
8216   {
8217     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8218   }
8219
8220   /* give the player one last chance (one more frame) to move away */
8221   if (CAN_FALL(element) && direction == MV_DOWN &&
8222       (last_line || (!IS_FREE(x, newy + 1) &&
8223                      (!IS_PLAYER(x, newy + 1) ||
8224                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8225     Impact(x, newy);
8226
8227   if (pushed_by_player && !game.use_change_when_pushing_bug)
8228   {
8229     int push_side = MV_DIR_OPPOSITE(direction);
8230     struct PlayerInfo *player = PLAYERINFO(x, y);
8231
8232     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8233                                player->index_bit, push_side);
8234     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8235                                         player->index_bit, push_side);
8236   }
8237
8238   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8239     MovDelay[newx][newy] = 1;
8240
8241   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8242
8243   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8244   TestIfElementHitsCustomElement(newx, newy, direction);
8245   TestIfPlayerTouchesCustomElement(newx, newy);
8246   TestIfElementTouchesCustomElement(newx, newy);
8247
8248   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8249       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8250     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8251                              MV_DIR_OPPOSITE(direction));
8252 }
8253
8254 int AmoebeNachbarNr(int ax, int ay)
8255 {
8256   int i;
8257   int element = Feld[ax][ay];
8258   int group_nr = 0;
8259   static int xy[4][2] =
8260   {
8261     { 0, -1 },
8262     { -1, 0 },
8263     { +1, 0 },
8264     { 0, +1 }
8265   };
8266
8267   for (i = 0; i < NUM_DIRECTIONS; i++)
8268   {
8269     int x = ax + xy[i][0];
8270     int y = ay + xy[i][1];
8271
8272     if (!IN_LEV_FIELD(x, y))
8273       continue;
8274
8275     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8276       group_nr = AmoebaNr[x][y];
8277   }
8278
8279   return group_nr;
8280 }
8281
8282 void AmoebenVereinigen(int ax, int ay)
8283 {
8284   int i, x, y, xx, yy;
8285   int new_group_nr = AmoebaNr[ax][ay];
8286   static int xy[4][2] =
8287   {
8288     { 0, -1 },
8289     { -1, 0 },
8290     { +1, 0 },
8291     { 0, +1 }
8292   };
8293
8294   if (new_group_nr == 0)
8295     return;
8296
8297   for (i = 0; i < NUM_DIRECTIONS; i++)
8298   {
8299     x = ax + xy[i][0];
8300     y = ay + xy[i][1];
8301
8302     if (!IN_LEV_FIELD(x, y))
8303       continue;
8304
8305     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8306          Feld[x][y] == EL_BD_AMOEBA ||
8307          Feld[x][y] == EL_AMOEBA_DEAD) &&
8308         AmoebaNr[x][y] != new_group_nr)
8309     {
8310       int old_group_nr = AmoebaNr[x][y];
8311
8312       if (old_group_nr == 0)
8313         return;
8314
8315       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8316       AmoebaCnt[old_group_nr] = 0;
8317       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8318       AmoebaCnt2[old_group_nr] = 0;
8319
8320       SCAN_PLAYFIELD(xx, yy)
8321       {
8322         if (AmoebaNr[xx][yy] == old_group_nr)
8323           AmoebaNr[xx][yy] = new_group_nr;
8324       }
8325     }
8326   }
8327 }
8328
8329 void AmoebeUmwandeln(int ax, int ay)
8330 {
8331   int i, x, y;
8332
8333   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8334   {
8335     int group_nr = AmoebaNr[ax][ay];
8336
8337 #ifdef DEBUG
8338     if (group_nr == 0)
8339     {
8340       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8341       printf("AmoebeUmwandeln(): This should never happen!\n");
8342       return;
8343     }
8344 #endif
8345
8346     SCAN_PLAYFIELD(x, y)
8347     {
8348       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8349       {
8350         AmoebaNr[x][y] = 0;
8351         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8352       }
8353     }
8354
8355     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8356                             SND_AMOEBA_TURNING_TO_GEM :
8357                             SND_AMOEBA_TURNING_TO_ROCK));
8358     Bang(ax, ay);
8359   }
8360   else
8361   {
8362     static int xy[4][2] =
8363     {
8364       { 0, -1 },
8365       { -1, 0 },
8366       { +1, 0 },
8367       { 0, +1 }
8368     };
8369
8370     for (i = 0; i < NUM_DIRECTIONS; i++)
8371     {
8372       x = ax + xy[i][0];
8373       y = ay + xy[i][1];
8374
8375       if (!IN_LEV_FIELD(x, y))
8376         continue;
8377
8378       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8379       {
8380         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8381                               SND_AMOEBA_TURNING_TO_GEM :
8382                               SND_AMOEBA_TURNING_TO_ROCK));
8383         Bang(x, y);
8384       }
8385     }
8386   }
8387 }
8388
8389 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8390 {
8391   int x, y;
8392   int group_nr = AmoebaNr[ax][ay];
8393   boolean done = FALSE;
8394
8395 #ifdef DEBUG
8396   if (group_nr == 0)
8397   {
8398     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8399     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8400     return;
8401   }
8402 #endif
8403
8404   SCAN_PLAYFIELD(x, y)
8405   {
8406     if (AmoebaNr[x][y] == group_nr &&
8407         (Feld[x][y] == EL_AMOEBA_DEAD ||
8408          Feld[x][y] == EL_BD_AMOEBA ||
8409          Feld[x][y] == EL_AMOEBA_GROWING))
8410     {
8411       AmoebaNr[x][y] = 0;
8412       Feld[x][y] = new_element;
8413       InitField(x, y, FALSE);
8414       TEST_DrawLevelField(x, y);
8415       done = TRUE;
8416     }
8417   }
8418
8419   if (done)
8420     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8421                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8422                             SND_BD_AMOEBA_TURNING_TO_GEM));
8423 }
8424
8425 void AmoebeWaechst(int x, int y)
8426 {
8427   static unsigned int sound_delay = 0;
8428   static unsigned int sound_delay_value = 0;
8429
8430   if (!MovDelay[x][y])          /* start new growing cycle */
8431   {
8432     MovDelay[x][y] = 7;
8433
8434     if (DelayReached(&sound_delay, sound_delay_value))
8435     {
8436       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8437       sound_delay_value = 30;
8438     }
8439   }
8440
8441   if (MovDelay[x][y])           /* wait some time before growing bigger */
8442   {
8443     MovDelay[x][y]--;
8444     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8445     {
8446       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8447                                            6 - MovDelay[x][y]);
8448
8449       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8450     }
8451
8452     if (!MovDelay[x][y])
8453     {
8454       Feld[x][y] = Store[x][y];
8455       Store[x][y] = 0;
8456       TEST_DrawLevelField(x, y);
8457     }
8458   }
8459 }
8460
8461 void AmoebaDisappearing(int x, int y)
8462 {
8463   static unsigned int sound_delay = 0;
8464   static unsigned int sound_delay_value = 0;
8465
8466   if (!MovDelay[x][y])          /* start new shrinking cycle */
8467   {
8468     MovDelay[x][y] = 7;
8469
8470     if (DelayReached(&sound_delay, sound_delay_value))
8471       sound_delay_value = 30;
8472   }
8473
8474   if (MovDelay[x][y])           /* wait some time before shrinking */
8475   {
8476     MovDelay[x][y]--;
8477     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8478     {
8479       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8480                                            6 - MovDelay[x][y]);
8481
8482       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8483     }
8484
8485     if (!MovDelay[x][y])
8486     {
8487       Feld[x][y] = EL_EMPTY;
8488       TEST_DrawLevelField(x, y);
8489
8490       /* don't let mole enter this field in this cycle;
8491          (give priority to objects falling to this field from above) */
8492       Stop[x][y] = TRUE;
8493     }
8494   }
8495 }
8496
8497 void AmoebeAbleger(int ax, int ay)
8498 {
8499   int i;
8500   int element = Feld[ax][ay];
8501   int graphic = el2img(element);
8502   int newax = ax, neway = ay;
8503   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8504   static int xy[4][2] =
8505   {
8506     { 0, -1 },
8507     { -1, 0 },
8508     { +1, 0 },
8509     { 0, +1 }
8510   };
8511
8512   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8513   {
8514     Feld[ax][ay] = EL_AMOEBA_DEAD;
8515     TEST_DrawLevelField(ax, ay);
8516     return;
8517   }
8518
8519   if (IS_ANIMATED(graphic))
8520     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8521
8522   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8523     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8524
8525   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8526   {
8527     MovDelay[ax][ay]--;
8528     if (MovDelay[ax][ay])
8529       return;
8530   }
8531
8532   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8533   {
8534     int start = RND(4);
8535     int x = ax + xy[start][0];
8536     int y = ay + xy[start][1];
8537
8538     if (!IN_LEV_FIELD(x, y))
8539       return;
8540
8541     if (IS_FREE(x, y) ||
8542         CAN_GROW_INTO(Feld[x][y]) ||
8543         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8544         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8545     {
8546       newax = x;
8547       neway = y;
8548     }
8549
8550     if (newax == ax && neway == ay)
8551       return;
8552   }
8553   else                          /* normal or "filled" (BD style) amoeba */
8554   {
8555     int start = RND(4);
8556     boolean waiting_for_player = FALSE;
8557
8558     for (i = 0; i < NUM_DIRECTIONS; i++)
8559     {
8560       int j = (start + i) % 4;
8561       int x = ax + xy[j][0];
8562       int y = ay + xy[j][1];
8563
8564       if (!IN_LEV_FIELD(x, y))
8565         continue;
8566
8567       if (IS_FREE(x, y) ||
8568           CAN_GROW_INTO(Feld[x][y]) ||
8569           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8570           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8571       {
8572         newax = x;
8573         neway = y;
8574         break;
8575       }
8576       else if (IS_PLAYER(x, y))
8577         waiting_for_player = TRUE;
8578     }
8579
8580     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8581     {
8582       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8583       {
8584         Feld[ax][ay] = EL_AMOEBA_DEAD;
8585         TEST_DrawLevelField(ax, ay);
8586         AmoebaCnt[AmoebaNr[ax][ay]]--;
8587
8588         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8589         {
8590           if (element == EL_AMOEBA_FULL)
8591             AmoebeUmwandeln(ax, ay);
8592           else if (element == EL_BD_AMOEBA)
8593             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8594         }
8595       }
8596       return;
8597     }
8598     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8599     {
8600       /* amoeba gets larger by growing in some direction */
8601
8602       int new_group_nr = AmoebaNr[ax][ay];
8603
8604 #ifdef DEBUG
8605   if (new_group_nr == 0)
8606   {
8607     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8608     printf("AmoebeAbleger(): This should never happen!\n");
8609     return;
8610   }
8611 #endif
8612
8613       AmoebaNr[newax][neway] = new_group_nr;
8614       AmoebaCnt[new_group_nr]++;
8615       AmoebaCnt2[new_group_nr]++;
8616
8617       /* if amoeba touches other amoeba(s) after growing, unify them */
8618       AmoebenVereinigen(newax, neway);
8619
8620       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8621       {
8622         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8623         return;
8624       }
8625     }
8626   }
8627
8628   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8629       (neway == lev_fieldy - 1 && newax != ax))
8630   {
8631     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8632     Store[newax][neway] = element;
8633   }
8634   else if (neway == ay || element == EL_EMC_DRIPPER)
8635   {
8636     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8637
8638     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8639   }
8640   else
8641   {
8642     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8643     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8644     Store[ax][ay] = EL_AMOEBA_DROP;
8645     ContinueMoving(ax, ay);
8646     return;
8647   }
8648
8649   TEST_DrawLevelField(newax, neway);
8650 }
8651
8652 void Life(int ax, int ay)
8653 {
8654   int x1, y1, x2, y2;
8655   int life_time = 40;
8656   int element = Feld[ax][ay];
8657   int graphic = el2img(element);
8658   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8659                          level.biomaze);
8660   boolean changed = FALSE;
8661
8662   if (IS_ANIMATED(graphic))
8663     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8664
8665   if (Stop[ax][ay])
8666     return;
8667
8668   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8669     MovDelay[ax][ay] = life_time;
8670
8671   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8672   {
8673     MovDelay[ax][ay]--;
8674     if (MovDelay[ax][ay])
8675       return;
8676   }
8677
8678   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8679   {
8680     int xx = ax+x1, yy = ay+y1;
8681     int nachbarn = 0;
8682
8683     if (!IN_LEV_FIELD(xx, yy))
8684       continue;
8685
8686     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8687     {
8688       int x = xx+x2, y = yy+y2;
8689
8690       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8691         continue;
8692
8693       if (((Feld[x][y] == element ||
8694             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8695            !Stop[x][y]) ||
8696           (IS_FREE(x, y) && Stop[x][y]))
8697         nachbarn++;
8698     }
8699
8700     if (xx == ax && yy == ay)           /* field in the middle */
8701     {
8702       if (nachbarn < life_parameter[0] ||
8703           nachbarn > life_parameter[1])
8704       {
8705         Feld[xx][yy] = EL_EMPTY;
8706         if (!Stop[xx][yy])
8707           TEST_DrawLevelField(xx, yy);
8708         Stop[xx][yy] = TRUE;
8709         changed = TRUE;
8710       }
8711     }
8712     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8713     {                                   /* free border field */
8714       if (nachbarn >= life_parameter[2] &&
8715           nachbarn <= life_parameter[3])
8716       {
8717         Feld[xx][yy] = element;
8718         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8719         if (!Stop[xx][yy])
8720           TEST_DrawLevelField(xx, yy);
8721         Stop[xx][yy] = TRUE;
8722         changed = TRUE;
8723       }
8724     }
8725   }
8726
8727   if (changed)
8728     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8729                    SND_GAME_OF_LIFE_GROWING);
8730 }
8731
8732 static void InitRobotWheel(int x, int y)
8733 {
8734   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8735 }
8736
8737 static void RunRobotWheel(int x, int y)
8738 {
8739   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8740 }
8741
8742 static void StopRobotWheel(int x, int y)
8743 {
8744   if (ZX == x && ZY == y)
8745   {
8746     ZX = ZY = -1;
8747
8748     game.robot_wheel_active = FALSE;
8749   }
8750 }
8751
8752 static void InitTimegateWheel(int x, int y)
8753 {
8754   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8755 }
8756
8757 static void RunTimegateWheel(int x, int y)
8758 {
8759   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8760 }
8761
8762 static void InitMagicBallDelay(int x, int y)
8763 {
8764   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8765 }
8766
8767 static void ActivateMagicBall(int bx, int by)
8768 {
8769   int x, y;
8770
8771   if (level.ball_random)
8772   {
8773     int pos_border = RND(8);    /* select one of the eight border elements */
8774     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8775     int xx = pos_content % 3;
8776     int yy = pos_content / 3;
8777
8778     x = bx - 1 + xx;
8779     y = by - 1 + yy;
8780
8781     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8782       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8783   }
8784   else
8785   {
8786     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8787     {
8788       int xx = x - bx + 1;
8789       int yy = y - by + 1;
8790
8791       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8792         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8793     }
8794   }
8795
8796   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8797 }
8798
8799 void CheckExit(int x, int y)
8800 {
8801   if (local_player->gems_still_needed > 0 ||
8802       local_player->sokobanfields_still_needed > 0 ||
8803       local_player->lights_still_needed > 0)
8804   {
8805     int element = Feld[x][y];
8806     int graphic = el2img(element);
8807
8808     if (IS_ANIMATED(graphic))
8809       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8810
8811     return;
8812   }
8813
8814   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8815     return;
8816
8817   Feld[x][y] = EL_EXIT_OPENING;
8818
8819   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8820 }
8821
8822 void CheckExitEM(int x, int y)
8823 {
8824   if (local_player->gems_still_needed > 0 ||
8825       local_player->sokobanfields_still_needed > 0 ||
8826       local_player->lights_still_needed > 0)
8827   {
8828     int element = Feld[x][y];
8829     int graphic = el2img(element);
8830
8831     if (IS_ANIMATED(graphic))
8832       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8833
8834     return;
8835   }
8836
8837   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8838     return;
8839
8840   Feld[x][y] = EL_EM_EXIT_OPENING;
8841
8842   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8843 }
8844
8845 void CheckExitSteel(int x, int y)
8846 {
8847   if (local_player->gems_still_needed > 0 ||
8848       local_player->sokobanfields_still_needed > 0 ||
8849       local_player->lights_still_needed > 0)
8850   {
8851     int element = Feld[x][y];
8852     int graphic = el2img(element);
8853
8854     if (IS_ANIMATED(graphic))
8855       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8856
8857     return;
8858   }
8859
8860   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8861     return;
8862
8863   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8864
8865   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8866 }
8867
8868 void CheckExitSteelEM(int x, int y)
8869 {
8870   if (local_player->gems_still_needed > 0 ||
8871       local_player->sokobanfields_still_needed > 0 ||
8872       local_player->lights_still_needed > 0)
8873   {
8874     int element = Feld[x][y];
8875     int graphic = el2img(element);
8876
8877     if (IS_ANIMATED(graphic))
8878       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8879
8880     return;
8881   }
8882
8883   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8884     return;
8885
8886   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8887
8888   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8889 }
8890
8891 void CheckExitSP(int x, int y)
8892 {
8893   if (local_player->gems_still_needed > 0)
8894   {
8895     int element = Feld[x][y];
8896     int graphic = el2img(element);
8897
8898     if (IS_ANIMATED(graphic))
8899       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8900
8901     return;
8902   }
8903
8904   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8905     return;
8906
8907   Feld[x][y] = EL_SP_EXIT_OPENING;
8908
8909   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8910 }
8911
8912 static void CloseAllOpenTimegates()
8913 {
8914   int x, y;
8915
8916   SCAN_PLAYFIELD(x, y)
8917   {
8918     int element = Feld[x][y];
8919
8920     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8921     {
8922       Feld[x][y] = EL_TIMEGATE_CLOSING;
8923
8924       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8925     }
8926   }
8927 }
8928
8929 void DrawTwinkleOnField(int x, int y)
8930 {
8931   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8932     return;
8933
8934   if (Feld[x][y] == EL_BD_DIAMOND)
8935     return;
8936
8937   if (MovDelay[x][y] == 0)      /* next animation frame */
8938     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8939
8940   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8941   {
8942     MovDelay[x][y]--;
8943
8944     DrawLevelElementAnimation(x, y, Feld[x][y]);
8945
8946     if (MovDelay[x][y] != 0)
8947     {
8948       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8949                                            10 - MovDelay[x][y]);
8950
8951       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8952     }
8953   }
8954 }
8955
8956 void MauerWaechst(int x, int y)
8957 {
8958   int delay = 6;
8959
8960   if (!MovDelay[x][y])          /* next animation frame */
8961     MovDelay[x][y] = 3 * delay;
8962
8963   if (MovDelay[x][y])           /* wait some time before next frame */
8964   {
8965     MovDelay[x][y]--;
8966
8967     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8968     {
8969       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8970       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8971
8972       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8973     }
8974
8975     if (!MovDelay[x][y])
8976     {
8977       if (MovDir[x][y] == MV_LEFT)
8978       {
8979         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8980           TEST_DrawLevelField(x - 1, y);
8981       }
8982       else if (MovDir[x][y] == MV_RIGHT)
8983       {
8984         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8985           TEST_DrawLevelField(x + 1, y);
8986       }
8987       else if (MovDir[x][y] == MV_UP)
8988       {
8989         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8990           TEST_DrawLevelField(x, y - 1);
8991       }
8992       else
8993       {
8994         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8995           TEST_DrawLevelField(x, y + 1);
8996       }
8997
8998       Feld[x][y] = Store[x][y];
8999       Store[x][y] = 0;
9000       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9001       TEST_DrawLevelField(x, y);
9002     }
9003   }
9004 }
9005
9006 void MauerAbleger(int ax, int ay)
9007 {
9008   int element = Feld[ax][ay];
9009   int graphic = el2img(element);
9010   boolean oben_frei = FALSE, unten_frei = FALSE;
9011   boolean links_frei = FALSE, rechts_frei = FALSE;
9012   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9013   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9014   boolean new_wall = FALSE;
9015
9016   if (IS_ANIMATED(graphic))
9017     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9018
9019   if (!MovDelay[ax][ay])        /* start building new wall */
9020     MovDelay[ax][ay] = 6;
9021
9022   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9023   {
9024     MovDelay[ax][ay]--;
9025     if (MovDelay[ax][ay])
9026       return;
9027   }
9028
9029   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9030     oben_frei = TRUE;
9031   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9032     unten_frei = TRUE;
9033   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9034     links_frei = TRUE;
9035   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9036     rechts_frei = TRUE;
9037
9038   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9039       element == EL_EXPANDABLE_WALL_ANY)
9040   {
9041     if (oben_frei)
9042     {
9043       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9044       Store[ax][ay-1] = element;
9045       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9046       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9047         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9048                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9049       new_wall = TRUE;
9050     }
9051     if (unten_frei)
9052     {
9053       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9054       Store[ax][ay+1] = element;
9055       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9056       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9057         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9058                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9059       new_wall = TRUE;
9060     }
9061   }
9062
9063   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9064       element == EL_EXPANDABLE_WALL_ANY ||
9065       element == EL_EXPANDABLE_WALL ||
9066       element == EL_BD_EXPANDABLE_WALL)
9067   {
9068     if (links_frei)
9069     {
9070       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9071       Store[ax-1][ay] = element;
9072       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9073       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9074         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9075                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9076       new_wall = TRUE;
9077     }
9078
9079     if (rechts_frei)
9080     {
9081       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9082       Store[ax+1][ay] = element;
9083       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9084       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9085         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9086                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9087       new_wall = TRUE;
9088     }
9089   }
9090
9091   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9092     TEST_DrawLevelField(ax, ay);
9093
9094   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9095     oben_massiv = TRUE;
9096   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9097     unten_massiv = TRUE;
9098   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9099     links_massiv = TRUE;
9100   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9101     rechts_massiv = TRUE;
9102
9103   if (((oben_massiv && unten_massiv) ||
9104        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9105        element == EL_EXPANDABLE_WALL) &&
9106       ((links_massiv && rechts_massiv) ||
9107        element == EL_EXPANDABLE_WALL_VERTICAL))
9108     Feld[ax][ay] = EL_WALL;
9109
9110   if (new_wall)
9111     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9112 }
9113
9114 void MauerAblegerStahl(int ax, int ay)
9115 {
9116   int element = Feld[ax][ay];
9117   int graphic = el2img(element);
9118   boolean oben_frei = FALSE, unten_frei = FALSE;
9119   boolean links_frei = FALSE, rechts_frei = FALSE;
9120   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9121   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9122   boolean new_wall = FALSE;
9123
9124   if (IS_ANIMATED(graphic))
9125     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9126
9127   if (!MovDelay[ax][ay])        /* start building new wall */
9128     MovDelay[ax][ay] = 6;
9129
9130   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9131   {
9132     MovDelay[ax][ay]--;
9133     if (MovDelay[ax][ay])
9134       return;
9135   }
9136
9137   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9138     oben_frei = TRUE;
9139   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9140     unten_frei = TRUE;
9141   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9142     links_frei = TRUE;
9143   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9144     rechts_frei = TRUE;
9145
9146   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9147       element == EL_EXPANDABLE_STEELWALL_ANY)
9148   {
9149     if (oben_frei)
9150     {
9151       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9152       Store[ax][ay-1] = element;
9153       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9154       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9155         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9156                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9157       new_wall = TRUE;
9158     }
9159     if (unten_frei)
9160     {
9161       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9162       Store[ax][ay+1] = element;
9163       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9164       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9165         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9166                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9167       new_wall = TRUE;
9168     }
9169   }
9170
9171   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9172       element == EL_EXPANDABLE_STEELWALL_ANY)
9173   {
9174     if (links_frei)
9175     {
9176       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9177       Store[ax-1][ay] = element;
9178       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9179       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9180         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9181                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9182       new_wall = TRUE;
9183     }
9184
9185     if (rechts_frei)
9186     {
9187       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9188       Store[ax+1][ay] = element;
9189       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9190       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9191         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9192                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9193       new_wall = TRUE;
9194     }
9195   }
9196
9197   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9198     oben_massiv = TRUE;
9199   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9200     unten_massiv = TRUE;
9201   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9202     links_massiv = TRUE;
9203   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9204     rechts_massiv = TRUE;
9205
9206   if (((oben_massiv && unten_massiv) ||
9207        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9208       ((links_massiv && rechts_massiv) ||
9209        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9210     Feld[ax][ay] = EL_STEELWALL;
9211
9212   if (new_wall)
9213     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9214 }
9215
9216 void CheckForDragon(int x, int y)
9217 {
9218   int i, j;
9219   boolean dragon_found = FALSE;
9220   static int xy[4][2] =
9221   {
9222     { 0, -1 },
9223     { -1, 0 },
9224     { +1, 0 },
9225     { 0, +1 }
9226   };
9227
9228   for (i = 0; i < NUM_DIRECTIONS; i++)
9229   {
9230     for (j = 0; j < 4; j++)
9231     {
9232       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9233
9234       if (IN_LEV_FIELD(xx, yy) &&
9235           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9236       {
9237         if (Feld[xx][yy] == EL_DRAGON)
9238           dragon_found = TRUE;
9239       }
9240       else
9241         break;
9242     }
9243   }
9244
9245   if (!dragon_found)
9246   {
9247     for (i = 0; i < NUM_DIRECTIONS; i++)
9248     {
9249       for (j = 0; j < 3; j++)
9250       {
9251         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9252   
9253         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9254         {
9255           Feld[xx][yy] = EL_EMPTY;
9256           TEST_DrawLevelField(xx, yy);
9257         }
9258         else
9259           break;
9260       }
9261     }
9262   }
9263 }
9264
9265 static void InitBuggyBase(int x, int y)
9266 {
9267   int element = Feld[x][y];
9268   int activating_delay = FRAMES_PER_SECOND / 4;
9269
9270   ChangeDelay[x][y] =
9271     (element == EL_SP_BUGGY_BASE ?
9272      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9273      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9274      activating_delay :
9275      element == EL_SP_BUGGY_BASE_ACTIVE ?
9276      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9277 }
9278
9279 static void WarnBuggyBase(int x, int y)
9280 {
9281   int i;
9282   static int xy[4][2] =
9283   {
9284     { 0, -1 },
9285     { -1, 0 },
9286     { +1, 0 },
9287     { 0, +1 }
9288   };
9289
9290   for (i = 0; i < NUM_DIRECTIONS; i++)
9291   {
9292     int xx = x + xy[i][0];
9293     int yy = y + xy[i][1];
9294
9295     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9296     {
9297       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9298
9299       break;
9300     }
9301   }
9302 }
9303
9304 static void InitTrap(int x, int y)
9305 {
9306   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9307 }
9308
9309 static void ActivateTrap(int x, int y)
9310 {
9311   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9312 }
9313
9314 static void ChangeActiveTrap(int x, int y)
9315 {
9316   int graphic = IMG_TRAP_ACTIVE;
9317
9318   /* if new animation frame was drawn, correct crumbled sand border */
9319   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9320     TEST_DrawLevelFieldCrumbled(x, y);
9321 }
9322
9323 static int getSpecialActionElement(int element, int number, int base_element)
9324 {
9325   return (element != EL_EMPTY ? element :
9326           number != -1 ? base_element + number - 1 :
9327           EL_EMPTY);
9328 }
9329
9330 static int getModifiedActionNumber(int value_old, int operator, int operand,
9331                                    int value_min, int value_max)
9332 {
9333   int value_new = (operator == CA_MODE_SET      ? operand :
9334                    operator == CA_MODE_ADD      ? value_old + operand :
9335                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9336                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9337                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9338                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9339                    value_old);
9340
9341   return (value_new < value_min ? value_min :
9342           value_new > value_max ? value_max :
9343           value_new);
9344 }
9345
9346 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9347 {
9348   struct ElementInfo *ei = &element_info[element];
9349   struct ElementChangeInfo *change = &ei->change_page[page];
9350   int target_element = change->target_element;
9351   int action_type = change->action_type;
9352   int action_mode = change->action_mode;
9353   int action_arg = change->action_arg;
9354   int action_element = change->action_element;
9355   int i;
9356
9357   if (!change->has_action)
9358     return;
9359
9360   /* ---------- determine action paramater values -------------------------- */
9361
9362   int level_time_value =
9363     (level.time > 0 ? TimeLeft :
9364      TimePlayed);
9365
9366   int action_arg_element_raw =
9367     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9368      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9369      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9370      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9371      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9372      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9373      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9374      EL_EMPTY);
9375   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9376
9377   int action_arg_direction =
9378     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9379      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9380      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9381      change->actual_trigger_side :
9382      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9383      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9384      MV_NONE);
9385
9386   int action_arg_number_min =
9387     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9388      CA_ARG_MIN);
9389
9390   int action_arg_number_max =
9391     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9392      action_type == CA_SET_LEVEL_GEMS ? 999 :
9393      action_type == CA_SET_LEVEL_TIME ? 9999 :
9394      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9395      action_type == CA_SET_CE_VALUE ? 9999 :
9396      action_type == CA_SET_CE_SCORE ? 9999 :
9397      CA_ARG_MAX);
9398
9399   int action_arg_number_reset =
9400     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9401      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9402      action_type == CA_SET_LEVEL_TIME ? level.time :
9403      action_type == CA_SET_LEVEL_SCORE ? 0 :
9404      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9405      action_type == CA_SET_CE_SCORE ? 0 :
9406      0);
9407
9408   int action_arg_number =
9409     (action_arg <= CA_ARG_MAX ? action_arg :
9410      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9411      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9412      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9413      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9414      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9415      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9416      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9417      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9418      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9419      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9420      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9421      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9422      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9423      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9424      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9425      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9426      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9427      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9428      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9429      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9430      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9431      -1);
9432
9433   int action_arg_number_old =
9434     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9435      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9436      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9437      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9438      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9439      0);
9440
9441   int action_arg_number_new =
9442     getModifiedActionNumber(action_arg_number_old,
9443                             action_mode, action_arg_number,
9444                             action_arg_number_min, action_arg_number_max);
9445
9446   int trigger_player_bits =
9447     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9448      change->actual_trigger_player_bits : change->trigger_player);
9449
9450   int action_arg_player_bits =
9451     (action_arg >= CA_ARG_PLAYER_1 &&
9452      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9453      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9454      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9455      PLAYER_BITS_ANY);
9456
9457   /* ---------- execute action  -------------------------------------------- */
9458
9459   switch (action_type)
9460   {
9461     case CA_NO_ACTION:
9462     {
9463       return;
9464     }
9465
9466     /* ---------- level actions  ------------------------------------------- */
9467
9468     case CA_RESTART_LEVEL:
9469     {
9470       game.restart_level = TRUE;
9471
9472       break;
9473     }
9474
9475     case CA_SHOW_ENVELOPE:
9476     {
9477       int element = getSpecialActionElement(action_arg_element,
9478                                             action_arg_number, EL_ENVELOPE_1);
9479
9480       if (IS_ENVELOPE(element))
9481         local_player->show_envelope = element;
9482
9483       break;
9484     }
9485
9486     case CA_SET_LEVEL_TIME:
9487     {
9488       if (level.time > 0)       /* only modify limited time value */
9489       {
9490         TimeLeft = action_arg_number_new;
9491
9492         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9493
9494         DisplayGameControlValues();
9495
9496         if (!TimeLeft && setup.time_limit)
9497           for (i = 0; i < MAX_PLAYERS; i++)
9498             KillPlayer(&stored_player[i]);
9499       }
9500
9501       break;
9502     }
9503
9504     case CA_SET_LEVEL_SCORE:
9505     {
9506       local_player->score = action_arg_number_new;
9507
9508       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9509
9510       DisplayGameControlValues();
9511
9512       break;
9513     }
9514
9515     case CA_SET_LEVEL_GEMS:
9516     {
9517       local_player->gems_still_needed = action_arg_number_new;
9518
9519       game.snapshot.collected_item = TRUE;
9520
9521       game_panel_controls[GAME_PANEL_GEMS].value =
9522         local_player->gems_still_needed;
9523
9524       DisplayGameControlValues();
9525
9526       break;
9527     }
9528
9529     case CA_SET_LEVEL_WIND:
9530     {
9531       game.wind_direction = action_arg_direction;
9532
9533       break;
9534     }
9535
9536     case CA_SET_LEVEL_RANDOM_SEED:
9537     {
9538       /* ensure that setting a new random seed while playing is predictable */
9539       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9540
9541       break;
9542     }
9543
9544     /* ---------- player actions  ------------------------------------------ */
9545
9546     case CA_MOVE_PLAYER:
9547     {
9548       /* automatically move to the next field in specified direction */
9549       for (i = 0; i < MAX_PLAYERS; i++)
9550         if (trigger_player_bits & (1 << i))
9551           stored_player[i].programmed_action = action_arg_direction;
9552
9553       break;
9554     }
9555
9556     case CA_EXIT_PLAYER:
9557     {
9558       for (i = 0; i < MAX_PLAYERS; i++)
9559         if (action_arg_player_bits & (1 << i))
9560           PlayerWins(&stored_player[i]);
9561
9562       break;
9563     }
9564
9565     case CA_KILL_PLAYER:
9566     {
9567       for (i = 0; i < MAX_PLAYERS; i++)
9568         if (action_arg_player_bits & (1 << i))
9569           KillPlayer(&stored_player[i]);
9570
9571       break;
9572     }
9573
9574     case CA_SET_PLAYER_KEYS:
9575     {
9576       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9577       int element = getSpecialActionElement(action_arg_element,
9578                                             action_arg_number, EL_KEY_1);
9579
9580       if (IS_KEY(element))
9581       {
9582         for (i = 0; i < MAX_PLAYERS; i++)
9583         {
9584           if (trigger_player_bits & (1 << i))
9585           {
9586             stored_player[i].key[KEY_NR(element)] = key_state;
9587
9588             DrawGameDoorValues();
9589           }
9590         }
9591       }
9592
9593       break;
9594     }
9595
9596     case CA_SET_PLAYER_SPEED:
9597     {
9598       for (i = 0; i < MAX_PLAYERS; i++)
9599       {
9600         if (trigger_player_bits & (1 << i))
9601         {
9602           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9603
9604           if (action_arg == CA_ARG_SPEED_FASTER &&
9605               stored_player[i].cannot_move)
9606           {
9607             action_arg_number = STEPSIZE_VERY_SLOW;
9608           }
9609           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9610                    action_arg == CA_ARG_SPEED_FASTER)
9611           {
9612             action_arg_number = 2;
9613             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9614                            CA_MODE_MULTIPLY);
9615           }
9616           else if (action_arg == CA_ARG_NUMBER_RESET)
9617           {
9618             action_arg_number = level.initial_player_stepsize[i];
9619           }
9620
9621           move_stepsize =
9622             getModifiedActionNumber(move_stepsize,
9623                                     action_mode,
9624                                     action_arg_number,
9625                                     action_arg_number_min,
9626                                     action_arg_number_max);
9627
9628           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9629         }
9630       }
9631
9632       break;
9633     }
9634
9635     case CA_SET_PLAYER_SHIELD:
9636     {
9637       for (i = 0; i < MAX_PLAYERS; i++)
9638       {
9639         if (trigger_player_bits & (1 << i))
9640         {
9641           if (action_arg == CA_ARG_SHIELD_OFF)
9642           {
9643             stored_player[i].shield_normal_time_left = 0;
9644             stored_player[i].shield_deadly_time_left = 0;
9645           }
9646           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9647           {
9648             stored_player[i].shield_normal_time_left = 999999;
9649           }
9650           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9651           {
9652             stored_player[i].shield_normal_time_left = 999999;
9653             stored_player[i].shield_deadly_time_left = 999999;
9654           }
9655         }
9656       }
9657
9658       break;
9659     }
9660
9661     case CA_SET_PLAYER_GRAVITY:
9662     {
9663       for (i = 0; i < MAX_PLAYERS; i++)
9664       {
9665         if (trigger_player_bits & (1 << i))
9666         {
9667           stored_player[i].gravity =
9668             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9669              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9670              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9671              stored_player[i].gravity);
9672         }
9673       }
9674
9675       break;
9676     }
9677
9678     case CA_SET_PLAYER_ARTWORK:
9679     {
9680       for (i = 0; i < MAX_PLAYERS; i++)
9681       {
9682         if (trigger_player_bits & (1 << i))
9683         {
9684           int artwork_element = action_arg_element;
9685
9686           if (action_arg == CA_ARG_ELEMENT_RESET)
9687             artwork_element =
9688               (level.use_artwork_element[i] ? level.artwork_element[i] :
9689                stored_player[i].element_nr);
9690
9691           if (stored_player[i].artwork_element != artwork_element)
9692             stored_player[i].Frame = 0;
9693
9694           stored_player[i].artwork_element = artwork_element;
9695
9696           SetPlayerWaiting(&stored_player[i], FALSE);
9697
9698           /* set number of special actions for bored and sleeping animation */
9699           stored_player[i].num_special_action_bored =
9700             get_num_special_action(artwork_element,
9701                                    ACTION_BORING_1, ACTION_BORING_LAST);
9702           stored_player[i].num_special_action_sleeping =
9703             get_num_special_action(artwork_element,
9704                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9705         }
9706       }
9707
9708       break;
9709     }
9710
9711     case CA_SET_PLAYER_INVENTORY:
9712     {
9713       for (i = 0; i < MAX_PLAYERS; i++)
9714       {
9715         struct PlayerInfo *player = &stored_player[i];
9716         int j, k;
9717
9718         if (trigger_player_bits & (1 << i))
9719         {
9720           int inventory_element = action_arg_element;
9721
9722           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9723               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9724               action_arg == CA_ARG_ELEMENT_ACTION)
9725           {
9726             int element = inventory_element;
9727             int collect_count = element_info[element].collect_count_initial;
9728
9729             if (!IS_CUSTOM_ELEMENT(element))
9730               collect_count = 1;
9731
9732             if (collect_count == 0)
9733               player->inventory_infinite_element = element;
9734             else
9735               for (k = 0; k < collect_count; k++)
9736                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9737                   player->inventory_element[player->inventory_size++] =
9738                     element;
9739           }
9740           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9741                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9742                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9743           {
9744             if (player->inventory_infinite_element != EL_UNDEFINED &&
9745                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9746                                      action_arg_element_raw))
9747               player->inventory_infinite_element = EL_UNDEFINED;
9748
9749             for (k = 0, j = 0; j < player->inventory_size; j++)
9750             {
9751               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9752                                         action_arg_element_raw))
9753                 player->inventory_element[k++] = player->inventory_element[j];
9754             }
9755
9756             player->inventory_size = k;
9757           }
9758           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9759           {
9760             if (player->inventory_size > 0)
9761             {
9762               for (j = 0; j < player->inventory_size - 1; j++)
9763                 player->inventory_element[j] = player->inventory_element[j + 1];
9764
9765               player->inventory_size--;
9766             }
9767           }
9768           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9769           {
9770             if (player->inventory_size > 0)
9771               player->inventory_size--;
9772           }
9773           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9774           {
9775             player->inventory_infinite_element = EL_UNDEFINED;
9776             player->inventory_size = 0;
9777           }
9778           else if (action_arg == CA_ARG_INVENTORY_RESET)
9779           {
9780             player->inventory_infinite_element = EL_UNDEFINED;
9781             player->inventory_size = 0;
9782
9783             if (level.use_initial_inventory[i])
9784             {
9785               for (j = 0; j < level.initial_inventory_size[i]; j++)
9786               {
9787                 int element = level.initial_inventory_content[i][j];
9788                 int collect_count = element_info[element].collect_count_initial;
9789
9790                 if (!IS_CUSTOM_ELEMENT(element))
9791                   collect_count = 1;
9792
9793                 if (collect_count == 0)
9794                   player->inventory_infinite_element = element;
9795                 else
9796                   for (k = 0; k < collect_count; k++)
9797                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9798                       player->inventory_element[player->inventory_size++] =
9799                         element;
9800               }
9801             }
9802           }
9803         }
9804       }
9805
9806       break;
9807     }
9808
9809     /* ---------- CE actions  ---------------------------------------------- */
9810
9811     case CA_SET_CE_VALUE:
9812     {
9813       int last_ce_value = CustomValue[x][y];
9814
9815       CustomValue[x][y] = action_arg_number_new;
9816
9817       if (CustomValue[x][y] != last_ce_value)
9818       {
9819         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9820         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9821
9822         if (CustomValue[x][y] == 0)
9823         {
9824           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9825           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9826         }
9827       }
9828
9829       break;
9830     }
9831
9832     case CA_SET_CE_SCORE:
9833     {
9834       int last_ce_score = ei->collect_score;
9835
9836       ei->collect_score = action_arg_number_new;
9837
9838       if (ei->collect_score != last_ce_score)
9839       {
9840         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9841         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9842
9843         if (ei->collect_score == 0)
9844         {
9845           int xx, yy;
9846
9847           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9848           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9849
9850           /*
9851             This is a very special case that seems to be a mixture between
9852             CheckElementChange() and CheckTriggeredElementChange(): while
9853             the first one only affects single elements that are triggered
9854             directly, the second one affects multiple elements in the playfield
9855             that are triggered indirectly by another element. This is a third
9856             case: Changing the CE score always affects multiple identical CEs,
9857             so every affected CE must be checked, not only the single CE for
9858             which the CE score was changed in the first place (as every instance
9859             of that CE shares the same CE score, and therefore also can change)!
9860           */
9861           SCAN_PLAYFIELD(xx, yy)
9862           {
9863             if (Feld[xx][yy] == element)
9864               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9865                                  CE_SCORE_GETS_ZERO);
9866           }
9867         }
9868       }
9869
9870       break;
9871     }
9872
9873     case CA_SET_CE_ARTWORK:
9874     {
9875       int artwork_element = action_arg_element;
9876       boolean reset_frame = FALSE;
9877       int xx, yy;
9878
9879       if (action_arg == CA_ARG_ELEMENT_RESET)
9880         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9881                            element);
9882
9883       if (ei->gfx_element != artwork_element)
9884         reset_frame = TRUE;
9885
9886       ei->gfx_element = artwork_element;
9887
9888       SCAN_PLAYFIELD(xx, yy)
9889       {
9890         if (Feld[xx][yy] == element)
9891         {
9892           if (reset_frame)
9893           {
9894             ResetGfxAnimation(xx, yy);
9895             ResetRandomAnimationValue(xx, yy);
9896           }
9897
9898           TEST_DrawLevelField(xx, yy);
9899         }
9900       }
9901
9902       break;
9903     }
9904
9905     /* ---------- engine actions  ------------------------------------------ */
9906
9907     case CA_SET_ENGINE_SCAN_MODE:
9908     {
9909       InitPlayfieldScanMode(action_arg);
9910
9911       break;
9912     }
9913
9914     default:
9915       break;
9916   }
9917 }
9918
9919 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9920 {
9921   int old_element = Feld[x][y];
9922   int new_element = GetElementFromGroupElement(element);
9923   int previous_move_direction = MovDir[x][y];
9924   int last_ce_value = CustomValue[x][y];
9925   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9926   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9927   boolean add_player_onto_element = (new_element_is_player &&
9928                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9929                                      IS_WALKABLE(old_element));
9930
9931   if (!add_player_onto_element)
9932   {
9933     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9934       RemoveMovingField(x, y);
9935     else
9936       RemoveField(x, y);
9937
9938     Feld[x][y] = new_element;
9939
9940     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9941       MovDir[x][y] = previous_move_direction;
9942
9943     if (element_info[new_element].use_last_ce_value)
9944       CustomValue[x][y] = last_ce_value;
9945
9946     InitField_WithBug1(x, y, FALSE);
9947
9948     new_element = Feld[x][y];   /* element may have changed */
9949
9950     ResetGfxAnimation(x, y);
9951     ResetRandomAnimationValue(x, y);
9952
9953     TEST_DrawLevelField(x, y);
9954
9955     if (GFX_CRUMBLED(new_element))
9956       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9957   }
9958
9959   /* check if element under the player changes from accessible to unaccessible
9960      (needed for special case of dropping element which then changes) */
9961   /* (must be checked after creating new element for walkable group elements) */
9962   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9963       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9964   {
9965     Bang(x, y);
9966
9967     return;
9968   }
9969
9970   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9971   if (new_element_is_player)
9972     RelocatePlayer(x, y, new_element);
9973
9974   if (is_change)
9975     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9976
9977   TestIfBadThingTouchesPlayer(x, y);
9978   TestIfPlayerTouchesCustomElement(x, y);
9979   TestIfElementTouchesCustomElement(x, y);
9980 }
9981
9982 static void CreateField(int x, int y, int element)
9983 {
9984   CreateFieldExt(x, y, element, FALSE);
9985 }
9986
9987 static void CreateElementFromChange(int x, int y, int element)
9988 {
9989   element = GET_VALID_RUNTIME_ELEMENT(element);
9990
9991   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9992   {
9993     int old_element = Feld[x][y];
9994
9995     /* prevent changed element from moving in same engine frame
9996        unless both old and new element can either fall or move */
9997     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9998         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9999       Stop[x][y] = TRUE;
10000   }
10001
10002   CreateFieldExt(x, y, element, TRUE);
10003 }
10004
10005 static boolean ChangeElement(int x, int y, int element, int page)
10006 {
10007   struct ElementInfo *ei = &element_info[element];
10008   struct ElementChangeInfo *change = &ei->change_page[page];
10009   int ce_value = CustomValue[x][y];
10010   int ce_score = ei->collect_score;
10011   int target_element;
10012   int old_element = Feld[x][y];
10013
10014   /* always use default change event to prevent running into a loop */
10015   if (ChangeEvent[x][y] == -1)
10016     ChangeEvent[x][y] = CE_DELAY;
10017
10018   if (ChangeEvent[x][y] == CE_DELAY)
10019   {
10020     /* reset actual trigger element, trigger player and action element */
10021     change->actual_trigger_element = EL_EMPTY;
10022     change->actual_trigger_player = EL_EMPTY;
10023     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10024     change->actual_trigger_side = CH_SIDE_NONE;
10025     change->actual_trigger_ce_value = 0;
10026     change->actual_trigger_ce_score = 0;
10027   }
10028
10029   /* do not change elements more than a specified maximum number of changes */
10030   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10031     return FALSE;
10032
10033   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10034
10035   if (change->explode)
10036   {
10037     Bang(x, y);
10038
10039     return TRUE;
10040   }
10041
10042   if (change->use_target_content)
10043   {
10044     boolean complete_replace = TRUE;
10045     boolean can_replace[3][3];
10046     int xx, yy;
10047
10048     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10049     {
10050       boolean is_empty;
10051       boolean is_walkable;
10052       boolean is_diggable;
10053       boolean is_collectible;
10054       boolean is_removable;
10055       boolean is_destructible;
10056       int ex = x + xx - 1;
10057       int ey = y + yy - 1;
10058       int content_element = change->target_content.e[xx][yy];
10059       int e;
10060
10061       can_replace[xx][yy] = TRUE;
10062
10063       if (ex == x && ey == y)   /* do not check changing element itself */
10064         continue;
10065
10066       if (content_element == EL_EMPTY_SPACE)
10067       {
10068         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10069
10070         continue;
10071       }
10072
10073       if (!IN_LEV_FIELD(ex, ey))
10074       {
10075         can_replace[xx][yy] = FALSE;
10076         complete_replace = FALSE;
10077
10078         continue;
10079       }
10080
10081       e = Feld[ex][ey];
10082
10083       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10084         e = MovingOrBlocked2Element(ex, ey);
10085
10086       is_empty = (IS_FREE(ex, ey) ||
10087                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10088
10089       is_walkable     = (is_empty || IS_WALKABLE(e));
10090       is_diggable     = (is_empty || IS_DIGGABLE(e));
10091       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10092       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10093       is_removable    = (is_diggable || is_collectible);
10094
10095       can_replace[xx][yy] =
10096         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10097           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10098           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10099           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10100           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10101           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10102          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10103
10104       if (!can_replace[xx][yy])
10105         complete_replace = FALSE;
10106     }
10107
10108     if (!change->only_if_complete || complete_replace)
10109     {
10110       boolean something_has_changed = FALSE;
10111
10112       if (change->only_if_complete && change->use_random_replace &&
10113           RND(100) < change->random_percentage)
10114         return FALSE;
10115
10116       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10117       {
10118         int ex = x + xx - 1;
10119         int ey = y + yy - 1;
10120         int content_element;
10121
10122         if (can_replace[xx][yy] && (!change->use_random_replace ||
10123                                     RND(100) < change->random_percentage))
10124         {
10125           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10126             RemoveMovingField(ex, ey);
10127
10128           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10129
10130           content_element = change->target_content.e[xx][yy];
10131           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10132                                               ce_value, ce_score);
10133
10134           CreateElementFromChange(ex, ey, target_element);
10135
10136           something_has_changed = TRUE;
10137
10138           /* for symmetry reasons, freeze newly created border elements */
10139           if (ex != x || ey != y)
10140             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10141         }
10142       }
10143
10144       if (something_has_changed)
10145       {
10146         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10147         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10148       }
10149     }
10150   }
10151   else
10152   {
10153     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10154                                         ce_value, ce_score);
10155
10156     if (element == EL_DIAGONAL_GROWING ||
10157         element == EL_DIAGONAL_SHRINKING)
10158     {
10159       target_element = Store[x][y];
10160
10161       Store[x][y] = EL_EMPTY;
10162     }
10163
10164     CreateElementFromChange(x, y, target_element);
10165
10166     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10167     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10168   }
10169
10170   /* this uses direct change before indirect change */
10171   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10172
10173   return TRUE;
10174 }
10175
10176 static void HandleElementChange(int x, int y, int page)
10177 {
10178   int element = MovingOrBlocked2Element(x, y);
10179   struct ElementInfo *ei = &element_info[element];
10180   struct ElementChangeInfo *change = &ei->change_page[page];
10181   boolean handle_action_before_change = FALSE;
10182
10183 #ifdef DEBUG
10184   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10185       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10186   {
10187     printf("\n\n");
10188     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10189            x, y, element, element_info[element].token_name);
10190     printf("HandleElementChange(): This should never happen!\n");
10191     printf("\n\n");
10192   }
10193 #endif
10194
10195   /* this can happen with classic bombs on walkable, changing elements */
10196   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10197   {
10198     return;
10199   }
10200
10201   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10202   {
10203     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10204
10205     if (change->can_change)
10206     {
10207       /* !!! not clear why graphic animation should be reset at all here !!! */
10208       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10209       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10210
10211       /*
10212         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10213
10214         When using an animation frame delay of 1 (this only happens with
10215         "sp_zonk.moving.left/right" in the classic graphics), the default
10216         (non-moving) animation shows wrong animation frames (while the
10217         moving animation, like "sp_zonk.moving.left/right", is correct,
10218         so this graphical bug never shows up with the classic graphics).
10219         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10220         be drawn instead of the correct frames 0,1,2,3. This is caused by
10221         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10222         an element change: First when the change delay ("ChangeDelay[][]")
10223         counter has reached zero after decrementing, then a second time in
10224         the next frame (after "GfxFrame[][]" was already incremented) when
10225         "ChangeDelay[][]" is reset to the initial delay value again.
10226
10227         This causes frame 0 to be drawn twice, while the last frame won't
10228         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10229
10230         As some animations may already be cleverly designed around this bug
10231         (at least the "Snake Bite" snake tail animation does this), it cannot
10232         simply be fixed here without breaking such existing animations.
10233         Unfortunately, it cannot easily be detected if a graphics set was
10234         designed "before" or "after" the bug was fixed. As a workaround,
10235         a new graphics set option "game.graphics_engine_version" was added
10236         to be able to specify the game's major release version for which the
10237         graphics set was designed, which can then be used to decide if the
10238         bugfix should be used (version 4 and above) or not (version 3 or
10239         below, or if no version was specified at all, as with old sets).
10240
10241         (The wrong/fixed animation frames can be tested with the test level set
10242         "test_gfxframe" and level "000", which contains a specially prepared
10243         custom element at level position (x/y) == (11/9) which uses the zonk
10244         animation mentioned above. Using "game.graphics_engine_version: 4"
10245         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10246         This can also be seen from the debug output for this test element.)
10247       */
10248
10249       /* when a custom element is about to change (for example by change delay),
10250          do not reset graphic animation when the custom element is moving */
10251       if (game.graphics_engine_version < 4 &&
10252           !IS_MOVING(x, y))
10253       {
10254         ResetGfxAnimation(x, y);
10255         ResetRandomAnimationValue(x, y);
10256       }
10257
10258       if (change->pre_change_function)
10259         change->pre_change_function(x, y);
10260     }
10261   }
10262
10263   ChangeDelay[x][y]--;
10264
10265   if (ChangeDelay[x][y] != 0)           /* continue element change */
10266   {
10267     if (change->can_change)
10268     {
10269       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10270
10271       if (IS_ANIMATED(graphic))
10272         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10273
10274       if (change->change_function)
10275         change->change_function(x, y);
10276     }
10277   }
10278   else                                  /* finish element change */
10279   {
10280     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10281     {
10282       page = ChangePage[x][y];
10283       ChangePage[x][y] = -1;
10284
10285       change = &ei->change_page[page];
10286     }
10287
10288     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10289     {
10290       ChangeDelay[x][y] = 1;            /* try change after next move step */
10291       ChangePage[x][y] = page;          /* remember page to use for change */
10292
10293       return;
10294     }
10295
10296     /* special case: set new level random seed before changing element */
10297     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10298       handle_action_before_change = TRUE;
10299
10300     if (change->has_action && handle_action_before_change)
10301       ExecuteCustomElementAction(x, y, element, page);
10302
10303     if (change->can_change)
10304     {
10305       if (ChangeElement(x, y, element, page))
10306       {
10307         if (change->post_change_function)
10308           change->post_change_function(x, y);
10309       }
10310     }
10311
10312     if (change->has_action && !handle_action_before_change)
10313       ExecuteCustomElementAction(x, y, element, page);
10314   }
10315 }
10316
10317 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10318                                               int trigger_element,
10319                                               int trigger_event,
10320                                               int trigger_player,
10321                                               int trigger_side,
10322                                               int trigger_page)
10323 {
10324   boolean change_done_any = FALSE;
10325   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10326   int i;
10327
10328   if (!(trigger_events[trigger_element][trigger_event]))
10329     return FALSE;
10330
10331   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10332
10333   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10334   {
10335     int element = EL_CUSTOM_START + i;
10336     boolean change_done = FALSE;
10337     int p;
10338
10339     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10340         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10341       continue;
10342
10343     for (p = 0; p < element_info[element].num_change_pages; p++)
10344     {
10345       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10346
10347       if (change->can_change_or_has_action &&
10348           change->has_event[trigger_event] &&
10349           change->trigger_side & trigger_side &&
10350           change->trigger_player & trigger_player &&
10351           change->trigger_page & trigger_page_bits &&
10352           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10353       {
10354         change->actual_trigger_element = trigger_element;
10355         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10356         change->actual_trigger_player_bits = trigger_player;
10357         change->actual_trigger_side = trigger_side;
10358         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10359         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10360
10361         if ((change->can_change && !change_done) || change->has_action)
10362         {
10363           int x, y;
10364
10365           SCAN_PLAYFIELD(x, y)
10366           {
10367             if (Feld[x][y] == element)
10368             {
10369               if (change->can_change && !change_done)
10370               {
10371                 /* if element already changed in this frame, not only prevent
10372                    another element change (checked in ChangeElement()), but
10373                    also prevent additional element actions for this element */
10374
10375                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10376                     !level.use_action_after_change_bug)
10377                   continue;
10378
10379                 ChangeDelay[x][y] = 1;
10380                 ChangeEvent[x][y] = trigger_event;
10381
10382                 HandleElementChange(x, y, p);
10383               }
10384               else if (change->has_action)
10385               {
10386                 /* if element already changed in this frame, not only prevent
10387                    another element change (checked in ChangeElement()), but
10388                    also prevent additional element actions for this element */
10389
10390                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10391                     !level.use_action_after_change_bug)
10392                   continue;
10393
10394                 ExecuteCustomElementAction(x, y, element, p);
10395                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10396               }
10397             }
10398           }
10399
10400           if (change->can_change)
10401           {
10402             change_done = TRUE;
10403             change_done_any = TRUE;
10404           }
10405         }
10406       }
10407     }
10408   }
10409
10410   RECURSION_LOOP_DETECTION_END();
10411
10412   return change_done_any;
10413 }
10414
10415 static boolean CheckElementChangeExt(int x, int y,
10416                                      int element,
10417                                      int trigger_element,
10418                                      int trigger_event,
10419                                      int trigger_player,
10420                                      int trigger_side)
10421 {
10422   boolean change_done = FALSE;
10423   int p;
10424
10425   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10426       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10427     return FALSE;
10428
10429   if (Feld[x][y] == EL_BLOCKED)
10430   {
10431     Blocked2Moving(x, y, &x, &y);
10432     element = Feld[x][y];
10433   }
10434
10435   /* check if element has already changed or is about to change after moving */
10436   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10437        Feld[x][y] != element) ||
10438
10439       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10440        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10441         ChangePage[x][y] != -1)))
10442     return FALSE;
10443
10444   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10445
10446   for (p = 0; p < element_info[element].num_change_pages; p++)
10447   {
10448     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10449
10450     /* check trigger element for all events where the element that is checked
10451        for changing interacts with a directly adjacent element -- this is
10452        different to element changes that affect other elements to change on the
10453        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10454     boolean check_trigger_element =
10455       (trigger_event == CE_TOUCHING_X ||
10456        trigger_event == CE_HITTING_X ||
10457        trigger_event == CE_HIT_BY_X ||
10458        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10459
10460     if (change->can_change_or_has_action &&
10461         change->has_event[trigger_event] &&
10462         change->trigger_side & trigger_side &&
10463         change->trigger_player & trigger_player &&
10464         (!check_trigger_element ||
10465          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10466     {
10467       change->actual_trigger_element = trigger_element;
10468       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10469       change->actual_trigger_player_bits = trigger_player;
10470       change->actual_trigger_side = trigger_side;
10471       change->actual_trigger_ce_value = CustomValue[x][y];
10472       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10473
10474       /* special case: trigger element not at (x,y) position for some events */
10475       if (check_trigger_element)
10476       {
10477         static struct
10478         {
10479           int dx, dy;
10480         } move_xy[] =
10481           {
10482             {  0,  0 },
10483             { -1,  0 },
10484             { +1,  0 },
10485             {  0,  0 },
10486             {  0, -1 },
10487             {  0,  0 }, { 0, 0 }, { 0, 0 },
10488             {  0, +1 }
10489           };
10490
10491         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10492         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10493
10494         change->actual_trigger_ce_value = CustomValue[xx][yy];
10495         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10496       }
10497
10498       if (change->can_change && !change_done)
10499       {
10500         ChangeDelay[x][y] = 1;
10501         ChangeEvent[x][y] = trigger_event;
10502
10503         HandleElementChange(x, y, p);
10504
10505         change_done = TRUE;
10506       }
10507       else if (change->has_action)
10508       {
10509         ExecuteCustomElementAction(x, y, element, p);
10510         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10511       }
10512     }
10513   }
10514
10515   RECURSION_LOOP_DETECTION_END();
10516
10517   return change_done;
10518 }
10519
10520 static void PlayPlayerSound(struct PlayerInfo *player)
10521 {
10522   int jx = player->jx, jy = player->jy;
10523   int sound_element = player->artwork_element;
10524   int last_action = player->last_action_waiting;
10525   int action = player->action_waiting;
10526
10527   if (player->is_waiting)
10528   {
10529     if (action != last_action)
10530       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10531     else
10532       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10533   }
10534   else
10535   {
10536     if (action != last_action)
10537       StopSound(element_info[sound_element].sound[last_action]);
10538
10539     if (last_action == ACTION_SLEEPING)
10540       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10541   }
10542 }
10543
10544 static void PlayAllPlayersSound()
10545 {
10546   int i;
10547
10548   for (i = 0; i < MAX_PLAYERS; i++)
10549     if (stored_player[i].active)
10550       PlayPlayerSound(&stored_player[i]);
10551 }
10552
10553 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10554 {
10555   boolean last_waiting = player->is_waiting;
10556   int move_dir = player->MovDir;
10557
10558   player->dir_waiting = move_dir;
10559   player->last_action_waiting = player->action_waiting;
10560
10561   if (is_waiting)
10562   {
10563     if (!last_waiting)          /* not waiting -> waiting */
10564     {
10565       player->is_waiting = TRUE;
10566
10567       player->frame_counter_bored =
10568         FrameCounter +
10569         game.player_boring_delay_fixed +
10570         GetSimpleRandom(game.player_boring_delay_random);
10571       player->frame_counter_sleeping =
10572         FrameCounter +
10573         game.player_sleeping_delay_fixed +
10574         GetSimpleRandom(game.player_sleeping_delay_random);
10575
10576       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10577     }
10578
10579     if (game.player_sleeping_delay_fixed +
10580         game.player_sleeping_delay_random > 0 &&
10581         player->anim_delay_counter == 0 &&
10582         player->post_delay_counter == 0 &&
10583         FrameCounter >= player->frame_counter_sleeping)
10584       player->is_sleeping = TRUE;
10585     else if (game.player_boring_delay_fixed +
10586              game.player_boring_delay_random > 0 &&
10587              FrameCounter >= player->frame_counter_bored)
10588       player->is_bored = TRUE;
10589
10590     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10591                               player->is_bored ? ACTION_BORING :
10592                               ACTION_WAITING);
10593
10594     if (player->is_sleeping && player->use_murphy)
10595     {
10596       /* special case for sleeping Murphy when leaning against non-free tile */
10597
10598       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10599           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10600            !IS_MOVING(player->jx - 1, player->jy)))
10601         move_dir = MV_LEFT;
10602       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10603                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10604                 !IS_MOVING(player->jx + 1, player->jy)))
10605         move_dir = MV_RIGHT;
10606       else
10607         player->is_sleeping = FALSE;
10608
10609       player->dir_waiting = move_dir;
10610     }
10611
10612     if (player->is_sleeping)
10613     {
10614       if (player->num_special_action_sleeping > 0)
10615       {
10616         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10617         {
10618           int last_special_action = player->special_action_sleeping;
10619           int num_special_action = player->num_special_action_sleeping;
10620           int special_action =
10621             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10622              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10623              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10624              last_special_action + 1 : ACTION_SLEEPING);
10625           int special_graphic =
10626             el_act_dir2img(player->artwork_element, special_action, move_dir);
10627
10628           player->anim_delay_counter =
10629             graphic_info[special_graphic].anim_delay_fixed +
10630             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10631           player->post_delay_counter =
10632             graphic_info[special_graphic].post_delay_fixed +
10633             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10634
10635           player->special_action_sleeping = special_action;
10636         }
10637
10638         if (player->anim_delay_counter > 0)
10639         {
10640           player->action_waiting = player->special_action_sleeping;
10641           player->anim_delay_counter--;
10642         }
10643         else if (player->post_delay_counter > 0)
10644         {
10645           player->post_delay_counter--;
10646         }
10647       }
10648     }
10649     else if (player->is_bored)
10650     {
10651       if (player->num_special_action_bored > 0)
10652       {
10653         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10654         {
10655           int special_action =
10656             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10657           int special_graphic =
10658             el_act_dir2img(player->artwork_element, special_action, move_dir);
10659
10660           player->anim_delay_counter =
10661             graphic_info[special_graphic].anim_delay_fixed +
10662             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10663           player->post_delay_counter =
10664             graphic_info[special_graphic].post_delay_fixed +
10665             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10666
10667           player->special_action_bored = special_action;
10668         }
10669
10670         if (player->anim_delay_counter > 0)
10671         {
10672           player->action_waiting = player->special_action_bored;
10673           player->anim_delay_counter--;
10674         }
10675         else if (player->post_delay_counter > 0)
10676         {
10677           player->post_delay_counter--;
10678         }
10679       }
10680     }
10681   }
10682   else if (last_waiting)        /* waiting -> not waiting */
10683   {
10684     player->is_waiting = FALSE;
10685     player->is_bored = FALSE;
10686     player->is_sleeping = FALSE;
10687
10688     player->frame_counter_bored = -1;
10689     player->frame_counter_sleeping = -1;
10690
10691     player->anim_delay_counter = 0;
10692     player->post_delay_counter = 0;
10693
10694     player->dir_waiting = player->MovDir;
10695     player->action_waiting = ACTION_DEFAULT;
10696
10697     player->special_action_bored = ACTION_DEFAULT;
10698     player->special_action_sleeping = ACTION_DEFAULT;
10699   }
10700 }
10701
10702 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10703 {
10704   if ((!player->is_moving  && player->was_moving) ||
10705       (player->MovPos == 0 && player->was_moving) ||
10706       (player->is_snapping && !player->was_snapping) ||
10707       (player->is_dropping && !player->was_dropping))
10708   {
10709     if (!CheckSaveEngineSnapshotToList())
10710       return;
10711
10712     player->was_moving = FALSE;
10713     player->was_snapping = TRUE;
10714     player->was_dropping = TRUE;
10715   }
10716   else
10717   {
10718     if (player->is_moving)
10719       player->was_moving = TRUE;
10720
10721     if (!player->is_snapping)
10722       player->was_snapping = FALSE;
10723
10724     if (!player->is_dropping)
10725       player->was_dropping = FALSE;
10726   }
10727 }
10728
10729 static void CheckSingleStepMode(struct PlayerInfo *player)
10730 {
10731   if (tape.single_step && tape.recording && !tape.pausing)
10732   {
10733     /* as it is called "single step mode", just return to pause mode when the
10734        player stopped moving after one tile (or never starts moving at all) */
10735     if (!player->is_moving && !player->is_pushing)
10736     {
10737       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10738       SnapField(player, 0, 0);                  /* stop snapping */
10739     }
10740   }
10741
10742   CheckSaveEngineSnapshot(player);
10743 }
10744
10745 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10746 {
10747   int left      = player_action & JOY_LEFT;
10748   int right     = player_action & JOY_RIGHT;
10749   int up        = player_action & JOY_UP;
10750   int down      = player_action & JOY_DOWN;
10751   int button1   = player_action & JOY_BUTTON_1;
10752   int button2   = player_action & JOY_BUTTON_2;
10753   int dx        = (left ? -1 : right ? 1 : 0);
10754   int dy        = (up   ? -1 : down  ? 1 : 0);
10755
10756   if (!player->active || tape.pausing)
10757     return 0;
10758
10759   if (player_action)
10760   {
10761     if (button1)
10762       SnapField(player, dx, dy);
10763     else
10764     {
10765       if (button2)
10766         DropElement(player);
10767
10768       MovePlayer(player, dx, dy);
10769     }
10770
10771     CheckSingleStepMode(player);
10772
10773     SetPlayerWaiting(player, FALSE);
10774
10775     return player_action;
10776   }
10777   else
10778   {
10779     /* no actions for this player (no input at player's configured device) */
10780
10781     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10782     SnapField(player, 0, 0);
10783     CheckGravityMovementWhenNotMoving(player);
10784
10785     if (player->MovPos == 0)
10786       SetPlayerWaiting(player, TRUE);
10787
10788     if (player->MovPos == 0)    /* needed for tape.playing */
10789       player->is_moving = FALSE;
10790
10791     player->is_dropping = FALSE;
10792     player->is_dropping_pressed = FALSE;
10793     player->drop_pressed_delay = 0;
10794
10795     CheckSingleStepMode(player);
10796
10797     return 0;
10798   }
10799 }
10800
10801 static void CheckLevelTime()
10802 {
10803   int i;
10804
10805   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10806   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10807   {
10808     if (level.native_em_level->lev->home == 0)  /* all players at home */
10809     {
10810       PlayerWins(local_player);
10811
10812       AllPlayersGone = TRUE;
10813
10814       level.native_em_level->lev->home = -1;
10815     }
10816
10817     if (level.native_em_level->ply[0]->alive == 0 &&
10818         level.native_em_level->ply[1]->alive == 0 &&
10819         level.native_em_level->ply[2]->alive == 0 &&
10820         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10821       AllPlayersGone = TRUE;
10822   }
10823   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10824   {
10825     if (game_sp.LevelSolved &&
10826         !game_sp.GameOver)                              /* game won */
10827     {
10828       PlayerWins(local_player);
10829
10830       game_sp.GameOver = TRUE;
10831
10832       AllPlayersGone = TRUE;
10833     }
10834
10835     if (game_sp.GameOver)                               /* game lost */
10836       AllPlayersGone = TRUE;
10837   }
10838
10839   if (TimeFrames >= FRAMES_PER_SECOND)
10840   {
10841     TimeFrames = 0;
10842     TapeTime++;
10843
10844     for (i = 0; i < MAX_PLAYERS; i++)
10845     {
10846       struct PlayerInfo *player = &stored_player[i];
10847
10848       if (SHIELD_ON(player))
10849       {
10850         player->shield_normal_time_left--;
10851
10852         if (player->shield_deadly_time_left > 0)
10853           player->shield_deadly_time_left--;
10854       }
10855     }
10856
10857     if (!local_player->LevelSolved && !level.use_step_counter)
10858     {
10859       TimePlayed++;
10860
10861       if (TimeLeft > 0)
10862       {
10863         TimeLeft--;
10864
10865         if (TimeLeft <= 10 && setup.time_limit)
10866           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10867
10868         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10869            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10870
10871         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10872
10873         if (!TimeLeft && setup.time_limit)
10874         {
10875           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10876             level.native_em_level->lev->killed_out_of_time = TRUE;
10877           else
10878             for (i = 0; i < MAX_PLAYERS; i++)
10879               KillPlayer(&stored_player[i]);
10880         }
10881       }
10882       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10883       {
10884         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10885       }
10886
10887       level.native_em_level->lev->time =
10888         (game.no_time_limit ? TimePlayed : TimeLeft);
10889     }
10890
10891     if (tape.recording || tape.playing)
10892       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10893   }
10894
10895   if (tape.recording || tape.playing)
10896     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10897
10898   UpdateAndDisplayGameControlValues();
10899 }
10900
10901 void AdvanceFrameAndPlayerCounters(int player_nr)
10902 {
10903   int i;
10904
10905   /* advance frame counters (global frame counter and time frame counter) */
10906   FrameCounter++;
10907   TimeFrames++;
10908
10909   /* advance player counters (counters for move delay, move animation etc.) */
10910   for (i = 0; i < MAX_PLAYERS; i++)
10911   {
10912     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10913     int move_delay_value = stored_player[i].move_delay_value;
10914     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10915
10916     if (!advance_player_counters)       /* not all players may be affected */
10917       continue;
10918
10919     if (move_frames == 0)       /* less than one move per game frame */
10920     {
10921       int stepsize = TILEX / move_delay_value;
10922       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10923       int count = (stored_player[i].is_moving ?
10924                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10925
10926       if (count % delay == 0)
10927         move_frames = 1;
10928     }
10929
10930     stored_player[i].Frame += move_frames;
10931
10932     if (stored_player[i].MovPos != 0)
10933       stored_player[i].StepFrame += move_frames;
10934
10935     if (stored_player[i].move_delay > 0)
10936       stored_player[i].move_delay--;
10937
10938     /* due to bugs in previous versions, counter must count up, not down */
10939     if (stored_player[i].push_delay != -1)
10940       stored_player[i].push_delay++;
10941
10942     if (stored_player[i].drop_delay > 0)
10943       stored_player[i].drop_delay--;
10944
10945     if (stored_player[i].is_dropping_pressed)
10946       stored_player[i].drop_pressed_delay++;
10947   }
10948 }
10949
10950 void StartGameActions(boolean init_network_game, boolean record_tape,
10951                       int random_seed)
10952 {
10953   unsigned int new_random_seed = InitRND(random_seed);
10954
10955   if (record_tape)
10956     TapeStartRecording(new_random_seed);
10957
10958 #if defined(NETWORK_AVALIABLE)
10959   if (init_network_game)
10960   {
10961     SendToServer_StartPlaying();
10962
10963     return;
10964   }
10965 #endif
10966
10967   InitGame();
10968 }
10969
10970 void GameActionsExt()
10971 {
10972 #if 0
10973   static unsigned int game_frame_delay = 0;
10974 #endif
10975   unsigned int game_frame_delay_value;
10976   byte *recorded_player_action;
10977   byte summarized_player_action = 0;
10978   byte tape_action[MAX_PLAYERS];
10979   int i;
10980
10981   /* detect endless loops, caused by custom element programming */
10982   if (recursion_loop_detected && recursion_loop_depth == 0)
10983   {
10984     char *message = getStringCat3("Internal Error! Element ",
10985                                   EL_NAME(recursion_loop_element),
10986                                   " caused endless loop! Quit the game?");
10987
10988     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10989           EL_NAME(recursion_loop_element));
10990
10991     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10992
10993     recursion_loop_detected = FALSE;    /* if game should be continued */
10994
10995     free(message);
10996
10997     return;
10998   }
10999
11000   if (game.restart_level)
11001     StartGameActions(options.network, setup.autorecord, level.random_seed);
11002
11003   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11004   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11005   {
11006     if (level.native_em_level->lev->home == 0)  /* all players at home */
11007     {
11008       PlayerWins(local_player);
11009
11010       AllPlayersGone = TRUE;
11011
11012       level.native_em_level->lev->home = -1;
11013     }
11014
11015     if (level.native_em_level->ply[0]->alive == 0 &&
11016         level.native_em_level->ply[1]->alive == 0 &&
11017         level.native_em_level->ply[2]->alive == 0 &&
11018         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11019       AllPlayersGone = TRUE;
11020   }
11021   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11022   {
11023     if (game_sp.LevelSolved &&
11024         !game_sp.GameOver)                              /* game won */
11025     {
11026       PlayerWins(local_player);
11027
11028       game_sp.GameOver = TRUE;
11029
11030       AllPlayersGone = TRUE;
11031     }
11032
11033     if (game_sp.GameOver)                               /* game lost */
11034       AllPlayersGone = TRUE;
11035   }
11036
11037   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11038     GameWon();
11039
11040   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11041     TapeStop();
11042
11043   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11044     return;
11045
11046   game_frame_delay_value =
11047     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11048
11049   if (tape.playing && tape.warp_forward && !tape.pausing)
11050     game_frame_delay_value = 0;
11051
11052   SetVideoFrameDelay(game_frame_delay_value);
11053
11054 #if 0
11055 #if 0
11056   /* ---------- main game synchronization point ---------- */
11057
11058   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11059
11060   printf("::: skip == %d\n", skip);
11061
11062 #else
11063   /* ---------- main game synchronization point ---------- */
11064
11065   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11066 #endif
11067 #endif
11068
11069   if (network_playing && !network_player_action_received)
11070   {
11071     /* try to get network player actions in time */
11072
11073 #if defined(NETWORK_AVALIABLE)
11074     /* last chance to get network player actions without main loop delay */
11075     HandleNetworking();
11076 #endif
11077
11078     /* game was quit by network peer */
11079     if (game_status != GAME_MODE_PLAYING)
11080       return;
11081
11082     if (!network_player_action_received)
11083       return;           /* failed to get network player actions in time */
11084
11085     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11086   }
11087
11088   if (tape.pausing)
11089     return;
11090
11091   /* at this point we know that we really continue executing the game */
11092
11093   network_player_action_received = FALSE;
11094
11095   /* when playing tape, read previously recorded player input from tape data */
11096   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11097
11098   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11099   if (tape.pausing)
11100     return;
11101
11102   if (tape.set_centered_player)
11103   {
11104     game.centered_player_nr_next = tape.centered_player_nr_next;
11105     game.set_centered_player = TRUE;
11106   }
11107
11108   for (i = 0; i < MAX_PLAYERS; i++)
11109   {
11110     summarized_player_action |= stored_player[i].action;
11111
11112     if (!network_playing && (game.team_mode || tape.playing))
11113       stored_player[i].effective_action = stored_player[i].action;
11114   }
11115
11116 #if defined(NETWORK_AVALIABLE)
11117   if (network_playing)
11118     SendToServer_MovePlayer(summarized_player_action);
11119 #endif
11120
11121   // summarize all actions at local players mapped input device position
11122   // (this allows using different input devices in single player mode)
11123   if (!options.network && !game.team_mode)
11124     stored_player[map_player_action[local_player->index_nr]].effective_action =
11125       summarized_player_action;
11126
11127   if (tape.recording &&
11128       setup.team_mode &&
11129       setup.input_on_focus &&
11130       game.centered_player_nr != -1)
11131   {
11132     for (i = 0; i < MAX_PLAYERS; i++)
11133       stored_player[i].effective_action =
11134         (i == game.centered_player_nr ? summarized_player_action : 0);
11135   }
11136
11137   if (recorded_player_action != NULL)
11138     for (i = 0; i < MAX_PLAYERS; i++)
11139       stored_player[i].effective_action = recorded_player_action[i];
11140
11141   for (i = 0; i < MAX_PLAYERS; i++)
11142   {
11143     tape_action[i] = stored_player[i].effective_action;
11144
11145     /* (this may happen in the RND game engine if a player was not present on
11146        the playfield on level start, but appeared later from a custom element */
11147     if (setup.team_mode &&
11148         tape.recording &&
11149         tape_action[i] &&
11150         !tape.player_participates[i])
11151       tape.player_participates[i] = TRUE;
11152   }
11153
11154   /* only record actions from input devices, but not programmed actions */
11155   if (tape.recording)
11156     TapeRecordAction(tape_action);
11157
11158 #if USE_NEW_PLAYER_ASSIGNMENTS
11159   // !!! also map player actions in single player mode !!!
11160   // if (game.team_mode)
11161   if (1)
11162   {
11163     byte mapped_action[MAX_PLAYERS];
11164
11165 #if DEBUG_PLAYER_ACTIONS
11166     printf(":::");
11167     for (i = 0; i < MAX_PLAYERS; i++)
11168       printf(" %d, ", stored_player[i].effective_action);
11169 #endif
11170
11171     for (i = 0; i < MAX_PLAYERS; i++)
11172       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11173
11174     for (i = 0; i < MAX_PLAYERS; i++)
11175       stored_player[i].effective_action = mapped_action[i];
11176
11177 #if DEBUG_PLAYER_ACTIONS
11178     printf(" =>");
11179     for (i = 0; i < MAX_PLAYERS; i++)
11180       printf(" %d, ", stored_player[i].effective_action);
11181     printf("\n");
11182 #endif
11183   }
11184 #if DEBUG_PLAYER_ACTIONS
11185   else
11186   {
11187     printf(":::");
11188     for (i = 0; i < MAX_PLAYERS; i++)
11189       printf(" %d, ", stored_player[i].effective_action);
11190     printf("\n");
11191   }
11192 #endif
11193 #endif
11194
11195   for (i = 0; i < MAX_PLAYERS; i++)
11196   {
11197     // allow engine snapshot in case of changed movement attempt
11198     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11199         (stored_player[i].effective_action & KEY_MOTION))
11200       game.snapshot.changed_action = TRUE;
11201
11202     // allow engine snapshot in case of snapping/dropping attempt
11203     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11204         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11205       game.snapshot.changed_action = TRUE;
11206
11207     game.snapshot.last_action[i] = stored_player[i].effective_action;
11208   }
11209
11210   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11211   {
11212     GameActions_EM_Main();
11213   }
11214   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11215   {
11216     GameActions_SP_Main();
11217   }
11218   else
11219   {
11220     GameActions_RND_Main();
11221   }
11222
11223   BlitScreenToBitmap(backbuffer);
11224
11225   CheckLevelTime();
11226
11227   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11228
11229   if (options.debug)                    /* calculate frames per second */
11230   {
11231     static unsigned int fps_counter = 0;
11232     static int fps_frames = 0;
11233     unsigned int fps_delay_ms = Counter() - fps_counter;
11234
11235     fps_frames++;
11236
11237     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11238     {
11239       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11240
11241       fps_frames = 0;
11242       fps_counter = Counter();
11243     }
11244
11245     redraw_mask |= REDRAW_FPS;
11246   }
11247 }
11248
11249 static void GameActions_CheckSaveEngineSnapshot()
11250 {
11251   if (!game.snapshot.save_snapshot)
11252     return;
11253
11254   // clear flag for saving snapshot _before_ saving snapshot
11255   game.snapshot.save_snapshot = FALSE;
11256
11257   SaveEngineSnapshotToList();
11258 }
11259
11260 void GameActions()
11261 {
11262   GameActionsExt();
11263
11264   GameActions_CheckSaveEngineSnapshot();
11265 }
11266
11267 void GameActions_EM_Main()
11268 {
11269   byte effective_action[MAX_PLAYERS];
11270   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11271   int i;
11272
11273   for (i = 0; i < MAX_PLAYERS; i++)
11274     effective_action[i] = stored_player[i].effective_action;
11275
11276   GameActions_EM(effective_action, warp_mode);
11277 }
11278
11279 void GameActions_SP_Main()
11280 {
11281   byte effective_action[MAX_PLAYERS];
11282   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11283   int i;
11284
11285   for (i = 0; i < MAX_PLAYERS; i++)
11286     effective_action[i] = stored_player[i].effective_action;
11287
11288   GameActions_SP(effective_action, warp_mode);
11289 }
11290
11291 void GameActions_RND_Main()
11292 {
11293   GameActions_RND();
11294 }
11295
11296 void GameActions_RND()
11297 {
11298   int magic_wall_x = 0, magic_wall_y = 0;
11299   int i, x, y, element, graphic;
11300
11301   InitPlayfieldScanModeVars();
11302
11303   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11304   {
11305     SCAN_PLAYFIELD(x, y)
11306     {
11307       ChangeCount[x][y] = 0;
11308       ChangeEvent[x][y] = -1;
11309     }
11310   }
11311
11312   if (game.set_centered_player)
11313   {
11314     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11315
11316     /* switching to "all players" only possible if all players fit to screen */
11317     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11318     {
11319       game.centered_player_nr_next = game.centered_player_nr;
11320       game.set_centered_player = FALSE;
11321     }
11322
11323     /* do not switch focus to non-existing (or non-active) player */
11324     if (game.centered_player_nr_next >= 0 &&
11325         !stored_player[game.centered_player_nr_next].active)
11326     {
11327       game.centered_player_nr_next = game.centered_player_nr;
11328       game.set_centered_player = FALSE;
11329     }
11330   }
11331
11332   if (game.set_centered_player &&
11333       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11334   {
11335     int sx, sy;
11336
11337     if (game.centered_player_nr_next == -1)
11338     {
11339       setScreenCenteredToAllPlayers(&sx, &sy);
11340     }
11341     else
11342     {
11343       sx = stored_player[game.centered_player_nr_next].jx;
11344       sy = stored_player[game.centered_player_nr_next].jy;
11345     }
11346
11347     game.centered_player_nr = game.centered_player_nr_next;
11348     game.set_centered_player = FALSE;
11349
11350     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11351     DrawGameDoorValues();
11352   }
11353
11354   for (i = 0; i < MAX_PLAYERS; i++)
11355   {
11356     int actual_player_action = stored_player[i].effective_action;
11357
11358 #if 1
11359     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11360        - rnd_equinox_tetrachloride 048
11361        - rnd_equinox_tetrachloride_ii 096
11362        - rnd_emanuel_schmieg 002
11363        - doctor_sloan_ww 001, 020
11364     */
11365     if (stored_player[i].MovPos == 0)
11366       CheckGravityMovement(&stored_player[i]);
11367 #endif
11368
11369     /* overwrite programmed action with tape action */
11370     if (stored_player[i].programmed_action)
11371       actual_player_action = stored_player[i].programmed_action;
11372
11373     PlayerActions(&stored_player[i], actual_player_action);
11374
11375     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11376   }
11377
11378   ScrollScreen(NULL, SCROLL_GO_ON);
11379
11380   /* for backwards compatibility, the following code emulates a fixed bug that
11381      occured when pushing elements (causing elements that just made their last
11382      pushing step to already (if possible) make their first falling step in the
11383      same game frame, which is bad); this code is also needed to use the famous
11384      "spring push bug" which is used in older levels and might be wanted to be
11385      used also in newer levels, but in this case the buggy pushing code is only
11386      affecting the "spring" element and no other elements */
11387
11388   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11389   {
11390     for (i = 0; i < MAX_PLAYERS; i++)
11391     {
11392       struct PlayerInfo *player = &stored_player[i];
11393       int x = player->jx;
11394       int y = player->jy;
11395
11396       if (player->active && player->is_pushing && player->is_moving &&
11397           IS_MOVING(x, y) &&
11398           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11399            Feld[x][y] == EL_SPRING))
11400       {
11401         ContinueMoving(x, y);
11402
11403         /* continue moving after pushing (this is actually a bug) */
11404         if (!IS_MOVING(x, y))
11405           Stop[x][y] = FALSE;
11406       }
11407     }
11408   }
11409
11410   SCAN_PLAYFIELD(x, y)
11411   {
11412     ChangeCount[x][y] = 0;
11413     ChangeEvent[x][y] = -1;
11414
11415     /* this must be handled before main playfield loop */
11416     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11417     {
11418       MovDelay[x][y]--;
11419       if (MovDelay[x][y] <= 0)
11420         RemoveField(x, y);
11421     }
11422
11423     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11424     {
11425       MovDelay[x][y]--;
11426       if (MovDelay[x][y] <= 0)
11427       {
11428         RemoveField(x, y);
11429         TEST_DrawLevelField(x, y);
11430
11431         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11432       }
11433     }
11434
11435 #if DEBUG
11436     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11437     {
11438       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11439       printf("GameActions(): This should never happen!\n");
11440
11441       ChangePage[x][y] = -1;
11442     }
11443 #endif
11444
11445     Stop[x][y] = FALSE;
11446     if (WasJustMoving[x][y] > 0)
11447       WasJustMoving[x][y]--;
11448     if (WasJustFalling[x][y] > 0)
11449       WasJustFalling[x][y]--;
11450     if (CheckCollision[x][y] > 0)
11451       CheckCollision[x][y]--;
11452     if (CheckImpact[x][y] > 0)
11453       CheckImpact[x][y]--;
11454
11455     GfxFrame[x][y]++;
11456
11457     /* reset finished pushing action (not done in ContinueMoving() to allow
11458        continuous pushing animation for elements with zero push delay) */
11459     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11460     {
11461       ResetGfxAnimation(x, y);
11462       TEST_DrawLevelField(x, y);
11463     }
11464
11465 #if DEBUG
11466     if (IS_BLOCKED(x, y))
11467     {
11468       int oldx, oldy;
11469
11470       Blocked2Moving(x, y, &oldx, &oldy);
11471       if (!IS_MOVING(oldx, oldy))
11472       {
11473         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11474         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11475         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11476         printf("GameActions(): This should never happen!\n");
11477       }
11478     }
11479 #endif
11480   }
11481
11482   SCAN_PLAYFIELD(x, y)
11483   {
11484     element = Feld[x][y];
11485     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11486
11487     ResetGfxFrame(x, y, TRUE);
11488
11489     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11490         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11491       ResetRandomAnimationValue(x, y);
11492
11493     SetRandomAnimationValue(x, y);
11494
11495     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11496
11497     if (IS_INACTIVE(element))
11498     {
11499       if (IS_ANIMATED(graphic))
11500         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11501
11502       continue;
11503     }
11504
11505     /* this may take place after moving, so 'element' may have changed */
11506     if (IS_CHANGING(x, y) &&
11507         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11508     {
11509       int page = element_info[element].event_page_nr[CE_DELAY];
11510
11511       HandleElementChange(x, y, page);
11512
11513       element = Feld[x][y];
11514       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11515     }
11516
11517     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11518     {
11519       StartMoving(x, y);
11520
11521       element = Feld[x][y];
11522       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11523
11524       if (IS_ANIMATED(graphic) &&
11525           !IS_MOVING(x, y) &&
11526           !Stop[x][y])
11527         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11528
11529       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11530         TEST_DrawTwinkleOnField(x, y);
11531     }
11532     else if ((element == EL_ACID ||
11533               element == EL_EXIT_OPEN ||
11534               element == EL_EM_EXIT_OPEN ||
11535               element == EL_SP_EXIT_OPEN ||
11536               element == EL_STEEL_EXIT_OPEN ||
11537               element == EL_EM_STEEL_EXIT_OPEN ||
11538               element == EL_SP_TERMINAL ||
11539               element == EL_SP_TERMINAL_ACTIVE ||
11540               element == EL_EXTRA_TIME ||
11541               element == EL_SHIELD_NORMAL ||
11542               element == EL_SHIELD_DEADLY) &&
11543              IS_ANIMATED(graphic))
11544       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11545     else if (IS_MOVING(x, y))
11546       ContinueMoving(x, y);
11547     else if (IS_ACTIVE_BOMB(element))
11548       CheckDynamite(x, y);
11549     else if (element == EL_AMOEBA_GROWING)
11550       AmoebeWaechst(x, y);
11551     else if (element == EL_AMOEBA_SHRINKING)
11552       AmoebaDisappearing(x, y);
11553
11554 #if !USE_NEW_AMOEBA_CODE
11555     else if (IS_AMOEBALIVE(element))
11556       AmoebeAbleger(x, y);
11557 #endif
11558
11559     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11560       Life(x, y);
11561     else if (element == EL_EXIT_CLOSED)
11562       CheckExit(x, y);
11563     else if (element == EL_EM_EXIT_CLOSED)
11564       CheckExitEM(x, y);
11565     else if (element == EL_STEEL_EXIT_CLOSED)
11566       CheckExitSteel(x, y);
11567     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11568       CheckExitSteelEM(x, y);
11569     else if (element == EL_SP_EXIT_CLOSED)
11570       CheckExitSP(x, y);
11571     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11572              element == EL_EXPANDABLE_STEELWALL_GROWING)
11573       MauerWaechst(x, y);
11574     else if (element == EL_EXPANDABLE_WALL ||
11575              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11576              element == EL_EXPANDABLE_WALL_VERTICAL ||
11577              element == EL_EXPANDABLE_WALL_ANY ||
11578              element == EL_BD_EXPANDABLE_WALL)
11579       MauerAbleger(x, y);
11580     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11581              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11582              element == EL_EXPANDABLE_STEELWALL_ANY)
11583       MauerAblegerStahl(x, y);
11584     else if (element == EL_FLAMES)
11585       CheckForDragon(x, y);
11586     else if (element == EL_EXPLOSION)
11587       ; /* drawing of correct explosion animation is handled separately */
11588     else if (element == EL_ELEMENT_SNAPPING ||
11589              element == EL_DIAGONAL_SHRINKING ||
11590              element == EL_DIAGONAL_GROWING)
11591     {
11592       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11593
11594       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11595     }
11596     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11597       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11598
11599     if (IS_BELT_ACTIVE(element))
11600       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11601
11602     if (game.magic_wall_active)
11603     {
11604       int jx = local_player->jx, jy = local_player->jy;
11605
11606       /* play the element sound at the position nearest to the player */
11607       if ((element == EL_MAGIC_WALL_FULL ||
11608            element == EL_MAGIC_WALL_ACTIVE ||
11609            element == EL_MAGIC_WALL_EMPTYING ||
11610            element == EL_BD_MAGIC_WALL_FULL ||
11611            element == EL_BD_MAGIC_WALL_ACTIVE ||
11612            element == EL_BD_MAGIC_WALL_EMPTYING ||
11613            element == EL_DC_MAGIC_WALL_FULL ||
11614            element == EL_DC_MAGIC_WALL_ACTIVE ||
11615            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11616           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11617       {
11618         magic_wall_x = x;
11619         magic_wall_y = y;
11620       }
11621     }
11622   }
11623
11624 #if USE_NEW_AMOEBA_CODE
11625   /* new experimental amoeba growth stuff */
11626   if (!(FrameCounter % 8))
11627   {
11628     static unsigned int random = 1684108901;
11629
11630     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11631     {
11632       x = RND(lev_fieldx);
11633       y = RND(lev_fieldy);
11634       element = Feld[x][y];
11635
11636       if (!IS_PLAYER(x,y) &&
11637           (element == EL_EMPTY ||
11638            CAN_GROW_INTO(element) ||
11639            element == EL_QUICKSAND_EMPTY ||
11640            element == EL_QUICKSAND_FAST_EMPTY ||
11641            element == EL_ACID_SPLASH_LEFT ||
11642            element == EL_ACID_SPLASH_RIGHT))
11643       {
11644         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11645             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11646             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11647             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11648           Feld[x][y] = EL_AMOEBA_DROP;
11649       }
11650
11651       random = random * 129 + 1;
11652     }
11653   }
11654 #endif
11655
11656   game.explosions_delayed = FALSE;
11657
11658   SCAN_PLAYFIELD(x, y)
11659   {
11660     element = Feld[x][y];
11661
11662     if (ExplodeField[x][y])
11663       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11664     else if (element == EL_EXPLOSION)
11665       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11666
11667     ExplodeField[x][y] = EX_TYPE_NONE;
11668   }
11669
11670   game.explosions_delayed = TRUE;
11671
11672   if (game.magic_wall_active)
11673   {
11674     if (!(game.magic_wall_time_left % 4))
11675     {
11676       int element = Feld[magic_wall_x][magic_wall_y];
11677
11678       if (element == EL_BD_MAGIC_WALL_FULL ||
11679           element == EL_BD_MAGIC_WALL_ACTIVE ||
11680           element == EL_BD_MAGIC_WALL_EMPTYING)
11681         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11682       else if (element == EL_DC_MAGIC_WALL_FULL ||
11683                element == EL_DC_MAGIC_WALL_ACTIVE ||
11684                element == EL_DC_MAGIC_WALL_EMPTYING)
11685         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11686       else
11687         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11688     }
11689
11690     if (game.magic_wall_time_left > 0)
11691     {
11692       game.magic_wall_time_left--;
11693
11694       if (!game.magic_wall_time_left)
11695       {
11696         SCAN_PLAYFIELD(x, y)
11697         {
11698           element = Feld[x][y];
11699
11700           if (element == EL_MAGIC_WALL_ACTIVE ||
11701               element == EL_MAGIC_WALL_FULL)
11702           {
11703             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11704             TEST_DrawLevelField(x, y);
11705           }
11706           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11707                    element == EL_BD_MAGIC_WALL_FULL)
11708           {
11709             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11710             TEST_DrawLevelField(x, y);
11711           }
11712           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11713                    element == EL_DC_MAGIC_WALL_FULL)
11714           {
11715             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11716             TEST_DrawLevelField(x, y);
11717           }
11718         }
11719
11720         game.magic_wall_active = FALSE;
11721       }
11722     }
11723   }
11724
11725   if (game.light_time_left > 0)
11726   {
11727     game.light_time_left--;
11728
11729     if (game.light_time_left == 0)
11730       RedrawAllLightSwitchesAndInvisibleElements();
11731   }
11732
11733   if (game.timegate_time_left > 0)
11734   {
11735     game.timegate_time_left--;
11736
11737     if (game.timegate_time_left == 0)
11738       CloseAllOpenTimegates();
11739   }
11740
11741   if (game.lenses_time_left > 0)
11742   {
11743     game.lenses_time_left--;
11744
11745     if (game.lenses_time_left == 0)
11746       RedrawAllInvisibleElementsForLenses();
11747   }
11748
11749   if (game.magnify_time_left > 0)
11750   {
11751     game.magnify_time_left--;
11752
11753     if (game.magnify_time_left == 0)
11754       RedrawAllInvisibleElementsForMagnifier();
11755   }
11756
11757   for (i = 0; i < MAX_PLAYERS; i++)
11758   {
11759     struct PlayerInfo *player = &stored_player[i];
11760
11761     if (SHIELD_ON(player))
11762     {
11763       if (player->shield_deadly_time_left)
11764         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11765       else if (player->shield_normal_time_left)
11766         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11767     }
11768   }
11769
11770 #if USE_DELAYED_GFX_REDRAW
11771   SCAN_PLAYFIELD(x, y)
11772   {
11773     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11774     {
11775       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11776          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11777
11778       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11779         DrawLevelField(x, y);
11780
11781       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11782         DrawLevelFieldCrumbled(x, y);
11783
11784       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11785         DrawLevelFieldCrumbledNeighbours(x, y);
11786
11787       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11788         DrawTwinkleOnField(x, y);
11789     }
11790
11791     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11792   }
11793 #endif
11794
11795   DrawAllPlayers();
11796   PlayAllPlayersSound();
11797
11798   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11799   {
11800     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11801
11802     local_player->show_envelope = 0;
11803   }
11804
11805   /* use random number generator in every frame to make it less predictable */
11806   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11807     RND(1);
11808 }
11809
11810 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11811 {
11812   int min_x = x, min_y = y, max_x = x, max_y = y;
11813   int i;
11814
11815   for (i = 0; i < MAX_PLAYERS; i++)
11816   {
11817     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11818
11819     if (!stored_player[i].active || &stored_player[i] == player)
11820       continue;
11821
11822     min_x = MIN(min_x, jx);
11823     min_y = MIN(min_y, jy);
11824     max_x = MAX(max_x, jx);
11825     max_y = MAX(max_y, jy);
11826   }
11827
11828   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11829 }
11830
11831 static boolean AllPlayersInVisibleScreen()
11832 {
11833   int i;
11834
11835   for (i = 0; i < MAX_PLAYERS; i++)
11836   {
11837     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11838
11839     if (!stored_player[i].active)
11840       continue;
11841
11842     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11843       return FALSE;
11844   }
11845
11846   return TRUE;
11847 }
11848
11849 void ScrollLevel(int dx, int dy)
11850 {
11851   int scroll_offset = 2 * TILEX_VAR;
11852   int x, y;
11853
11854   BlitBitmap(drawto_field, drawto_field,
11855              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11856              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11857              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11858              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11859              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11860              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11861
11862   if (dx != 0)
11863   {
11864     x = (dx == 1 ? BX1 : BX2);
11865     for (y = BY1; y <= BY2; y++)
11866       DrawScreenField(x, y);
11867   }
11868
11869   if (dy != 0)
11870   {
11871     y = (dy == 1 ? BY1 : BY2);
11872     for (x = BX1; x <= BX2; x++)
11873       DrawScreenField(x, y);
11874   }
11875
11876   redraw_mask |= REDRAW_FIELD;
11877 }
11878
11879 static boolean canFallDown(struct PlayerInfo *player)
11880 {
11881   int jx = player->jx, jy = player->jy;
11882
11883   return (IN_LEV_FIELD(jx, jy + 1) &&
11884           (IS_FREE(jx, jy + 1) ||
11885            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11886           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11887           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11888 }
11889
11890 static boolean canPassField(int x, int y, int move_dir)
11891 {
11892   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11893   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11894   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11895   int nextx = x + dx;
11896   int nexty = y + dy;
11897   int element = Feld[x][y];
11898
11899   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11900           !CAN_MOVE(element) &&
11901           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11902           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11903           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11904 }
11905
11906 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11907 {
11908   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11909   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11910   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11911   int newx = x + dx;
11912   int newy = y + dy;
11913
11914   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11915           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11916           (IS_DIGGABLE(Feld[newx][newy]) ||
11917            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11918            canPassField(newx, newy, move_dir)));
11919 }
11920
11921 static void CheckGravityMovement(struct PlayerInfo *player)
11922 {
11923   if (player->gravity && !player->programmed_action)
11924   {
11925     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11926     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11927     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11928     int jx = player->jx, jy = player->jy;
11929     boolean player_is_moving_to_valid_field =
11930       (!player_is_snapping &&
11931        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11932         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11933     boolean player_can_fall_down = canFallDown(player);
11934
11935     if (player_can_fall_down &&
11936         !player_is_moving_to_valid_field)
11937       player->programmed_action = MV_DOWN;
11938   }
11939 }
11940
11941 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11942 {
11943   return CheckGravityMovement(player);
11944
11945   if (player->gravity && !player->programmed_action)
11946   {
11947     int jx = player->jx, jy = player->jy;
11948     boolean field_under_player_is_free =
11949       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11950     boolean player_is_standing_on_valid_field =
11951       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11952        (IS_WALKABLE(Feld[jx][jy]) &&
11953         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11954
11955     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11956       player->programmed_action = MV_DOWN;
11957   }
11958 }
11959
11960 /*
11961   MovePlayerOneStep()
11962   -----------------------------------------------------------------------------
11963   dx, dy:               direction (non-diagonal) to try to move the player to
11964   real_dx, real_dy:     direction as read from input device (can be diagonal)
11965 */
11966
11967 boolean MovePlayerOneStep(struct PlayerInfo *player,
11968                           int dx, int dy, int real_dx, int real_dy)
11969 {
11970   int jx = player->jx, jy = player->jy;
11971   int new_jx = jx + dx, new_jy = jy + dy;
11972   int can_move;
11973   boolean player_can_move = !player->cannot_move;
11974
11975   if (!player->active || (!dx && !dy))
11976     return MP_NO_ACTION;
11977
11978   player->MovDir = (dx < 0 ? MV_LEFT :
11979                     dx > 0 ? MV_RIGHT :
11980                     dy < 0 ? MV_UP :
11981                     dy > 0 ? MV_DOWN :  MV_NONE);
11982
11983   if (!IN_LEV_FIELD(new_jx, new_jy))
11984     return MP_NO_ACTION;
11985
11986   if (!player_can_move)
11987   {
11988     if (player->MovPos == 0)
11989     {
11990       player->is_moving = FALSE;
11991       player->is_digging = FALSE;
11992       player->is_collecting = FALSE;
11993       player->is_snapping = FALSE;
11994       player->is_pushing = FALSE;
11995     }
11996   }
11997
11998   if (!options.network && game.centered_player_nr == -1 &&
11999       !AllPlayersInSight(player, new_jx, new_jy))
12000     return MP_NO_ACTION;
12001
12002   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12003   if (can_move != MP_MOVING)
12004     return can_move;
12005
12006   /* check if DigField() has caused relocation of the player */
12007   if (player->jx != jx || player->jy != jy)
12008     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12009
12010   StorePlayer[jx][jy] = 0;
12011   player->last_jx = jx;
12012   player->last_jy = jy;
12013   player->jx = new_jx;
12014   player->jy = new_jy;
12015   StorePlayer[new_jx][new_jy] = player->element_nr;
12016
12017   if (player->move_delay_value_next != -1)
12018   {
12019     player->move_delay_value = player->move_delay_value_next;
12020     player->move_delay_value_next = -1;
12021   }
12022
12023   player->MovPos =
12024     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12025
12026   player->step_counter++;
12027
12028   PlayerVisit[jx][jy] = FrameCounter;
12029
12030   player->is_moving = TRUE;
12031
12032 #if 1
12033   /* should better be called in MovePlayer(), but this breaks some tapes */
12034   ScrollPlayer(player, SCROLL_INIT);
12035 #endif
12036
12037   return MP_MOVING;
12038 }
12039
12040 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12041 {
12042   int jx = player->jx, jy = player->jy;
12043   int old_jx = jx, old_jy = jy;
12044   int moved = MP_NO_ACTION;
12045
12046   if (!player->active)
12047     return FALSE;
12048
12049   if (!dx && !dy)
12050   {
12051     if (player->MovPos == 0)
12052     {
12053       player->is_moving = FALSE;
12054       player->is_digging = FALSE;
12055       player->is_collecting = FALSE;
12056       player->is_snapping = FALSE;
12057       player->is_pushing = FALSE;
12058     }
12059
12060     return FALSE;
12061   }
12062
12063   if (player->move_delay > 0)
12064     return FALSE;
12065
12066   player->move_delay = -1;              /* set to "uninitialized" value */
12067
12068   /* store if player is automatically moved to next field */
12069   player->is_auto_moving = (player->programmed_action != MV_NONE);
12070
12071   /* remove the last programmed player action */
12072   player->programmed_action = 0;
12073
12074   if (player->MovPos)
12075   {
12076     /* should only happen if pre-1.2 tape recordings are played */
12077     /* this is only for backward compatibility */
12078
12079     int original_move_delay_value = player->move_delay_value;
12080
12081 #if DEBUG
12082     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12083            tape.counter);
12084 #endif
12085
12086     /* scroll remaining steps with finest movement resolution */
12087     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12088
12089     while (player->MovPos)
12090     {
12091       ScrollPlayer(player, SCROLL_GO_ON);
12092       ScrollScreen(NULL, SCROLL_GO_ON);
12093
12094       AdvanceFrameAndPlayerCounters(player->index_nr);
12095
12096       DrawAllPlayers();
12097       BackToFront_WithFrameDelay(0);
12098     }
12099
12100     player->move_delay_value = original_move_delay_value;
12101   }
12102
12103   player->is_active = FALSE;
12104
12105   if (player->last_move_dir & MV_HORIZONTAL)
12106   {
12107     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12108       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12109   }
12110   else
12111   {
12112     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12113       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12114   }
12115
12116   if (!moved && !player->is_active)
12117   {
12118     player->is_moving = FALSE;
12119     player->is_digging = FALSE;
12120     player->is_collecting = FALSE;
12121     player->is_snapping = FALSE;
12122     player->is_pushing = FALSE;
12123   }
12124
12125   jx = player->jx;
12126   jy = player->jy;
12127
12128   if (moved & MP_MOVING && !ScreenMovPos &&
12129       (player->index_nr == game.centered_player_nr ||
12130        game.centered_player_nr == -1))
12131   {
12132     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12133     int offset = game.scroll_delay_value;
12134
12135     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12136     {
12137       /* actual player has left the screen -- scroll in that direction */
12138       if (jx != old_jx)         /* player has moved horizontally */
12139         scroll_x += (jx - old_jx);
12140       else                      /* player has moved vertically */
12141         scroll_y += (jy - old_jy);
12142     }
12143     else
12144     {
12145       if (jx != old_jx)         /* player has moved horizontally */
12146       {
12147         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12148             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12149           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12150
12151         /* don't scroll over playfield boundaries */
12152         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12153           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12154
12155         /* don't scroll more than one field at a time */
12156         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12157
12158         /* don't scroll against the player's moving direction */
12159         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12160             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12161           scroll_x = old_scroll_x;
12162       }
12163       else                      /* player has moved vertically */
12164       {
12165         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12166             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12167           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12168
12169         /* don't scroll over playfield boundaries */
12170         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12171           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12172
12173         /* don't scroll more than one field at a time */
12174         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12175
12176         /* don't scroll against the player's moving direction */
12177         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12178             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12179           scroll_y = old_scroll_y;
12180       }
12181     }
12182
12183     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12184     {
12185       if (!options.network && game.centered_player_nr == -1 &&
12186           !AllPlayersInVisibleScreen())
12187       {
12188         scroll_x = old_scroll_x;
12189         scroll_y = old_scroll_y;
12190       }
12191       else
12192       {
12193         ScrollScreen(player, SCROLL_INIT);
12194         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12195       }
12196     }
12197   }
12198
12199   player->StepFrame = 0;
12200
12201   if (moved & MP_MOVING)
12202   {
12203     if (old_jx != jx && old_jy == jy)
12204       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12205     else if (old_jx == jx && old_jy != jy)
12206       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12207
12208     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12209
12210     player->last_move_dir = player->MovDir;
12211     player->is_moving = TRUE;
12212     player->is_snapping = FALSE;
12213     player->is_switching = FALSE;
12214     player->is_dropping = FALSE;
12215     player->is_dropping_pressed = FALSE;
12216     player->drop_pressed_delay = 0;
12217
12218 #if 0
12219     /* should better be called here than above, but this breaks some tapes */
12220     ScrollPlayer(player, SCROLL_INIT);
12221 #endif
12222   }
12223   else
12224   {
12225     CheckGravityMovementWhenNotMoving(player);
12226
12227     player->is_moving = FALSE;
12228
12229     /* at this point, the player is allowed to move, but cannot move right now
12230        (e.g. because of something blocking the way) -- ensure that the player
12231        is also allowed to move in the next frame (in old versions before 3.1.1,
12232        the player was forced to wait again for eight frames before next try) */
12233
12234     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12235       player->move_delay = 0;   /* allow direct movement in the next frame */
12236   }
12237
12238   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12239     player->move_delay = player->move_delay_value;
12240
12241   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12242   {
12243     TestIfPlayerTouchesBadThing(jx, jy);
12244     TestIfPlayerTouchesCustomElement(jx, jy);
12245   }
12246
12247   if (!player->active)
12248     RemovePlayer(player);
12249
12250   return moved;
12251 }
12252
12253 void ScrollPlayer(struct PlayerInfo *player, int mode)
12254 {
12255   int jx = player->jx, jy = player->jy;
12256   int last_jx = player->last_jx, last_jy = player->last_jy;
12257   int move_stepsize = TILEX / player->move_delay_value;
12258
12259   if (!player->active)
12260     return;
12261
12262   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12263     return;
12264
12265   if (mode == SCROLL_INIT)
12266   {
12267     player->actual_frame_counter = FrameCounter;
12268     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12269
12270     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12271         Feld[last_jx][last_jy] == EL_EMPTY)
12272     {
12273       int last_field_block_delay = 0;   /* start with no blocking at all */
12274       int block_delay_adjustment = player->block_delay_adjustment;
12275
12276       /* if player blocks last field, add delay for exactly one move */
12277       if (player->block_last_field)
12278       {
12279         last_field_block_delay += player->move_delay_value;
12280
12281         /* when blocking enabled, prevent moving up despite gravity */
12282         if (player->gravity && player->MovDir == MV_UP)
12283           block_delay_adjustment = -1;
12284       }
12285
12286       /* add block delay adjustment (also possible when not blocking) */
12287       last_field_block_delay += block_delay_adjustment;
12288
12289       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12290       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12291     }
12292
12293     if (player->MovPos != 0)    /* player has not yet reached destination */
12294       return;
12295   }
12296   else if (!FrameReached(&player->actual_frame_counter, 1))
12297     return;
12298
12299   if (player->MovPos != 0)
12300   {
12301     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12302     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12303
12304     /* before DrawPlayer() to draw correct player graphic for this case */
12305     if (player->MovPos == 0)
12306       CheckGravityMovement(player);
12307   }
12308
12309   if (player->MovPos == 0)      /* player reached destination field */
12310   {
12311     if (player->move_delay_reset_counter > 0)
12312     {
12313       player->move_delay_reset_counter--;
12314
12315       if (player->move_delay_reset_counter == 0)
12316       {
12317         /* continue with normal speed after quickly moving through gate */
12318         HALVE_PLAYER_SPEED(player);
12319
12320         /* be able to make the next move without delay */
12321         player->move_delay = 0;
12322       }
12323     }
12324
12325     player->last_jx = jx;
12326     player->last_jy = jy;
12327
12328     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12329         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12330         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12331         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12332         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12333         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12334         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12335         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12336     {
12337       DrawPlayer(player);       /* needed here only to cleanup last field */
12338       RemovePlayer(player);
12339
12340       if (local_player->friends_still_needed == 0 ||
12341           IS_SP_ELEMENT(Feld[jx][jy]))
12342         PlayerWins(player);
12343     }
12344
12345     /* this breaks one level: "machine", level 000 */
12346     {
12347       int move_direction = player->MovDir;
12348       int enter_side = MV_DIR_OPPOSITE(move_direction);
12349       int leave_side = move_direction;
12350       int old_jx = last_jx;
12351       int old_jy = last_jy;
12352       int old_element = Feld[old_jx][old_jy];
12353       int new_element = Feld[jx][jy];
12354
12355       if (IS_CUSTOM_ELEMENT(old_element))
12356         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12357                                    CE_LEFT_BY_PLAYER,
12358                                    player->index_bit, leave_side);
12359
12360       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12361                                           CE_PLAYER_LEAVES_X,
12362                                           player->index_bit, leave_side);
12363
12364       if (IS_CUSTOM_ELEMENT(new_element))
12365         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12366                                    player->index_bit, enter_side);
12367
12368       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12369                                           CE_PLAYER_ENTERS_X,
12370                                           player->index_bit, enter_side);
12371
12372       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12373                                         CE_MOVE_OF_X, move_direction);
12374     }
12375
12376     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12377     {
12378       TestIfPlayerTouchesBadThing(jx, jy);
12379       TestIfPlayerTouchesCustomElement(jx, jy);
12380
12381       /* needed because pushed element has not yet reached its destination,
12382          so it would trigger a change event at its previous field location */
12383       if (!player->is_pushing)
12384         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12385
12386       if (!player->active)
12387         RemovePlayer(player);
12388     }
12389
12390     if (!local_player->LevelSolved && level.use_step_counter)
12391     {
12392       int i;
12393
12394       TimePlayed++;
12395
12396       if (TimeLeft > 0)
12397       {
12398         TimeLeft--;
12399
12400         if (TimeLeft <= 10 && setup.time_limit)
12401           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12402
12403         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12404
12405         DisplayGameControlValues();
12406
12407         if (!TimeLeft && setup.time_limit)
12408           for (i = 0; i < MAX_PLAYERS; i++)
12409             KillPlayer(&stored_player[i]);
12410       }
12411       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12412       {
12413         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12414
12415         DisplayGameControlValues();
12416       }
12417     }
12418
12419     if (tape.single_step && tape.recording && !tape.pausing &&
12420         !player->programmed_action)
12421       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12422
12423     if (!player->programmed_action)
12424       CheckSaveEngineSnapshot(player);
12425   }
12426 }
12427
12428 void ScrollScreen(struct PlayerInfo *player, int mode)
12429 {
12430   static unsigned int screen_frame_counter = 0;
12431
12432   if (mode == SCROLL_INIT)
12433   {
12434     /* set scrolling step size according to actual player's moving speed */
12435     ScrollStepSize = TILEX / player->move_delay_value;
12436
12437     screen_frame_counter = FrameCounter;
12438     ScreenMovDir = player->MovDir;
12439     ScreenMovPos = player->MovPos;
12440     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12441     return;
12442   }
12443   else if (!FrameReached(&screen_frame_counter, 1))
12444     return;
12445
12446   if (ScreenMovPos)
12447   {
12448     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12449     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12450     redraw_mask |= REDRAW_FIELD;
12451   }
12452   else
12453     ScreenMovDir = MV_NONE;
12454 }
12455
12456 void TestIfPlayerTouchesCustomElement(int x, int y)
12457 {
12458   static int xy[4][2] =
12459   {
12460     { 0, -1 },
12461     { -1, 0 },
12462     { +1, 0 },
12463     { 0, +1 }
12464   };
12465   static int trigger_sides[4][2] =
12466   {
12467     /* center side       border side */
12468     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12469     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12470     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12471     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12472   };
12473   static int touch_dir[4] =
12474   {
12475     MV_LEFT | MV_RIGHT,
12476     MV_UP   | MV_DOWN,
12477     MV_UP   | MV_DOWN,
12478     MV_LEFT | MV_RIGHT
12479   };
12480   int center_element = Feld[x][y];      /* should always be non-moving! */
12481   int i;
12482
12483   for (i = 0; i < NUM_DIRECTIONS; i++)
12484   {
12485     int xx = x + xy[i][0];
12486     int yy = y + xy[i][1];
12487     int center_side = trigger_sides[i][0];
12488     int border_side = trigger_sides[i][1];
12489     int border_element;
12490
12491     if (!IN_LEV_FIELD(xx, yy))
12492       continue;
12493
12494     if (IS_PLAYER(x, y))                /* player found at center element */
12495     {
12496       struct PlayerInfo *player = PLAYERINFO(x, y);
12497
12498       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12499         border_element = Feld[xx][yy];          /* may be moving! */
12500       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12501         border_element = Feld[xx][yy];
12502       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12503         border_element = MovingOrBlocked2Element(xx, yy);
12504       else
12505         continue;               /* center and border element do not touch */
12506
12507       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12508                                  player->index_bit, border_side);
12509       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12510                                           CE_PLAYER_TOUCHES_X,
12511                                           player->index_bit, border_side);
12512
12513       {
12514         /* use player element that is initially defined in the level playfield,
12515            not the player element that corresponds to the runtime player number
12516            (example: a level that contains EL_PLAYER_3 as the only player would
12517            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12518         int player_element = PLAYERINFO(x, y)->initial_element;
12519
12520         CheckElementChangeBySide(xx, yy, border_element, player_element,
12521                                  CE_TOUCHING_X, border_side);
12522       }
12523     }
12524     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12525     {
12526       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12527
12528       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12529       {
12530         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12531           continue;             /* center and border element do not touch */
12532       }
12533
12534       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12535                                  player->index_bit, center_side);
12536       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12537                                           CE_PLAYER_TOUCHES_X,
12538                                           player->index_bit, center_side);
12539
12540       {
12541         /* use player element that is initially defined in the level playfield,
12542            not the player element that corresponds to the runtime player number
12543            (example: a level that contains EL_PLAYER_3 as the only player would
12544            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12545         int player_element = PLAYERINFO(xx, yy)->initial_element;
12546
12547         CheckElementChangeBySide(x, y, center_element, player_element,
12548                                  CE_TOUCHING_X, center_side);
12549       }
12550
12551       break;
12552     }
12553   }
12554 }
12555
12556 void TestIfElementTouchesCustomElement(int x, int y)
12557 {
12558   static int xy[4][2] =
12559   {
12560     { 0, -1 },
12561     { -1, 0 },
12562     { +1, 0 },
12563     { 0, +1 }
12564   };
12565   static int trigger_sides[4][2] =
12566   {
12567     /* center side      border side */
12568     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12569     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12570     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12571     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12572   };
12573   static int touch_dir[4] =
12574   {
12575     MV_LEFT | MV_RIGHT,
12576     MV_UP   | MV_DOWN,
12577     MV_UP   | MV_DOWN,
12578     MV_LEFT | MV_RIGHT
12579   };
12580   boolean change_center_element = FALSE;
12581   int center_element = Feld[x][y];      /* should always be non-moving! */
12582   int border_element_old[NUM_DIRECTIONS];
12583   int i;
12584
12585   for (i = 0; i < NUM_DIRECTIONS; i++)
12586   {
12587     int xx = x + xy[i][0];
12588     int yy = y + xy[i][1];
12589     int border_element;
12590
12591     border_element_old[i] = -1;
12592
12593     if (!IN_LEV_FIELD(xx, yy))
12594       continue;
12595
12596     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12597       border_element = Feld[xx][yy];    /* may be moving! */
12598     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12599       border_element = Feld[xx][yy];
12600     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12601       border_element = MovingOrBlocked2Element(xx, yy);
12602     else
12603       continue;                 /* center and border element do not touch */
12604
12605     border_element_old[i] = border_element;
12606   }
12607
12608   for (i = 0; i < NUM_DIRECTIONS; i++)
12609   {
12610     int xx = x + xy[i][0];
12611     int yy = y + xy[i][1];
12612     int center_side = trigger_sides[i][0];
12613     int border_element = border_element_old[i];
12614
12615     if (border_element == -1)
12616       continue;
12617
12618     /* check for change of border element */
12619     CheckElementChangeBySide(xx, yy, border_element, center_element,
12620                              CE_TOUCHING_X, center_side);
12621
12622     /* (center element cannot be player, so we dont have to check this here) */
12623   }
12624
12625   for (i = 0; i < NUM_DIRECTIONS; i++)
12626   {
12627     int xx = x + xy[i][0];
12628     int yy = y + xy[i][1];
12629     int border_side = trigger_sides[i][1];
12630     int border_element = border_element_old[i];
12631
12632     if (border_element == -1)
12633       continue;
12634
12635     /* check for change of center element (but change it only once) */
12636     if (!change_center_element)
12637       change_center_element =
12638         CheckElementChangeBySide(x, y, center_element, border_element,
12639                                  CE_TOUCHING_X, border_side);
12640
12641     if (IS_PLAYER(xx, yy))
12642     {
12643       /* use player element that is initially defined in the level playfield,
12644          not the player element that corresponds to the runtime player number
12645          (example: a level that contains EL_PLAYER_3 as the only player would
12646          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12647       int player_element = PLAYERINFO(xx, yy)->initial_element;
12648
12649       CheckElementChangeBySide(x, y, center_element, player_element,
12650                                CE_TOUCHING_X, border_side);
12651     }
12652   }
12653 }
12654
12655 void TestIfElementHitsCustomElement(int x, int y, int direction)
12656 {
12657   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12658   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12659   int hitx = x + dx, hity = y + dy;
12660   int hitting_element = Feld[x][y];
12661   int touched_element;
12662
12663   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12664     return;
12665
12666   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12667                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12668
12669   if (IN_LEV_FIELD(hitx, hity))
12670   {
12671     int opposite_direction = MV_DIR_OPPOSITE(direction);
12672     int hitting_side = direction;
12673     int touched_side = opposite_direction;
12674     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12675                           MovDir[hitx][hity] != direction ||
12676                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12677
12678     object_hit = TRUE;
12679
12680     if (object_hit)
12681     {
12682       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12683                                CE_HITTING_X, touched_side);
12684
12685       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12686                                CE_HIT_BY_X, hitting_side);
12687
12688       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12689                                CE_HIT_BY_SOMETHING, opposite_direction);
12690
12691       if (IS_PLAYER(hitx, hity))
12692       {
12693         /* use player element that is initially defined in the level playfield,
12694            not the player element that corresponds to the runtime player number
12695            (example: a level that contains EL_PLAYER_3 as the only player would
12696            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12697         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12698
12699         CheckElementChangeBySide(x, y, hitting_element, player_element,
12700                                  CE_HITTING_X, touched_side);
12701       }
12702     }
12703   }
12704
12705   /* "hitting something" is also true when hitting the playfield border */
12706   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12707                            CE_HITTING_SOMETHING, direction);
12708 }
12709
12710 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12711 {
12712   int i, kill_x = -1, kill_y = -1;
12713
12714   int bad_element = -1;
12715   static int test_xy[4][2] =
12716   {
12717     { 0, -1 },
12718     { -1, 0 },
12719     { +1, 0 },
12720     { 0, +1 }
12721   };
12722   static int test_dir[4] =
12723   {
12724     MV_UP,
12725     MV_LEFT,
12726     MV_RIGHT,
12727     MV_DOWN
12728   };
12729
12730   for (i = 0; i < NUM_DIRECTIONS; i++)
12731   {
12732     int test_x, test_y, test_move_dir, test_element;
12733
12734     test_x = good_x + test_xy[i][0];
12735     test_y = good_y + test_xy[i][1];
12736
12737     if (!IN_LEV_FIELD(test_x, test_y))
12738       continue;
12739
12740     test_move_dir =
12741       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12742
12743     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12744
12745     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12746        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12747     */
12748     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12749         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12750     {
12751       kill_x = test_x;
12752       kill_y = test_y;
12753       bad_element = test_element;
12754
12755       break;
12756     }
12757   }
12758
12759   if (kill_x != -1 || kill_y != -1)
12760   {
12761     if (IS_PLAYER(good_x, good_y))
12762     {
12763       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12764
12765       if (player->shield_deadly_time_left > 0 &&
12766           !IS_INDESTRUCTIBLE(bad_element))
12767         Bang(kill_x, kill_y);
12768       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12769         KillPlayer(player);
12770     }
12771     else
12772       Bang(good_x, good_y);
12773   }
12774 }
12775
12776 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12777 {
12778   int i, kill_x = -1, kill_y = -1;
12779   int bad_element = Feld[bad_x][bad_y];
12780   static int test_xy[4][2] =
12781   {
12782     { 0, -1 },
12783     { -1, 0 },
12784     { +1, 0 },
12785     { 0, +1 }
12786   };
12787   static int touch_dir[4] =
12788   {
12789     MV_LEFT | MV_RIGHT,
12790     MV_UP   | MV_DOWN,
12791     MV_UP   | MV_DOWN,
12792     MV_LEFT | MV_RIGHT
12793   };
12794   static int test_dir[4] =
12795   {
12796     MV_UP,
12797     MV_LEFT,
12798     MV_RIGHT,
12799     MV_DOWN
12800   };
12801
12802   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12803     return;
12804
12805   for (i = 0; i < NUM_DIRECTIONS; i++)
12806   {
12807     int test_x, test_y, test_move_dir, test_element;
12808
12809     test_x = bad_x + test_xy[i][0];
12810     test_y = bad_y + test_xy[i][1];
12811
12812     if (!IN_LEV_FIELD(test_x, test_y))
12813       continue;
12814
12815     test_move_dir =
12816       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12817
12818     test_element = Feld[test_x][test_y];
12819
12820     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12821        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12822     */
12823     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12824         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12825     {
12826       /* good thing is player or penguin that does not move away */
12827       if (IS_PLAYER(test_x, test_y))
12828       {
12829         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12830
12831         if (bad_element == EL_ROBOT && player->is_moving)
12832           continue;     /* robot does not kill player if he is moving */
12833
12834         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12835         {
12836           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12837             continue;           /* center and border element do not touch */
12838         }
12839
12840         kill_x = test_x;
12841         kill_y = test_y;
12842
12843         break;
12844       }
12845       else if (test_element == EL_PENGUIN)
12846       {
12847         kill_x = test_x;
12848         kill_y = test_y;
12849
12850         break;
12851       }
12852     }
12853   }
12854
12855   if (kill_x != -1 || kill_y != -1)
12856   {
12857     if (IS_PLAYER(kill_x, kill_y))
12858     {
12859       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12860
12861       if (player->shield_deadly_time_left > 0 &&
12862           !IS_INDESTRUCTIBLE(bad_element))
12863         Bang(bad_x, bad_y);
12864       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12865         KillPlayer(player);
12866     }
12867     else
12868       Bang(kill_x, kill_y);
12869   }
12870 }
12871
12872 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12873 {
12874   int bad_element = Feld[bad_x][bad_y];
12875   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12876   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12877   int test_x = bad_x + dx, test_y = bad_y + dy;
12878   int test_move_dir, test_element;
12879   int kill_x = -1, kill_y = -1;
12880
12881   if (!IN_LEV_FIELD(test_x, test_y))
12882     return;
12883
12884   test_move_dir =
12885     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12886
12887   test_element = Feld[test_x][test_y];
12888
12889   if (test_move_dir != bad_move_dir)
12890   {
12891     /* good thing can be player or penguin that does not move away */
12892     if (IS_PLAYER(test_x, test_y))
12893     {
12894       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12895
12896       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12897          player as being hit when he is moving towards the bad thing, because
12898          the "get hit by" condition would be lost after the player stops) */
12899       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12900         return;         /* player moves away from bad thing */
12901
12902       kill_x = test_x;
12903       kill_y = test_y;
12904     }
12905     else if (test_element == EL_PENGUIN)
12906     {
12907       kill_x = test_x;
12908       kill_y = test_y;
12909     }
12910   }
12911
12912   if (kill_x != -1 || kill_y != -1)
12913   {
12914     if (IS_PLAYER(kill_x, kill_y))
12915     {
12916       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12917
12918       if (player->shield_deadly_time_left > 0 &&
12919           !IS_INDESTRUCTIBLE(bad_element))
12920         Bang(bad_x, bad_y);
12921       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12922         KillPlayer(player);
12923     }
12924     else
12925       Bang(kill_x, kill_y);
12926   }
12927 }
12928
12929 void TestIfPlayerTouchesBadThing(int x, int y)
12930 {
12931   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12932 }
12933
12934 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12935 {
12936   TestIfGoodThingHitsBadThing(x, y, move_dir);
12937 }
12938
12939 void TestIfBadThingTouchesPlayer(int x, int y)
12940 {
12941   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12942 }
12943
12944 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12945 {
12946   TestIfBadThingHitsGoodThing(x, y, move_dir);
12947 }
12948
12949 void TestIfFriendTouchesBadThing(int x, int y)
12950 {
12951   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12952 }
12953
12954 void TestIfBadThingTouchesFriend(int x, int y)
12955 {
12956   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12957 }
12958
12959 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12960 {
12961   int i, kill_x = bad_x, kill_y = bad_y;
12962   static int xy[4][2] =
12963   {
12964     { 0, -1 },
12965     { -1, 0 },
12966     { +1, 0 },
12967     { 0, +1 }
12968   };
12969
12970   for (i = 0; i < NUM_DIRECTIONS; i++)
12971   {
12972     int x, y, element;
12973
12974     x = bad_x + xy[i][0];
12975     y = bad_y + xy[i][1];
12976     if (!IN_LEV_FIELD(x, y))
12977       continue;
12978
12979     element = Feld[x][y];
12980     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12981         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12982     {
12983       kill_x = x;
12984       kill_y = y;
12985       break;
12986     }
12987   }
12988
12989   if (kill_x != bad_x || kill_y != bad_y)
12990     Bang(bad_x, bad_y);
12991 }
12992
12993 void KillPlayer(struct PlayerInfo *player)
12994 {
12995   int jx = player->jx, jy = player->jy;
12996
12997   if (!player->active)
12998     return;
12999
13000 #if 0
13001   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13002          player->killed, player->active, player->reanimated);
13003 #endif
13004
13005   /* the following code was introduced to prevent an infinite loop when calling
13006      -> Bang()
13007      -> CheckTriggeredElementChangeExt()
13008      -> ExecuteCustomElementAction()
13009      -> KillPlayer()
13010      -> (infinitely repeating the above sequence of function calls)
13011      which occurs when killing the player while having a CE with the setting
13012      "kill player X when explosion of <player X>"; the solution using a new
13013      field "player->killed" was chosen for backwards compatibility, although
13014      clever use of the fields "player->active" etc. would probably also work */
13015 #if 1
13016   if (player->killed)
13017     return;
13018 #endif
13019
13020   player->killed = TRUE;
13021
13022   /* remove accessible field at the player's position */
13023   Feld[jx][jy] = EL_EMPTY;
13024
13025   /* deactivate shield (else Bang()/Explode() would not work right) */
13026   player->shield_normal_time_left = 0;
13027   player->shield_deadly_time_left = 0;
13028
13029 #if 0
13030   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13031          player->killed, player->active, player->reanimated);
13032 #endif
13033
13034   Bang(jx, jy);
13035
13036 #if 0
13037   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13038          player->killed, player->active, player->reanimated);
13039 #endif
13040
13041   if (player->reanimated)       /* killed player may have been reanimated */
13042     player->killed = player->reanimated = FALSE;
13043   else
13044     BuryPlayer(player);
13045 }
13046
13047 static void KillPlayerUnlessEnemyProtected(int x, int y)
13048 {
13049   if (!PLAYER_ENEMY_PROTECTED(x, y))
13050     KillPlayer(PLAYERINFO(x, y));
13051 }
13052
13053 static void KillPlayerUnlessExplosionProtected(int x, int y)
13054 {
13055   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13056     KillPlayer(PLAYERINFO(x, y));
13057 }
13058
13059 void BuryPlayer(struct PlayerInfo *player)
13060 {
13061   int jx = player->jx, jy = player->jy;
13062
13063   if (!player->active)
13064     return;
13065
13066   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13067   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13068
13069   player->GameOver = TRUE;
13070   RemovePlayer(player);
13071 }
13072
13073 void RemovePlayer(struct PlayerInfo *player)
13074 {
13075   int jx = player->jx, jy = player->jy;
13076   int i, found = FALSE;
13077
13078   player->present = FALSE;
13079   player->active = FALSE;
13080
13081   if (!ExplodeField[jx][jy])
13082     StorePlayer[jx][jy] = 0;
13083
13084   if (player->is_moving)
13085     TEST_DrawLevelField(player->last_jx, player->last_jy);
13086
13087   for (i = 0; i < MAX_PLAYERS; i++)
13088     if (stored_player[i].active)
13089       found = TRUE;
13090
13091   if (!found)
13092     AllPlayersGone = TRUE;
13093
13094   ExitX = ZX = jx;
13095   ExitY = ZY = jy;
13096 }
13097
13098 static void setFieldForSnapping(int x, int y, int element, int direction)
13099 {
13100   struct ElementInfo *ei = &element_info[element];
13101   int direction_bit = MV_DIR_TO_BIT(direction);
13102   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13103   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13104                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13105
13106   Feld[x][y] = EL_ELEMENT_SNAPPING;
13107   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13108
13109   ResetGfxAnimation(x, y);
13110
13111   GfxElement[x][y] = element;
13112   GfxAction[x][y] = action;
13113   GfxDir[x][y] = direction;
13114   GfxFrame[x][y] = -1;
13115 }
13116
13117 /*
13118   =============================================================================
13119   checkDiagonalPushing()
13120   -----------------------------------------------------------------------------
13121   check if diagonal input device direction results in pushing of object
13122   (by checking if the alternative direction is walkable, diggable, ...)
13123   =============================================================================
13124 */
13125
13126 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13127                                     int x, int y, int real_dx, int real_dy)
13128 {
13129   int jx, jy, dx, dy, xx, yy;
13130
13131   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13132     return TRUE;
13133
13134   /* diagonal direction: check alternative direction */
13135   jx = player->jx;
13136   jy = player->jy;
13137   dx = x - jx;
13138   dy = y - jy;
13139   xx = jx + (dx == 0 ? real_dx : 0);
13140   yy = jy + (dy == 0 ? real_dy : 0);
13141
13142   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13143 }
13144
13145 /*
13146   =============================================================================
13147   DigField()
13148   -----------------------------------------------------------------------------
13149   x, y:                 field next to player (non-diagonal) to try to dig to
13150   real_dx, real_dy:     direction as read from input device (can be diagonal)
13151   =============================================================================
13152 */
13153
13154 static int DigField(struct PlayerInfo *player,
13155                     int oldx, int oldy, int x, int y,
13156                     int real_dx, int real_dy, int mode)
13157 {
13158   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13159   boolean player_was_pushing = player->is_pushing;
13160   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13161   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13162   int jx = oldx, jy = oldy;
13163   int dx = x - jx, dy = y - jy;
13164   int nextx = x + dx, nexty = y + dy;
13165   int move_direction = (dx == -1 ? MV_LEFT  :
13166                         dx == +1 ? MV_RIGHT :
13167                         dy == -1 ? MV_UP    :
13168                         dy == +1 ? MV_DOWN  : MV_NONE);
13169   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13170   int dig_side = MV_DIR_OPPOSITE(move_direction);
13171   int old_element = Feld[jx][jy];
13172   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13173   int collect_count;
13174
13175   if (is_player)                /* function can also be called by EL_PENGUIN */
13176   {
13177     if (player->MovPos == 0)
13178     {
13179       player->is_digging = FALSE;
13180       player->is_collecting = FALSE;
13181     }
13182
13183     if (player->MovPos == 0)    /* last pushing move finished */
13184       player->is_pushing = FALSE;
13185
13186     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13187     {
13188       player->is_switching = FALSE;
13189       player->push_delay = -1;
13190
13191       return MP_NO_ACTION;
13192     }
13193   }
13194
13195   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13196     old_element = Back[jx][jy];
13197
13198   /* in case of element dropped at player position, check background */
13199   else if (Back[jx][jy] != EL_EMPTY &&
13200            game.engine_version >= VERSION_IDENT(2,2,0,0))
13201     old_element = Back[jx][jy];
13202
13203   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13204     return MP_NO_ACTION;        /* field has no opening in this direction */
13205
13206   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13207     return MP_NO_ACTION;        /* field has no opening in this direction */
13208
13209   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13210   {
13211     SplashAcid(x, y);
13212
13213     Feld[jx][jy] = player->artwork_element;
13214     InitMovingField(jx, jy, MV_DOWN);
13215     Store[jx][jy] = EL_ACID;
13216     ContinueMoving(jx, jy);
13217     BuryPlayer(player);
13218
13219     return MP_DONT_RUN_INTO;
13220   }
13221
13222   if (player_can_move && DONT_RUN_INTO(element))
13223   {
13224     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13225
13226     return MP_DONT_RUN_INTO;
13227   }
13228
13229   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13230     return MP_NO_ACTION;
13231
13232   collect_count = element_info[element].collect_count_initial;
13233
13234   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13235     return MP_NO_ACTION;
13236
13237   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13238     player_can_move = player_can_move_or_snap;
13239
13240   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13241       game.engine_version >= VERSION_IDENT(2,2,0,0))
13242   {
13243     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13244                                player->index_bit, dig_side);
13245     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13246                                         player->index_bit, dig_side);
13247
13248     if (element == EL_DC_LANDMINE)
13249       Bang(x, y);
13250
13251     if (Feld[x][y] != element)          /* field changed by snapping */
13252       return MP_ACTION;
13253
13254     return MP_NO_ACTION;
13255   }
13256
13257   if (player->gravity && is_player && !player->is_auto_moving &&
13258       canFallDown(player) && move_direction != MV_DOWN &&
13259       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13260     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13261
13262   if (player_can_move &&
13263       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13264   {
13265     int sound_element = SND_ELEMENT(element);
13266     int sound_action = ACTION_WALKING;
13267
13268     if (IS_RND_GATE(element))
13269     {
13270       if (!player->key[RND_GATE_NR(element)])
13271         return MP_NO_ACTION;
13272     }
13273     else if (IS_RND_GATE_GRAY(element))
13274     {
13275       if (!player->key[RND_GATE_GRAY_NR(element)])
13276         return MP_NO_ACTION;
13277     }
13278     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13279     {
13280       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13281         return MP_NO_ACTION;
13282     }
13283     else if (element == EL_EXIT_OPEN ||
13284              element == EL_EM_EXIT_OPEN ||
13285              element == EL_EM_EXIT_OPENING ||
13286              element == EL_STEEL_EXIT_OPEN ||
13287              element == EL_EM_STEEL_EXIT_OPEN ||
13288              element == EL_EM_STEEL_EXIT_OPENING ||
13289              element == EL_SP_EXIT_OPEN ||
13290              element == EL_SP_EXIT_OPENING)
13291     {
13292       sound_action = ACTION_PASSING;    /* player is passing exit */
13293     }
13294     else if (element == EL_EMPTY)
13295     {
13296       sound_action = ACTION_MOVING;             /* nothing to walk on */
13297     }
13298
13299     /* play sound from background or player, whatever is available */
13300     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13301       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13302     else
13303       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13304   }
13305   else if (player_can_move &&
13306            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13307   {
13308     if (!ACCESS_FROM(element, opposite_direction))
13309       return MP_NO_ACTION;      /* field not accessible from this direction */
13310
13311     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13312       return MP_NO_ACTION;
13313
13314     if (IS_EM_GATE(element))
13315     {
13316       if (!player->key[EM_GATE_NR(element)])
13317         return MP_NO_ACTION;
13318     }
13319     else if (IS_EM_GATE_GRAY(element))
13320     {
13321       if (!player->key[EM_GATE_GRAY_NR(element)])
13322         return MP_NO_ACTION;
13323     }
13324     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13325     {
13326       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13327         return MP_NO_ACTION;
13328     }
13329     else if (IS_EMC_GATE(element))
13330     {
13331       if (!player->key[EMC_GATE_NR(element)])
13332         return MP_NO_ACTION;
13333     }
13334     else if (IS_EMC_GATE_GRAY(element))
13335     {
13336       if (!player->key[EMC_GATE_GRAY_NR(element)])
13337         return MP_NO_ACTION;
13338     }
13339     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13340     {
13341       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13342         return MP_NO_ACTION;
13343     }
13344     else if (element == EL_DC_GATE_WHITE ||
13345              element == EL_DC_GATE_WHITE_GRAY ||
13346              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13347     {
13348       if (player->num_white_keys == 0)
13349         return MP_NO_ACTION;
13350
13351       player->num_white_keys--;
13352     }
13353     else if (IS_SP_PORT(element))
13354     {
13355       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13356           element == EL_SP_GRAVITY_PORT_RIGHT ||
13357           element == EL_SP_GRAVITY_PORT_UP ||
13358           element == EL_SP_GRAVITY_PORT_DOWN)
13359         player->gravity = !player->gravity;
13360       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13361                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13362                element == EL_SP_GRAVITY_ON_PORT_UP ||
13363                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13364         player->gravity = TRUE;
13365       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13366                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13367                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13368                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13369         player->gravity = FALSE;
13370     }
13371
13372     /* automatically move to the next field with double speed */
13373     player->programmed_action = move_direction;
13374
13375     if (player->move_delay_reset_counter == 0)
13376     {
13377       player->move_delay_reset_counter = 2;     /* two double speed steps */
13378
13379       DOUBLE_PLAYER_SPEED(player);
13380     }
13381
13382     PlayLevelSoundAction(x, y, ACTION_PASSING);
13383   }
13384   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13385   {
13386     RemoveField(x, y);
13387
13388     if (mode != DF_SNAP)
13389     {
13390       GfxElement[x][y] = GFX_ELEMENT(element);
13391       player->is_digging = TRUE;
13392     }
13393
13394     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13395
13396     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13397                                         player->index_bit, dig_side);
13398
13399     if (mode == DF_SNAP)
13400     {
13401       if (level.block_snap_field)
13402         setFieldForSnapping(x, y, element, move_direction);
13403       else
13404         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13405
13406       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13407                                           player->index_bit, dig_side);
13408     }
13409   }
13410   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13411   {
13412     RemoveField(x, y);
13413
13414     if (is_player && mode != DF_SNAP)
13415     {
13416       GfxElement[x][y] = element;
13417       player->is_collecting = TRUE;
13418     }
13419
13420     if (element == EL_SPEED_PILL)
13421     {
13422       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13423     }
13424     else if (element == EL_EXTRA_TIME && level.time > 0)
13425     {
13426       TimeLeft += level.extra_time;
13427
13428       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13429
13430       DisplayGameControlValues();
13431     }
13432     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13433     {
13434       player->shield_normal_time_left += level.shield_normal_time;
13435       if (element == EL_SHIELD_DEADLY)
13436         player->shield_deadly_time_left += level.shield_deadly_time;
13437     }
13438     else if (element == EL_DYNAMITE ||
13439              element == EL_EM_DYNAMITE ||
13440              element == EL_SP_DISK_RED)
13441     {
13442       if (player->inventory_size < MAX_INVENTORY_SIZE)
13443         player->inventory_element[player->inventory_size++] = element;
13444
13445       DrawGameDoorValues();
13446     }
13447     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13448     {
13449       player->dynabomb_count++;
13450       player->dynabombs_left++;
13451     }
13452     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13453     {
13454       player->dynabomb_size++;
13455     }
13456     else if (element == EL_DYNABOMB_INCREASE_POWER)
13457     {
13458       player->dynabomb_xl = TRUE;
13459     }
13460     else if (IS_KEY(element))
13461     {
13462       player->key[KEY_NR(element)] = TRUE;
13463
13464       DrawGameDoorValues();
13465     }
13466     else if (element == EL_DC_KEY_WHITE)
13467     {
13468       player->num_white_keys++;
13469
13470       /* display white keys? */
13471       /* DrawGameDoorValues(); */
13472     }
13473     else if (IS_ENVELOPE(element))
13474     {
13475       player->show_envelope = element;
13476     }
13477     else if (element == EL_EMC_LENSES)
13478     {
13479       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13480
13481       RedrawAllInvisibleElementsForLenses();
13482     }
13483     else if (element == EL_EMC_MAGNIFIER)
13484     {
13485       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13486
13487       RedrawAllInvisibleElementsForMagnifier();
13488     }
13489     else if (IS_DROPPABLE(element) ||
13490              IS_THROWABLE(element))     /* can be collected and dropped */
13491     {
13492       int i;
13493
13494       if (collect_count == 0)
13495         player->inventory_infinite_element = element;
13496       else
13497         for (i = 0; i < collect_count; i++)
13498           if (player->inventory_size < MAX_INVENTORY_SIZE)
13499             player->inventory_element[player->inventory_size++] = element;
13500
13501       DrawGameDoorValues();
13502     }
13503     else if (collect_count > 0)
13504     {
13505       local_player->gems_still_needed -= collect_count;
13506       if (local_player->gems_still_needed < 0)
13507         local_player->gems_still_needed = 0;
13508
13509       game.snapshot.collected_item = TRUE;
13510
13511       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13512
13513       DisplayGameControlValues();
13514     }
13515
13516     RaiseScoreElement(element);
13517     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13518
13519     if (is_player)
13520       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13521                                           player->index_bit, dig_side);
13522
13523     if (mode == DF_SNAP)
13524     {
13525       if (level.block_snap_field)
13526         setFieldForSnapping(x, y, element, move_direction);
13527       else
13528         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13529
13530       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13531                                           player->index_bit, dig_side);
13532     }
13533   }
13534   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13535   {
13536     if (mode == DF_SNAP && element != EL_BD_ROCK)
13537       return MP_NO_ACTION;
13538
13539     if (CAN_FALL(element) && dy)
13540       return MP_NO_ACTION;
13541
13542     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13543         !(element == EL_SPRING && level.use_spring_bug))
13544       return MP_NO_ACTION;
13545
13546     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13547         ((move_direction & MV_VERTICAL &&
13548           ((element_info[element].move_pattern & MV_LEFT &&
13549             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13550            (element_info[element].move_pattern & MV_RIGHT &&
13551             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13552          (move_direction & MV_HORIZONTAL &&
13553           ((element_info[element].move_pattern & MV_UP &&
13554             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13555            (element_info[element].move_pattern & MV_DOWN &&
13556             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13557       return MP_NO_ACTION;
13558
13559     /* do not push elements already moving away faster than player */
13560     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13561         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13562       return MP_NO_ACTION;
13563
13564     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13565     {
13566       if (player->push_delay_value == -1 || !player_was_pushing)
13567         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13568     }
13569     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13570     {
13571       if (player->push_delay_value == -1)
13572         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13573     }
13574     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13575     {
13576       if (!player->is_pushing)
13577         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13578     }
13579
13580     player->is_pushing = TRUE;
13581     player->is_active = TRUE;
13582
13583     if (!(IN_LEV_FIELD(nextx, nexty) &&
13584           (IS_FREE(nextx, nexty) ||
13585            (IS_SB_ELEMENT(element) &&
13586             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13587            (IS_CUSTOM_ELEMENT(element) &&
13588             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13589       return MP_NO_ACTION;
13590
13591     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13592       return MP_NO_ACTION;
13593
13594     if (player->push_delay == -1)       /* new pushing; restart delay */
13595       player->push_delay = 0;
13596
13597     if (player->push_delay < player->push_delay_value &&
13598         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13599         element != EL_SPRING && element != EL_BALLOON)
13600     {
13601       /* make sure that there is no move delay before next try to push */
13602       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13603         player->move_delay = 0;
13604
13605       return MP_NO_ACTION;
13606     }
13607
13608     if (IS_CUSTOM_ELEMENT(element) &&
13609         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13610     {
13611       if (!DigFieldByCE(nextx, nexty, element))
13612         return MP_NO_ACTION;
13613     }
13614
13615     if (IS_SB_ELEMENT(element))
13616     {
13617       if (element == EL_SOKOBAN_FIELD_FULL)
13618       {
13619         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13620         local_player->sokobanfields_still_needed++;
13621       }
13622
13623       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13624       {
13625         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13626         local_player->sokobanfields_still_needed--;
13627       }
13628
13629       Feld[x][y] = EL_SOKOBAN_OBJECT;
13630
13631       if (Back[x][y] == Back[nextx][nexty])
13632         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13633       else if (Back[x][y] != 0)
13634         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13635                                     ACTION_EMPTYING);
13636       else
13637         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13638                                     ACTION_FILLING);
13639
13640       if (local_player->sokobanfields_still_needed == 0 &&
13641           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13642       {
13643         PlayerWins(player);
13644
13645         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13646       }
13647     }
13648     else
13649       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13650
13651     InitMovingField(x, y, move_direction);
13652     GfxAction[x][y] = ACTION_PUSHING;
13653
13654     if (mode == DF_SNAP)
13655       ContinueMoving(x, y);
13656     else
13657       MovPos[x][y] = (dx != 0 ? dx : dy);
13658
13659     Pushed[x][y] = TRUE;
13660     Pushed[nextx][nexty] = TRUE;
13661
13662     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13663       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13664     else
13665       player->push_delay_value = -1;    /* get new value later */
13666
13667     /* check for element change _after_ element has been pushed */
13668     if (game.use_change_when_pushing_bug)
13669     {
13670       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13671                                  player->index_bit, dig_side);
13672       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13673                                           player->index_bit, dig_side);
13674     }
13675   }
13676   else if (IS_SWITCHABLE(element))
13677   {
13678     if (PLAYER_SWITCHING(player, x, y))
13679     {
13680       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13681                                           player->index_bit, dig_side);
13682
13683       return MP_ACTION;
13684     }
13685
13686     player->is_switching = TRUE;
13687     player->switch_x = x;
13688     player->switch_y = y;
13689
13690     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13691
13692     if (element == EL_ROBOT_WHEEL)
13693     {
13694       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13695       ZX = x;
13696       ZY = y;
13697
13698       game.robot_wheel_active = TRUE;
13699
13700       TEST_DrawLevelField(x, y);
13701     }
13702     else if (element == EL_SP_TERMINAL)
13703     {
13704       int xx, yy;
13705
13706       SCAN_PLAYFIELD(xx, yy)
13707       {
13708         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13709         {
13710           Bang(xx, yy);
13711         }
13712         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13713         {
13714           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13715
13716           ResetGfxAnimation(xx, yy);
13717           TEST_DrawLevelField(xx, yy);
13718         }
13719       }
13720     }
13721     else if (IS_BELT_SWITCH(element))
13722     {
13723       ToggleBeltSwitch(x, y);
13724     }
13725     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13726              element == EL_SWITCHGATE_SWITCH_DOWN ||
13727              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13728              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13729     {
13730       ToggleSwitchgateSwitch(x, y);
13731     }
13732     else if (element == EL_LIGHT_SWITCH ||
13733              element == EL_LIGHT_SWITCH_ACTIVE)
13734     {
13735       ToggleLightSwitch(x, y);
13736     }
13737     else if (element == EL_TIMEGATE_SWITCH ||
13738              element == EL_DC_TIMEGATE_SWITCH)
13739     {
13740       ActivateTimegateSwitch(x, y);
13741     }
13742     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13743              element == EL_BALLOON_SWITCH_RIGHT ||
13744              element == EL_BALLOON_SWITCH_UP    ||
13745              element == EL_BALLOON_SWITCH_DOWN  ||
13746              element == EL_BALLOON_SWITCH_NONE  ||
13747              element == EL_BALLOON_SWITCH_ANY)
13748     {
13749       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13750                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13751                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13752                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13753                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13754                              move_direction);
13755     }
13756     else if (element == EL_LAMP)
13757     {
13758       Feld[x][y] = EL_LAMP_ACTIVE;
13759       local_player->lights_still_needed--;
13760
13761       ResetGfxAnimation(x, y);
13762       TEST_DrawLevelField(x, y);
13763     }
13764     else if (element == EL_TIME_ORB_FULL)
13765     {
13766       Feld[x][y] = EL_TIME_ORB_EMPTY;
13767
13768       if (level.time > 0 || level.use_time_orb_bug)
13769       {
13770         TimeLeft += level.time_orb_time;
13771         game.no_time_limit = FALSE;
13772
13773         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13774
13775         DisplayGameControlValues();
13776       }
13777
13778       ResetGfxAnimation(x, y);
13779       TEST_DrawLevelField(x, y);
13780     }
13781     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13782              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13783     {
13784       int xx, yy;
13785
13786       game.ball_state = !game.ball_state;
13787
13788       SCAN_PLAYFIELD(xx, yy)
13789       {
13790         int e = Feld[xx][yy];
13791
13792         if (game.ball_state)
13793         {
13794           if (e == EL_EMC_MAGIC_BALL)
13795             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13796           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13797             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13798         }
13799         else
13800         {
13801           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13802             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13803           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13804             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13805         }
13806       }
13807     }
13808
13809     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13810                                         player->index_bit, dig_side);
13811
13812     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13813                                         player->index_bit, dig_side);
13814
13815     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13816                                         player->index_bit, dig_side);
13817
13818     return MP_ACTION;
13819   }
13820   else
13821   {
13822     if (!PLAYER_SWITCHING(player, x, y))
13823     {
13824       player->is_switching = TRUE;
13825       player->switch_x = x;
13826       player->switch_y = y;
13827
13828       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13829                                  player->index_bit, dig_side);
13830       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13831                                           player->index_bit, dig_side);
13832
13833       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13834                                  player->index_bit, dig_side);
13835       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13836                                           player->index_bit, dig_side);
13837     }
13838
13839     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13840                                player->index_bit, dig_side);
13841     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13842                                         player->index_bit, dig_side);
13843
13844     return MP_NO_ACTION;
13845   }
13846
13847   player->push_delay = -1;
13848
13849   if (is_player)                /* function can also be called by EL_PENGUIN */
13850   {
13851     if (Feld[x][y] != element)          /* really digged/collected something */
13852     {
13853       player->is_collecting = !player->is_digging;
13854       player->is_active = TRUE;
13855     }
13856   }
13857
13858   return MP_MOVING;
13859 }
13860
13861 static boolean DigFieldByCE(int x, int y, int digging_element)
13862 {
13863   int element = Feld[x][y];
13864
13865   if (!IS_FREE(x, y))
13866   {
13867     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13868                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13869                   ACTION_BREAKING);
13870
13871     /* no element can dig solid indestructible elements */
13872     if (IS_INDESTRUCTIBLE(element) &&
13873         !IS_DIGGABLE(element) &&
13874         !IS_COLLECTIBLE(element))
13875       return FALSE;
13876
13877     if (AmoebaNr[x][y] &&
13878         (element == EL_AMOEBA_FULL ||
13879          element == EL_BD_AMOEBA ||
13880          element == EL_AMOEBA_GROWING))
13881     {
13882       AmoebaCnt[AmoebaNr[x][y]]--;
13883       AmoebaCnt2[AmoebaNr[x][y]]--;
13884     }
13885
13886     if (IS_MOVING(x, y))
13887       RemoveMovingField(x, y);
13888     else
13889     {
13890       RemoveField(x, y);
13891       TEST_DrawLevelField(x, y);
13892     }
13893
13894     /* if digged element was about to explode, prevent the explosion */
13895     ExplodeField[x][y] = EX_TYPE_NONE;
13896
13897     PlayLevelSoundAction(x, y, action);
13898   }
13899
13900   Store[x][y] = EL_EMPTY;
13901
13902   /* this makes it possible to leave the removed element again */
13903   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13904     Store[x][y] = element;
13905
13906   return TRUE;
13907 }
13908
13909 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13910 {
13911   int jx = player->jx, jy = player->jy;
13912   int x = jx + dx, y = jy + dy;
13913   int snap_direction = (dx == -1 ? MV_LEFT  :
13914                         dx == +1 ? MV_RIGHT :
13915                         dy == -1 ? MV_UP    :
13916                         dy == +1 ? MV_DOWN  : MV_NONE);
13917   boolean can_continue_snapping = (level.continuous_snapping &&
13918                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13919
13920   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13921     return FALSE;
13922
13923   if (!player->active || !IN_LEV_FIELD(x, y))
13924     return FALSE;
13925
13926   if (dx && dy)
13927     return FALSE;
13928
13929   if (!dx && !dy)
13930   {
13931     if (player->MovPos == 0)
13932       player->is_pushing = FALSE;
13933
13934     player->is_snapping = FALSE;
13935
13936     if (player->MovPos == 0)
13937     {
13938       player->is_moving = FALSE;
13939       player->is_digging = FALSE;
13940       player->is_collecting = FALSE;
13941     }
13942
13943     return FALSE;
13944   }
13945
13946   /* prevent snapping with already pressed snap key when not allowed */
13947   if (player->is_snapping && !can_continue_snapping)
13948     return FALSE;
13949
13950   player->MovDir = snap_direction;
13951
13952   if (player->MovPos == 0)
13953   {
13954     player->is_moving = FALSE;
13955     player->is_digging = FALSE;
13956     player->is_collecting = FALSE;
13957   }
13958
13959   player->is_dropping = FALSE;
13960   player->is_dropping_pressed = FALSE;
13961   player->drop_pressed_delay = 0;
13962
13963   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13964     return FALSE;
13965
13966   player->is_snapping = TRUE;
13967   player->is_active = TRUE;
13968
13969   if (player->MovPos == 0)
13970   {
13971     player->is_moving = FALSE;
13972     player->is_digging = FALSE;
13973     player->is_collecting = FALSE;
13974   }
13975
13976   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13977     TEST_DrawLevelField(player->last_jx, player->last_jy);
13978
13979   TEST_DrawLevelField(x, y);
13980
13981   return TRUE;
13982 }
13983
13984 static boolean DropElement(struct PlayerInfo *player)
13985 {
13986   int old_element, new_element;
13987   int dropx = player->jx, dropy = player->jy;
13988   int drop_direction = player->MovDir;
13989   int drop_side = drop_direction;
13990   int drop_element = get_next_dropped_element(player);
13991
13992   player->is_dropping_pressed = TRUE;
13993
13994   /* do not drop an element on top of another element; when holding drop key
13995      pressed without moving, dropped element must move away before the next
13996      element can be dropped (this is especially important if the next element
13997      is dynamite, which can be placed on background for historical reasons) */
13998   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13999     return MP_ACTION;
14000
14001   if (IS_THROWABLE(drop_element))
14002   {
14003     dropx += GET_DX_FROM_DIR(drop_direction);
14004     dropy += GET_DY_FROM_DIR(drop_direction);
14005
14006     if (!IN_LEV_FIELD(dropx, dropy))
14007       return FALSE;
14008   }
14009
14010   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14011   new_element = drop_element;           /* default: no change when dropping */
14012
14013   /* check if player is active, not moving and ready to drop */
14014   if (!player->active || player->MovPos || player->drop_delay > 0)
14015     return FALSE;
14016
14017   /* check if player has anything that can be dropped */
14018   if (new_element == EL_UNDEFINED)
14019     return FALSE;
14020
14021   /* check if drop key was pressed long enough for EM style dynamite */
14022   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14023     return FALSE;
14024
14025   /* check if anything can be dropped at the current position */
14026   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14027     return FALSE;
14028
14029   /* collected custom elements can only be dropped on empty fields */
14030   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14031     return FALSE;
14032
14033   if (old_element != EL_EMPTY)
14034     Back[dropx][dropy] = old_element;   /* store old element on this field */
14035
14036   ResetGfxAnimation(dropx, dropy);
14037   ResetRandomAnimationValue(dropx, dropy);
14038
14039   if (player->inventory_size > 0 ||
14040       player->inventory_infinite_element != EL_UNDEFINED)
14041   {
14042     if (player->inventory_size > 0)
14043     {
14044       player->inventory_size--;
14045
14046       DrawGameDoorValues();
14047
14048       if (new_element == EL_DYNAMITE)
14049         new_element = EL_DYNAMITE_ACTIVE;
14050       else if (new_element == EL_EM_DYNAMITE)
14051         new_element = EL_EM_DYNAMITE_ACTIVE;
14052       else if (new_element == EL_SP_DISK_RED)
14053         new_element = EL_SP_DISK_RED_ACTIVE;
14054     }
14055
14056     Feld[dropx][dropy] = new_element;
14057
14058     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14059       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14060                           el2img(Feld[dropx][dropy]), 0);
14061
14062     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14063
14064     /* needed if previous element just changed to "empty" in the last frame */
14065     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14066
14067     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14068                                player->index_bit, drop_side);
14069     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14070                                         CE_PLAYER_DROPS_X,
14071                                         player->index_bit, drop_side);
14072
14073     TestIfElementTouchesCustomElement(dropx, dropy);
14074   }
14075   else          /* player is dropping a dyna bomb */
14076   {
14077     player->dynabombs_left--;
14078
14079     Feld[dropx][dropy] = new_element;
14080
14081     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14082       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14083                           el2img(Feld[dropx][dropy]), 0);
14084
14085     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14086   }
14087
14088   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14089     InitField_WithBug1(dropx, dropy, FALSE);
14090
14091   new_element = Feld[dropx][dropy];     /* element might have changed */
14092
14093   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14094       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14095   {
14096     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14097       MovDir[dropx][dropy] = drop_direction;
14098
14099     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14100
14101     /* do not cause impact style collision by dropping elements that can fall */
14102     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14103   }
14104
14105   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14106   player->is_dropping = TRUE;
14107
14108   player->drop_pressed_delay = 0;
14109   player->is_dropping_pressed = FALSE;
14110
14111   player->drop_x = dropx;
14112   player->drop_y = dropy;
14113
14114   return TRUE;
14115 }
14116
14117 /* ------------------------------------------------------------------------- */
14118 /* game sound playing functions                                              */
14119 /* ------------------------------------------------------------------------- */
14120
14121 static int *loop_sound_frame = NULL;
14122 static int *loop_sound_volume = NULL;
14123
14124 void InitPlayLevelSound()
14125 {
14126   int num_sounds = getSoundListSize();
14127
14128   checked_free(loop_sound_frame);
14129   checked_free(loop_sound_volume);
14130
14131   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14132   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14133 }
14134
14135 static void PlayLevelSound(int x, int y, int nr)
14136 {
14137   int sx = SCREENX(x), sy = SCREENY(y);
14138   int volume, stereo_position;
14139   int max_distance = 8;
14140   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14141
14142   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14143       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14144     return;
14145
14146   if (!IN_LEV_FIELD(x, y) ||
14147       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14148       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14149     return;
14150
14151   volume = SOUND_MAX_VOLUME;
14152
14153   if (!IN_SCR_FIELD(sx, sy))
14154   {
14155     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14156     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14157
14158     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14159   }
14160
14161   stereo_position = (SOUND_MAX_LEFT +
14162                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14163                      (SCR_FIELDX + 2 * max_distance));
14164
14165   if (IS_LOOP_SOUND(nr))
14166   {
14167     /* This assures that quieter loop sounds do not overwrite louder ones,
14168        while restarting sound volume comparison with each new game frame. */
14169
14170     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14171       return;
14172
14173     loop_sound_volume[nr] = volume;
14174     loop_sound_frame[nr] = FrameCounter;
14175   }
14176
14177   PlaySoundExt(nr, volume, stereo_position, type);
14178 }
14179
14180 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14181 {
14182   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14183                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14184                  y < LEVELY(BY1) ? LEVELY(BY1) :
14185                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14186                  sound_action);
14187 }
14188
14189 static void PlayLevelSoundAction(int x, int y, int action)
14190 {
14191   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14192 }
14193
14194 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14195 {
14196   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14197
14198   if (sound_effect != SND_UNDEFINED)
14199     PlayLevelSound(x, y, sound_effect);
14200 }
14201
14202 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14203                                               int action)
14204 {
14205   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14206
14207   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14208     PlayLevelSound(x, y, sound_effect);
14209 }
14210
14211 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14212 {
14213   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14214
14215   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14216     PlayLevelSound(x, y, sound_effect);
14217 }
14218
14219 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14220 {
14221   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14222
14223   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14224     StopSound(sound_effect);
14225 }
14226
14227 static void PlayLevelMusic()
14228 {
14229   if (levelset.music[level_nr] != MUS_UNDEFINED)
14230     PlayMusic(levelset.music[level_nr]);        /* from config file */
14231   else
14232     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14233 }
14234
14235 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14236 {
14237   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14238   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14239   int x = xx - 1 - offset;
14240   int y = yy - 1 - offset;
14241
14242   switch (sample)
14243   {
14244     case SAMPLE_blank:
14245       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14246       break;
14247
14248     case SAMPLE_roll:
14249       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14250       break;
14251
14252     case SAMPLE_stone:
14253       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14254       break;
14255
14256     case SAMPLE_nut:
14257       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14258       break;
14259
14260     case SAMPLE_crack:
14261       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14262       break;
14263
14264     case SAMPLE_bug:
14265       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14266       break;
14267
14268     case SAMPLE_tank:
14269       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14270       break;
14271
14272     case SAMPLE_android_clone:
14273       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14274       break;
14275
14276     case SAMPLE_android_move:
14277       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14278       break;
14279
14280     case SAMPLE_spring:
14281       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14282       break;
14283
14284     case SAMPLE_slurp:
14285       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14286       break;
14287
14288     case SAMPLE_eater:
14289       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14290       break;
14291
14292     case SAMPLE_eater_eat:
14293       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14294       break;
14295
14296     case SAMPLE_alien:
14297       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14298       break;
14299
14300     case SAMPLE_collect:
14301       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14302       break;
14303
14304     case SAMPLE_diamond:
14305       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14306       break;
14307
14308     case SAMPLE_squash:
14309       /* !!! CHECK THIS !!! */
14310 #if 1
14311       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14312 #else
14313       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14314 #endif
14315       break;
14316
14317     case SAMPLE_wonderfall:
14318       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14319       break;
14320
14321     case SAMPLE_drip:
14322       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14323       break;
14324
14325     case SAMPLE_push:
14326       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14327       break;
14328
14329     case SAMPLE_dirt:
14330       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14331       break;
14332
14333     case SAMPLE_acid:
14334       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14335       break;
14336
14337     case SAMPLE_ball:
14338       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14339       break;
14340
14341     case SAMPLE_grow:
14342       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14343       break;
14344
14345     case SAMPLE_wonder:
14346       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14347       break;
14348
14349     case SAMPLE_door:
14350       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14351       break;
14352
14353     case SAMPLE_exit_open:
14354       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14355       break;
14356
14357     case SAMPLE_exit_leave:
14358       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14359       break;
14360
14361     case SAMPLE_dynamite:
14362       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14363       break;
14364
14365     case SAMPLE_tick:
14366       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14367       break;
14368
14369     case SAMPLE_press:
14370       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14371       break;
14372
14373     case SAMPLE_wheel:
14374       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14375       break;
14376
14377     case SAMPLE_boom:
14378       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14379       break;
14380
14381     case SAMPLE_die:
14382       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14383       break;
14384
14385     case SAMPLE_time:
14386       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14387       break;
14388
14389     default:
14390       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14391       break;
14392   }
14393 }
14394
14395 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14396 {
14397   int element = map_element_SP_to_RND(element_sp);
14398   int action = map_action_SP_to_RND(action_sp);
14399   int offset = (setup.sp_show_border_elements ? 0 : 1);
14400   int x = xx - offset;
14401   int y = yy - offset;
14402
14403   PlayLevelSoundElementAction(x, y, element, action);
14404 }
14405
14406 void RaiseScore(int value)
14407 {
14408   local_player->score += value;
14409
14410   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14411
14412   DisplayGameControlValues();
14413 }
14414
14415 void RaiseScoreElement(int element)
14416 {
14417   switch (element)
14418   {
14419     case EL_EMERALD:
14420     case EL_BD_DIAMOND:
14421     case EL_EMERALD_YELLOW:
14422     case EL_EMERALD_RED:
14423     case EL_EMERALD_PURPLE:
14424     case EL_SP_INFOTRON:
14425       RaiseScore(level.score[SC_EMERALD]);
14426       break;
14427     case EL_DIAMOND:
14428       RaiseScore(level.score[SC_DIAMOND]);
14429       break;
14430     case EL_CRYSTAL:
14431       RaiseScore(level.score[SC_CRYSTAL]);
14432       break;
14433     case EL_PEARL:
14434       RaiseScore(level.score[SC_PEARL]);
14435       break;
14436     case EL_BUG:
14437     case EL_BD_BUTTERFLY:
14438     case EL_SP_ELECTRON:
14439       RaiseScore(level.score[SC_BUG]);
14440       break;
14441     case EL_SPACESHIP:
14442     case EL_BD_FIREFLY:
14443     case EL_SP_SNIKSNAK:
14444       RaiseScore(level.score[SC_SPACESHIP]);
14445       break;
14446     case EL_YAMYAM:
14447     case EL_DARK_YAMYAM:
14448       RaiseScore(level.score[SC_YAMYAM]);
14449       break;
14450     case EL_ROBOT:
14451       RaiseScore(level.score[SC_ROBOT]);
14452       break;
14453     case EL_PACMAN:
14454       RaiseScore(level.score[SC_PACMAN]);
14455       break;
14456     case EL_NUT:
14457       RaiseScore(level.score[SC_NUT]);
14458       break;
14459     case EL_DYNAMITE:
14460     case EL_EM_DYNAMITE:
14461     case EL_SP_DISK_RED:
14462     case EL_DYNABOMB_INCREASE_NUMBER:
14463     case EL_DYNABOMB_INCREASE_SIZE:
14464     case EL_DYNABOMB_INCREASE_POWER:
14465       RaiseScore(level.score[SC_DYNAMITE]);
14466       break;
14467     case EL_SHIELD_NORMAL:
14468     case EL_SHIELD_DEADLY:
14469       RaiseScore(level.score[SC_SHIELD]);
14470       break;
14471     case EL_EXTRA_TIME:
14472       RaiseScore(level.extra_time_score);
14473       break;
14474     case EL_KEY_1:
14475     case EL_KEY_2:
14476     case EL_KEY_3:
14477     case EL_KEY_4:
14478     case EL_EM_KEY_1:
14479     case EL_EM_KEY_2:
14480     case EL_EM_KEY_3:
14481     case EL_EM_KEY_4:
14482     case EL_EMC_KEY_5:
14483     case EL_EMC_KEY_6:
14484     case EL_EMC_KEY_7:
14485     case EL_EMC_KEY_8:
14486     case EL_DC_KEY_WHITE:
14487       RaiseScore(level.score[SC_KEY]);
14488       break;
14489     default:
14490       RaiseScore(element_info[element].collect_score);
14491       break;
14492   }
14493 }
14494
14495 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14496 {
14497   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14498   {
14499     /* closing door required in case of envelope style request dialogs */
14500     if (!skip_request)
14501       CloseDoor(DOOR_CLOSE_1);
14502
14503 #if defined(NETWORK_AVALIABLE)
14504     if (options.network)
14505       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14506     else
14507 #endif
14508     {
14509       if (quick_quit)
14510         FadeSkipNextFadeIn();
14511
14512       SetGameStatus(GAME_MODE_MAIN);
14513
14514       DrawMainMenu();
14515     }
14516   }
14517   else          /* continue playing the game */
14518   {
14519     if (tape.playing && tape.deactivate_display)
14520       TapeDeactivateDisplayOff(TRUE);
14521
14522     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14523
14524     if (tape.playing && tape.deactivate_display)
14525       TapeDeactivateDisplayOn();
14526   }
14527 }
14528
14529 void RequestQuitGame(boolean ask_if_really_quit)
14530 {
14531   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14532   boolean skip_request = AllPlayersGone || quick_quit;
14533
14534   RequestQuitGameExt(skip_request, quick_quit,
14535                      "Do you really want to quit the game?");
14536 }
14537
14538
14539 /* ------------------------------------------------------------------------- */
14540 /* random generator functions                                                */
14541 /* ------------------------------------------------------------------------- */
14542
14543 unsigned int InitEngineRandom_RND(int seed)
14544 {
14545   game.num_random_calls = 0;
14546
14547   return InitEngineRandom(seed);
14548 }
14549
14550 unsigned int RND(int max)
14551 {
14552   if (max > 0)
14553   {
14554     game.num_random_calls++;
14555
14556     return GetEngineRandom(max);
14557   }
14558
14559   return 0;
14560 }
14561
14562
14563 /* ------------------------------------------------------------------------- */
14564 /* game engine snapshot handling functions                                   */
14565 /* ------------------------------------------------------------------------- */
14566
14567 struct EngineSnapshotInfo
14568 {
14569   /* runtime values for custom element collect score */
14570   int collect_score[NUM_CUSTOM_ELEMENTS];
14571
14572   /* runtime values for group element choice position */
14573   int choice_pos[NUM_GROUP_ELEMENTS];
14574
14575   /* runtime values for belt position animations */
14576   int belt_graphic[4][NUM_BELT_PARTS];
14577   int belt_anim_mode[4][NUM_BELT_PARTS];
14578 };
14579
14580 static struct EngineSnapshotInfo engine_snapshot_rnd;
14581 static char *snapshot_level_identifier = NULL;
14582 static int snapshot_level_nr = -1;
14583
14584 static void SaveEngineSnapshotValues_RND()
14585 {
14586   static int belt_base_active_element[4] =
14587   {
14588     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14589     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14590     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14591     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14592   };
14593   int i, j;
14594
14595   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14596   {
14597     int element = EL_CUSTOM_START + i;
14598
14599     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14600   }
14601
14602   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14603   {
14604     int element = EL_GROUP_START + i;
14605
14606     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14607   }
14608
14609   for (i = 0; i < 4; i++)
14610   {
14611     for (j = 0; j < NUM_BELT_PARTS; j++)
14612     {
14613       int element = belt_base_active_element[i] + j;
14614       int graphic = el2img(element);
14615       int anim_mode = graphic_info[graphic].anim_mode;
14616
14617       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14618       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14619     }
14620   }
14621 }
14622
14623 static void LoadEngineSnapshotValues_RND()
14624 {
14625   unsigned int num_random_calls = game.num_random_calls;
14626   int i, j;
14627
14628   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14629   {
14630     int element = EL_CUSTOM_START + i;
14631
14632     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14633   }
14634
14635   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14636   {
14637     int element = EL_GROUP_START + i;
14638
14639     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14640   }
14641
14642   for (i = 0; i < 4; i++)
14643   {
14644     for (j = 0; j < NUM_BELT_PARTS; j++)
14645     {
14646       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14647       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14648
14649       graphic_info[graphic].anim_mode = anim_mode;
14650     }
14651   }
14652
14653   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14654   {
14655     InitRND(tape.random_seed);
14656     for (i = 0; i < num_random_calls; i++)
14657       RND(1);
14658   }
14659
14660   if (game.num_random_calls != num_random_calls)
14661   {
14662     Error(ERR_INFO, "number of random calls out of sync");
14663     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14664     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14665     Error(ERR_EXIT, "this should not happen -- please debug");
14666   }
14667 }
14668
14669 void FreeEngineSnapshotSingle()
14670 {
14671   FreeSnapshotSingle();
14672
14673   setString(&snapshot_level_identifier, NULL);
14674   snapshot_level_nr = -1;
14675 }
14676
14677 void FreeEngineSnapshotList()
14678 {
14679   FreeSnapshotList();
14680 }
14681
14682 ListNode *SaveEngineSnapshotBuffers()
14683 {
14684   ListNode *buffers = NULL;
14685
14686   /* copy some special values to a structure better suited for the snapshot */
14687
14688   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14689     SaveEngineSnapshotValues_RND();
14690   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14691     SaveEngineSnapshotValues_EM();
14692   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14693     SaveEngineSnapshotValues_SP(&buffers);
14694
14695   /* save values stored in special snapshot structure */
14696
14697   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14698     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14699   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14700     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14701   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14702     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14703
14704   /* save further RND engine values */
14705
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14709
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14714
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14720
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14724
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14726
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14728
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14731
14732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14750
14751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14753
14754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14757
14758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14760
14761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14766
14767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14769
14770 #if 0
14771   ListNode *node = engine_snapshot_list_rnd;
14772   int num_bytes = 0;
14773
14774   while (node != NULL)
14775   {
14776     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14777
14778     node = node->next;
14779   }
14780
14781   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14782 #endif
14783
14784   return buffers;
14785 }
14786
14787 void SaveEngineSnapshotSingle()
14788 {
14789   ListNode *buffers = SaveEngineSnapshotBuffers();
14790
14791   /* finally save all snapshot buffers to single snapshot */
14792   SaveSnapshotSingle(buffers);
14793
14794   /* save level identification information */
14795   setString(&snapshot_level_identifier, leveldir_current->identifier);
14796   snapshot_level_nr = level_nr;
14797 }
14798
14799 boolean CheckSaveEngineSnapshotToList()
14800 {
14801   boolean save_snapshot =
14802     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14803      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14804       game.snapshot.changed_action) ||
14805      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14806       game.snapshot.collected_item));
14807
14808   game.snapshot.changed_action = FALSE;
14809   game.snapshot.collected_item = FALSE;
14810   game.snapshot.save_snapshot = save_snapshot;
14811
14812   return save_snapshot;
14813 }
14814
14815 void SaveEngineSnapshotToList()
14816 {
14817   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14818       tape.quick_resume)
14819     return;
14820
14821   ListNode *buffers = SaveEngineSnapshotBuffers();
14822
14823   /* finally save all snapshot buffers to snapshot list */
14824   SaveSnapshotToList(buffers);
14825 }
14826
14827 void SaveEngineSnapshotToListInitial()
14828 {
14829   FreeEngineSnapshotList();
14830
14831   SaveEngineSnapshotToList();
14832 }
14833
14834 void LoadEngineSnapshotValues()
14835 {
14836   /* restore special values from snapshot structure */
14837
14838   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14839     LoadEngineSnapshotValues_RND();
14840   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14841     LoadEngineSnapshotValues_EM();
14842   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14843     LoadEngineSnapshotValues_SP();
14844 }
14845
14846 void LoadEngineSnapshotSingle()
14847 {
14848   LoadSnapshotSingle();
14849
14850   LoadEngineSnapshotValues();
14851 }
14852
14853 void LoadEngineSnapshot_Undo(int steps)
14854 {
14855   LoadSnapshotFromList_Older(steps);
14856
14857   LoadEngineSnapshotValues();
14858 }
14859
14860 void LoadEngineSnapshot_Redo(int steps)
14861 {
14862   LoadSnapshotFromList_Newer(steps);
14863
14864   LoadEngineSnapshotValues();
14865 }
14866
14867 boolean CheckEngineSnapshotSingle()
14868 {
14869   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14870           snapshot_level_nr == level_nr);
14871 }
14872
14873 boolean CheckEngineSnapshotList()
14874 {
14875   return CheckSnapshotList();
14876 }
14877
14878
14879 /* ---------- new game button stuff ---------------------------------------- */
14880
14881 static struct
14882 {
14883   int graphic;
14884   struct XY *pos;
14885   int gadget_id;
14886   char *infotext;
14887 } gamebutton_info[NUM_GAME_BUTTONS] =
14888 {
14889   {
14890     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14891     GAME_CTRL_ID_STOP,                  "stop game"
14892   },
14893   {
14894     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14895     GAME_CTRL_ID_PAUSE,                 "pause game"
14896   },
14897   {
14898     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14899     GAME_CTRL_ID_PLAY,                  "play game"
14900   },
14901   {
14902     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14903     GAME_CTRL_ID_UNDO,                  "undo step"
14904   },
14905   {
14906     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14907     GAME_CTRL_ID_REDO,                  "redo step"
14908   },
14909   {
14910     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14911     GAME_CTRL_ID_SAVE,                  "save game"
14912   },
14913   {
14914     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14915     GAME_CTRL_ID_PAUSE2,                "pause game"
14916   },
14917   {
14918     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14919     GAME_CTRL_ID_LOAD,                  "load game"
14920   },
14921   {
14922     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14923     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14924   },
14925   {
14926     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14927     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14928   },
14929   {
14930     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14931     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14932   }
14933 };
14934
14935 void CreateGameButtons()
14936 {
14937   int i;
14938
14939   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14940   {
14941     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14942     struct XY *pos = gamebutton_info[i].pos;
14943     struct GadgetInfo *gi;
14944     int button_type;
14945     boolean checked;
14946     unsigned int event_mask;
14947     int base_x = (tape.show_game_buttons ? VX : DX);
14948     int base_y = (tape.show_game_buttons ? VY : DY);
14949     int gd_x   = gfx->src_x;
14950     int gd_y   = gfx->src_y;
14951     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14952     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14953     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14954     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14955     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14956     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14957     int id = i;
14958
14959     if (gfx->bitmap == NULL)
14960     {
14961       game_gadget[id] = NULL;
14962
14963       continue;
14964     }
14965
14966     if (id == GAME_CTRL_ID_STOP ||
14967         id == GAME_CTRL_ID_PLAY ||
14968         id == GAME_CTRL_ID_SAVE ||
14969         id == GAME_CTRL_ID_LOAD)
14970     {
14971       button_type = GD_TYPE_NORMAL_BUTTON;
14972       checked = FALSE;
14973       event_mask = GD_EVENT_RELEASED;
14974     }
14975     else if (id == GAME_CTRL_ID_UNDO ||
14976              id == GAME_CTRL_ID_REDO)
14977     {
14978       button_type = GD_TYPE_NORMAL_BUTTON;
14979       checked = FALSE;
14980       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14981     }
14982     else
14983     {
14984       button_type = GD_TYPE_CHECK_BUTTON;
14985       checked =
14986         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14987          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14988          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14989       event_mask = GD_EVENT_PRESSED;
14990     }
14991
14992     gi = CreateGadget(GDI_CUSTOM_ID, id,
14993                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14994                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14995                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14996                       GDI_WIDTH, gfx->width,
14997                       GDI_HEIGHT, gfx->height,
14998                       GDI_TYPE, button_type,
14999                       GDI_STATE, GD_BUTTON_UNPRESSED,
15000                       GDI_CHECKED, checked,
15001                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15002                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15003                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15004                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15005                       GDI_DIRECT_DRAW, FALSE,
15006                       GDI_EVENT_MASK, event_mask,
15007                       GDI_CALLBACK_ACTION, HandleGameButtons,
15008                       GDI_END);
15009
15010     if (gi == NULL)
15011       Error(ERR_EXIT, "cannot create gadget");
15012
15013     game_gadget[id] = gi;
15014   }
15015 }
15016
15017 void FreeGameButtons()
15018 {
15019   int i;
15020
15021   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15022     FreeGadget(game_gadget[i]);
15023 }
15024
15025 static void UnmapGameButtonsAtSamePosition(int id)
15026 {
15027   int i;
15028
15029   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15030     if (i != id &&
15031         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15032         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15033       UnmapGadget(game_gadget[i]);
15034 }
15035
15036 static void UnmapGameButtonsAtSamePosition_All()
15037 {
15038   if (setup.show_snapshot_buttons)
15039   {
15040     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15041     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15042     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15043   }
15044   else
15045   {
15046     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15047     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15048     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15049   }
15050 }
15051
15052 static void MapGameButtonsAtSamePosition(int id)
15053 {
15054   int i;
15055
15056   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15057     if (i != id &&
15058         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15059         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15060       MapGadget(game_gadget[i]);
15061
15062   UnmapGameButtonsAtSamePosition_All();
15063 }
15064
15065 void MapUndoRedoButtons()
15066 {
15067   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15068   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15069
15070   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15071   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15072
15073   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15074 }
15075
15076 void UnmapUndoRedoButtons()
15077 {
15078   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15079   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15080
15081   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15082   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15083
15084   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15085 }
15086
15087 void MapGameButtons()
15088 {
15089   int i;
15090
15091   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15092     if (i != GAME_CTRL_ID_UNDO &&
15093         i != GAME_CTRL_ID_REDO)
15094       MapGadget(game_gadget[i]);
15095
15096   UnmapGameButtonsAtSamePosition_All();
15097
15098   RedrawGameButtons();
15099 }
15100
15101 void UnmapGameButtons()
15102 {
15103   int i;
15104
15105   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15106     UnmapGadget(game_gadget[i]);
15107 }
15108
15109 void RedrawGameButtons()
15110 {
15111   int i;
15112
15113   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15114     RedrawGadget(game_gadget[i]);
15115
15116   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15117   redraw_mask &= ~REDRAW_ALL;
15118 }
15119
15120 void GameUndoRedoExt()
15121 {
15122   ClearPlayerAction();
15123
15124   tape.pausing = TRUE;
15125
15126   RedrawPlayfield();
15127   UpdateAndDisplayGameControlValues();
15128
15129   DrawCompleteVideoDisplay();
15130   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15131   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15132   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15133
15134   BackToFront();
15135 }
15136
15137 void GameUndo(int steps)
15138 {
15139   if (!CheckEngineSnapshotList())
15140     return;
15141
15142   LoadEngineSnapshot_Undo(steps);
15143
15144   GameUndoRedoExt();
15145 }
15146
15147 void GameRedo(int steps)
15148 {
15149   if (!CheckEngineSnapshotList())
15150     return;
15151
15152   LoadEngineSnapshot_Redo(steps);
15153
15154   GameUndoRedoExt();
15155 }
15156
15157 static void HandleGameButtonsExt(int id, int button)
15158 {
15159   static boolean game_undo_executed = FALSE;
15160   int steps = BUTTON_STEPSIZE(button);
15161   boolean handle_game_buttons =
15162     (game_status == GAME_MODE_PLAYING ||
15163      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15164
15165   if (!handle_game_buttons)
15166     return;
15167
15168   switch (id)
15169   {
15170     case GAME_CTRL_ID_STOP:
15171       if (game_status == GAME_MODE_MAIN)
15172         break;
15173
15174       if (tape.playing)
15175         TapeStop();
15176       else
15177         RequestQuitGame(TRUE);
15178
15179       break;
15180
15181     case GAME_CTRL_ID_PAUSE:
15182     case GAME_CTRL_ID_PAUSE2:
15183       if (options.network && game_status == GAME_MODE_PLAYING)
15184       {
15185 #if defined(NETWORK_AVALIABLE)
15186         if (tape.pausing)
15187           SendToServer_ContinuePlaying();
15188         else
15189           SendToServer_PausePlaying();
15190 #endif
15191       }
15192       else
15193         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15194
15195       game_undo_executed = FALSE;
15196
15197       break;
15198
15199     case GAME_CTRL_ID_PLAY:
15200       if (game_status == GAME_MODE_MAIN)
15201       {
15202         StartGameActions(options.network, setup.autorecord, level.random_seed);
15203       }
15204       else if (tape.pausing)
15205       {
15206 #if defined(NETWORK_AVALIABLE)
15207         if (options.network)
15208           SendToServer_ContinuePlaying();
15209         else
15210 #endif
15211           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15212       }
15213       break;
15214
15215     case GAME_CTRL_ID_UNDO:
15216       // Important: When using "save snapshot when collecting an item" mode,
15217       // load last (current) snapshot for first "undo" after pressing "pause"
15218       // (else the last-but-one snapshot would be loaded, because the snapshot
15219       // pointer already points to the last snapshot when pressing "pause",
15220       // which is fine for "every step/move" mode, but not for "every collect")
15221       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15222           !game_undo_executed)
15223         steps--;
15224
15225       game_undo_executed = TRUE;
15226
15227       GameUndo(steps);
15228       break;
15229
15230     case GAME_CTRL_ID_REDO:
15231       GameRedo(steps);
15232       break;
15233
15234     case GAME_CTRL_ID_SAVE:
15235       TapeQuickSave();
15236       break;
15237
15238     case GAME_CTRL_ID_LOAD:
15239       TapeQuickLoad();
15240       break;
15241
15242     case SOUND_CTRL_ID_MUSIC:
15243       if (setup.sound_music)
15244       { 
15245         setup.sound_music = FALSE;
15246
15247         FadeMusic();
15248       }
15249       else if (audio.music_available)
15250       { 
15251         setup.sound = setup.sound_music = TRUE;
15252
15253         SetAudioMode(setup.sound);
15254
15255         PlayLevelMusic();
15256       }
15257       break;
15258
15259     case SOUND_CTRL_ID_LOOPS:
15260       if (setup.sound_loops)
15261         setup.sound_loops = FALSE;
15262       else if (audio.loops_available)
15263       {
15264         setup.sound = setup.sound_loops = TRUE;
15265
15266         SetAudioMode(setup.sound);
15267       }
15268       break;
15269
15270     case SOUND_CTRL_ID_SIMPLE:
15271       if (setup.sound_simple)
15272         setup.sound_simple = FALSE;
15273       else if (audio.sound_available)
15274       {
15275         setup.sound = setup.sound_simple = TRUE;
15276
15277         SetAudioMode(setup.sound);
15278       }
15279       break;
15280
15281     default:
15282       break;
15283   }
15284 }
15285
15286 static void HandleGameButtons(struct GadgetInfo *gi)
15287 {
15288   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15289 }
15290
15291 void HandleSoundButtonKeys(Key key)
15292 {
15293
15294   if (key == setup.shortcut.sound_simple)
15295     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15296   else if (key == setup.shortcut.sound_loops)
15297     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15298   else if (key == setup.shortcut.sound_music)
15299     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15300 }