added function to get music filename by music ID
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_FRAME                        35
126 #define GAME_PANEL_SHIELD_NORMAL                36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
128 #define GAME_PANEL_SHIELD_DEADLY                38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
130 #define GAME_PANEL_EXIT                         40
131 #define GAME_PANEL_EMC_MAGIC_BALL               41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
133 #define GAME_PANEL_LIGHT_SWITCH                 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
135 #define GAME_PANEL_TIMEGATE_SWITCH              45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
137 #define GAME_PANEL_SWITCHGATE_SWITCH            47
138 #define GAME_PANEL_EMC_LENSES                   48
139 #define GAME_PANEL_EMC_LENSES_TIME              49
140 #define GAME_PANEL_EMC_MAGNIFIER                50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
142 #define GAME_PANEL_BALLOON_SWITCH               52
143 #define GAME_PANEL_DYNABOMB_NUMBER              53
144 #define GAME_PANEL_DYNABOMB_SIZE                54
145 #define GAME_PANEL_DYNABOMB_POWER               55
146 #define GAME_PANEL_PENGUINS                     56
147 #define GAME_PANEL_SOKOBAN_OBJECTS              57
148 #define GAME_PANEL_SOKOBAN_FIELDS               58
149 #define GAME_PANEL_ROBOT_WHEEL                  59
150 #define GAME_PANEL_CONVEYOR_BELT_1              60
151 #define GAME_PANEL_CONVEYOR_BELT_2              61
152 #define GAME_PANEL_CONVEYOR_BELT_3              62
153 #define GAME_PANEL_CONVEYOR_BELT_4              63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
158 #define GAME_PANEL_MAGIC_WALL                   68
159 #define GAME_PANEL_MAGIC_WALL_TIME              69
160 #define GAME_PANEL_GRAVITY_STATE                70
161 #define GAME_PANEL_GRAPHIC_1                    71
162 #define GAME_PANEL_GRAPHIC_2                    72
163 #define GAME_PANEL_GRAPHIC_3                    73
164 #define GAME_PANEL_GRAPHIC_4                    74
165 #define GAME_PANEL_GRAPHIC_5                    75
166 #define GAME_PANEL_GRAPHIC_6                    76
167 #define GAME_PANEL_GRAPHIC_7                    77
168 #define GAME_PANEL_GRAPHIC_8                    78
169 #define GAME_PANEL_ELEMENT_1                    79
170 #define GAME_PANEL_ELEMENT_2                    80
171 #define GAME_PANEL_ELEMENT_3                    81
172 #define GAME_PANEL_ELEMENT_4                    82
173 #define GAME_PANEL_ELEMENT_5                    83
174 #define GAME_PANEL_ELEMENT_6                    84
175 #define GAME_PANEL_ELEMENT_7                    85
176 #define GAME_PANEL_ELEMENT_8                    86
177 #define GAME_PANEL_ELEMENT_COUNT_1              87
178 #define GAME_PANEL_ELEMENT_COUNT_2              88
179 #define GAME_PANEL_ELEMENT_COUNT_3              89
180 #define GAME_PANEL_ELEMENT_COUNT_4              90
181 #define GAME_PANEL_ELEMENT_COUNT_5              91
182 #define GAME_PANEL_ELEMENT_COUNT_6              92
183 #define GAME_PANEL_ELEMENT_COUNT_7              93
184 #define GAME_PANEL_ELEMENT_COUNT_8              94
185 #define GAME_PANEL_CE_SCORE_1                   95
186 #define GAME_PANEL_CE_SCORE_2                   96
187 #define GAME_PANEL_CE_SCORE_3                   97
188 #define GAME_PANEL_CE_SCORE_4                   98
189 #define GAME_PANEL_CE_SCORE_5                   99
190 #define GAME_PANEL_CE_SCORE_6                   100
191 #define GAME_PANEL_CE_SCORE_7                   101
192 #define GAME_PANEL_CE_SCORE_8                   102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
201 #define GAME_PANEL_PLAYER_NAME                  111
202 #define GAME_PANEL_LEVEL_NAME                   112
203 #define GAME_PANEL_LEVEL_AUTHOR                 113
204
205 #define NUM_GAME_PANEL_CONTROLS                 114
206
207 struct GamePanelOrderInfo
208 {
209   int nr;
210   int sort_priority;
211 };
212
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
214
215 struct GamePanelControlInfo
216 {
217   int nr;
218
219   struct TextPosInfo *pos;
220   int type;
221
222   int value, last_value;
223   int frame, last_frame;
224   int gfx_frame;
225   int gfx_random;
226 };
227
228 static struct GamePanelControlInfo game_panel_controls[] =
229 {
230   {
231     GAME_PANEL_LEVEL_NUMBER,
232     &game.panel.level_number,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_PANEL_GEMS,
237     &game.panel.gems,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_INVENTORY_COUNT,
242     &game.panel.inventory_count,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_FIRST_1,
247     &game.panel.inventory_first[0],
248     TYPE_ELEMENT,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_2,
252     &game.panel.inventory_first[1],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_3,
257     &game.panel.inventory_first[2],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_4,
262     &game.panel.inventory_first[3],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_5,
267     &game.panel.inventory_first[4],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_6,
272     &game.panel.inventory_first[5],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_7,
277     &game.panel.inventory_first[6],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_8,
282     &game.panel.inventory_first[7],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_LAST_1,
287     &game.panel.inventory_last[0],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_2,
292     &game.panel.inventory_last[1],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_3,
297     &game.panel.inventory_last[2],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_4,
302     &game.panel.inventory_last[3],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_5,
307     &game.panel.inventory_last[4],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_6,
312     &game.panel.inventory_last[5],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_7,
317     &game.panel.inventory_last[6],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_8,
322     &game.panel.inventory_last[7],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_KEY_1,
327     &game.panel.key[0],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_2,
332     &game.panel.key[1],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_3,
337     &game.panel.key[2],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_4,
342     &game.panel.key[3],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_5,
347     &game.panel.key[4],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_6,
352     &game.panel.key[5],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_7,
357     &game.panel.key[6],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_8,
362     &game.panel.key[7],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_WHITE,
367     &game.panel.key_white,
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE_COUNT,
372     &game.panel.key_white_count,
373     TYPE_INTEGER,
374   },
375   {
376     GAME_PANEL_SCORE,
377     &game.panel.score,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_HIGHSCORE,
382     &game.panel.highscore,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_TIME,
387     &game.panel.time,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME_HH,
392     &game.panel.time_hh,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_MM,
397     &game.panel.time_mm,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_SS,
402     &game.panel.time_ss,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_FRAME,
407     &game.panel.frame,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_SHIELD_NORMAL,
412     &game.panel.shield_normal,
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_PANEL_SHIELD_NORMAL_TIME,
417     &game.panel.shield_normal_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_PANEL_SHIELD_DEADLY,
422     &game.panel.shield_deadly,
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_PANEL_SHIELD_DEADLY_TIME,
427     &game.panel.shield_deadly_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_PANEL_EXIT,
432     &game.panel.exit,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_PANEL_EMC_MAGIC_BALL,
437     &game.panel.emc_magic_ball,
438     TYPE_ELEMENT,
439   },
440   {
441     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442     &game.panel.emc_magic_ball_switch,
443     TYPE_ELEMENT,
444   },
445   {
446     GAME_PANEL_LIGHT_SWITCH,
447     &game.panel.light_switch,
448     TYPE_ELEMENT,
449   },
450   {
451     GAME_PANEL_LIGHT_SWITCH_TIME,
452     &game.panel.light_switch_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_PANEL_TIMEGATE_SWITCH,
457     &game.panel.timegate_switch,
458     TYPE_ELEMENT,
459   },
460   {
461     GAME_PANEL_TIMEGATE_SWITCH_TIME,
462     &game.panel.timegate_switch_time,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_PANEL_SWITCHGATE_SWITCH,
467     &game.panel.switchgate_switch,
468     TYPE_ELEMENT,
469   },
470   {
471     GAME_PANEL_EMC_LENSES,
472     &game.panel.emc_lenses,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_PANEL_EMC_LENSES_TIME,
477     &game.panel.emc_lenses_time,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_PANEL_EMC_MAGNIFIER,
482     &game.panel.emc_magnifier,
483     TYPE_ELEMENT,
484   },
485   {
486     GAME_PANEL_EMC_MAGNIFIER_TIME,
487     &game.panel.emc_magnifier_time,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_PANEL_BALLOON_SWITCH,
492     &game.panel.balloon_switch,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_PANEL_DYNABOMB_NUMBER,
497     &game.panel.dynabomb_number,
498     TYPE_INTEGER,
499   },
500   {
501     GAME_PANEL_DYNABOMB_SIZE,
502     &game.panel.dynabomb_size,
503     TYPE_INTEGER,
504   },
505   {
506     GAME_PANEL_DYNABOMB_POWER,
507     &game.panel.dynabomb_power,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_PANEL_PENGUINS,
512     &game.panel.penguins,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_PANEL_SOKOBAN_OBJECTS,
517     &game.panel.sokoban_objects,
518     TYPE_INTEGER,
519   },
520   {
521     GAME_PANEL_SOKOBAN_FIELDS,
522     &game.panel.sokoban_fields,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_PANEL_ROBOT_WHEEL,
527     &game.panel.robot_wheel,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_PANEL_CONVEYOR_BELT_1,
532     &game.panel.conveyor_belt[0],
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_PANEL_CONVEYOR_BELT_2,
537     &game.panel.conveyor_belt[1],
538     TYPE_ELEMENT,
539   },
540   {
541     GAME_PANEL_CONVEYOR_BELT_3,
542     &game.panel.conveyor_belt[2],
543     TYPE_ELEMENT,
544   },
545   {
546     GAME_PANEL_CONVEYOR_BELT_4,
547     &game.panel.conveyor_belt[3],
548     TYPE_ELEMENT,
549   },
550   {
551     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552     &game.panel.conveyor_belt_switch[0],
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557     &game.panel.conveyor_belt_switch[1],
558     TYPE_ELEMENT,
559   },
560   {
561     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562     &game.panel.conveyor_belt_switch[2],
563     TYPE_ELEMENT,
564   },
565   {
566     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567     &game.panel.conveyor_belt_switch[3],
568     TYPE_ELEMENT,
569   },
570   {
571     GAME_PANEL_MAGIC_WALL,
572     &game.panel.magic_wall,
573     TYPE_ELEMENT,
574   },
575   {
576     GAME_PANEL_MAGIC_WALL_TIME,
577     &game.panel.magic_wall_time,
578     TYPE_INTEGER,
579   },
580   {
581     GAME_PANEL_GRAVITY_STATE,
582     &game.panel.gravity_state,
583     TYPE_STRING,
584   },
585   {
586     GAME_PANEL_GRAPHIC_1,
587     &game.panel.graphic[0],
588     TYPE_ELEMENT,
589   },
590   {
591     GAME_PANEL_GRAPHIC_2,
592     &game.panel.graphic[1],
593     TYPE_ELEMENT,
594   },
595   {
596     GAME_PANEL_GRAPHIC_3,
597     &game.panel.graphic[2],
598     TYPE_ELEMENT,
599   },
600   {
601     GAME_PANEL_GRAPHIC_4,
602     &game.panel.graphic[3],
603     TYPE_ELEMENT,
604   },
605   {
606     GAME_PANEL_GRAPHIC_5,
607     &game.panel.graphic[4],
608     TYPE_ELEMENT,
609   },
610   {
611     GAME_PANEL_GRAPHIC_6,
612     &game.panel.graphic[5],
613     TYPE_ELEMENT,
614   },
615   {
616     GAME_PANEL_GRAPHIC_7,
617     &game.panel.graphic[6],
618     TYPE_ELEMENT,
619   },
620   {
621     GAME_PANEL_GRAPHIC_8,
622     &game.panel.graphic[7],
623     TYPE_ELEMENT,
624   },
625   {
626     GAME_PANEL_ELEMENT_1,
627     &game.panel.element[0],
628     TYPE_ELEMENT,
629   },
630   {
631     GAME_PANEL_ELEMENT_2,
632     &game.panel.element[1],
633     TYPE_ELEMENT,
634   },
635   {
636     GAME_PANEL_ELEMENT_3,
637     &game.panel.element[2],
638     TYPE_ELEMENT,
639   },
640   {
641     GAME_PANEL_ELEMENT_4,
642     &game.panel.element[3],
643     TYPE_ELEMENT,
644   },
645   {
646     GAME_PANEL_ELEMENT_5,
647     &game.panel.element[4],
648     TYPE_ELEMENT,
649   },
650   {
651     GAME_PANEL_ELEMENT_6,
652     &game.panel.element[5],
653     TYPE_ELEMENT,
654   },
655   {
656     GAME_PANEL_ELEMENT_7,
657     &game.panel.element[6],
658     TYPE_ELEMENT,
659   },
660   {
661     GAME_PANEL_ELEMENT_8,
662     &game.panel.element[7],
663     TYPE_ELEMENT,
664   },
665   {
666     GAME_PANEL_ELEMENT_COUNT_1,
667     &game.panel.element_count[0],
668     TYPE_INTEGER,
669   },
670   {
671     GAME_PANEL_ELEMENT_COUNT_2,
672     &game.panel.element_count[1],
673     TYPE_INTEGER,
674   },
675   {
676     GAME_PANEL_ELEMENT_COUNT_3,
677     &game.panel.element_count[2],
678     TYPE_INTEGER,
679   },
680   {
681     GAME_PANEL_ELEMENT_COUNT_4,
682     &game.panel.element_count[3],
683     TYPE_INTEGER,
684   },
685   {
686     GAME_PANEL_ELEMENT_COUNT_5,
687     &game.panel.element_count[4],
688     TYPE_INTEGER,
689   },
690   {
691     GAME_PANEL_ELEMENT_COUNT_6,
692     &game.panel.element_count[5],
693     TYPE_INTEGER,
694   },
695   {
696     GAME_PANEL_ELEMENT_COUNT_7,
697     &game.panel.element_count[6],
698     TYPE_INTEGER,
699   },
700   {
701     GAME_PANEL_ELEMENT_COUNT_8,
702     &game.panel.element_count[7],
703     TYPE_INTEGER,
704   },
705   {
706     GAME_PANEL_CE_SCORE_1,
707     &game.panel.ce_score[0],
708     TYPE_INTEGER,
709   },
710   {
711     GAME_PANEL_CE_SCORE_2,
712     &game.panel.ce_score[1],
713     TYPE_INTEGER,
714   },
715   {
716     GAME_PANEL_CE_SCORE_3,
717     &game.panel.ce_score[2],
718     TYPE_INTEGER,
719   },
720   {
721     GAME_PANEL_CE_SCORE_4,
722     &game.panel.ce_score[3],
723     TYPE_INTEGER,
724   },
725   {
726     GAME_PANEL_CE_SCORE_5,
727     &game.panel.ce_score[4],
728     TYPE_INTEGER,
729   },
730   {
731     GAME_PANEL_CE_SCORE_6,
732     &game.panel.ce_score[5],
733     TYPE_INTEGER,
734   },
735   {
736     GAME_PANEL_CE_SCORE_7,
737     &game.panel.ce_score[6],
738     TYPE_INTEGER,
739   },
740   {
741     GAME_PANEL_CE_SCORE_8,
742     &game.panel.ce_score[7],
743     TYPE_INTEGER,
744   },
745   {
746     GAME_PANEL_CE_SCORE_1_ELEMENT,
747     &game.panel.ce_score_element[0],
748     TYPE_ELEMENT,
749   },
750   {
751     GAME_PANEL_CE_SCORE_2_ELEMENT,
752     &game.panel.ce_score_element[1],
753     TYPE_ELEMENT,
754   },
755   {
756     GAME_PANEL_CE_SCORE_3_ELEMENT,
757     &game.panel.ce_score_element[2],
758     TYPE_ELEMENT,
759   },
760   {
761     GAME_PANEL_CE_SCORE_4_ELEMENT,
762     &game.panel.ce_score_element[3],
763     TYPE_ELEMENT,
764   },
765   {
766     GAME_PANEL_CE_SCORE_5_ELEMENT,
767     &game.panel.ce_score_element[4],
768     TYPE_ELEMENT,
769   },
770   {
771     GAME_PANEL_CE_SCORE_6_ELEMENT,
772     &game.panel.ce_score_element[5],
773     TYPE_ELEMENT,
774   },
775   {
776     GAME_PANEL_CE_SCORE_7_ELEMENT,
777     &game.panel.ce_score_element[6],
778     TYPE_ELEMENT,
779   },
780   {
781     GAME_PANEL_CE_SCORE_8_ELEMENT,
782     &game.panel.ce_score_element[7],
783     TYPE_ELEMENT,
784   },
785   {
786     GAME_PANEL_PLAYER_NAME,
787     &game.panel.player_name,
788     TYPE_STRING,
789   },
790   {
791     GAME_PANEL_LEVEL_NAME,
792     &game.panel.level_name,
793     TYPE_STRING,
794   },
795   {
796     GAME_PANEL_LEVEL_AUTHOR,
797     &game.panel.level_author,
798     TYPE_STRING,
799   },
800
801   {
802     -1,
803     NULL,
804     -1,
805   }
806 };
807
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING      3
810 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION   2
812 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
813
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF  -1
816 #define INITIAL_MOVE_DELAY_ON   0
817
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED    32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED   4
822 #define MOVE_DELAY_MAX_SPEED    1
823
824 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
826
827 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
829
830 /* values for scroll positions */
831 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
832                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
833                                  (x) - MIDPOSX)
834 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
835                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
836                                  (y) - MIDPOSY)
837
838 /* values for other actions */
839 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
840 #define MOVE_STEPSIZE_MIN       (1)
841 #define MOVE_STEPSIZE_MAX       (TILEX)
842
843 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
844 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
845
846 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
847
848 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
849                                  RND(element_info[e].push_delay_random))
850 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
851                                  RND(element_info[e].drop_delay_random))
852 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
853                                  RND(element_info[e].move_delay_random))
854 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
855                                     (element_info[e].move_delay_random))
856 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
857                                  RND(element_info[e].ce_value_random_initial))
858 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
859 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
860                                  RND((c)->delay_random * (c)->delay_frames))
861 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
862                                  RND((c)->delay_random))
863
864
865 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
866          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
867
868 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
869         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
870          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
871          (be) + (e) - EL_SELF)
872
873 #define GET_PLAYER_FROM_BITS(p)                                         \
874         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
875
876 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
877         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
878          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
879          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
880          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
881          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
882          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
883          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
884          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
885          (e))
886
887 #define CAN_GROW_INTO(e)                                                \
888         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
889
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
891                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
892                                         (condition)))
893
894 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
895                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
896                                         (CAN_MOVE_INTO_ACID(e) &&       \
897                                          Feld[x][y] == EL_ACID) ||      \
898                                         (condition)))
899
900 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
901                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
902                                         (CAN_MOVE_INTO_ACID(e) &&       \
903                                          Feld[x][y] == EL_ACID) ||      \
904                                         (condition)))
905
906 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
907                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
908                                         (condition) ||                  \
909                                         (CAN_MOVE_INTO_ACID(e) &&       \
910                                          Feld[x][y] == EL_ACID) ||      \
911                                         (DONT_COLLIDE_WITH(e) &&        \
912                                          IS_PLAYER(x, y) &&             \
913                                          !PLAYER_ENEMY_PROTECTED(x, y))))
914
915 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
916         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
917
918 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
919         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
920
921 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
922         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
923
924 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
925         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
926                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
927
928 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
929         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
930
931 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
932         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
933
934 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
935         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
936
937 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
938         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
939
940 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
942
943 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
945                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
946                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
947                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
948                                                  IS_FOOD_PENGUIN(Feld[x][y])))
949 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
950         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
951
952 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
953         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
954
955 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
956         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957
958 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
959         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
960                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
961
962 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
963
964 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
965                 (!IS_PLAYER(x, y) &&                                    \
966                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
967
968 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
969         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
970
971 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
972 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
973
974 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
975 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
976 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
977 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
978
979 /* game button identifiers */
980 #define GAME_CTRL_ID_STOP               0
981 #define GAME_CTRL_ID_PAUSE              1
982 #define GAME_CTRL_ID_PLAY               2
983 #define GAME_CTRL_ID_UNDO               3
984 #define GAME_CTRL_ID_REDO               4
985 #define GAME_CTRL_ID_SAVE               5
986 #define GAME_CTRL_ID_PAUSE2             6
987 #define GAME_CTRL_ID_LOAD               7
988 #define SOUND_CTRL_ID_MUSIC             8
989 #define SOUND_CTRL_ID_LOOPS             9
990 #define SOUND_CTRL_ID_SIMPLE            10
991
992 #define NUM_GAME_BUTTONS                11
993
994
995 /* forward declaration for internal use */
996
997 static void CreateField(int, int, int);
998
999 static void ResetGfxAnimation(int, int);
1000
1001 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1002 static void AdvanceFrameAndPlayerCounters(int);
1003
1004 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1005 static boolean MovePlayer(struct PlayerInfo *, int, int);
1006 static void ScrollPlayer(struct PlayerInfo *, int);
1007 static void ScrollScreen(struct PlayerInfo *, int);
1008
1009 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1010 static boolean DigFieldByCE(int, int, int);
1011 static boolean SnapField(struct PlayerInfo *, int, int);
1012 static boolean DropElement(struct PlayerInfo *);
1013
1014 static void InitBeltMovement(void);
1015 static void CloseAllOpenTimegates(void);
1016 static void CheckGravityMovement(struct PlayerInfo *);
1017 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1018 static void KillPlayerUnlessEnemyProtected(int, int);
1019 static void KillPlayerUnlessExplosionProtected(int, int);
1020
1021 static void TestIfPlayerTouchesCustomElement(int, int);
1022 static void TestIfElementTouchesCustomElement(int, int);
1023 static void TestIfElementHitsCustomElement(int, int, int);
1024
1025 static void HandleElementChange(int, int, int);
1026 static void ExecuteCustomElementAction(int, int, int, int);
1027 static boolean ChangeElement(int, int, int, int);
1028
1029 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1030 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1031         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1032 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1033         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1034 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1035         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1036 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1037         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1038
1039 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1040 #define CheckElementChange(x, y, e, te, ev)                             \
1041         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1042 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1043         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1044 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1045         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1046
1047 static void PlayLevelSound(int, int, int);
1048 static void PlayLevelSoundNearest(int, int, int);
1049 static void PlayLevelSoundAction(int, int, int);
1050 static void PlayLevelSoundElementAction(int, int, int, int);
1051 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1052 static void PlayLevelSoundActionIfLoop(int, int, int);
1053 static void StopLevelSoundActionIfLoop(int, int, int);
1054 static void PlayLevelMusic();
1055 static void FadeLevelSoundsAndMusic();
1056
1057 static void HandleGameButtons(struct GadgetInfo *);
1058
1059 int AmoebeNachbarNr(int, int);
1060 void AmoebeUmwandeln(int, int);
1061 void ContinueMoving(int, int);
1062 void Bang(int, int);
1063 void InitMovDir(int, int);
1064 void InitAmoebaNr(int, int);
1065 int NewHiScore(void);
1066
1067 void TestIfGoodThingHitsBadThing(int, int, int);
1068 void TestIfBadThingHitsGoodThing(int, int, int);
1069 void TestIfPlayerTouchesBadThing(int, int);
1070 void TestIfPlayerRunsIntoBadThing(int, int, int);
1071 void TestIfBadThingTouchesPlayer(int, int);
1072 void TestIfBadThingRunsIntoPlayer(int, int, int);
1073 void TestIfFriendTouchesBadThing(int, int);
1074 void TestIfBadThingTouchesFriend(int, int);
1075 void TestIfBadThingTouchesOtherBadThing(int, int);
1076 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1077
1078 void KillPlayer(struct PlayerInfo *);
1079 void BuryPlayer(struct PlayerInfo *);
1080 void RemovePlayer(struct PlayerInfo *);
1081
1082 static int getInvisibleActiveFromInvisibleElement(int);
1083 static int getInvisibleFromInvisibleActiveElement(int);
1084
1085 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1086
1087 /* for detection of endless loops, caused by custom element programming */
1088 /* (using maximal playfield width x 10 is just a rough approximation) */
1089 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1090
1091 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1092 {                                                                       \
1093   if (recursion_loop_detected)                                          \
1094     return (rc);                                                        \
1095                                                                         \
1096   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1097   {                                                                     \
1098     recursion_loop_detected = TRUE;                                     \
1099     recursion_loop_element = (e);                                       \
1100   }                                                                     \
1101                                                                         \
1102   recursion_loop_depth++;                                               \
1103 }
1104
1105 #define RECURSION_LOOP_DETECTION_END()                                  \
1106 {                                                                       \
1107   recursion_loop_depth--;                                               \
1108 }
1109
1110 static int recursion_loop_depth;
1111 static boolean recursion_loop_detected;
1112 static boolean recursion_loop_element;
1113
1114 static int map_player_action[MAX_PLAYERS];
1115
1116
1117 /* ------------------------------------------------------------------------- */
1118 /* definition of elements that automatically change to other elements after  */
1119 /* a specified time, eventually calling a function when changing             */
1120 /* ------------------------------------------------------------------------- */
1121
1122 /* forward declaration for changer functions */
1123 static void InitBuggyBase(int, int);
1124 static void WarnBuggyBase(int, int);
1125
1126 static void InitTrap(int, int);
1127 static void ActivateTrap(int, int);
1128 static void ChangeActiveTrap(int, int);
1129
1130 static void InitRobotWheel(int, int);
1131 static void RunRobotWheel(int, int);
1132 static void StopRobotWheel(int, int);
1133
1134 static void InitTimegateWheel(int, int);
1135 static void RunTimegateWheel(int, int);
1136
1137 static void InitMagicBallDelay(int, int);
1138 static void ActivateMagicBall(int, int);
1139
1140 struct ChangingElementInfo
1141 {
1142   int element;
1143   int target_element;
1144   int change_delay;
1145   void (*pre_change_function)(int x, int y);
1146   void (*change_function)(int x, int y);
1147   void (*post_change_function)(int x, int y);
1148 };
1149
1150 static struct ChangingElementInfo change_delay_list[] =
1151 {
1152   {
1153     EL_NUT_BREAKING,
1154     EL_EMERALD,
1155     6,
1156     NULL,
1157     NULL,
1158     NULL
1159   },
1160   {
1161     EL_PEARL_BREAKING,
1162     EL_EMPTY,
1163     8,
1164     NULL,
1165     NULL,
1166     NULL
1167   },
1168   {
1169     EL_EXIT_OPENING,
1170     EL_EXIT_OPEN,
1171     29,
1172     NULL,
1173     NULL,
1174     NULL
1175   },
1176   {
1177     EL_EXIT_CLOSING,
1178     EL_EXIT_CLOSED,
1179     29,
1180     NULL,
1181     NULL,
1182     NULL
1183   },
1184   {
1185     EL_STEEL_EXIT_OPENING,
1186     EL_STEEL_EXIT_OPEN,
1187     29,
1188     NULL,
1189     NULL,
1190     NULL
1191   },
1192   {
1193     EL_STEEL_EXIT_CLOSING,
1194     EL_STEEL_EXIT_CLOSED,
1195     29,
1196     NULL,
1197     NULL,
1198     NULL
1199   },
1200   {
1201     EL_EM_EXIT_OPENING,
1202     EL_EM_EXIT_OPEN,
1203     29,
1204     NULL,
1205     NULL,
1206     NULL
1207   },
1208   {
1209     EL_EM_EXIT_CLOSING,
1210     EL_EMPTY,
1211     29,
1212     NULL,
1213     NULL,
1214     NULL
1215   },
1216   {
1217     EL_EM_STEEL_EXIT_OPENING,
1218     EL_EM_STEEL_EXIT_OPEN,
1219     29,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_EM_STEEL_EXIT_CLOSING,
1226     EL_STEELWALL,
1227     29,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_SP_EXIT_OPENING,
1234     EL_SP_EXIT_OPEN,
1235     29,
1236     NULL,
1237     NULL,
1238     NULL
1239   },
1240   {
1241     EL_SP_EXIT_CLOSING,
1242     EL_SP_EXIT_CLOSED,
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_SWITCHGATE_OPENING,
1250     EL_SWITCHGATE_OPEN,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_SWITCHGATE_CLOSING,
1258     EL_SWITCHGATE_CLOSED,
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_TIMEGATE_OPENING,
1266     EL_TIMEGATE_OPEN,
1267     29,
1268     NULL,
1269     NULL,
1270     NULL
1271   },
1272   {
1273     EL_TIMEGATE_CLOSING,
1274     EL_TIMEGATE_CLOSED,
1275     29,
1276     NULL,
1277     NULL,
1278     NULL
1279   },
1280
1281   {
1282     EL_ACID_SPLASH_LEFT,
1283     EL_EMPTY,
1284     8,
1285     NULL,
1286     NULL,
1287     NULL
1288   },
1289   {
1290     EL_ACID_SPLASH_RIGHT,
1291     EL_EMPTY,
1292     8,
1293     NULL,
1294     NULL,
1295     NULL
1296   },
1297   {
1298     EL_SP_BUGGY_BASE,
1299     EL_SP_BUGGY_BASE_ACTIVATING,
1300     0,
1301     InitBuggyBase,
1302     NULL,
1303     NULL
1304   },
1305   {
1306     EL_SP_BUGGY_BASE_ACTIVATING,
1307     EL_SP_BUGGY_BASE_ACTIVE,
1308     0,
1309     InitBuggyBase,
1310     NULL,
1311     NULL
1312   },
1313   {
1314     EL_SP_BUGGY_BASE_ACTIVE,
1315     EL_SP_BUGGY_BASE,
1316     0,
1317     InitBuggyBase,
1318     WarnBuggyBase,
1319     NULL
1320   },
1321   {
1322     EL_TRAP,
1323     EL_TRAP_ACTIVE,
1324     0,
1325     InitTrap,
1326     NULL,
1327     ActivateTrap
1328   },
1329   {
1330     EL_TRAP_ACTIVE,
1331     EL_TRAP,
1332     31,
1333     NULL,
1334     ChangeActiveTrap,
1335     NULL
1336   },
1337   {
1338     EL_ROBOT_WHEEL_ACTIVE,
1339     EL_ROBOT_WHEEL,
1340     0,
1341     InitRobotWheel,
1342     RunRobotWheel,
1343     StopRobotWheel
1344   },
1345   {
1346     EL_TIMEGATE_SWITCH_ACTIVE,
1347     EL_TIMEGATE_SWITCH,
1348     0,
1349     InitTimegateWheel,
1350     RunTimegateWheel,
1351     NULL
1352   },
1353   {
1354     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1355     EL_DC_TIMEGATE_SWITCH,
1356     0,
1357     InitTimegateWheel,
1358     RunTimegateWheel,
1359     NULL
1360   },
1361   {
1362     EL_EMC_MAGIC_BALL_ACTIVE,
1363     EL_EMC_MAGIC_BALL_ACTIVE,
1364     0,
1365     InitMagicBallDelay,
1366     NULL,
1367     ActivateMagicBall
1368   },
1369   {
1370     EL_EMC_SPRING_BUMPER_ACTIVE,
1371     EL_EMC_SPRING_BUMPER,
1372     8,
1373     NULL,
1374     NULL,
1375     NULL
1376   },
1377   {
1378     EL_DIAGONAL_SHRINKING,
1379     EL_UNDEFINED,
1380     0,
1381     NULL,
1382     NULL,
1383     NULL
1384   },
1385   {
1386     EL_DIAGONAL_GROWING,
1387     EL_UNDEFINED,
1388     0,
1389     NULL,
1390     NULL,
1391     NULL,
1392   },
1393
1394   {
1395     EL_UNDEFINED,
1396     EL_UNDEFINED,
1397     -1,
1398     NULL,
1399     NULL,
1400     NULL
1401   }
1402 };
1403
1404 struct
1405 {
1406   int element;
1407   int push_delay_fixed, push_delay_random;
1408 }
1409 push_delay_list[] =
1410 {
1411   { EL_SPRING,                  0, 0 },
1412   { EL_BALLOON,                 0, 0 },
1413
1414   { EL_SOKOBAN_OBJECT,          2, 0 },
1415   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1416   { EL_SATELLITE,               2, 0 },
1417   { EL_SP_DISK_YELLOW,          2, 0 },
1418
1419   { EL_UNDEFINED,               0, 0 },
1420 };
1421
1422 struct
1423 {
1424   int element;
1425   int move_stepsize;
1426 }
1427 move_stepsize_list[] =
1428 {
1429   { EL_AMOEBA_DROP,             2 },
1430   { EL_AMOEBA_DROPPING,         2 },
1431   { EL_QUICKSAND_FILLING,       1 },
1432   { EL_QUICKSAND_EMPTYING,      1 },
1433   { EL_QUICKSAND_FAST_FILLING,  2 },
1434   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1435   { EL_MAGIC_WALL_FILLING,      2 },
1436   { EL_MAGIC_WALL_EMPTYING,     2 },
1437   { EL_BD_MAGIC_WALL_FILLING,   2 },
1438   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1439   { EL_DC_MAGIC_WALL_FILLING,   2 },
1440   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1441
1442   { EL_UNDEFINED,               0 },
1443 };
1444
1445 struct
1446 {
1447   int element;
1448   int count;
1449 }
1450 collect_count_list[] =
1451 {
1452   { EL_EMERALD,                 1 },
1453   { EL_BD_DIAMOND,              1 },
1454   { EL_EMERALD_YELLOW,          1 },
1455   { EL_EMERALD_RED,             1 },
1456   { EL_EMERALD_PURPLE,          1 },
1457   { EL_DIAMOND,                 3 },
1458   { EL_SP_INFOTRON,             1 },
1459   { EL_PEARL,                   5 },
1460   { EL_CRYSTAL,                 8 },
1461
1462   { EL_UNDEFINED,               0 },
1463 };
1464
1465 struct
1466 {
1467   int element;
1468   int direction;
1469 }
1470 access_direction_list[] =
1471 {
1472   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1473   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1474   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1475   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1476   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1477   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1478   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1479   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1480   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1481   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1482   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1483
1484   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1485   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1486   { EL_SP_PORT_UP,                                                   MV_DOWN },
1487   { EL_SP_PORT_DOWN,                                         MV_UP           },
1488   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1489   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1490   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1491   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1492   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1493   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1494   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1495   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1496   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1497   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1498   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1499   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1500   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1501   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1502   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1503
1504   { EL_UNDEFINED,                       MV_NONE                              }
1505 };
1506
1507 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1508
1509 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1510 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1511 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1512                                  IS_JUST_CHANGING(x, y))
1513
1514 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1515
1516 /* static variables for playfield scan mode (scanning forward or backward) */
1517 static int playfield_scan_start_x = 0;
1518 static int playfield_scan_start_y = 0;
1519 static int playfield_scan_delta_x = 1;
1520 static int playfield_scan_delta_y = 1;
1521
1522 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1523                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1524                                      (y) += playfield_scan_delta_y)     \
1525                                 for ((x) = playfield_scan_start_x;      \
1526                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1527                                      (x) += playfield_scan_delta_x)
1528
1529 #ifdef DEBUG
1530 void DEBUG_SetMaximumDynamite()
1531 {
1532   int i;
1533
1534   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1535     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1536       local_player->inventory_element[local_player->inventory_size++] =
1537         EL_DYNAMITE;
1538 }
1539 #endif
1540
1541 static void InitPlayfieldScanModeVars()
1542 {
1543   if (game.use_reverse_scan_direction)
1544   {
1545     playfield_scan_start_x = lev_fieldx - 1;
1546     playfield_scan_start_y = lev_fieldy - 1;
1547
1548     playfield_scan_delta_x = -1;
1549     playfield_scan_delta_y = -1;
1550   }
1551   else
1552   {
1553     playfield_scan_start_x = 0;
1554     playfield_scan_start_y = 0;
1555
1556     playfield_scan_delta_x = 1;
1557     playfield_scan_delta_y = 1;
1558   }
1559 }
1560
1561 static void InitPlayfieldScanMode(int mode)
1562 {
1563   game.use_reverse_scan_direction =
1564     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1565
1566   InitPlayfieldScanModeVars();
1567 }
1568
1569 static int get_move_delay_from_stepsize(int move_stepsize)
1570 {
1571   move_stepsize =
1572     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1573
1574   /* make sure that stepsize value is always a power of 2 */
1575   move_stepsize = (1 << log_2(move_stepsize));
1576
1577   return TILEX / move_stepsize;
1578 }
1579
1580 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1581                                boolean init_game)
1582 {
1583   int player_nr = player->index_nr;
1584   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1585   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1586
1587   /* do no immediately change move delay -- the player might just be moving */
1588   player->move_delay_value_next = move_delay;
1589
1590   /* information if player can move must be set separately */
1591   player->cannot_move = cannot_move;
1592
1593   if (init_game)
1594   {
1595     player->move_delay       = game.initial_move_delay[player_nr];
1596     player->move_delay_value = game.initial_move_delay_value[player_nr];
1597
1598     player->move_delay_value_next = -1;
1599
1600     player->move_delay_reset_counter = 0;
1601   }
1602 }
1603
1604 void GetPlayerConfig()
1605 {
1606   GameFrameDelay = setup.game_frame_delay;
1607
1608   if (!audio.sound_available)
1609     setup.sound_simple = FALSE;
1610
1611   if (!audio.loops_available)
1612     setup.sound_loops = FALSE;
1613
1614   if (!audio.music_available)
1615     setup.sound_music = FALSE;
1616
1617   if (!video.fullscreen_available)
1618     setup.fullscreen = FALSE;
1619
1620   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1621
1622   SetAudioMode(setup.sound);
1623 }
1624
1625 int GetElementFromGroupElement(int element)
1626 {
1627   if (IS_GROUP_ELEMENT(element))
1628   {
1629     struct ElementGroupInfo *group = element_info[element].group;
1630     int last_anim_random_frame = gfx.anim_random_frame;
1631     int element_pos;
1632
1633     if (group->choice_mode == ANIM_RANDOM)
1634       gfx.anim_random_frame = RND(group->num_elements_resolved);
1635
1636     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1637                                     group->choice_mode, 0,
1638                                     group->choice_pos);
1639
1640     if (group->choice_mode == ANIM_RANDOM)
1641       gfx.anim_random_frame = last_anim_random_frame;
1642
1643     group->choice_pos++;
1644
1645     element = group->element_resolved[element_pos];
1646   }
1647
1648   return element;
1649 }
1650
1651 static void InitPlayerField(int x, int y, int element, boolean init_game)
1652 {
1653   if (element == EL_SP_MURPHY)
1654   {
1655     if (init_game)
1656     {
1657       if (stored_player[0].present)
1658       {
1659         Feld[x][y] = EL_SP_MURPHY_CLONE;
1660
1661         return;
1662       }
1663       else
1664       {
1665         stored_player[0].initial_element = element;
1666         stored_player[0].use_murphy = TRUE;
1667
1668         if (!level.use_artwork_element[0])
1669           stored_player[0].artwork_element = EL_SP_MURPHY;
1670       }
1671
1672       Feld[x][y] = EL_PLAYER_1;
1673     }
1674   }
1675
1676   if (init_game)
1677   {
1678     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1679     int jx = player->jx, jy = player->jy;
1680
1681     player->present = TRUE;
1682
1683     player->block_last_field = (element == EL_SP_MURPHY ?
1684                                 level.sp_block_last_field :
1685                                 level.block_last_field);
1686
1687     /* ---------- initialize player's last field block delay --------------- */
1688
1689     /* always start with reliable default value (no adjustment needed) */
1690     player->block_delay_adjustment = 0;
1691
1692     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1693     if (player->block_last_field && element == EL_SP_MURPHY)
1694       player->block_delay_adjustment = 1;
1695
1696     /* special case 2: in game engines before 3.1.1, blocking was different */
1697     if (game.use_block_last_field_bug)
1698       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1699
1700     if (!options.network || player->connected)
1701     {
1702       player->active = TRUE;
1703
1704       /* remove potentially duplicate players */
1705       if (StorePlayer[jx][jy] == Feld[x][y])
1706         StorePlayer[jx][jy] = 0;
1707
1708       StorePlayer[x][y] = Feld[x][y];
1709
1710 #if DEBUG_INIT_PLAYER
1711       if (options.debug)
1712       {
1713         printf("- player element %d activated", player->element_nr);
1714         printf(" (local player is %d and currently %s)\n",
1715                local_player->element_nr,
1716                local_player->active ? "active" : "not active");
1717       }
1718     }
1719 #endif
1720
1721     Feld[x][y] = EL_EMPTY;
1722
1723     player->jx = player->last_jx = x;
1724     player->jy = player->last_jy = y;
1725   }
1726
1727   if (!init_game)
1728   {
1729     int player_nr = GET_PLAYER_NR(element);
1730     struct PlayerInfo *player = &stored_player[player_nr];
1731
1732     if (player->active && player->killed)
1733       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1734   }
1735 }
1736
1737 static void InitField(int x, int y, boolean init_game)
1738 {
1739   int element = Feld[x][y];
1740
1741   switch (element)
1742   {
1743     case EL_SP_MURPHY:
1744     case EL_PLAYER_1:
1745     case EL_PLAYER_2:
1746     case EL_PLAYER_3:
1747     case EL_PLAYER_4:
1748       InitPlayerField(x, y, element, init_game);
1749       break;
1750
1751     case EL_SOKOBAN_FIELD_PLAYER:
1752       element = Feld[x][y] = EL_PLAYER_1;
1753       InitField(x, y, init_game);
1754
1755       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1756       InitField(x, y, init_game);
1757       break;
1758
1759     case EL_SOKOBAN_FIELD_EMPTY:
1760       local_player->sokobanfields_still_needed++;
1761       break;
1762
1763     case EL_STONEBLOCK:
1764       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1765         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1766       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1767         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1768       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1769         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1770       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1771         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1772       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1773         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1774       break;
1775
1776     case EL_BUG:
1777     case EL_BUG_RIGHT:
1778     case EL_BUG_UP:
1779     case EL_BUG_LEFT:
1780     case EL_BUG_DOWN:
1781     case EL_SPACESHIP:
1782     case EL_SPACESHIP_RIGHT:
1783     case EL_SPACESHIP_UP:
1784     case EL_SPACESHIP_LEFT:
1785     case EL_SPACESHIP_DOWN:
1786     case EL_BD_BUTTERFLY:
1787     case EL_BD_BUTTERFLY_RIGHT:
1788     case EL_BD_BUTTERFLY_UP:
1789     case EL_BD_BUTTERFLY_LEFT:
1790     case EL_BD_BUTTERFLY_DOWN:
1791     case EL_BD_FIREFLY:
1792     case EL_BD_FIREFLY_RIGHT:
1793     case EL_BD_FIREFLY_UP:
1794     case EL_BD_FIREFLY_LEFT:
1795     case EL_BD_FIREFLY_DOWN:
1796     case EL_PACMAN_RIGHT:
1797     case EL_PACMAN_UP:
1798     case EL_PACMAN_LEFT:
1799     case EL_PACMAN_DOWN:
1800     case EL_YAMYAM:
1801     case EL_YAMYAM_LEFT:
1802     case EL_YAMYAM_RIGHT:
1803     case EL_YAMYAM_UP:
1804     case EL_YAMYAM_DOWN:
1805     case EL_DARK_YAMYAM:
1806     case EL_ROBOT:
1807     case EL_PACMAN:
1808     case EL_SP_SNIKSNAK:
1809     case EL_SP_ELECTRON:
1810     case EL_MOLE:
1811     case EL_MOLE_LEFT:
1812     case EL_MOLE_RIGHT:
1813     case EL_MOLE_UP:
1814     case EL_MOLE_DOWN:
1815       InitMovDir(x, y);
1816       break;
1817
1818     case EL_AMOEBA_FULL:
1819     case EL_BD_AMOEBA:
1820       InitAmoebaNr(x, y);
1821       break;
1822
1823     case EL_AMOEBA_DROP:
1824       if (y == lev_fieldy - 1)
1825       {
1826         Feld[x][y] = EL_AMOEBA_GROWING;
1827         Store[x][y] = EL_AMOEBA_WET;
1828       }
1829       break;
1830
1831     case EL_DYNAMITE_ACTIVE:
1832     case EL_SP_DISK_RED_ACTIVE:
1833     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1834     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1835     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1836     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1837       MovDelay[x][y] = 96;
1838       break;
1839
1840     case EL_EM_DYNAMITE_ACTIVE:
1841       MovDelay[x][y] = 32;
1842       break;
1843
1844     case EL_LAMP:
1845       local_player->lights_still_needed++;
1846       break;
1847
1848     case EL_PENGUIN:
1849       local_player->friends_still_needed++;
1850       break;
1851
1852     case EL_PIG:
1853     case EL_DRAGON:
1854       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1855       break;
1856
1857     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1860     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1861     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1862     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1863     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1864     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1865     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1866     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1867     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1868     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1869       if (init_game)
1870       {
1871         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1872         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1873         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1874
1875         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1876         {
1877           game.belt_dir[belt_nr] = belt_dir;
1878           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1879         }
1880         else    /* more than one switch -- set it like the first switch */
1881         {
1882           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1883         }
1884       }
1885       break;
1886
1887     case EL_LIGHT_SWITCH_ACTIVE:
1888       if (init_game)
1889         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1890       break;
1891
1892     case EL_INVISIBLE_STEELWALL:
1893     case EL_INVISIBLE_WALL:
1894     case EL_INVISIBLE_SAND:
1895       if (game.light_time_left > 0 ||
1896           game.lenses_time_left > 0)
1897         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1898       break;
1899
1900     case EL_EMC_MAGIC_BALL:
1901       if (game.ball_state)
1902         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1903       break;
1904
1905     case EL_EMC_MAGIC_BALL_SWITCH:
1906       if (game.ball_state)
1907         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1908       break;
1909
1910     case EL_TRIGGER_PLAYER:
1911     case EL_TRIGGER_ELEMENT:
1912     case EL_TRIGGER_CE_VALUE:
1913     case EL_TRIGGER_CE_SCORE:
1914     case EL_SELF:
1915     case EL_ANY_ELEMENT:
1916     case EL_CURRENT_CE_VALUE:
1917     case EL_CURRENT_CE_SCORE:
1918     case EL_PREV_CE_1:
1919     case EL_PREV_CE_2:
1920     case EL_PREV_CE_3:
1921     case EL_PREV_CE_4:
1922     case EL_PREV_CE_5:
1923     case EL_PREV_CE_6:
1924     case EL_PREV_CE_7:
1925     case EL_PREV_CE_8:
1926     case EL_NEXT_CE_1:
1927     case EL_NEXT_CE_2:
1928     case EL_NEXT_CE_3:
1929     case EL_NEXT_CE_4:
1930     case EL_NEXT_CE_5:
1931     case EL_NEXT_CE_6:
1932     case EL_NEXT_CE_7:
1933     case EL_NEXT_CE_8:
1934       /* reference elements should not be used on the playfield */
1935       Feld[x][y] = EL_EMPTY;
1936       break;
1937
1938     default:
1939       if (IS_CUSTOM_ELEMENT(element))
1940       {
1941         if (CAN_MOVE(element))
1942           InitMovDir(x, y);
1943
1944         if (!element_info[element].use_last_ce_value || init_game)
1945           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1946       }
1947       else if (IS_GROUP_ELEMENT(element))
1948       {
1949         Feld[x][y] = GetElementFromGroupElement(element);
1950
1951         InitField(x, y, init_game);
1952       }
1953
1954       break;
1955   }
1956
1957   if (!init_game)
1958     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1959 }
1960
1961 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1962 {
1963   InitField(x, y, init_game);
1964
1965   /* not needed to call InitMovDir() -- already done by InitField()! */
1966   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1967       CAN_MOVE(Feld[x][y]))
1968     InitMovDir(x, y);
1969 }
1970
1971 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1972 {
1973   int old_element = Feld[x][y];
1974
1975   InitField(x, y, init_game);
1976
1977   /* not needed to call InitMovDir() -- already done by InitField()! */
1978   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1979       CAN_MOVE(old_element) &&
1980       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1981     InitMovDir(x, y);
1982
1983   /* this case is in fact a combination of not less than three bugs:
1984      first, it calls InitMovDir() for elements that can move, although this is
1985      already done by InitField(); then, it checks the element that was at this
1986      field _before_ the call to InitField() (which can change it); lastly, it
1987      was not called for "mole with direction" elements, which were treated as
1988      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1989   */
1990 }
1991
1992 static int get_key_element_from_nr(int key_nr)
1993 {
1994   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1995                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1996                           EL_EM_KEY_1 : EL_KEY_1);
1997
1998   return key_base_element + key_nr;
1999 }
2000
2001 static int get_next_dropped_element(struct PlayerInfo *player)
2002 {
2003   return (player->inventory_size > 0 ?
2004           player->inventory_element[player->inventory_size - 1] :
2005           player->inventory_infinite_element != EL_UNDEFINED ?
2006           player->inventory_infinite_element :
2007           player->dynabombs_left > 0 ?
2008           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2009           EL_UNDEFINED);
2010 }
2011
2012 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2013 {
2014   /* pos >= 0: get element from bottom of the stack;
2015      pos <  0: get element from top of the stack */
2016
2017   if (pos < 0)
2018   {
2019     int min_inventory_size = -pos;
2020     int inventory_pos = player->inventory_size - min_inventory_size;
2021     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2022
2023     return (player->inventory_size >= min_inventory_size ?
2024             player->inventory_element[inventory_pos] :
2025             player->inventory_infinite_element != EL_UNDEFINED ?
2026             player->inventory_infinite_element :
2027             player->dynabombs_left >= min_dynabombs_left ?
2028             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2029             EL_UNDEFINED);
2030   }
2031   else
2032   {
2033     int min_dynabombs_left = pos + 1;
2034     int min_inventory_size = pos + 1 - player->dynabombs_left;
2035     int inventory_pos = pos - player->dynabombs_left;
2036
2037     return (player->inventory_infinite_element != EL_UNDEFINED ?
2038             player->inventory_infinite_element :
2039             player->dynabombs_left >= min_dynabombs_left ?
2040             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2041             player->inventory_size >= min_inventory_size ?
2042             player->inventory_element[inventory_pos] :
2043             EL_UNDEFINED);
2044   }
2045 }
2046
2047 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2048 {
2049   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2050   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2051   int compare_result;
2052
2053   if (gpo1->sort_priority != gpo2->sort_priority)
2054     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2055   else
2056     compare_result = gpo1->nr - gpo2->nr;
2057
2058   return compare_result;
2059 }
2060
2061 int getPlayerInventorySize(int player_nr)
2062 {
2063   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2064     return level.native_em_level->ply[player_nr]->dynamite;
2065   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2066     return level.native_sp_level->game_sp->red_disk_count;
2067   else
2068     return stored_player[player_nr].inventory_size;
2069 }
2070
2071 void InitGameControlValues()
2072 {
2073   int i;
2074
2075   for (i = 0; game_panel_controls[i].nr != -1; i++)
2076   {
2077     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2078     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2079     struct TextPosInfo *pos = gpc->pos;
2080     int nr = gpc->nr;
2081     int type = gpc->type;
2082
2083     if (nr != i)
2084     {
2085       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2086       Error(ERR_EXIT, "this should not happen -- please debug");
2087     }
2088
2089     /* force update of game controls after initialization */
2090     gpc->value = gpc->last_value = -1;
2091     gpc->frame = gpc->last_frame = -1;
2092     gpc->gfx_frame = -1;
2093
2094     /* determine panel value width for later calculation of alignment */
2095     if (type == TYPE_INTEGER || type == TYPE_STRING)
2096     {
2097       pos->width = pos->size * getFontWidth(pos->font);
2098       pos->height = getFontHeight(pos->font);
2099     }
2100     else if (type == TYPE_ELEMENT)
2101     {
2102       pos->width = pos->size;
2103       pos->height = pos->size;
2104     }
2105
2106     /* fill structure for game panel draw order */
2107     gpo->nr = gpc->nr;
2108     gpo->sort_priority = pos->sort_priority;
2109   }
2110
2111   /* sort game panel controls according to sort_priority and control number */
2112   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2113         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2114 }
2115
2116 void UpdatePlayfieldElementCount()
2117 {
2118   boolean use_element_count = FALSE;
2119   int i, j, x, y;
2120
2121   /* first check if it is needed at all to calculate playfield element count */
2122   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2123     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2124       use_element_count = TRUE;
2125
2126   if (!use_element_count)
2127     return;
2128
2129   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2130     element_info[i].element_count = 0;
2131
2132   SCAN_PLAYFIELD(x, y)
2133   {
2134     element_info[Feld[x][y]].element_count++;
2135   }
2136
2137   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2138     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2139       if (IS_IN_GROUP(j, i))
2140         element_info[EL_GROUP_START + i].element_count +=
2141           element_info[j].element_count;
2142 }
2143
2144 void UpdateGameControlValues()
2145 {
2146   int i, k;
2147   int time = (local_player->LevelSolved ?
2148               local_player->LevelSolved_CountingTime :
2149               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2150               level.native_em_level->lev->time :
2151               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2152               level.native_sp_level->game_sp->time_played :
2153               game.no_time_limit ? TimePlayed : TimeLeft);
2154   int score = (local_player->LevelSolved ?
2155                local_player->LevelSolved_CountingScore :
2156                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157                level.native_em_level->lev->score :
2158                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2159                level.native_sp_level->game_sp->score :
2160                local_player->score);
2161   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2162               level.native_em_level->lev->required :
2163               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2164               level.native_sp_level->game_sp->infotrons_still_needed :
2165               local_player->gems_still_needed);
2166   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2167                      level.native_em_level->lev->required > 0 :
2168                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2169                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2170                      local_player->gems_still_needed > 0 ||
2171                      local_player->sokobanfields_still_needed > 0 ||
2172                      local_player->lights_still_needed > 0);
2173
2174   UpdatePlayfieldElementCount();
2175
2176   /* update game panel control values */
2177
2178   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2179   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2180
2181   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2182   for (i = 0; i < MAX_NUM_KEYS; i++)
2183     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2184   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2185   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2186
2187   if (game.centered_player_nr == -1)
2188   {
2189     for (i = 0; i < MAX_PLAYERS; i++)
2190     {
2191       /* only one player in Supaplex game engine */
2192       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2193         break;
2194
2195       for (k = 0; k < MAX_NUM_KEYS; k++)
2196       {
2197         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2198         {
2199           if (level.native_em_level->ply[i]->keys & (1 << k))
2200             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2201               get_key_element_from_nr(k);
2202         }
2203         else if (stored_player[i].key[k])
2204           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2205             get_key_element_from_nr(k);
2206       }
2207
2208       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2209         getPlayerInventorySize(i);
2210
2211       if (stored_player[i].num_white_keys > 0)
2212         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2213           EL_DC_KEY_WHITE;
2214
2215       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2216         stored_player[i].num_white_keys;
2217     }
2218   }
2219   else
2220   {
2221     int player_nr = game.centered_player_nr;
2222
2223     for (k = 0; k < MAX_NUM_KEYS; k++)
2224     {
2225       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226       {
2227         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2228           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2229             get_key_element_from_nr(k);
2230       }
2231       else if (stored_player[player_nr].key[k])
2232         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2233           get_key_element_from_nr(k);
2234     }
2235
2236     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2237       getPlayerInventorySize(player_nr);
2238
2239     if (stored_player[player_nr].num_white_keys > 0)
2240       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2241
2242     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2243       stored_player[player_nr].num_white_keys;
2244   }
2245
2246   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2247   {
2248     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2249       get_inventory_element_from_pos(local_player, i);
2250     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2251       get_inventory_element_from_pos(local_player, -i - 1);
2252   }
2253
2254   game_panel_controls[GAME_PANEL_SCORE].value = score;
2255   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2256
2257   game_panel_controls[GAME_PANEL_TIME].value = time;
2258
2259   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2260   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2261   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2262
2263   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2264
2265   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2266     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2269     local_player->shield_normal_time_left;
2270   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2271     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2272      EL_EMPTY);
2273   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2274     local_player->shield_deadly_time_left;
2275
2276   game_panel_controls[GAME_PANEL_EXIT].value =
2277     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2278
2279   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2280     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2281   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2282     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2283      EL_EMC_MAGIC_BALL_SWITCH);
2284
2285   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2286     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2287   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2288     game.light_time_left;
2289
2290   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2291     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2292   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2293     game.timegate_time_left;
2294
2295   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2296     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2297
2298   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2299     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2301     game.lenses_time_left;
2302
2303   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2304     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2305   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2306     game.magnify_time_left;
2307
2308   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2309     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2310      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2311      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2312      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2313      EL_BALLOON_SWITCH_NONE);
2314
2315   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2316     local_player->dynabomb_count;
2317   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2318     local_player->dynabomb_size;
2319   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2320     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2321
2322   game_panel_controls[GAME_PANEL_PENGUINS].value =
2323     local_player->friends_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2326     local_player->sokobanfields_still_needed;
2327   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2328     local_player->sokobanfields_still_needed;
2329
2330   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2331     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2332
2333   for (i = 0; i < NUM_BELTS; i++)
2334   {
2335     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2336       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2337        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2338     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2339       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2340   }
2341
2342   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2343     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2344   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2345     game.magic_wall_time_left;
2346
2347   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2348     local_player->gravity;
2349
2350   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2351     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2355       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2356        game.panel.element[i].id : EL_UNDEFINED);
2357
2358   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2359     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2360       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2361        element_info[game.panel.element_count[i].id].element_count : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2366        element_info[game.panel.ce_score[i].id].collect_score : 0);
2367
2368   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2369     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2370       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2371        element_info[game.panel.ce_score_element[i].id].collect_score :
2372        EL_UNDEFINED);
2373
2374   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2375   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2376   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2377
2378   /* update game panel control frames */
2379
2380   for (i = 0; game_panel_controls[i].nr != -1; i++)
2381   {
2382     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2383
2384     if (gpc->type == TYPE_ELEMENT)
2385     {
2386       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2387       {
2388         int last_anim_random_frame = gfx.anim_random_frame;
2389         int element = gpc->value;
2390         int graphic = el2panelimg(element);
2391
2392         if (gpc->value != gpc->last_value)
2393         {
2394           gpc->gfx_frame = 0;
2395           gpc->gfx_random = INIT_GFX_RANDOM();
2396         }
2397         else
2398         {
2399           gpc->gfx_frame++;
2400
2401           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2402               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2403             gpc->gfx_random = INIT_GFX_RANDOM();
2404         }
2405
2406         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407           gfx.anim_random_frame = gpc->gfx_random;
2408
2409         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2410           gpc->gfx_frame = element_info[element].collect_score;
2411
2412         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2413                                               gpc->gfx_frame);
2414
2415         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2416           gfx.anim_random_frame = last_anim_random_frame;
2417       }
2418     }
2419   }
2420 }
2421
2422 void DisplayGameControlValues()
2423 {
2424   boolean redraw_panel = FALSE;
2425   int i;
2426
2427   for (i = 0; game_panel_controls[i].nr != -1; i++)
2428   {
2429     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2430
2431     if (PANEL_DEACTIVATED(gpc->pos))
2432       continue;
2433
2434     if (gpc->value == gpc->last_value &&
2435         gpc->frame == gpc->last_frame)
2436       continue;
2437
2438     redraw_panel = TRUE;
2439   }
2440
2441   if (!redraw_panel)
2442     return;
2443
2444   /* copy default game door content to main double buffer */
2445
2446   /* !!! CHECK AGAIN !!! */
2447   SetPanelBackground();
2448   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2449   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2450
2451   /* redraw game control buttons */
2452   RedrawGameButtons();
2453
2454   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2455
2456   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2457   {
2458     int nr = game_panel_order[i].nr;
2459     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2460     struct TextPosInfo *pos = gpc->pos;
2461     int type = gpc->type;
2462     int value = gpc->value;
2463     int frame = gpc->frame;
2464     int size = pos->size;
2465     int font = pos->font;
2466     boolean draw_masked = pos->draw_masked;
2467     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2468
2469     if (PANEL_DEACTIVATED(pos))
2470       continue;
2471
2472     gpc->last_value = value;
2473     gpc->last_frame = frame;
2474
2475     if (type == TYPE_INTEGER)
2476     {
2477       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2478           nr == GAME_PANEL_TIME)
2479       {
2480         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2481
2482         if (use_dynamic_size)           /* use dynamic number of digits */
2483         {
2484           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2485           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2486           int size2 = size1 + 1;
2487           int font1 = pos->font;
2488           int font2 = pos->font_alt;
2489
2490           size = (value < value_change ? size1 : size2);
2491           font = (value < value_change ? font1 : font2);
2492         }
2493       }
2494
2495       /* correct text size if "digits" is zero or less */
2496       if (size <= 0)
2497         size = strlen(int2str(value, size));
2498
2499       /* dynamically correct text alignment */
2500       pos->width = size * getFontWidth(font);
2501
2502       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2503                   int2str(value, size), font, mask_mode);
2504     }
2505     else if (type == TYPE_ELEMENT)
2506     {
2507       int element, graphic;
2508       Bitmap *src_bitmap;
2509       int src_x, src_y;
2510       int width, height;
2511       int dst_x = PANEL_XPOS(pos);
2512       int dst_y = PANEL_YPOS(pos);
2513
2514       if (value != EL_UNDEFINED && value != EL_EMPTY)
2515       {
2516         element = value;
2517         graphic = el2panelimg(value);
2518
2519         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2520
2521         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2522           size = TILESIZE;
2523
2524         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2525                               &src_x, &src_y);
2526
2527         width  = graphic_info[graphic].width  * size / TILESIZE;
2528         height = graphic_info[graphic].height * size / TILESIZE;
2529
2530         if (draw_masked)
2531           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2532                            dst_x, dst_y);
2533         else
2534           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2535                      dst_x, dst_y);
2536       }
2537     }
2538     else if (type == TYPE_STRING)
2539     {
2540       boolean active = (value != 0);
2541       char *state_normal = "off";
2542       char *state_active = "on";
2543       char *state = (active ? state_active : state_normal);
2544       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2545                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2546                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2547                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2548
2549       if (nr == GAME_PANEL_GRAVITY_STATE)
2550       {
2551         int font1 = pos->font;          /* (used for normal state) */
2552         int font2 = pos->font_alt;      /* (used for active state) */
2553
2554         font = (active ? font2 : font1);
2555       }
2556
2557       if (s != NULL)
2558       {
2559         char *s_cut;
2560
2561         if (size <= 0)
2562         {
2563           /* don't truncate output if "chars" is zero or less */
2564           size = strlen(s);
2565
2566           /* dynamically correct text alignment */
2567           pos->width = size * getFontWidth(font);
2568         }
2569
2570         s_cut = getStringCopyN(s, size);
2571
2572         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2573                     s_cut, font, mask_mode);
2574
2575         free(s_cut);
2576       }
2577     }
2578
2579     redraw_mask |= REDRAW_DOOR_1;
2580   }
2581
2582   SetGameStatus(GAME_MODE_PLAYING);
2583 }
2584
2585 void UpdateAndDisplayGameControlValues()
2586 {
2587   if (tape.deactivate_display)
2588     return;
2589
2590   UpdateGameControlValues();
2591   DisplayGameControlValues();
2592 }
2593
2594 void UpdateGameDoorValues()
2595 {
2596   UpdateGameControlValues();
2597 }
2598
2599 void DrawGameDoorValues()
2600 {
2601   DisplayGameControlValues();
2602 }
2603
2604
2605 /*
2606   =============================================================================
2607   InitGameEngine()
2608   -----------------------------------------------------------------------------
2609   initialize game engine due to level / tape version number
2610   =============================================================================
2611 */
2612
2613 static void InitGameEngine()
2614 {
2615   int i, j, k, l, x, y;
2616
2617   /* set game engine from tape file when re-playing, else from level file */
2618   game.engine_version = (tape.playing ? tape.engine_version :
2619                          level.game_version);
2620
2621   /* set single or multi-player game mode (needed for re-playing tapes) */
2622   game.team_mode = setup.team_mode;
2623
2624   if (tape.playing)
2625   {
2626     int num_players = 0;
2627
2628     for (i = 0; i < MAX_PLAYERS; i++)
2629       if (tape.player_participates[i])
2630         num_players++;
2631
2632     /* multi-player tapes contain input data for more than one player */
2633     game.team_mode = (num_players > 1);
2634   }
2635
2636   /* ---------------------------------------------------------------------- */
2637   /* set flags for bugs and changes according to active game engine version */
2638   /* ---------------------------------------------------------------------- */
2639
2640   /*
2641     Summary of bugfix/change:
2642     Fixed handling for custom elements that change when pushed by the player.
2643
2644     Fixed/changed in version:
2645     3.1.0
2646
2647     Description:
2648     Before 3.1.0, custom elements that "change when pushing" changed directly
2649     after the player started pushing them (until then handled in "DigField()").
2650     Since 3.1.0, these custom elements are not changed until the "pushing"
2651     move of the element is finished (now handled in "ContinueMoving()").
2652
2653     Affected levels/tapes:
2654     The first condition is generally needed for all levels/tapes before version
2655     3.1.0, which might use the old behaviour before it was changed; known tapes
2656     that are affected are some tapes from the level set "Walpurgis Gardens" by
2657     Jamie Cullen.
2658     The second condition is an exception from the above case and is needed for
2659     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2660     above (including some development versions of 3.1.0), but before it was
2661     known that this change would break tapes like the above and was fixed in
2662     3.1.1, so that the changed behaviour was active although the engine version
2663     while recording maybe was before 3.1.0. There is at least one tape that is
2664     affected by this exception, which is the tape for the one-level set "Bug
2665     Machine" by Juergen Bonhagen.
2666   */
2667
2668   game.use_change_when_pushing_bug =
2669     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2670      !(tape.playing &&
2671        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2672        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2673
2674   /*
2675     Summary of bugfix/change:
2676     Fixed handling for blocking the field the player leaves when moving.
2677
2678     Fixed/changed in version:
2679     3.1.1
2680
2681     Description:
2682     Before 3.1.1, when "block last field when moving" was enabled, the field
2683     the player is leaving when moving was blocked for the time of the move,
2684     and was directly unblocked afterwards. This resulted in the last field
2685     being blocked for exactly one less than the number of frames of one player
2686     move. Additionally, even when blocking was disabled, the last field was
2687     blocked for exactly one frame.
2688     Since 3.1.1, due to changes in player movement handling, the last field
2689     is not blocked at all when blocking is disabled. When blocking is enabled,
2690     the last field is blocked for exactly the number of frames of one player
2691     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2692     last field is blocked for exactly one more than the number of frames of
2693     one player move.
2694
2695     Affected levels/tapes:
2696     (!!! yet to be determined -- probably many !!!)
2697   */
2698
2699   game.use_block_last_field_bug =
2700     (game.engine_version < VERSION_IDENT(3,1,1,0));
2701
2702   game_em.use_single_button =
2703     (game.engine_version > VERSION_IDENT(4,0,0,2));
2704
2705   /* ---------------------------------------------------------------------- */
2706
2707   /* set maximal allowed number of custom element changes per game frame */
2708   game.max_num_changes_per_frame = 1;
2709
2710   /* default scan direction: scan playfield from top/left to bottom/right */
2711   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2712
2713   /* dynamically adjust element properties according to game engine version */
2714   InitElementPropertiesEngine(game.engine_version);
2715
2716 #if 0
2717   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2718   printf("          tape version == %06d [%s] [file: %06d]\n",
2719          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2720          tape.file_version);
2721   printf("       => game.engine_version == %06d\n", game.engine_version);
2722 #endif
2723
2724   /* ---------- initialize player's initial move delay --------------------- */
2725
2726   /* dynamically adjust player properties according to level information */
2727   for (i = 0; i < MAX_PLAYERS; i++)
2728     game.initial_move_delay_value[i] =
2729       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2730
2731   /* dynamically adjust player properties according to game engine version */
2732   for (i = 0; i < MAX_PLAYERS; i++)
2733     game.initial_move_delay[i] =
2734       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2735        game.initial_move_delay_value[i] : 0);
2736
2737   /* ---------- initialize player's initial push delay --------------------- */
2738
2739   /* dynamically adjust player properties according to game engine version */
2740   game.initial_push_delay_value =
2741     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2742
2743   /* ---------- initialize changing elements ------------------------------- */
2744
2745   /* initialize changing elements information */
2746   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2747   {
2748     struct ElementInfo *ei = &element_info[i];
2749
2750     /* this pointer might have been changed in the level editor */
2751     ei->change = &ei->change_page[0];
2752
2753     if (!IS_CUSTOM_ELEMENT(i))
2754     {
2755       ei->change->target_element = EL_EMPTY_SPACE;
2756       ei->change->delay_fixed = 0;
2757       ei->change->delay_random = 0;
2758       ei->change->delay_frames = 1;
2759     }
2760
2761     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2762     {
2763       ei->has_change_event[j] = FALSE;
2764
2765       ei->event_page_nr[j] = 0;
2766       ei->event_page[j] = &ei->change_page[0];
2767     }
2768   }
2769
2770   /* add changing elements from pre-defined list */
2771   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2772   {
2773     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2774     struct ElementInfo *ei = &element_info[ch_delay->element];
2775
2776     ei->change->target_element       = ch_delay->target_element;
2777     ei->change->delay_fixed          = ch_delay->change_delay;
2778
2779     ei->change->pre_change_function  = ch_delay->pre_change_function;
2780     ei->change->change_function      = ch_delay->change_function;
2781     ei->change->post_change_function = ch_delay->post_change_function;
2782
2783     ei->change->can_change = TRUE;
2784     ei->change->can_change_or_has_action = TRUE;
2785
2786     ei->has_change_event[CE_DELAY] = TRUE;
2787
2788     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2789     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2790   }
2791
2792   /* ---------- initialize internal run-time variables --------------------- */
2793
2794   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2795   {
2796     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2797
2798     for (j = 0; j < ei->num_change_pages; j++)
2799     {
2800       ei->change_page[j].can_change_or_has_action =
2801         (ei->change_page[j].can_change |
2802          ei->change_page[j].has_action);
2803     }
2804   }
2805
2806   /* add change events from custom element configuration */
2807   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2808   {
2809     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2810
2811     for (j = 0; j < ei->num_change_pages; j++)
2812     {
2813       if (!ei->change_page[j].can_change_or_has_action)
2814         continue;
2815
2816       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2817       {
2818         /* only add event page for the first page found with this event */
2819         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2820         {
2821           ei->has_change_event[k] = TRUE;
2822
2823           ei->event_page_nr[k] = j;
2824           ei->event_page[k] = &ei->change_page[j];
2825         }
2826       }
2827     }
2828   }
2829
2830   /* ---------- initialize reference elements in change conditions --------- */
2831
2832   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2833   {
2834     int element = EL_CUSTOM_START + i;
2835     struct ElementInfo *ei = &element_info[element];
2836
2837     for (j = 0; j < ei->num_change_pages; j++)
2838     {
2839       int trigger_element = ei->change_page[j].initial_trigger_element;
2840
2841       if (trigger_element >= EL_PREV_CE_8 &&
2842           trigger_element <= EL_NEXT_CE_8)
2843         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2844
2845       ei->change_page[j].trigger_element = trigger_element;
2846     }
2847   }
2848
2849   /* ---------- initialize run-time trigger player and element ------------- */
2850
2851   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2852   {
2853     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2854
2855     for (j = 0; j < ei->num_change_pages; j++)
2856     {
2857       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2858       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2859       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2860       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2861       ei->change_page[j].actual_trigger_ce_value = 0;
2862       ei->change_page[j].actual_trigger_ce_score = 0;
2863     }
2864   }
2865
2866   /* ---------- initialize trigger events ---------------------------------- */
2867
2868   /* initialize trigger events information */
2869   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2870     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2871       trigger_events[i][j] = FALSE;
2872
2873   /* add trigger events from element change event properties */
2874   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2875   {
2876     struct ElementInfo *ei = &element_info[i];
2877
2878     for (j = 0; j < ei->num_change_pages; j++)
2879     {
2880       if (!ei->change_page[j].can_change_or_has_action)
2881         continue;
2882
2883       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2884       {
2885         int trigger_element = ei->change_page[j].trigger_element;
2886
2887         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2888         {
2889           if (ei->change_page[j].has_event[k])
2890           {
2891             if (IS_GROUP_ELEMENT(trigger_element))
2892             {
2893               struct ElementGroupInfo *group =
2894                 element_info[trigger_element].group;
2895
2896               for (l = 0; l < group->num_elements_resolved; l++)
2897                 trigger_events[group->element_resolved[l]][k] = TRUE;
2898             }
2899             else if (trigger_element == EL_ANY_ELEMENT)
2900               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2901                 trigger_events[l][k] = TRUE;
2902             else
2903               trigger_events[trigger_element][k] = TRUE;
2904           }
2905         }
2906       }
2907     }
2908   }
2909
2910   /* ---------- initialize push delay -------------------------------------- */
2911
2912   /* initialize push delay values to default */
2913   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2914   {
2915     if (!IS_CUSTOM_ELEMENT(i))
2916     {
2917       /* set default push delay values (corrected since version 3.0.7-1) */
2918       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2919       {
2920         element_info[i].push_delay_fixed = 2;
2921         element_info[i].push_delay_random = 8;
2922       }
2923       else
2924       {
2925         element_info[i].push_delay_fixed = 8;
2926         element_info[i].push_delay_random = 8;
2927       }
2928     }
2929   }
2930
2931   /* set push delay value for certain elements from pre-defined list */
2932   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2933   {
2934     int e = push_delay_list[i].element;
2935
2936     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2937     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2938   }
2939
2940   /* set push delay value for Supaplex elements for newer engine versions */
2941   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2942   {
2943     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2944     {
2945       if (IS_SP_ELEMENT(i))
2946       {
2947         /* set SP push delay to just enough to push under a falling zonk */
2948         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2949
2950         element_info[i].push_delay_fixed  = delay;
2951         element_info[i].push_delay_random = 0;
2952       }
2953     }
2954   }
2955
2956   /* ---------- initialize move stepsize ----------------------------------- */
2957
2958   /* initialize move stepsize values to default */
2959   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2960     if (!IS_CUSTOM_ELEMENT(i))
2961       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2962
2963   /* set move stepsize value for certain elements from pre-defined list */
2964   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2965   {
2966     int e = move_stepsize_list[i].element;
2967
2968     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2969   }
2970
2971   /* ---------- initialize collect score ----------------------------------- */
2972
2973   /* initialize collect score values for custom elements from initial value */
2974   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2975     if (IS_CUSTOM_ELEMENT(i))
2976       element_info[i].collect_score = element_info[i].collect_score_initial;
2977
2978   /* ---------- initialize collect count ----------------------------------- */
2979
2980   /* initialize collect count values for non-custom elements */
2981   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2982     if (!IS_CUSTOM_ELEMENT(i))
2983       element_info[i].collect_count_initial = 0;
2984
2985   /* add collect count values for all elements from pre-defined list */
2986   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2987     element_info[collect_count_list[i].element].collect_count_initial =
2988       collect_count_list[i].count;
2989
2990   /* ---------- initialize access direction -------------------------------- */
2991
2992   /* initialize access direction values to default (access from every side) */
2993   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2994     if (!IS_CUSTOM_ELEMENT(i))
2995       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2996
2997   /* set access direction value for certain elements from pre-defined list */
2998   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2999     element_info[access_direction_list[i].element].access_direction =
3000       access_direction_list[i].direction;
3001
3002   /* ---------- initialize explosion content ------------------------------- */
3003   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3004   {
3005     if (IS_CUSTOM_ELEMENT(i))
3006       continue;
3007
3008     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3009     {
3010       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3011
3012       element_info[i].content.e[x][y] =
3013         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3014          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3015          i == EL_PLAYER_3 ? EL_EMERALD :
3016          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3017          i == EL_MOLE ? EL_EMERALD_RED :
3018          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3019          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3020          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3021          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3022          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3023          i == EL_WALL_EMERALD ? EL_EMERALD :
3024          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3025          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3026          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3027          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3028          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3029          i == EL_WALL_PEARL ? EL_PEARL :
3030          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3031          EL_EMPTY);
3032     }
3033   }
3034
3035   /* ---------- initialize recursion detection ------------------------------ */
3036   recursion_loop_depth = 0;
3037   recursion_loop_detected = FALSE;
3038   recursion_loop_element = EL_UNDEFINED;
3039
3040   /* ---------- initialize graphics engine ---------------------------------- */
3041   game.scroll_delay_value =
3042     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3043      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3044   game.scroll_delay_value =
3045     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3046
3047   /* ---------- initialize game engine snapshots ---------------------------- */
3048   for (i = 0; i < MAX_PLAYERS; i++)
3049     game.snapshot.last_action[i] = 0;
3050   game.snapshot.changed_action = FALSE;
3051   game.snapshot.collected_item = FALSE;
3052   game.snapshot.mode =
3053     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3054      SNAPSHOT_MODE_EVERY_STEP :
3055      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3056      SNAPSHOT_MODE_EVERY_MOVE :
3057      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3058      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3059   game.snapshot.save_snapshot = FALSE;
3060 }
3061
3062 int get_num_special_action(int element, int action_first, int action_last)
3063 {
3064   int num_special_action = 0;
3065   int i, j;
3066
3067   for (i = action_first; i <= action_last; i++)
3068   {
3069     boolean found = FALSE;
3070
3071     for (j = 0; j < NUM_DIRECTIONS; j++)
3072       if (el_act_dir2img(element, i, j) !=
3073           el_act_dir2img(element, ACTION_DEFAULT, j))
3074         found = TRUE;
3075
3076     if (found)
3077       num_special_action++;
3078     else
3079       break;
3080   }
3081
3082   return num_special_action;
3083 }
3084
3085
3086 /*
3087   =============================================================================
3088   InitGame()
3089   -----------------------------------------------------------------------------
3090   initialize and start new game
3091   =============================================================================
3092 */
3093
3094 void InitGame()
3095 {
3096   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3097   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3098   int fade_mask = REDRAW_FIELD;
3099
3100   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3101   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3102   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3103   int initial_move_dir = MV_DOWN;
3104   int i, j, x, y;
3105
3106   // required here to update video display before fading (FIX THIS)
3107   DrawMaskedBorder(REDRAW_DOOR_2);
3108
3109   if (!game.restart_level)
3110     CloseDoor(DOOR_CLOSE_1);
3111
3112   SetGameStatus(GAME_MODE_PLAYING);
3113
3114   if (level_editor_test_game)
3115     FadeSkipNextFadeIn();
3116   else
3117     FadeSetEnterScreen();
3118
3119   if (CheckIfGlobalBorderHasChanged())
3120     fade_mask = REDRAW_ALL;
3121
3122   FadeLevelSoundsAndMusic();
3123
3124   ExpireSoundLoops(TRUE);
3125
3126   FadeOut(fade_mask);
3127
3128   /* needed if different viewport properties defined for playing */
3129   ChangeViewportPropertiesIfNeeded();
3130
3131   ClearField();
3132
3133   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3134
3135   DrawCompleteVideoDisplay();
3136
3137   InitGameEngine();
3138   InitGameControlValues();
3139
3140   /* don't play tapes over network */
3141   network_playing = (options.network && !tape.playing);
3142
3143   for (i = 0; i < MAX_PLAYERS; i++)
3144   {
3145     struct PlayerInfo *player = &stored_player[i];
3146
3147     player->index_nr = i;
3148     player->index_bit = (1 << i);
3149     player->element_nr = EL_PLAYER_1 + i;
3150
3151     player->present = FALSE;
3152     player->active = FALSE;
3153     player->mapped = FALSE;
3154
3155     player->killed = FALSE;
3156     player->reanimated = FALSE;
3157
3158     player->action = 0;
3159     player->effective_action = 0;
3160     player->programmed_action = 0;
3161
3162     player->score = 0;
3163     player->score_final = 0;
3164
3165     player->gems_still_needed = level.gems_needed;
3166     player->sokobanfields_still_needed = 0;
3167     player->lights_still_needed = 0;
3168     player->friends_still_needed = 0;
3169
3170     for (j = 0; j < MAX_NUM_KEYS; j++)
3171       player->key[j] = FALSE;
3172
3173     player->num_white_keys = 0;
3174
3175     player->dynabomb_count = 0;
3176     player->dynabomb_size = 1;
3177     player->dynabombs_left = 0;
3178     player->dynabomb_xl = FALSE;
3179
3180     player->MovDir = initial_move_dir;
3181     player->MovPos = 0;
3182     player->GfxPos = 0;
3183     player->GfxDir = initial_move_dir;
3184     player->GfxAction = ACTION_DEFAULT;
3185     player->Frame = 0;
3186     player->StepFrame = 0;
3187
3188     player->initial_element = player->element_nr;
3189     player->artwork_element =
3190       (level.use_artwork_element[i] ? level.artwork_element[i] :
3191        player->element_nr);
3192     player->use_murphy = FALSE;
3193
3194     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3195     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3196
3197     player->gravity = level.initial_player_gravity[i];
3198
3199     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3200
3201     player->actual_frame_counter = 0;
3202
3203     player->step_counter = 0;
3204
3205     player->last_move_dir = initial_move_dir;
3206
3207     player->is_active = FALSE;
3208
3209     player->is_waiting = FALSE;
3210     player->is_moving = FALSE;
3211     player->is_auto_moving = FALSE;
3212     player->is_digging = FALSE;
3213     player->is_snapping = FALSE;
3214     player->is_collecting = FALSE;
3215     player->is_pushing = FALSE;
3216     player->is_switching = FALSE;
3217     player->is_dropping = FALSE;
3218     player->is_dropping_pressed = FALSE;
3219
3220     player->is_bored = FALSE;
3221     player->is_sleeping = FALSE;
3222
3223     player->was_waiting = TRUE;
3224     player->was_moving = FALSE;
3225     player->was_snapping = FALSE;
3226     player->was_dropping = FALSE;
3227
3228     player->force_dropping = FALSE;
3229
3230     player->frame_counter_bored = -1;
3231     player->frame_counter_sleeping = -1;
3232
3233     player->anim_delay_counter = 0;
3234     player->post_delay_counter = 0;
3235
3236     player->dir_waiting = initial_move_dir;
3237     player->action_waiting = ACTION_DEFAULT;
3238     player->last_action_waiting = ACTION_DEFAULT;
3239     player->special_action_bored = ACTION_DEFAULT;
3240     player->special_action_sleeping = ACTION_DEFAULT;
3241
3242     player->switch_x = -1;
3243     player->switch_y = -1;
3244
3245     player->drop_x = -1;
3246     player->drop_y = -1;
3247
3248     player->show_envelope = 0;
3249
3250     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3251
3252     player->push_delay       = -1;      /* initialized when pushing starts */
3253     player->push_delay_value = game.initial_push_delay_value;
3254
3255     player->drop_delay = 0;
3256     player->drop_pressed_delay = 0;
3257
3258     player->last_jx = -1;
3259     player->last_jy = -1;
3260     player->jx = -1;
3261     player->jy = -1;
3262
3263     player->shield_normal_time_left = 0;
3264     player->shield_deadly_time_left = 0;
3265
3266     player->inventory_infinite_element = EL_UNDEFINED;
3267     player->inventory_size = 0;
3268
3269     if (level.use_initial_inventory[i])
3270     {
3271       for (j = 0; j < level.initial_inventory_size[i]; j++)
3272       {
3273         int element = level.initial_inventory_content[i][j];
3274         int collect_count = element_info[element].collect_count_initial;
3275         int k;
3276
3277         if (!IS_CUSTOM_ELEMENT(element))
3278           collect_count = 1;
3279
3280         if (collect_count == 0)
3281           player->inventory_infinite_element = element;
3282         else
3283           for (k = 0; k < collect_count; k++)
3284             if (player->inventory_size < MAX_INVENTORY_SIZE)
3285               player->inventory_element[player->inventory_size++] = element;
3286       }
3287     }
3288
3289     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3290     SnapField(player, 0, 0);
3291
3292     player->LevelSolved = FALSE;
3293     player->GameOver = FALSE;
3294
3295     player->LevelSolved_GameWon = FALSE;
3296     player->LevelSolved_GameEnd = FALSE;
3297     player->LevelSolved_PanelOff = FALSE;
3298     player->LevelSolved_SaveTape = FALSE;
3299     player->LevelSolved_SaveScore = FALSE;
3300     player->LevelSolved_CountingTime = 0;
3301     player->LevelSolved_CountingScore = 0;
3302
3303     map_player_action[i] = i;
3304   }
3305
3306   network_player_action_received = FALSE;
3307
3308 #if defined(NETWORK_AVALIABLE)
3309   /* initial null action */
3310   if (network_playing)
3311     SendToServer_MovePlayer(MV_NONE);
3312 #endif
3313
3314   ZX = ZY = -1;
3315   ExitX = ExitY = -1;
3316
3317   FrameCounter = 0;
3318   TimeFrames = 0;
3319   TimePlayed = 0;
3320   TimeLeft = level.time;
3321   TapeTime = 0;
3322
3323   ScreenMovDir = MV_NONE;
3324   ScreenMovPos = 0;
3325   ScreenGfxPos = 0;
3326
3327   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3328
3329   AllPlayersGone = FALSE;
3330
3331   game.no_time_limit = (level.time == 0);
3332
3333   game.yamyam_content_nr = 0;
3334   game.robot_wheel_active = FALSE;
3335   game.magic_wall_active = FALSE;
3336   game.magic_wall_time_left = 0;
3337   game.light_time_left = 0;
3338   game.timegate_time_left = 0;
3339   game.switchgate_pos = 0;
3340   game.wind_direction = level.wind_direction_initial;
3341
3342   game.lenses_time_left = 0;
3343   game.magnify_time_left = 0;
3344
3345   game.ball_state = level.ball_state_initial;
3346   game.ball_content_nr = 0;
3347
3348   game.envelope_active = FALSE;
3349
3350   /* set focus to local player for network games, else to all players */
3351   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3352   game.centered_player_nr_next = game.centered_player_nr;
3353   game.set_centered_player = FALSE;
3354
3355   if (network_playing && tape.recording)
3356   {
3357     /* store client dependent player focus when recording network games */
3358     tape.centered_player_nr_next = game.centered_player_nr_next;
3359     tape.set_centered_player = TRUE;
3360   }
3361
3362   for (i = 0; i < NUM_BELTS; i++)
3363   {
3364     game.belt_dir[i] = MV_NONE;
3365     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3366   }
3367
3368   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3369     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3370
3371 #if DEBUG_INIT_PLAYER
3372   if (options.debug)
3373   {
3374     printf("Player status at level initialization:\n");
3375   }
3376 #endif
3377
3378   SCAN_PLAYFIELD(x, y)
3379   {
3380     Feld[x][y] = level.field[x][y];
3381     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3382     ChangeDelay[x][y] = 0;
3383     ChangePage[x][y] = -1;
3384     CustomValue[x][y] = 0;              /* initialized in InitField() */
3385     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3386     AmoebaNr[x][y] = 0;
3387     WasJustMoving[x][y] = 0;
3388     WasJustFalling[x][y] = 0;
3389     CheckCollision[x][y] = 0;
3390     CheckImpact[x][y] = 0;
3391     Stop[x][y] = FALSE;
3392     Pushed[x][y] = FALSE;
3393
3394     ChangeCount[x][y] = 0;
3395     ChangeEvent[x][y] = -1;
3396
3397     ExplodePhase[x][y] = 0;
3398     ExplodeDelay[x][y] = 0;
3399     ExplodeField[x][y] = EX_TYPE_NONE;
3400
3401     RunnerVisit[x][y] = 0;
3402     PlayerVisit[x][y] = 0;
3403
3404     GfxFrame[x][y] = 0;
3405     GfxRandom[x][y] = INIT_GFX_RANDOM();
3406     GfxElement[x][y] = EL_UNDEFINED;
3407     GfxAction[x][y] = ACTION_DEFAULT;
3408     GfxDir[x][y] = MV_NONE;
3409     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3410   }
3411
3412   SCAN_PLAYFIELD(x, y)
3413   {
3414     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3415       emulate_bd = FALSE;
3416     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3417       emulate_sb = FALSE;
3418     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3419       emulate_sp = FALSE;
3420
3421     InitField(x, y, TRUE);
3422
3423     ResetGfxAnimation(x, y);
3424   }
3425
3426   InitBeltMovement();
3427
3428   for (i = 0; i < MAX_PLAYERS; i++)
3429   {
3430     struct PlayerInfo *player = &stored_player[i];
3431
3432     /* set number of special actions for bored and sleeping animation */
3433     player->num_special_action_bored =
3434       get_num_special_action(player->artwork_element,
3435                              ACTION_BORING_1, ACTION_BORING_LAST);
3436     player->num_special_action_sleeping =
3437       get_num_special_action(player->artwork_element,
3438                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3439   }
3440
3441   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3442                     emulate_sb ? EMU_SOKOBAN :
3443                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3444
3445   /* initialize type of slippery elements */
3446   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3447   {
3448     if (!IS_CUSTOM_ELEMENT(i))
3449     {
3450       /* default: elements slip down either to the left or right randomly */
3451       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3452
3453       /* SP style elements prefer to slip down on the left side */
3454       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3455         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3456
3457       /* BD style elements prefer to slip down on the left side */
3458       if (game.emulation == EMU_BOULDERDASH)
3459         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3460     }
3461   }
3462
3463   /* initialize explosion and ignition delay */
3464   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3465   {
3466     if (!IS_CUSTOM_ELEMENT(i))
3467     {
3468       int num_phase = 8;
3469       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3470                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3471                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3472       int last_phase = (num_phase + 1) * delay;
3473       int half_phase = (num_phase / 2) * delay;
3474
3475       element_info[i].explosion_delay = last_phase - 1;
3476       element_info[i].ignition_delay = half_phase;
3477
3478       if (i == EL_BLACK_ORB)
3479         element_info[i].ignition_delay = 1;
3480     }
3481   }
3482
3483   /* correct non-moving belts to start moving left */
3484   for (i = 0; i < NUM_BELTS; i++)
3485     if (game.belt_dir[i] == MV_NONE)
3486       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3487
3488 #if USE_NEW_PLAYER_ASSIGNMENTS
3489   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3490   /* choose default local player */
3491   local_player = &stored_player[0];
3492
3493   for (i = 0; i < MAX_PLAYERS; i++)
3494     stored_player[i].connected = FALSE;
3495
3496   local_player->connected = TRUE;
3497   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3498
3499   if (tape.playing)
3500   {
3501     for (i = 0; i < MAX_PLAYERS; i++)
3502       stored_player[i].connected = tape.player_participates[i];
3503   }
3504   else if (game.team_mode && !options.network)
3505   {
3506     /* try to guess locally connected team mode players (needed for correct
3507        assignment of player figures from level to locally playing players) */
3508
3509     for (i = 0; i < MAX_PLAYERS; i++)
3510       if (setup.input[i].use_joystick ||
3511           setup.input[i].key.left != KSYM_UNDEFINED)
3512         stored_player[i].connected = TRUE;
3513   }
3514
3515 #if DEBUG_INIT_PLAYER
3516   if (options.debug)
3517   {
3518     printf("Player status after level initialization:\n");
3519
3520     for (i = 0; i < MAX_PLAYERS; i++)
3521     {
3522       struct PlayerInfo *player = &stored_player[i];
3523
3524       printf("- player %d: present == %d, connected == %d, active == %d",
3525              i + 1,
3526              player->present,
3527              player->connected,
3528              player->active);
3529
3530       if (local_player == player)
3531         printf(" (local player)");
3532
3533       printf("\n");
3534     }
3535   }
3536 #endif
3537
3538 #if DEBUG_INIT_PLAYER
3539   if (options.debug)
3540     printf("Reassigning players ...\n");
3541 #endif
3542
3543   /* check if any connected player was not found in playfield */
3544   for (i = 0; i < MAX_PLAYERS; i++)
3545   {
3546     struct PlayerInfo *player = &stored_player[i];
3547
3548     if (player->connected && !player->present)
3549     {
3550       struct PlayerInfo *field_player = NULL;
3551
3552 #if DEBUG_INIT_PLAYER
3553       if (options.debug)
3554         printf("- looking for field player for player %d ...\n", i + 1);
3555 #endif
3556
3557       /* assign first free player found that is present in the playfield */
3558
3559       /* first try: look for unmapped playfield player that is not connected */
3560       for (j = 0; j < MAX_PLAYERS; j++)
3561         if (field_player == NULL &&
3562             stored_player[j].present &&
3563             !stored_player[j].mapped &&
3564             !stored_player[j].connected)
3565           field_player = &stored_player[j];
3566
3567       /* second try: look for *any* unmapped playfield player */
3568       for (j = 0; j < MAX_PLAYERS; j++)
3569         if (field_player == NULL &&
3570             stored_player[j].present &&
3571             !stored_player[j].mapped)
3572           field_player = &stored_player[j];
3573
3574       if (field_player != NULL)
3575       {
3576         int jx = field_player->jx, jy = field_player->jy;
3577
3578 #if DEBUG_INIT_PLAYER
3579         if (options.debug)
3580           printf("- found player %d\n", field_player->index_nr + 1);
3581 #endif
3582
3583         player->present = FALSE;
3584         player->active = FALSE;
3585
3586         field_player->present = TRUE;
3587         field_player->active = TRUE;
3588
3589         /*
3590         player->initial_element = field_player->initial_element;
3591         player->artwork_element = field_player->artwork_element;
3592
3593         player->block_last_field       = field_player->block_last_field;
3594         player->block_delay_adjustment = field_player->block_delay_adjustment;
3595         */
3596
3597         StorePlayer[jx][jy] = field_player->element_nr;
3598
3599         field_player->jx = field_player->last_jx = jx;
3600         field_player->jy = field_player->last_jy = jy;
3601
3602         if (local_player == player)
3603           local_player = field_player;
3604
3605         map_player_action[field_player->index_nr] = i;
3606
3607         field_player->mapped = TRUE;
3608
3609 #if DEBUG_INIT_PLAYER
3610         if (options.debug)
3611           printf("- map_player_action[%d] == %d\n",
3612                  field_player->index_nr + 1, i + 1);
3613 #endif
3614       }
3615     }
3616
3617     if (player->connected && player->present)
3618       player->mapped = TRUE;
3619   }
3620
3621 #if DEBUG_INIT_PLAYER
3622   if (options.debug)
3623   {
3624     printf("Player status after player assignment (first stage):\n");
3625
3626     for (i = 0; i < MAX_PLAYERS; i++)
3627     {
3628       struct PlayerInfo *player = &stored_player[i];
3629
3630       printf("- player %d: present == %d, connected == %d, active == %d",
3631              i + 1,
3632              player->present,
3633              player->connected,
3634              player->active);
3635
3636       if (local_player == player)
3637         printf(" (local player)");
3638
3639       printf("\n");
3640     }
3641   }
3642 #endif
3643
3644 #else
3645
3646   /* check if any connected player was not found in playfield */
3647   for (i = 0; i < MAX_PLAYERS; i++)
3648   {
3649     struct PlayerInfo *player = &stored_player[i];
3650
3651     if (player->connected && !player->present)
3652     {
3653       for (j = 0; j < MAX_PLAYERS; j++)
3654       {
3655         struct PlayerInfo *field_player = &stored_player[j];
3656         int jx = field_player->jx, jy = field_player->jy;
3657
3658         /* assign first free player found that is present in the playfield */
3659         if (field_player->present && !field_player->connected)
3660         {
3661           player->present = TRUE;
3662           player->active = TRUE;
3663
3664           field_player->present = FALSE;
3665           field_player->active = FALSE;
3666
3667           player->initial_element = field_player->initial_element;
3668           player->artwork_element = field_player->artwork_element;
3669
3670           player->block_last_field       = field_player->block_last_field;
3671           player->block_delay_adjustment = field_player->block_delay_adjustment;
3672
3673           StorePlayer[jx][jy] = player->element_nr;
3674
3675           player->jx = player->last_jx = jx;
3676           player->jy = player->last_jy = jy;
3677
3678           break;
3679         }
3680       }
3681     }
3682   }
3683 #endif
3684
3685 #if 0
3686   printf("::: local_player->present == %d\n", local_player->present);
3687 #endif
3688
3689   if (tape.playing)
3690   {
3691     /* when playing a tape, eliminate all players who do not participate */
3692
3693 #if USE_NEW_PLAYER_ASSIGNMENTS
3694
3695     if (!game.team_mode)
3696     {
3697       for (i = 0; i < MAX_PLAYERS; i++)
3698       {
3699         if (stored_player[i].active &&
3700             !tape.player_participates[map_player_action[i]])
3701         {
3702           struct PlayerInfo *player = &stored_player[i];
3703           int jx = player->jx, jy = player->jy;
3704
3705 #if DEBUG_INIT_PLAYER
3706           if (options.debug)
3707             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3708 #endif
3709
3710           player->active = FALSE;
3711           StorePlayer[jx][jy] = 0;
3712           Feld[jx][jy] = EL_EMPTY;
3713         }
3714       }
3715     }
3716
3717 #else
3718
3719     for (i = 0; i < MAX_PLAYERS; i++)
3720     {
3721       if (stored_player[i].active &&
3722           !tape.player_participates[i])
3723       {
3724         struct PlayerInfo *player = &stored_player[i];
3725         int jx = player->jx, jy = player->jy;
3726
3727         player->active = FALSE;
3728         StorePlayer[jx][jy] = 0;
3729         Feld[jx][jy] = EL_EMPTY;
3730       }
3731     }
3732 #endif
3733   }
3734   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3735   {
3736     /* when in single player mode, eliminate all but the first active player */
3737
3738     for (i = 0; i < MAX_PLAYERS; i++)
3739     {
3740       if (stored_player[i].active)
3741       {
3742         for (j = i + 1; j < MAX_PLAYERS; j++)
3743         {
3744           if (stored_player[j].active)
3745           {
3746             struct PlayerInfo *player = &stored_player[j];
3747             int jx = player->jx, jy = player->jy;
3748
3749             player->active = FALSE;
3750             player->present = FALSE;
3751
3752             StorePlayer[jx][jy] = 0;
3753             Feld[jx][jy] = EL_EMPTY;
3754           }
3755         }
3756       }
3757     }
3758   }
3759
3760   /* when recording the game, store which players take part in the game */
3761   if (tape.recording)
3762   {
3763 #if USE_NEW_PLAYER_ASSIGNMENTS
3764     for (i = 0; i < MAX_PLAYERS; i++)
3765       if (stored_player[i].connected)
3766         tape.player_participates[i] = TRUE;
3767 #else
3768     for (i = 0; i < MAX_PLAYERS; i++)
3769       if (stored_player[i].active)
3770         tape.player_participates[i] = TRUE;
3771 #endif
3772   }
3773
3774 #if DEBUG_INIT_PLAYER
3775   if (options.debug)
3776   {
3777     printf("Player status after player assignment (final stage):\n");
3778
3779     for (i = 0; i < MAX_PLAYERS; i++)
3780     {
3781       struct PlayerInfo *player = &stored_player[i];
3782
3783       printf("- player %d: present == %d, connected == %d, active == %d",
3784              i + 1,
3785              player->present,
3786              player->connected,
3787              player->active);
3788
3789       if (local_player == player)
3790         printf(" (local player)");
3791
3792       printf("\n");
3793     }
3794   }
3795 #endif
3796
3797   if (BorderElement == EL_EMPTY)
3798   {
3799     SBX_Left = 0;
3800     SBX_Right = lev_fieldx - SCR_FIELDX;
3801     SBY_Upper = 0;
3802     SBY_Lower = lev_fieldy - SCR_FIELDY;
3803   }
3804   else
3805   {
3806     SBX_Left = -1;
3807     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3808     SBY_Upper = -1;
3809     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3810   }
3811
3812   if (full_lev_fieldx <= SCR_FIELDX)
3813     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3814   if (full_lev_fieldy <= SCR_FIELDY)
3815     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3816
3817   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3818     SBX_Left--;
3819   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3820     SBY_Upper--;
3821
3822   /* if local player not found, look for custom element that might create
3823      the player (make some assumptions about the right custom element) */
3824   if (!local_player->present)
3825   {
3826     int start_x = 0, start_y = 0;
3827     int found_rating = 0;
3828     int found_element = EL_UNDEFINED;
3829     int player_nr = local_player->index_nr;
3830
3831     SCAN_PLAYFIELD(x, y)
3832     {
3833       int element = Feld[x][y];
3834       int content;
3835       int xx, yy;
3836       boolean is_player;
3837
3838       if (level.use_start_element[player_nr] &&
3839           level.start_element[player_nr] == element &&
3840           found_rating < 4)
3841       {
3842         start_x = x;
3843         start_y = y;
3844
3845         found_rating = 4;
3846         found_element = element;
3847       }
3848
3849       if (!IS_CUSTOM_ELEMENT(element))
3850         continue;
3851
3852       if (CAN_CHANGE(element))
3853       {
3854         for (i = 0; i < element_info[element].num_change_pages; i++)
3855         {
3856           /* check for player created from custom element as single target */
3857           content = element_info[element].change_page[i].target_element;
3858           is_player = ELEM_IS_PLAYER(content);
3859
3860           if (is_player && (found_rating < 3 ||
3861                             (found_rating == 3 && element < found_element)))
3862           {
3863             start_x = x;
3864             start_y = y;
3865
3866             found_rating = 3;
3867             found_element = element;
3868           }
3869         }
3870       }
3871
3872       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3873       {
3874         /* check for player created from custom element as explosion content */
3875         content = element_info[element].content.e[xx][yy];
3876         is_player = ELEM_IS_PLAYER(content);
3877
3878         if (is_player && (found_rating < 2 ||
3879                           (found_rating == 2 && element < found_element)))
3880         {
3881           start_x = x + xx - 1;
3882           start_y = y + yy - 1;
3883
3884           found_rating = 2;
3885           found_element = element;
3886         }
3887
3888         if (!CAN_CHANGE(element))
3889           continue;
3890
3891         for (i = 0; i < element_info[element].num_change_pages; i++)
3892         {
3893           /* check for player created from custom element as extended target */
3894           content =
3895             element_info[element].change_page[i].target_content.e[xx][yy];
3896
3897           is_player = ELEM_IS_PLAYER(content);
3898
3899           if (is_player && (found_rating < 1 ||
3900                             (found_rating == 1 && element < found_element)))
3901           {
3902             start_x = x + xx - 1;
3903             start_y = y + yy - 1;
3904
3905             found_rating = 1;
3906             found_element = element;
3907           }
3908         }
3909       }
3910     }
3911
3912     scroll_x = SCROLL_POSITION_X(start_x);
3913     scroll_y = SCROLL_POSITION_Y(start_y);
3914   }
3915   else
3916   {
3917     scroll_x = SCROLL_POSITION_X(local_player->jx);
3918     scroll_y = SCROLL_POSITION_Y(local_player->jy);
3919   }
3920
3921   /* !!! FIX THIS (START) !!! */
3922   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3923   {
3924     InitGameEngine_EM();
3925   }
3926   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3927   {
3928     InitGameEngine_SP();
3929   }
3930   else
3931   {
3932     DrawLevel(REDRAW_FIELD);
3933     DrawAllPlayers();
3934
3935     /* after drawing the level, correct some elements */
3936     if (game.timegate_time_left == 0)
3937       CloseAllOpenTimegates();
3938   }
3939
3940   /* blit playfield from scroll buffer to normal back buffer for fading in */
3941   BlitScreenToBitmap(backbuffer);
3942   /* !!! FIX THIS (END) !!! */
3943
3944   DrawMaskedBorder(fade_mask);
3945
3946   FadeIn(fade_mask);
3947
3948 #if 1
3949   // full screen redraw is required at this point in the following cases:
3950   // - special editor door undrawn when game was started from level editor
3951   // - drawing area (playfield) was changed and has to be removed completely
3952   redraw_mask = REDRAW_ALL;
3953   BackToFront();
3954 #endif
3955
3956   if (!game.restart_level)
3957   {
3958     /* copy default game door content to main double buffer */
3959
3960     /* !!! CHECK AGAIN !!! */
3961     SetPanelBackground();
3962     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3963     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3964   }
3965
3966   SetPanelBackground();
3967   SetDrawBackgroundMask(REDRAW_DOOR_1);
3968
3969   UpdateAndDisplayGameControlValues();
3970
3971   if (!game.restart_level)
3972   {
3973     UnmapGameButtons();
3974     UnmapTapeButtons();
3975
3976     FreeGameButtons();
3977     CreateGameButtons();
3978
3979     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3980     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3981     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3982
3983     MapGameButtons();
3984     MapTapeButtons();
3985
3986     /* copy actual game door content to door double buffer for OpenDoor() */
3987     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3988
3989     OpenDoor(DOOR_OPEN_ALL);
3990
3991     PlaySound(SND_GAME_STARTING);
3992
3993     if (setup.sound_music)
3994       PlayLevelMusic();
3995
3996     KeyboardAutoRepeatOffUnlessAutoplay();
3997
3998 #if DEBUG_INIT_PLAYER
3999     if (options.debug)
4000     {
4001       printf("Player status (final):\n");
4002
4003       for (i = 0; i < MAX_PLAYERS; i++)
4004       {
4005         struct PlayerInfo *player = &stored_player[i];
4006
4007         printf("- player %d: present == %d, connected == %d, active == %d",
4008                i + 1,
4009                player->present,
4010                player->connected,
4011                player->active);
4012
4013         if (local_player == player)
4014           printf(" (local player)");
4015
4016         printf("\n");
4017       }
4018     }
4019 #endif
4020   }
4021
4022   UnmapAllGadgets();
4023
4024   MapGameButtons();
4025   MapTapeButtons();
4026
4027   if (!game.restart_level && !tape.playing)
4028   {
4029     LevelStats_incPlayed(level_nr);
4030
4031     SaveLevelSetup_SeriesInfo();
4032   }
4033
4034   game.restart_level = FALSE;
4035
4036   SaveEngineSnapshotToListInitial();
4037 }
4038
4039 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4040                         int actual_player_x, int actual_player_y)
4041 {
4042   /* this is used for non-R'n'D game engines to update certain engine values */
4043
4044   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4045   {
4046     actual_player_x = correctLevelPosX_EM(actual_player_x);
4047     actual_player_y = correctLevelPosY_EM(actual_player_y);
4048   }
4049
4050   /* needed to determine if sounds are played within the visible screen area */
4051   scroll_x = actual_scroll_x;
4052   scroll_y = actual_scroll_y;
4053
4054   /* needed to get player position for "follow finger" playing input method */
4055   local_player->jx = actual_player_x;
4056   local_player->jy = actual_player_y;
4057 }
4058
4059 void InitMovDir(int x, int y)
4060 {
4061   int i, element = Feld[x][y];
4062   static int xy[4][2] =
4063   {
4064     {  0, +1 },
4065     { +1,  0 },
4066     {  0, -1 },
4067     { -1,  0 }
4068   };
4069   static int direction[3][4] =
4070   {
4071     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4072     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4073     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4074   };
4075
4076   switch (element)
4077   {
4078     case EL_BUG_RIGHT:
4079     case EL_BUG_UP:
4080     case EL_BUG_LEFT:
4081     case EL_BUG_DOWN:
4082       Feld[x][y] = EL_BUG;
4083       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4084       break;
4085
4086     case EL_SPACESHIP_RIGHT:
4087     case EL_SPACESHIP_UP:
4088     case EL_SPACESHIP_LEFT:
4089     case EL_SPACESHIP_DOWN:
4090       Feld[x][y] = EL_SPACESHIP;
4091       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4092       break;
4093
4094     case EL_BD_BUTTERFLY_RIGHT:
4095     case EL_BD_BUTTERFLY_UP:
4096     case EL_BD_BUTTERFLY_LEFT:
4097     case EL_BD_BUTTERFLY_DOWN:
4098       Feld[x][y] = EL_BD_BUTTERFLY;
4099       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4100       break;
4101
4102     case EL_BD_FIREFLY_RIGHT:
4103     case EL_BD_FIREFLY_UP:
4104     case EL_BD_FIREFLY_LEFT:
4105     case EL_BD_FIREFLY_DOWN:
4106       Feld[x][y] = EL_BD_FIREFLY;
4107       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4108       break;
4109
4110     case EL_PACMAN_RIGHT:
4111     case EL_PACMAN_UP:
4112     case EL_PACMAN_LEFT:
4113     case EL_PACMAN_DOWN:
4114       Feld[x][y] = EL_PACMAN;
4115       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4116       break;
4117
4118     case EL_YAMYAM_LEFT:
4119     case EL_YAMYAM_RIGHT:
4120     case EL_YAMYAM_UP:
4121     case EL_YAMYAM_DOWN:
4122       Feld[x][y] = EL_YAMYAM;
4123       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4124       break;
4125
4126     case EL_SP_SNIKSNAK:
4127       MovDir[x][y] = MV_UP;
4128       break;
4129
4130     case EL_SP_ELECTRON:
4131       MovDir[x][y] = MV_LEFT;
4132       break;
4133
4134     case EL_MOLE_LEFT:
4135     case EL_MOLE_RIGHT:
4136     case EL_MOLE_UP:
4137     case EL_MOLE_DOWN:
4138       Feld[x][y] = EL_MOLE;
4139       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4140       break;
4141
4142     default:
4143       if (IS_CUSTOM_ELEMENT(element))
4144       {
4145         struct ElementInfo *ei = &element_info[element];
4146         int move_direction_initial = ei->move_direction_initial;
4147         int move_pattern = ei->move_pattern;
4148
4149         if (move_direction_initial == MV_START_PREVIOUS)
4150         {
4151           if (MovDir[x][y] != MV_NONE)
4152             return;
4153
4154           move_direction_initial = MV_START_AUTOMATIC;
4155         }
4156
4157         if (move_direction_initial == MV_START_RANDOM)
4158           MovDir[x][y] = 1 << RND(4);
4159         else if (move_direction_initial & MV_ANY_DIRECTION)
4160           MovDir[x][y] = move_direction_initial;
4161         else if (move_pattern == MV_ALL_DIRECTIONS ||
4162                  move_pattern == MV_TURNING_LEFT ||
4163                  move_pattern == MV_TURNING_RIGHT ||
4164                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4165                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4166                  move_pattern == MV_TURNING_RANDOM)
4167           MovDir[x][y] = 1 << RND(4);
4168         else if (move_pattern == MV_HORIZONTAL)
4169           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4170         else if (move_pattern == MV_VERTICAL)
4171           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4172         else if (move_pattern & MV_ANY_DIRECTION)
4173           MovDir[x][y] = element_info[element].move_pattern;
4174         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4175                  move_pattern == MV_ALONG_RIGHT_SIDE)
4176         {
4177           /* use random direction as default start direction */
4178           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4179             MovDir[x][y] = 1 << RND(4);
4180
4181           for (i = 0; i < NUM_DIRECTIONS; i++)
4182           {
4183             int x1 = x + xy[i][0];
4184             int y1 = y + xy[i][1];
4185
4186             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4187             {
4188               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4189                 MovDir[x][y] = direction[0][i];
4190               else
4191                 MovDir[x][y] = direction[1][i];
4192
4193               break;
4194             }
4195           }
4196         }                
4197       }
4198       else
4199       {
4200         MovDir[x][y] = 1 << RND(4);
4201
4202         if (element != EL_BUG &&
4203             element != EL_SPACESHIP &&
4204             element != EL_BD_BUTTERFLY &&
4205             element != EL_BD_FIREFLY)
4206           break;
4207
4208         for (i = 0; i < NUM_DIRECTIONS; i++)
4209         {
4210           int x1 = x + xy[i][0];
4211           int y1 = y + xy[i][1];
4212
4213           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4214           {
4215             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4216             {
4217               MovDir[x][y] = direction[0][i];
4218               break;
4219             }
4220             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4221                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4222             {
4223               MovDir[x][y] = direction[1][i];
4224               break;
4225             }
4226           }
4227         }
4228       }
4229       break;
4230   }
4231
4232   GfxDir[x][y] = MovDir[x][y];
4233 }
4234
4235 void InitAmoebaNr(int x, int y)
4236 {
4237   int i;
4238   int group_nr = AmoebeNachbarNr(x, y);
4239
4240   if (group_nr == 0)
4241   {
4242     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4243     {
4244       if (AmoebaCnt[i] == 0)
4245       {
4246         group_nr = i;
4247         break;
4248       }
4249     }
4250   }
4251
4252   AmoebaNr[x][y] = group_nr;
4253   AmoebaCnt[group_nr]++;
4254   AmoebaCnt2[group_nr]++;
4255 }
4256
4257 static void PlayerWins(struct PlayerInfo *player)
4258 {
4259   player->LevelSolved = TRUE;
4260   player->GameOver = TRUE;
4261
4262   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4263                          level.native_em_level->lev->score : player->score);
4264
4265   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4266                                       TimeLeft);
4267   player->LevelSolved_CountingScore = player->score_final;
4268 }
4269
4270 void GameWon()
4271 {
4272   static int time, time_final;
4273   static int score, score_final;
4274   static int game_over_delay_1 = 0;
4275   static int game_over_delay_2 = 0;
4276   int game_over_delay_value_1 = 50;
4277   int game_over_delay_value_2 = 50;
4278
4279   if (!local_player->LevelSolved_GameWon)
4280   {
4281     int i;
4282
4283     /* do not start end game actions before the player stops moving (to exit) */
4284     if (local_player->MovPos)
4285       return;
4286
4287     local_player->LevelSolved_GameWon = TRUE;
4288     local_player->LevelSolved_SaveTape = tape.recording;
4289     local_player->LevelSolved_SaveScore = !tape.playing;
4290
4291     if (!tape.playing)
4292     {
4293       LevelStats_incSolved(level_nr);
4294
4295       SaveLevelSetup_SeriesInfo();
4296     }
4297
4298     if (tape.auto_play)         /* tape might already be stopped here */
4299       tape.auto_play_level_solved = TRUE;
4300
4301     TapeStop();
4302
4303     game_over_delay_1 = game_over_delay_value_1;
4304     game_over_delay_2 = game_over_delay_value_2;
4305
4306     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4307     score = score_final = local_player->score_final;
4308
4309     if (TimeLeft > 0)
4310     {
4311       time_final = 0;
4312       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4313     }
4314     else if (game.no_time_limit && TimePlayed < 999)
4315     {
4316       time_final = 999;
4317       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4318     }
4319
4320     local_player->score_final = score_final;
4321
4322     if (level_editor_test_game)
4323     {
4324       time = time_final;
4325       score = score_final;
4326
4327       local_player->LevelSolved_CountingTime = time;
4328       local_player->LevelSolved_CountingScore = score;
4329
4330       game_panel_controls[GAME_PANEL_TIME].value = time;
4331       game_panel_controls[GAME_PANEL_SCORE].value = score;
4332
4333       DisplayGameControlValues();
4334     }
4335
4336     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4337     {
4338       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4339       {
4340         /* close exit door after last player */
4341         if ((AllPlayersGone &&
4342              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4343               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4344               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4345             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4346             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4347         {
4348           int element = Feld[ExitX][ExitY];
4349
4350           Feld[ExitX][ExitY] =
4351             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4352              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4353              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4354              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4355              EL_EM_STEEL_EXIT_CLOSING);
4356
4357           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4358         }
4359
4360         /* player disappears */
4361         DrawLevelField(ExitX, ExitY);
4362       }
4363
4364       for (i = 0; i < MAX_PLAYERS; i++)
4365       {
4366         struct PlayerInfo *player = &stored_player[i];
4367
4368         if (player->present)
4369         {
4370           RemovePlayer(player);
4371
4372           /* player disappears */
4373           DrawLevelField(player->jx, player->jy);
4374         }
4375       }
4376     }
4377
4378     PlaySound(SND_GAME_WINNING);
4379   }
4380
4381   if (game_over_delay_1 > 0)
4382   {
4383     game_over_delay_1--;
4384
4385     return;
4386   }
4387
4388   if (time != time_final)
4389   {
4390     int time_to_go = ABS(time_final - time);
4391     int time_count_dir = (time < time_final ? +1 : -1);
4392     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4393
4394     time  += time_count_steps * time_count_dir;
4395     score += time_count_steps * level.score[SC_TIME_BONUS];
4396
4397     local_player->LevelSolved_CountingTime = time;
4398     local_player->LevelSolved_CountingScore = score;
4399
4400     game_panel_controls[GAME_PANEL_TIME].value = time;
4401     game_panel_controls[GAME_PANEL_SCORE].value = score;
4402
4403     DisplayGameControlValues();
4404
4405     if (time == time_final)
4406       StopSound(SND_GAME_LEVELTIME_BONUS);
4407     else if (setup.sound_loops)
4408       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4409     else
4410       PlaySound(SND_GAME_LEVELTIME_BONUS);
4411
4412     return;
4413   }
4414
4415   local_player->LevelSolved_PanelOff = TRUE;
4416
4417   if (game_over_delay_2 > 0)
4418   {
4419     game_over_delay_2--;
4420
4421     return;
4422   }
4423
4424   GameEnd();
4425 }
4426
4427 void GameEnd()
4428 {
4429   int hi_pos;
4430   boolean raise_level = FALSE;
4431
4432   local_player->LevelSolved_GameEnd = TRUE;
4433
4434   if (!global.use_envelope_request)
4435     CloseDoor(DOOR_CLOSE_1);
4436
4437   if (local_player->LevelSolved_SaveTape)
4438   {
4439     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4440   }
4441
4442   CloseDoor(DOOR_CLOSE_ALL);
4443
4444   if (level_editor_test_game)
4445   {
4446     SetGameStatus(GAME_MODE_MAIN);
4447
4448     DrawMainMenu();
4449
4450     return;
4451   }
4452
4453   if (!local_player->LevelSolved_SaveScore)
4454   {
4455     SetGameStatus(GAME_MODE_MAIN);
4456
4457     DrawMainMenu();
4458
4459     return;
4460   }
4461
4462   if (level_nr == leveldir_current->handicap_level)
4463   {
4464     leveldir_current->handicap_level++;
4465
4466     SaveLevelSetup_SeriesInfo();
4467   }
4468
4469   if (setup.increment_levels &&
4470       level_nr < leveldir_current->last_level)
4471     raise_level = TRUE;                 /* advance to next level */
4472
4473   if ((hi_pos = NewHiScore()) >= 0) 
4474   {
4475     SetGameStatus(GAME_MODE_SCORES);
4476
4477     DrawHallOfFame(hi_pos);
4478
4479     if (raise_level)
4480     {
4481       level_nr++;
4482       TapeErase();
4483     }
4484   }
4485   else
4486   {
4487     SetGameStatus(GAME_MODE_MAIN);
4488
4489     if (raise_level)
4490     {
4491       level_nr++;
4492       TapeErase();
4493     }
4494
4495     DrawMainMenu();
4496   }
4497 }
4498
4499 int NewHiScore()
4500 {
4501   int k, l;
4502   int position = -1;
4503   boolean one_score_entry_per_name = !program.many_scores_per_name;
4504
4505   LoadScore(level_nr);
4506
4507   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4508       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4509     return -1;
4510
4511   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4512   {
4513     if (local_player->score_final > highscore[k].Score)
4514     {
4515       /* player has made it to the hall of fame */
4516
4517       if (k < MAX_SCORE_ENTRIES - 1)
4518       {
4519         int m = MAX_SCORE_ENTRIES - 1;
4520
4521         if (one_score_entry_per_name)
4522         {
4523           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4524             if (strEqual(setup.player_name, highscore[l].Name))
4525               m = l;
4526
4527           if (m == k)   /* player's new highscore overwrites his old one */
4528             goto put_into_list;
4529         }
4530
4531         for (l = m; l > k; l--)
4532         {
4533           strcpy(highscore[l].Name, highscore[l - 1].Name);
4534           highscore[l].Score = highscore[l - 1].Score;
4535         }
4536       }
4537
4538       put_into_list:
4539
4540       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4541       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4542       highscore[k].Score = local_player->score_final; 
4543       position = k;
4544
4545       break;
4546     }
4547     else if (one_score_entry_per_name &&
4548              !strncmp(setup.player_name, highscore[k].Name,
4549                       MAX_PLAYER_NAME_LEN))
4550       break;    /* player already there with a higher score */
4551   }
4552
4553   if (position >= 0) 
4554     SaveScore(level_nr);
4555
4556   return position;
4557 }
4558
4559 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4560 {
4561   int element = Feld[x][y];
4562   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4563   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4564   int horiz_move = (dx != 0);
4565   int sign = (horiz_move ? dx : dy);
4566   int step = sign * element_info[element].move_stepsize;
4567
4568   /* special values for move stepsize for spring and things on conveyor belt */
4569   if (horiz_move)
4570   {
4571     if (CAN_FALL(element) &&
4572         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4573       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4574     else if (element == EL_SPRING)
4575       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4576   }
4577
4578   return step;
4579 }
4580
4581 inline static int getElementMoveStepsize(int x, int y)
4582 {
4583   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4584 }
4585
4586 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4587 {
4588   if (player->GfxAction != action || player->GfxDir != dir)
4589   {
4590     player->GfxAction = action;
4591     player->GfxDir = dir;
4592     player->Frame = 0;
4593     player->StepFrame = 0;
4594   }
4595 }
4596
4597 static void ResetGfxFrame(int x, int y)
4598 {
4599   int element = Feld[x][y];
4600   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4601
4602   if (graphic_info[graphic].anim_global_sync)
4603     GfxFrame[x][y] = FrameCounter;
4604   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4605     GfxFrame[x][y] = CustomValue[x][y];
4606   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4607     GfxFrame[x][y] = element_info[element].collect_score;
4608   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4609     GfxFrame[x][y] = ChangeDelay[x][y];
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);
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 = SCROLL_POSITION_X(x);
4932     new_scroll_y = SCROLL_POSITION_Y(y);
4933   }
4934   else
4935   {
4936     /* relocation _without_ centering of screen */
4937
4938     int center_scroll_x = SCROLL_POSITION_X(old_x);
4939     int center_scroll_y = SCROLL_POSITION_Y(old_y);
4940     int offset_x = x + (scroll_x - center_scroll_x);
4941     int offset_y = y + (scroll_y - center_scroll_y);
4942
4943     /* for new screen position, apply previous offset to center position */
4944     new_scroll_x = SCROLL_POSITION_X(offset_x);
4945     new_scroll_y = SCROLL_POSITION_Y(offset_y);
4946   }
4947
4948   if (quick_relocation)
4949   {
4950     /* case 2: quick relocation (redraw without visible scrolling) */
4951
4952     scroll_x = new_scroll_x;
4953     scroll_y = new_scroll_y;
4954
4955     RedrawPlayfield();
4956
4957     return;
4958   }
4959
4960   /* case 3: visible relocation (with scrolling to new position) */
4961
4962   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4963
4964   SetVideoFrameDelay(wait_delay_value);
4965
4966   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4967   {
4968     int dx = 0, dy = 0;
4969     int fx = FX, fy = FY;
4970
4971     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4972     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4973
4974     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4975       break;
4976
4977     scroll_x -= dx;
4978     scroll_y -= dy;
4979
4980     fx += dx * TILEX / 2;
4981     fy += dy * TILEY / 2;
4982
4983     ScrollLevel(dx, dy);
4984     DrawAllPlayers();
4985
4986     /* scroll in two steps of half tile size to make things smoother */
4987     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4988
4989     /* scroll second step to align at full tile size */
4990     BlitScreenToBitmap(window);
4991   }
4992
4993   DrawAllPlayers();
4994   BackToFront();
4995
4996   SetVideoFrameDelay(frame_delay_value_old);
4997 }
4998
4999 void RelocatePlayer(int jx, int jy, int el_player_raw)
5000 {
5001   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5002   int player_nr = GET_PLAYER_NR(el_player);
5003   struct PlayerInfo *player = &stored_player[player_nr];
5004   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5005   boolean no_delay = (tape.warp_forward);
5006   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5007   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5008   int old_jx = player->jx;
5009   int old_jy = player->jy;
5010   int old_element = Feld[old_jx][old_jy];
5011   int element = Feld[jx][jy];
5012   boolean player_relocated = (old_jx != jx || old_jy != jy);
5013
5014   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5015   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5016   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5017   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5018   int leave_side_horiz = move_dir_horiz;
5019   int leave_side_vert  = move_dir_vert;
5020   int enter_side = enter_side_horiz | enter_side_vert;
5021   int leave_side = leave_side_horiz | leave_side_vert;
5022
5023   if (player->GameOver)         /* do not reanimate dead player */
5024     return;
5025
5026   if (!player_relocated)        /* no need to relocate the player */
5027     return;
5028
5029   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5030   {
5031     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5032     DrawLevelField(jx, jy);
5033   }
5034
5035   if (player->present)
5036   {
5037     while (player->MovPos)
5038     {
5039       ScrollPlayer(player, SCROLL_GO_ON);
5040       ScrollScreen(NULL, SCROLL_GO_ON);
5041
5042       AdvanceFrameAndPlayerCounters(player->index_nr);
5043
5044       DrawPlayer(player);
5045
5046       BackToFront_WithFrameDelay(wait_delay_value);
5047     }
5048
5049     DrawPlayer(player);         /* needed here only to cleanup last field */
5050     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5051
5052     player->is_moving = FALSE;
5053   }
5054
5055   if (IS_CUSTOM_ELEMENT(old_element))
5056     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5057                                CE_LEFT_BY_PLAYER,
5058                                player->index_bit, leave_side);
5059
5060   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5061                                       CE_PLAYER_LEAVES_X,
5062                                       player->index_bit, leave_side);
5063
5064   Feld[jx][jy] = el_player;
5065   InitPlayerField(jx, jy, el_player, TRUE);
5066
5067   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5068      possible that the relocation target field did not contain a player element,
5069      but a walkable element, to which the new player was relocated -- in this
5070      case, restore that (already initialized!) element on the player field */
5071   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5072   {
5073     Feld[jx][jy] = element;     /* restore previously existing element */
5074   }
5075
5076   /* only visually relocate centered player */
5077   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5078                      FALSE, level.instant_relocation);
5079
5080   TestIfPlayerTouchesBadThing(jx, jy);
5081   TestIfPlayerTouchesCustomElement(jx, jy);
5082
5083   if (IS_CUSTOM_ELEMENT(element))
5084     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5085                                player->index_bit, enter_side);
5086
5087   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5088                                       player->index_bit, enter_side);
5089
5090   if (player->is_switching)
5091   {
5092     /* ensure that relocation while still switching an element does not cause
5093        a new element to be treated as also switched directly after relocation
5094        (this is important for teleporter switches that teleport the player to
5095        a place where another teleporter switch is in the same direction, which
5096        would then incorrectly be treated as immediately switched before the
5097        direction key that caused the switch was released) */
5098
5099     player->switch_x += jx - old_jx;
5100     player->switch_y += jy - old_jy;
5101   }
5102 }
5103
5104 void Explode(int ex, int ey, int phase, int mode)
5105 {
5106   int x, y;
5107   int last_phase;
5108   int border_element;
5109
5110   /* !!! eliminate this variable !!! */
5111   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5112
5113   if (game.explosions_delayed)
5114   {
5115     ExplodeField[ex][ey] = mode;
5116     return;
5117   }
5118
5119   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5120   {
5121     int center_element = Feld[ex][ey];
5122     int artwork_element, explosion_element;     /* set these values later */
5123
5124     /* remove things displayed in background while burning dynamite */
5125     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5126       Back[ex][ey] = 0;
5127
5128     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5129     {
5130       /* put moving element to center field (and let it explode there) */
5131       center_element = MovingOrBlocked2Element(ex, ey);
5132       RemoveMovingField(ex, ey);
5133       Feld[ex][ey] = center_element;
5134     }
5135
5136     /* now "center_element" is finally determined -- set related values now */
5137     artwork_element = center_element;           /* for custom player artwork */
5138     explosion_element = center_element;         /* for custom player artwork */
5139
5140     if (IS_PLAYER(ex, ey))
5141     {
5142       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5143
5144       artwork_element = stored_player[player_nr].artwork_element;
5145
5146       if (level.use_explosion_element[player_nr])
5147       {
5148         explosion_element = level.explosion_element[player_nr];
5149         artwork_element = explosion_element;
5150       }
5151     }
5152
5153     if (mode == EX_TYPE_NORMAL ||
5154         mode == EX_TYPE_CENTER ||
5155         mode == EX_TYPE_CROSS)
5156       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5157
5158     last_phase = element_info[explosion_element].explosion_delay + 1;
5159
5160     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5161     {
5162       int xx = x - ex + 1;
5163       int yy = y - ey + 1;
5164       int element;
5165
5166       if (!IN_LEV_FIELD(x, y) ||
5167           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5168           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5169         continue;
5170
5171       element = Feld[x][y];
5172
5173       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5174       {
5175         element = MovingOrBlocked2Element(x, y);
5176
5177         if (!IS_EXPLOSION_PROOF(element))
5178           RemoveMovingField(x, y);
5179       }
5180
5181       /* indestructible elements can only explode in center (but not flames) */
5182       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5183                                            mode == EX_TYPE_BORDER)) ||
5184           element == EL_FLAMES)
5185         continue;
5186
5187       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5188          behaviour, for example when touching a yamyam that explodes to rocks
5189          with active deadly shield, a rock is created under the player !!! */
5190       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5191 #if 0
5192       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5193           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5194            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5195 #else
5196       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5197 #endif
5198       {
5199         if (IS_ACTIVE_BOMB(element))
5200         {
5201           /* re-activate things under the bomb like gate or penguin */
5202           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5203           Back[x][y] = 0;
5204         }
5205
5206         continue;
5207       }
5208
5209       /* save walkable background elements while explosion on same tile */
5210       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5211           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5212         Back[x][y] = element;
5213
5214       /* ignite explodable elements reached by other explosion */
5215       if (element == EL_EXPLOSION)
5216         element = Store2[x][y];
5217
5218       if (AmoebaNr[x][y] &&
5219           (element == EL_AMOEBA_FULL ||
5220            element == EL_BD_AMOEBA ||
5221            element == EL_AMOEBA_GROWING))
5222       {
5223         AmoebaCnt[AmoebaNr[x][y]]--;
5224         AmoebaCnt2[AmoebaNr[x][y]]--;
5225       }
5226
5227       RemoveField(x, y);
5228
5229       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5230       {
5231         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5232
5233         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5234
5235         if (PLAYERINFO(ex, ey)->use_murphy)
5236           Store[x][y] = EL_EMPTY;
5237       }
5238
5239       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5240          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5241       else if (ELEM_IS_PLAYER(center_element))
5242         Store[x][y] = EL_EMPTY;
5243       else if (center_element == EL_YAMYAM)
5244         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5245       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5246         Store[x][y] = element_info[center_element].content.e[xx][yy];
5247 #if 1
5248       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5249          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5250          otherwise) -- FIX THIS !!! */
5251       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5252         Store[x][y] = element_info[element].content.e[1][1];
5253 #else
5254       else if (!CAN_EXPLODE(element))
5255         Store[x][y] = element_info[element].content.e[1][1];
5256 #endif
5257       else
5258         Store[x][y] = EL_EMPTY;
5259
5260       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5261           center_element == EL_AMOEBA_TO_DIAMOND)
5262         Store2[x][y] = element;
5263
5264       Feld[x][y] = EL_EXPLOSION;
5265       GfxElement[x][y] = artwork_element;
5266
5267       ExplodePhase[x][y] = 1;
5268       ExplodeDelay[x][y] = last_phase;
5269
5270       Stop[x][y] = TRUE;
5271     }
5272
5273     if (center_element == EL_YAMYAM)
5274       game.yamyam_content_nr =
5275         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5276
5277     return;
5278   }
5279
5280   if (Stop[ex][ey])
5281     return;
5282
5283   x = ex;
5284   y = ey;
5285
5286   if (phase == 1)
5287     GfxFrame[x][y] = 0;         /* restart explosion animation */
5288
5289   last_phase = ExplodeDelay[x][y];
5290
5291   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5292
5293   /* this can happen if the player leaves an explosion just in time */
5294   if (GfxElement[x][y] == EL_UNDEFINED)
5295     GfxElement[x][y] = EL_EMPTY;
5296
5297   border_element = Store2[x][y];
5298   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5299     border_element = StorePlayer[x][y];
5300
5301   if (phase == element_info[border_element].ignition_delay ||
5302       phase == last_phase)
5303   {
5304     boolean border_explosion = FALSE;
5305
5306     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5307         !PLAYER_EXPLOSION_PROTECTED(x, y))
5308     {
5309       KillPlayerUnlessExplosionProtected(x, y);
5310       border_explosion = TRUE;
5311     }
5312     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5313     {
5314       Feld[x][y] = Store2[x][y];
5315       Store2[x][y] = 0;
5316       Bang(x, y);
5317       border_explosion = TRUE;
5318     }
5319     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5320     {
5321       AmoebeUmwandeln(x, y);
5322       Store2[x][y] = 0;
5323       border_explosion = TRUE;
5324     }
5325
5326     /* if an element just explodes due to another explosion (chain-reaction),
5327        do not immediately end the new explosion when it was the last frame of
5328        the explosion (as it would be done in the following "if"-statement!) */
5329     if (border_explosion && phase == last_phase)
5330       return;
5331   }
5332
5333   if (phase == last_phase)
5334   {
5335     int element;
5336
5337     element = Feld[x][y] = Store[x][y];
5338     Store[x][y] = Store2[x][y] = 0;
5339     GfxElement[x][y] = EL_UNDEFINED;
5340
5341     /* player can escape from explosions and might therefore be still alive */
5342     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5343         element <= EL_PLAYER_IS_EXPLODING_4)
5344     {
5345       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5346       int explosion_element = EL_PLAYER_1 + player_nr;
5347       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5348       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5349
5350       if (level.use_explosion_element[player_nr])
5351         explosion_element = level.explosion_element[player_nr];
5352
5353       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5354                     element_info[explosion_element].content.e[xx][yy]);
5355     }
5356
5357     /* restore probably existing indestructible background element */
5358     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5359       element = Feld[x][y] = Back[x][y];
5360     Back[x][y] = 0;
5361
5362     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5363     GfxDir[x][y] = MV_NONE;
5364     ChangeDelay[x][y] = 0;
5365     ChangePage[x][y] = -1;
5366
5367     CustomValue[x][y] = 0;
5368
5369     InitField_WithBug2(x, y, FALSE);
5370
5371     TEST_DrawLevelField(x, y);
5372
5373     TestIfElementTouchesCustomElement(x, y);
5374
5375     if (GFX_CRUMBLED(element))
5376       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5377
5378     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5379       StorePlayer[x][y] = 0;
5380
5381     if (ELEM_IS_PLAYER(element))
5382       RelocatePlayer(x, y, element);
5383   }
5384   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5385   {
5386     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5387     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5388
5389     if (phase == delay)
5390       TEST_DrawLevelFieldCrumbled(x, y);
5391
5392     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5393     {
5394       DrawLevelElement(x, y, Back[x][y]);
5395       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5396     }
5397     else if (IS_WALKABLE_UNDER(Back[x][y]))
5398     {
5399       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5400       DrawLevelElementThruMask(x, y, Back[x][y]);
5401     }
5402     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5403       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5404   }
5405 }
5406
5407 void DynaExplode(int ex, int ey)
5408 {
5409   int i, j;
5410   int dynabomb_element = Feld[ex][ey];
5411   int dynabomb_size = 1;
5412   boolean dynabomb_xl = FALSE;
5413   struct PlayerInfo *player;
5414   static int xy[4][2] =
5415   {
5416     { 0, -1 },
5417     { -1, 0 },
5418     { +1, 0 },
5419     { 0, +1 }
5420   };
5421
5422   if (IS_ACTIVE_BOMB(dynabomb_element))
5423   {
5424     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5425     dynabomb_size = player->dynabomb_size;
5426     dynabomb_xl = player->dynabomb_xl;
5427     player->dynabombs_left++;
5428   }
5429
5430   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5431
5432   for (i = 0; i < NUM_DIRECTIONS; i++)
5433   {
5434     for (j = 1; j <= dynabomb_size; j++)
5435     {
5436       int x = ex + j * xy[i][0];
5437       int y = ey + j * xy[i][1];
5438       int element;
5439
5440       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5441         break;
5442
5443       element = Feld[x][y];
5444
5445       /* do not restart explosions of fields with active bombs */
5446       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5447         continue;
5448
5449       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5450
5451       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5452           !IS_DIGGABLE(element) && !dynabomb_xl)
5453         break;
5454     }
5455   }
5456 }
5457
5458 void Bang(int x, int y)
5459 {
5460   int element = MovingOrBlocked2Element(x, y);
5461   int explosion_type = EX_TYPE_NORMAL;
5462
5463   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5464   {
5465     struct PlayerInfo *player = PLAYERINFO(x, y);
5466
5467     element = Feld[x][y] = player->initial_element;
5468
5469     if (level.use_explosion_element[player->index_nr])
5470     {
5471       int explosion_element = level.explosion_element[player->index_nr];
5472
5473       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5474         explosion_type = EX_TYPE_CROSS;
5475       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5476         explosion_type = EX_TYPE_CENTER;
5477     }
5478   }
5479
5480   switch (element)
5481   {
5482     case EL_BUG:
5483     case EL_SPACESHIP:
5484     case EL_BD_BUTTERFLY:
5485     case EL_BD_FIREFLY:
5486     case EL_YAMYAM:
5487     case EL_DARK_YAMYAM:
5488     case EL_ROBOT:
5489     case EL_PACMAN:
5490     case EL_MOLE:
5491       RaiseScoreElement(element);
5492       break;
5493
5494     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5495     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5496     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5497     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5498     case EL_DYNABOMB_INCREASE_NUMBER:
5499     case EL_DYNABOMB_INCREASE_SIZE:
5500     case EL_DYNABOMB_INCREASE_POWER:
5501       explosion_type = EX_TYPE_DYNA;
5502       break;
5503
5504     case EL_DC_LANDMINE:
5505       explosion_type = EX_TYPE_CENTER;
5506       break;
5507
5508     case EL_PENGUIN:
5509     case EL_LAMP:
5510     case EL_LAMP_ACTIVE:
5511     case EL_AMOEBA_TO_DIAMOND:
5512       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5513         explosion_type = EX_TYPE_CENTER;
5514       break;
5515
5516     default:
5517       if (element_info[element].explosion_type == EXPLODES_CROSS)
5518         explosion_type = EX_TYPE_CROSS;
5519       else if (element_info[element].explosion_type == EXPLODES_1X1)
5520         explosion_type = EX_TYPE_CENTER;
5521       break;
5522   }
5523
5524   if (explosion_type == EX_TYPE_DYNA)
5525     DynaExplode(x, y);
5526   else
5527     Explode(x, y, EX_PHASE_START, explosion_type);
5528
5529   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5530 }
5531
5532 void SplashAcid(int x, int y)
5533 {
5534   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5535       (!IN_LEV_FIELD(x - 1, y - 2) ||
5536        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5537     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5538
5539   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5540       (!IN_LEV_FIELD(x + 1, y - 2) ||
5541        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5542     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5543
5544   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5545 }
5546
5547 static void InitBeltMovement()
5548 {
5549   static int belt_base_element[4] =
5550   {
5551     EL_CONVEYOR_BELT_1_LEFT,
5552     EL_CONVEYOR_BELT_2_LEFT,
5553     EL_CONVEYOR_BELT_3_LEFT,
5554     EL_CONVEYOR_BELT_4_LEFT
5555   };
5556   static int belt_base_active_element[4] =
5557   {
5558     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5559     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5560     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5561     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5562   };
5563
5564   int x, y, i, j;
5565
5566   /* set frame order for belt animation graphic according to belt direction */
5567   for (i = 0; i < NUM_BELTS; i++)
5568   {
5569     int belt_nr = i;
5570
5571     for (j = 0; j < NUM_BELT_PARTS; j++)
5572     {
5573       int element = belt_base_active_element[belt_nr] + j;
5574       int graphic_1 = el2img(element);
5575       int graphic_2 = el2panelimg(element);
5576
5577       if (game.belt_dir[i] == MV_LEFT)
5578       {
5579         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5580         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5581       }
5582       else
5583       {
5584         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5585         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5586       }
5587     }
5588   }
5589
5590   SCAN_PLAYFIELD(x, y)
5591   {
5592     int element = Feld[x][y];
5593
5594     for (i = 0; i < NUM_BELTS; i++)
5595     {
5596       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5597       {
5598         int e_belt_nr = getBeltNrFromBeltElement(element);
5599         int belt_nr = i;
5600
5601         if (e_belt_nr == belt_nr)
5602         {
5603           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5604
5605           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5606         }
5607       }
5608     }
5609   }
5610 }
5611
5612 static void ToggleBeltSwitch(int x, int y)
5613 {
5614   static int belt_base_element[4] =
5615   {
5616     EL_CONVEYOR_BELT_1_LEFT,
5617     EL_CONVEYOR_BELT_2_LEFT,
5618     EL_CONVEYOR_BELT_3_LEFT,
5619     EL_CONVEYOR_BELT_4_LEFT
5620   };
5621   static int belt_base_active_element[4] =
5622   {
5623     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5624     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5625     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5626     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5627   };
5628   static int belt_base_switch_element[4] =
5629   {
5630     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5631     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5632     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5633     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5634   };
5635   static int belt_move_dir[4] =
5636   {
5637     MV_LEFT,
5638     MV_NONE,
5639     MV_RIGHT,
5640     MV_NONE,
5641   };
5642
5643   int element = Feld[x][y];
5644   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5645   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5646   int belt_dir = belt_move_dir[belt_dir_nr];
5647   int xx, yy, i;
5648
5649   if (!IS_BELT_SWITCH(element))
5650     return;
5651
5652   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5653   game.belt_dir[belt_nr] = belt_dir;
5654
5655   if (belt_dir_nr == 3)
5656     belt_dir_nr = 1;
5657
5658   /* set frame order for belt animation graphic according to belt direction */
5659   for (i = 0; i < NUM_BELT_PARTS; i++)
5660   {
5661     int element = belt_base_active_element[belt_nr] + i;
5662     int graphic_1 = el2img(element);
5663     int graphic_2 = el2panelimg(element);
5664
5665     if (belt_dir == MV_LEFT)
5666     {
5667       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5668       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5669     }
5670     else
5671     {
5672       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5673       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5674     }
5675   }
5676
5677   SCAN_PLAYFIELD(xx, yy)
5678   {
5679     int element = Feld[xx][yy];
5680
5681     if (IS_BELT_SWITCH(element))
5682     {
5683       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5684
5685       if (e_belt_nr == belt_nr)
5686       {
5687         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5688         TEST_DrawLevelField(xx, yy);
5689       }
5690     }
5691     else if (IS_BELT(element) && belt_dir != MV_NONE)
5692     {
5693       int e_belt_nr = getBeltNrFromBeltElement(element);
5694
5695       if (e_belt_nr == belt_nr)
5696       {
5697         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5698
5699         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5700         TEST_DrawLevelField(xx, yy);
5701       }
5702     }
5703     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5704     {
5705       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5706
5707       if (e_belt_nr == belt_nr)
5708       {
5709         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5710
5711         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5712         TEST_DrawLevelField(xx, yy);
5713       }
5714     }
5715   }
5716 }
5717
5718 static void ToggleSwitchgateSwitch(int x, int y)
5719 {
5720   int xx, yy;
5721
5722   game.switchgate_pos = !game.switchgate_pos;
5723
5724   SCAN_PLAYFIELD(xx, yy)
5725   {
5726     int element = Feld[xx][yy];
5727
5728     if (element == EL_SWITCHGATE_SWITCH_UP)
5729     {
5730       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5731       TEST_DrawLevelField(xx, yy);
5732     }
5733     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5734     {
5735       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5736       TEST_DrawLevelField(xx, yy);
5737     }
5738     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5739     {
5740       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5741       TEST_DrawLevelField(xx, yy);
5742     }
5743     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5744     {
5745       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5746       TEST_DrawLevelField(xx, yy);
5747     }
5748     else if (element == EL_SWITCHGATE_OPEN ||
5749              element == EL_SWITCHGATE_OPENING)
5750     {
5751       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5752
5753       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5754     }
5755     else if (element == EL_SWITCHGATE_CLOSED ||
5756              element == EL_SWITCHGATE_CLOSING)
5757     {
5758       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5759
5760       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5761     }
5762   }
5763 }
5764
5765 static int getInvisibleActiveFromInvisibleElement(int element)
5766 {
5767   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5768           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5769           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5770           element);
5771 }
5772
5773 static int getInvisibleFromInvisibleActiveElement(int element)
5774 {
5775   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5776           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5777           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5778           element);
5779 }
5780
5781 static void RedrawAllLightSwitchesAndInvisibleElements()
5782 {
5783   int x, y;
5784
5785   SCAN_PLAYFIELD(x, y)
5786   {
5787     int element = Feld[x][y];
5788
5789     if (element == EL_LIGHT_SWITCH &&
5790         game.light_time_left > 0)
5791     {
5792       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5793       TEST_DrawLevelField(x, y);
5794     }
5795     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5796              game.light_time_left == 0)
5797     {
5798       Feld[x][y] = EL_LIGHT_SWITCH;
5799       TEST_DrawLevelField(x, y);
5800     }
5801     else if (element == EL_EMC_DRIPPER &&
5802              game.light_time_left > 0)
5803     {
5804       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5805       TEST_DrawLevelField(x, y);
5806     }
5807     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5808              game.light_time_left == 0)
5809     {
5810       Feld[x][y] = EL_EMC_DRIPPER;
5811       TEST_DrawLevelField(x, y);
5812     }
5813     else if (element == EL_INVISIBLE_STEELWALL ||
5814              element == EL_INVISIBLE_WALL ||
5815              element == EL_INVISIBLE_SAND)
5816     {
5817       if (game.light_time_left > 0)
5818         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5819
5820       TEST_DrawLevelField(x, y);
5821
5822       /* uncrumble neighbour fields, if needed */
5823       if (element == EL_INVISIBLE_SAND)
5824         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5825     }
5826     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5827              element == EL_INVISIBLE_WALL_ACTIVE ||
5828              element == EL_INVISIBLE_SAND_ACTIVE)
5829     {
5830       if (game.light_time_left == 0)
5831         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5832
5833       TEST_DrawLevelField(x, y);
5834
5835       /* re-crumble neighbour fields, if needed */
5836       if (element == EL_INVISIBLE_SAND)
5837         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5838     }
5839   }
5840 }
5841
5842 static void RedrawAllInvisibleElementsForLenses()
5843 {
5844   int x, y;
5845
5846   SCAN_PLAYFIELD(x, y)
5847   {
5848     int element = Feld[x][y];
5849
5850     if (element == EL_EMC_DRIPPER &&
5851         game.lenses_time_left > 0)
5852     {
5853       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5854       TEST_DrawLevelField(x, y);
5855     }
5856     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5857              game.lenses_time_left == 0)
5858     {
5859       Feld[x][y] = EL_EMC_DRIPPER;
5860       TEST_DrawLevelField(x, y);
5861     }
5862     else if (element == EL_INVISIBLE_STEELWALL ||
5863              element == EL_INVISIBLE_WALL ||
5864              element == EL_INVISIBLE_SAND)
5865     {
5866       if (game.lenses_time_left > 0)
5867         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5868
5869       TEST_DrawLevelField(x, y);
5870
5871       /* uncrumble neighbour fields, if needed */
5872       if (element == EL_INVISIBLE_SAND)
5873         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5874     }
5875     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5876              element == EL_INVISIBLE_WALL_ACTIVE ||
5877              element == EL_INVISIBLE_SAND_ACTIVE)
5878     {
5879       if (game.lenses_time_left == 0)
5880         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5881
5882       TEST_DrawLevelField(x, y);
5883
5884       /* re-crumble neighbour fields, if needed */
5885       if (element == EL_INVISIBLE_SAND)
5886         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5887     }
5888   }
5889 }
5890
5891 static void RedrawAllInvisibleElementsForMagnifier()
5892 {
5893   int x, y;
5894
5895   SCAN_PLAYFIELD(x, y)
5896   {
5897     int element = Feld[x][y];
5898
5899     if (element == EL_EMC_FAKE_GRASS &&
5900         game.magnify_time_left > 0)
5901     {
5902       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5903       TEST_DrawLevelField(x, y);
5904     }
5905     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5906              game.magnify_time_left == 0)
5907     {
5908       Feld[x][y] = EL_EMC_FAKE_GRASS;
5909       TEST_DrawLevelField(x, y);
5910     }
5911     else if (IS_GATE_GRAY(element) &&
5912              game.magnify_time_left > 0)
5913     {
5914       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5915                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5916                     IS_EM_GATE_GRAY(element) ?
5917                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5918                     IS_EMC_GATE_GRAY(element) ?
5919                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5920                     IS_DC_GATE_GRAY(element) ?
5921                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5922                     element);
5923       TEST_DrawLevelField(x, y);
5924     }
5925     else if (IS_GATE_GRAY_ACTIVE(element) &&
5926              game.magnify_time_left == 0)
5927     {
5928       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5929                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5930                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5931                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5932                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5933                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5934                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5935                     EL_DC_GATE_WHITE_GRAY :
5936                     element);
5937       TEST_DrawLevelField(x, y);
5938     }
5939   }
5940 }
5941
5942 static void ToggleLightSwitch(int x, int y)
5943 {
5944   int element = Feld[x][y];
5945
5946   game.light_time_left =
5947     (element == EL_LIGHT_SWITCH ?
5948      level.time_light * FRAMES_PER_SECOND : 0);
5949
5950   RedrawAllLightSwitchesAndInvisibleElements();
5951 }
5952
5953 static void ActivateTimegateSwitch(int x, int y)
5954 {
5955   int xx, yy;
5956
5957   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5958
5959   SCAN_PLAYFIELD(xx, yy)
5960   {
5961     int element = Feld[xx][yy];
5962
5963     if (element == EL_TIMEGATE_CLOSED ||
5964         element == EL_TIMEGATE_CLOSING)
5965     {
5966       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5967       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5968     }
5969
5970     /*
5971     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5972     {
5973       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5974       TEST_DrawLevelField(xx, yy);
5975     }
5976     */
5977
5978   }
5979
5980   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5981                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5982 }
5983
5984 void Impact(int x, int y)
5985 {
5986   boolean last_line = (y == lev_fieldy - 1);
5987   boolean object_hit = FALSE;
5988   boolean impact = (last_line || object_hit);
5989   int element = Feld[x][y];
5990   int smashed = EL_STEELWALL;
5991
5992   if (!last_line)       /* check if element below was hit */
5993   {
5994     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5995       return;
5996
5997     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5998                                          MovDir[x][y + 1] != MV_DOWN ||
5999                                          MovPos[x][y + 1] <= TILEY / 2));
6000
6001     /* do not smash moving elements that left the smashed field in time */
6002     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6003         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6004       object_hit = FALSE;
6005
6006 #if USE_QUICKSAND_IMPACT_BUGFIX
6007     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6008     {
6009       RemoveMovingField(x, y + 1);
6010       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6011       Feld[x][y + 2] = EL_ROCK;
6012       TEST_DrawLevelField(x, y + 2);
6013
6014       object_hit = TRUE;
6015     }
6016
6017     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6018     {
6019       RemoveMovingField(x, y + 1);
6020       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6021       Feld[x][y + 2] = EL_ROCK;
6022       TEST_DrawLevelField(x, y + 2);
6023
6024       object_hit = TRUE;
6025     }
6026 #endif
6027
6028     if (object_hit)
6029       smashed = MovingOrBlocked2Element(x, y + 1);
6030
6031     impact = (last_line || object_hit);
6032   }
6033
6034   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6035   {
6036     SplashAcid(x, y + 1);
6037     return;
6038   }
6039
6040   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6041   /* only reset graphic animation if graphic really changes after impact */
6042   if (impact &&
6043       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6044   {
6045     ResetGfxAnimation(x, y);
6046     TEST_DrawLevelField(x, y);
6047   }
6048
6049   if (impact && CAN_EXPLODE_IMPACT(element))
6050   {
6051     Bang(x, y);
6052     return;
6053   }
6054   else if (impact && element == EL_PEARL &&
6055            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6056   {
6057     ResetGfxAnimation(x, y);
6058
6059     Feld[x][y] = EL_PEARL_BREAKING;
6060     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6061     return;
6062   }
6063   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6064   {
6065     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6066
6067     return;
6068   }
6069
6070   if (impact && element == EL_AMOEBA_DROP)
6071   {
6072     if (object_hit && IS_PLAYER(x, y + 1))
6073       KillPlayerUnlessEnemyProtected(x, y + 1);
6074     else if (object_hit && smashed == EL_PENGUIN)
6075       Bang(x, y + 1);
6076     else
6077     {
6078       Feld[x][y] = EL_AMOEBA_GROWING;
6079       Store[x][y] = EL_AMOEBA_WET;
6080
6081       ResetRandomAnimationValue(x, y);
6082     }
6083     return;
6084   }
6085
6086   if (object_hit)               /* check which object was hit */
6087   {
6088     if ((CAN_PASS_MAGIC_WALL(element) && 
6089          (smashed == EL_MAGIC_WALL ||
6090           smashed == EL_BD_MAGIC_WALL)) ||
6091         (CAN_PASS_DC_MAGIC_WALL(element) &&
6092          smashed == EL_DC_MAGIC_WALL))
6093     {
6094       int xx, yy;
6095       int activated_magic_wall =
6096         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6097          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6098          EL_DC_MAGIC_WALL_ACTIVE);
6099
6100       /* activate magic wall / mill */
6101       SCAN_PLAYFIELD(xx, yy)
6102       {
6103         if (Feld[xx][yy] == smashed)
6104           Feld[xx][yy] = activated_magic_wall;
6105       }
6106
6107       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6108       game.magic_wall_active = TRUE;
6109
6110       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6111                             SND_MAGIC_WALL_ACTIVATING :
6112                             smashed == EL_BD_MAGIC_WALL ?
6113                             SND_BD_MAGIC_WALL_ACTIVATING :
6114                             SND_DC_MAGIC_WALL_ACTIVATING));
6115     }
6116
6117     if (IS_PLAYER(x, y + 1))
6118     {
6119       if (CAN_SMASH_PLAYER(element))
6120       {
6121         KillPlayerUnlessEnemyProtected(x, y + 1);
6122         return;
6123       }
6124     }
6125     else if (smashed == EL_PENGUIN)
6126     {
6127       if (CAN_SMASH_PLAYER(element))
6128       {
6129         Bang(x, y + 1);
6130         return;
6131       }
6132     }
6133     else if (element == EL_BD_DIAMOND)
6134     {
6135       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6136       {
6137         Bang(x, y + 1);
6138         return;
6139       }
6140     }
6141     else if (((element == EL_SP_INFOTRON ||
6142                element == EL_SP_ZONK) &&
6143               (smashed == EL_SP_SNIKSNAK ||
6144                smashed == EL_SP_ELECTRON ||
6145                smashed == EL_SP_DISK_ORANGE)) ||
6146              (element == EL_SP_INFOTRON &&
6147               smashed == EL_SP_DISK_YELLOW))
6148     {
6149       Bang(x, y + 1);
6150       return;
6151     }
6152     else if (CAN_SMASH_EVERYTHING(element))
6153     {
6154       if (IS_CLASSIC_ENEMY(smashed) ||
6155           CAN_EXPLODE_SMASHED(smashed))
6156       {
6157         Bang(x, y + 1);
6158         return;
6159       }
6160       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6161       {
6162         if (smashed == EL_LAMP ||
6163             smashed == EL_LAMP_ACTIVE)
6164         {
6165           Bang(x, y + 1);
6166           return;
6167         }
6168         else if (smashed == EL_NUT)
6169         {
6170           Feld[x][y + 1] = EL_NUT_BREAKING;
6171           PlayLevelSound(x, y, SND_NUT_BREAKING);
6172           RaiseScoreElement(EL_NUT);
6173           return;
6174         }
6175         else if (smashed == EL_PEARL)
6176         {
6177           ResetGfxAnimation(x, y);
6178
6179           Feld[x][y + 1] = EL_PEARL_BREAKING;
6180           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6181           return;
6182         }
6183         else if (smashed == EL_DIAMOND)
6184         {
6185           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6186           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6187           return;
6188         }
6189         else if (IS_BELT_SWITCH(smashed))
6190         {
6191           ToggleBeltSwitch(x, y + 1);
6192         }
6193         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6194                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6195                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6196                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6197         {
6198           ToggleSwitchgateSwitch(x, y + 1);
6199         }
6200         else if (smashed == EL_LIGHT_SWITCH ||
6201                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6202         {
6203           ToggleLightSwitch(x, y + 1);
6204         }
6205         else
6206         {
6207           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6208
6209           CheckElementChangeBySide(x, y + 1, smashed, element,
6210                                    CE_SWITCHED, CH_SIDE_TOP);
6211           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6212                                             CH_SIDE_TOP);
6213         }
6214       }
6215       else
6216       {
6217         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6218       }
6219     }
6220   }
6221
6222   /* play sound of magic wall / mill */
6223   if (!last_line &&
6224       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6225        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6226        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6227   {
6228     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6229       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6230     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6231       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6232     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6233       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6234
6235     return;
6236   }
6237
6238   /* play sound of object that hits the ground */
6239   if (last_line || object_hit)
6240     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6241 }
6242
6243 inline static void TurnRoundExt(int x, int y)
6244 {
6245   static struct
6246   {
6247     int dx, dy;
6248   } move_xy[] =
6249   {
6250     {  0,  0 },
6251     { -1,  0 },
6252     { +1,  0 },
6253     {  0,  0 },
6254     {  0, -1 },
6255     {  0,  0 }, { 0, 0 }, { 0, 0 },
6256     {  0, +1 }
6257   };
6258   static struct
6259   {
6260     int left, right, back;
6261   } turn[] =
6262   {
6263     { 0,        0,              0        },
6264     { MV_DOWN,  MV_UP,          MV_RIGHT },
6265     { MV_UP,    MV_DOWN,        MV_LEFT  },
6266     { 0,        0,              0        },
6267     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6268     { 0,        0,              0        },
6269     { 0,        0,              0        },
6270     { 0,        0,              0        },
6271     { MV_RIGHT, MV_LEFT,        MV_UP    }
6272   };
6273
6274   int element = Feld[x][y];
6275   int move_pattern = element_info[element].move_pattern;
6276
6277   int old_move_dir = MovDir[x][y];
6278   int left_dir  = turn[old_move_dir].left;
6279   int right_dir = turn[old_move_dir].right;
6280   int back_dir  = turn[old_move_dir].back;
6281
6282   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6283   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6284   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6285   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6286
6287   int left_x  = x + left_dx,  left_y  = y + left_dy;
6288   int right_x = x + right_dx, right_y = y + right_dy;
6289   int move_x  = x + move_dx,  move_y  = y + move_dy;
6290
6291   int xx, yy;
6292
6293   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6294   {
6295     TestIfBadThingTouchesOtherBadThing(x, y);
6296
6297     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6298       MovDir[x][y] = right_dir;
6299     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6300       MovDir[x][y] = left_dir;
6301
6302     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6303       MovDelay[x][y] = 9;
6304     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6305       MovDelay[x][y] = 1;
6306   }
6307   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6308   {
6309     TestIfBadThingTouchesOtherBadThing(x, y);
6310
6311     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6312       MovDir[x][y] = left_dir;
6313     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6314       MovDir[x][y] = right_dir;
6315
6316     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6317       MovDelay[x][y] = 9;
6318     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6319       MovDelay[x][y] = 1;
6320   }
6321   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6322   {
6323     TestIfBadThingTouchesOtherBadThing(x, y);
6324
6325     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6326       MovDir[x][y] = left_dir;
6327     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6328       MovDir[x][y] = right_dir;
6329
6330     if (MovDir[x][y] != old_move_dir)
6331       MovDelay[x][y] = 9;
6332   }
6333   else if (element == EL_YAMYAM)
6334   {
6335     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6336     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6337
6338     if (can_turn_left && can_turn_right)
6339       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6340     else if (can_turn_left)
6341       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6342     else if (can_turn_right)
6343       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6344     else
6345       MovDir[x][y] = back_dir;
6346
6347     MovDelay[x][y] = 16 + 16 * RND(3);
6348   }
6349   else if (element == EL_DARK_YAMYAM)
6350   {
6351     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6352                                                          left_x, left_y);
6353     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6354                                                          right_x, right_y);
6355
6356     if (can_turn_left && can_turn_right)
6357       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6358     else if (can_turn_left)
6359       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6360     else if (can_turn_right)
6361       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6362     else
6363       MovDir[x][y] = back_dir;
6364
6365     MovDelay[x][y] = 16 + 16 * RND(3);
6366   }
6367   else if (element == EL_PACMAN)
6368   {
6369     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6370     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6371
6372     if (can_turn_left && can_turn_right)
6373       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6374     else if (can_turn_left)
6375       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6376     else if (can_turn_right)
6377       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6378     else
6379       MovDir[x][y] = back_dir;
6380
6381     MovDelay[x][y] = 6 + RND(40);
6382   }
6383   else if (element == EL_PIG)
6384   {
6385     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6386     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6387     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6388     boolean should_turn_left, should_turn_right, should_move_on;
6389     int rnd_value = 24;
6390     int rnd = RND(rnd_value);
6391
6392     should_turn_left = (can_turn_left &&
6393                         (!can_move_on ||
6394                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6395                                                    y + back_dy + left_dy)));
6396     should_turn_right = (can_turn_right &&
6397                          (!can_move_on ||
6398                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6399                                                     y + back_dy + right_dy)));
6400     should_move_on = (can_move_on &&
6401                       (!can_turn_left ||
6402                        !can_turn_right ||
6403                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6404                                                  y + move_dy + left_dy) ||
6405                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6406                                                  y + move_dy + right_dy)));
6407
6408     if (should_turn_left || should_turn_right || should_move_on)
6409     {
6410       if (should_turn_left && should_turn_right && should_move_on)
6411         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6412                         rnd < 2 * rnd_value / 3 ? right_dir :
6413                         old_move_dir);
6414       else if (should_turn_left && should_turn_right)
6415         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6416       else if (should_turn_left && should_move_on)
6417         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6418       else if (should_turn_right && should_move_on)
6419         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6420       else if (should_turn_left)
6421         MovDir[x][y] = left_dir;
6422       else if (should_turn_right)
6423         MovDir[x][y] = right_dir;
6424       else if (should_move_on)
6425         MovDir[x][y] = old_move_dir;
6426     }
6427     else if (can_move_on && rnd > rnd_value / 8)
6428       MovDir[x][y] = old_move_dir;
6429     else if (can_turn_left && can_turn_right)
6430       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6431     else if (can_turn_left && rnd > rnd_value / 8)
6432       MovDir[x][y] = left_dir;
6433     else if (can_turn_right && rnd > rnd_value/8)
6434       MovDir[x][y] = right_dir;
6435     else
6436       MovDir[x][y] = back_dir;
6437
6438     xx = x + move_xy[MovDir[x][y]].dx;
6439     yy = y + move_xy[MovDir[x][y]].dy;
6440
6441     if (!IN_LEV_FIELD(xx, yy) ||
6442         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6443       MovDir[x][y] = old_move_dir;
6444
6445     MovDelay[x][y] = 0;
6446   }
6447   else if (element == EL_DRAGON)
6448   {
6449     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6450     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6451     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6452     int rnd_value = 24;
6453     int rnd = RND(rnd_value);
6454
6455     if (can_move_on && rnd > rnd_value / 8)
6456       MovDir[x][y] = old_move_dir;
6457     else if (can_turn_left && can_turn_right)
6458       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6459     else if (can_turn_left && rnd > rnd_value / 8)
6460       MovDir[x][y] = left_dir;
6461     else if (can_turn_right && rnd > rnd_value / 8)
6462       MovDir[x][y] = right_dir;
6463     else
6464       MovDir[x][y] = back_dir;
6465
6466     xx = x + move_xy[MovDir[x][y]].dx;
6467     yy = y + move_xy[MovDir[x][y]].dy;
6468
6469     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6470       MovDir[x][y] = old_move_dir;
6471
6472     MovDelay[x][y] = 0;
6473   }
6474   else if (element == EL_MOLE)
6475   {
6476     boolean can_move_on =
6477       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6478                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6479                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6480     if (!can_move_on)
6481     {
6482       boolean can_turn_left =
6483         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6484                               IS_AMOEBOID(Feld[left_x][left_y])));
6485
6486       boolean can_turn_right =
6487         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6488                               IS_AMOEBOID(Feld[right_x][right_y])));
6489
6490       if (can_turn_left && can_turn_right)
6491         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6492       else if (can_turn_left)
6493         MovDir[x][y] = left_dir;
6494       else
6495         MovDir[x][y] = right_dir;
6496     }
6497
6498     if (MovDir[x][y] != old_move_dir)
6499       MovDelay[x][y] = 9;
6500   }
6501   else if (element == EL_BALLOON)
6502   {
6503     MovDir[x][y] = game.wind_direction;
6504     MovDelay[x][y] = 0;
6505   }
6506   else if (element == EL_SPRING)
6507   {
6508     if (MovDir[x][y] & MV_HORIZONTAL)
6509     {
6510       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6511           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6512       {
6513         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6514         ResetGfxAnimation(move_x, move_y);
6515         TEST_DrawLevelField(move_x, move_y);
6516
6517         MovDir[x][y] = back_dir;
6518       }
6519       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6520                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6521         MovDir[x][y] = MV_NONE;
6522     }
6523
6524     MovDelay[x][y] = 0;
6525   }
6526   else if (element == EL_ROBOT ||
6527            element == EL_SATELLITE ||
6528            element == EL_PENGUIN ||
6529            element == EL_EMC_ANDROID)
6530   {
6531     int attr_x = -1, attr_y = -1;
6532
6533     if (AllPlayersGone)
6534     {
6535       attr_x = ExitX;
6536       attr_y = ExitY;
6537     }
6538     else
6539     {
6540       int i;
6541
6542       for (i = 0; i < MAX_PLAYERS; i++)
6543       {
6544         struct PlayerInfo *player = &stored_player[i];
6545         int jx = player->jx, jy = player->jy;
6546
6547         if (!player->active)
6548           continue;
6549
6550         if (attr_x == -1 ||
6551             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6552         {
6553           attr_x = jx;
6554           attr_y = jy;
6555         }
6556       }
6557     }
6558
6559     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6560         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6561          game.engine_version < VERSION_IDENT(3,1,0,0)))
6562     {
6563       attr_x = ZX;
6564       attr_y = ZY;
6565     }
6566
6567     if (element == EL_PENGUIN)
6568     {
6569       int i;
6570       static int xy[4][2] =
6571       {
6572         { 0, -1 },
6573         { -1, 0 },
6574         { +1, 0 },
6575         { 0, +1 }
6576       };
6577
6578       for (i = 0; i < NUM_DIRECTIONS; i++)
6579       {
6580         int ex = x + xy[i][0];
6581         int ey = y + xy[i][1];
6582
6583         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6584                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6585                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6586                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6587         {
6588           attr_x = ex;
6589           attr_y = ey;
6590           break;
6591         }
6592       }
6593     }
6594
6595     MovDir[x][y] = MV_NONE;
6596     if (attr_x < x)
6597       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6598     else if (attr_x > x)
6599       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6600     if (attr_y < y)
6601       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6602     else if (attr_y > y)
6603       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6604
6605     if (element == EL_ROBOT)
6606     {
6607       int newx, newy;
6608
6609       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6610         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6611       Moving2Blocked(x, y, &newx, &newy);
6612
6613       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6614         MovDelay[x][y] = 8 + 8 * !RND(3);
6615       else
6616         MovDelay[x][y] = 16;
6617     }
6618     else if (element == EL_PENGUIN)
6619     {
6620       int newx, newy;
6621
6622       MovDelay[x][y] = 1;
6623
6624       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6625       {
6626         boolean first_horiz = RND(2);
6627         int new_move_dir = MovDir[x][y];
6628
6629         MovDir[x][y] =
6630           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6631         Moving2Blocked(x, y, &newx, &newy);
6632
6633         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6634           return;
6635
6636         MovDir[x][y] =
6637           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6638         Moving2Blocked(x, y, &newx, &newy);
6639
6640         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6641           return;
6642
6643         MovDir[x][y] = old_move_dir;
6644         return;
6645       }
6646     }
6647     else if (element == EL_SATELLITE)
6648     {
6649       int newx, newy;
6650
6651       MovDelay[x][y] = 1;
6652
6653       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6654       {
6655         boolean first_horiz = RND(2);
6656         int new_move_dir = MovDir[x][y];
6657
6658         MovDir[x][y] =
6659           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6660         Moving2Blocked(x, y, &newx, &newy);
6661
6662         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6663           return;
6664
6665         MovDir[x][y] =
6666           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6667         Moving2Blocked(x, y, &newx, &newy);
6668
6669         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6670           return;
6671
6672         MovDir[x][y] = old_move_dir;
6673         return;
6674       }
6675     }
6676     else if (element == EL_EMC_ANDROID)
6677     {
6678       static int check_pos[16] =
6679       {
6680         -1,             /*  0 => (invalid)          */
6681         7,              /*  1 => MV_LEFT            */
6682         3,              /*  2 => MV_RIGHT           */
6683         -1,             /*  3 => (invalid)          */
6684         1,              /*  4 =>            MV_UP   */
6685         0,              /*  5 => MV_LEFT  | MV_UP   */
6686         2,              /*  6 => MV_RIGHT | MV_UP   */
6687         -1,             /*  7 => (invalid)          */
6688         5,              /*  8 =>            MV_DOWN */
6689         6,              /*  9 => MV_LEFT  | MV_DOWN */
6690         4,              /* 10 => MV_RIGHT | MV_DOWN */
6691         -1,             /* 11 => (invalid)          */
6692         -1,             /* 12 => (invalid)          */
6693         -1,             /* 13 => (invalid)          */
6694         -1,             /* 14 => (invalid)          */
6695         -1,             /* 15 => (invalid)          */
6696       };
6697       static struct
6698       {
6699         int dx, dy;
6700         int dir;
6701       } check_xy[8] =
6702       {
6703         { -1, -1,       MV_LEFT  | MV_UP   },
6704         {  0, -1,                  MV_UP   },
6705         { +1, -1,       MV_RIGHT | MV_UP   },
6706         { +1,  0,       MV_RIGHT           },
6707         { +1, +1,       MV_RIGHT | MV_DOWN },
6708         {  0, +1,                  MV_DOWN },
6709         { -1, +1,       MV_LEFT  | MV_DOWN },
6710         { -1,  0,       MV_LEFT            },
6711       };
6712       int start_pos, check_order;
6713       boolean can_clone = FALSE;
6714       int i;
6715
6716       /* check if there is any free field around current position */
6717       for (i = 0; i < 8; i++)
6718       {
6719         int newx = x + check_xy[i].dx;
6720         int newy = y + check_xy[i].dy;
6721
6722         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6723         {
6724           can_clone = TRUE;
6725
6726           break;
6727         }
6728       }
6729
6730       if (can_clone)            /* randomly find an element to clone */
6731       {
6732         can_clone = FALSE;
6733
6734         start_pos = check_pos[RND(8)];
6735         check_order = (RND(2) ? -1 : +1);
6736
6737         for (i = 0; i < 8; i++)
6738         {
6739           int pos_raw = start_pos + i * check_order;
6740           int pos = (pos_raw + 8) % 8;
6741           int newx = x + check_xy[pos].dx;
6742           int newy = y + check_xy[pos].dy;
6743
6744           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6745           {
6746             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6747             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6748
6749             Store[x][y] = Feld[newx][newy];
6750
6751             can_clone = TRUE;
6752
6753             break;
6754           }
6755         }
6756       }
6757
6758       if (can_clone)            /* randomly find a direction to move */
6759       {
6760         can_clone = FALSE;
6761
6762         start_pos = check_pos[RND(8)];
6763         check_order = (RND(2) ? -1 : +1);
6764
6765         for (i = 0; i < 8; i++)
6766         {
6767           int pos_raw = start_pos + i * check_order;
6768           int pos = (pos_raw + 8) % 8;
6769           int newx = x + check_xy[pos].dx;
6770           int newy = y + check_xy[pos].dy;
6771           int new_move_dir = check_xy[pos].dir;
6772
6773           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6774           {
6775             MovDir[x][y] = new_move_dir;
6776             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6777
6778             can_clone = TRUE;
6779
6780             break;
6781           }
6782         }
6783       }
6784
6785       if (can_clone)            /* cloning and moving successful */
6786         return;
6787
6788       /* cannot clone -- try to move towards player */
6789
6790       start_pos = check_pos[MovDir[x][y] & 0x0f];
6791       check_order = (RND(2) ? -1 : +1);
6792
6793       for (i = 0; i < 3; i++)
6794       {
6795         /* first check start_pos, then previous/next or (next/previous) pos */
6796         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6797         int pos = (pos_raw + 8) % 8;
6798         int newx = x + check_xy[pos].dx;
6799         int newy = y + check_xy[pos].dy;
6800         int new_move_dir = check_xy[pos].dir;
6801
6802         if (IS_PLAYER(newx, newy))
6803           break;
6804
6805         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6806         {
6807           MovDir[x][y] = new_move_dir;
6808           MovDelay[x][y] = level.android_move_time * 8 + 1;
6809
6810           break;
6811         }
6812       }
6813     }
6814   }
6815   else if (move_pattern == MV_TURNING_LEFT ||
6816            move_pattern == MV_TURNING_RIGHT ||
6817            move_pattern == MV_TURNING_LEFT_RIGHT ||
6818            move_pattern == MV_TURNING_RIGHT_LEFT ||
6819            move_pattern == MV_TURNING_RANDOM ||
6820            move_pattern == MV_ALL_DIRECTIONS)
6821   {
6822     boolean can_turn_left =
6823       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6824     boolean can_turn_right =
6825       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6826
6827     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6828       return;
6829
6830     if (move_pattern == MV_TURNING_LEFT)
6831       MovDir[x][y] = left_dir;
6832     else if (move_pattern == MV_TURNING_RIGHT)
6833       MovDir[x][y] = right_dir;
6834     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6835       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6836     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6837       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6838     else if (move_pattern == MV_TURNING_RANDOM)
6839       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6840                       can_turn_right && !can_turn_left ? right_dir :
6841                       RND(2) ? left_dir : right_dir);
6842     else if (can_turn_left && can_turn_right)
6843       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6844     else if (can_turn_left)
6845       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6846     else if (can_turn_right)
6847       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6848     else
6849       MovDir[x][y] = back_dir;
6850
6851     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6852   }
6853   else if (move_pattern == MV_HORIZONTAL ||
6854            move_pattern == MV_VERTICAL)
6855   {
6856     if (move_pattern & old_move_dir)
6857       MovDir[x][y] = back_dir;
6858     else if (move_pattern == MV_HORIZONTAL)
6859       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6860     else if (move_pattern == MV_VERTICAL)
6861       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6862
6863     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6864   }
6865   else if (move_pattern & MV_ANY_DIRECTION)
6866   {
6867     MovDir[x][y] = move_pattern;
6868     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6869   }
6870   else if (move_pattern & MV_WIND_DIRECTION)
6871   {
6872     MovDir[x][y] = game.wind_direction;
6873     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6874   }
6875   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6876   {
6877     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6878       MovDir[x][y] = left_dir;
6879     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6880       MovDir[x][y] = right_dir;
6881
6882     if (MovDir[x][y] != old_move_dir)
6883       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6884   }
6885   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6886   {
6887     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6888       MovDir[x][y] = right_dir;
6889     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6890       MovDir[x][y] = left_dir;
6891
6892     if (MovDir[x][y] != old_move_dir)
6893       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6894   }
6895   else if (move_pattern == MV_TOWARDS_PLAYER ||
6896            move_pattern == MV_AWAY_FROM_PLAYER)
6897   {
6898     int attr_x = -1, attr_y = -1;
6899     int newx, newy;
6900     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6901
6902     if (AllPlayersGone)
6903     {
6904       attr_x = ExitX;
6905       attr_y = ExitY;
6906     }
6907     else
6908     {
6909       int i;
6910
6911       for (i = 0; i < MAX_PLAYERS; i++)
6912       {
6913         struct PlayerInfo *player = &stored_player[i];
6914         int jx = player->jx, jy = player->jy;
6915
6916         if (!player->active)
6917           continue;
6918
6919         if (attr_x == -1 ||
6920             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6921         {
6922           attr_x = jx;
6923           attr_y = jy;
6924         }
6925       }
6926     }
6927
6928     MovDir[x][y] = MV_NONE;
6929     if (attr_x < x)
6930       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6931     else if (attr_x > x)
6932       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6933     if (attr_y < y)
6934       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6935     else if (attr_y > y)
6936       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6937
6938     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6939
6940     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6941     {
6942       boolean first_horiz = RND(2);
6943       int new_move_dir = MovDir[x][y];
6944
6945       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6946       {
6947         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6948         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6949
6950         return;
6951       }
6952
6953       MovDir[x][y] =
6954         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6955       Moving2Blocked(x, y, &newx, &newy);
6956
6957       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6958         return;
6959
6960       MovDir[x][y] =
6961         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6962       Moving2Blocked(x, y, &newx, &newy);
6963
6964       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6965         return;
6966
6967       MovDir[x][y] = old_move_dir;
6968     }
6969   }
6970   else if (move_pattern == MV_WHEN_PUSHED ||
6971            move_pattern == MV_WHEN_DROPPED)
6972   {
6973     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6974       MovDir[x][y] = MV_NONE;
6975
6976     MovDelay[x][y] = 0;
6977   }
6978   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6979   {
6980     static int test_xy[7][2] =
6981     {
6982       { 0, -1 },
6983       { -1, 0 },
6984       { +1, 0 },
6985       { 0, +1 },
6986       { 0, -1 },
6987       { -1, 0 },
6988       { +1, 0 },
6989     };
6990     static int test_dir[7] =
6991     {
6992       MV_UP,
6993       MV_LEFT,
6994       MV_RIGHT,
6995       MV_DOWN,
6996       MV_UP,
6997       MV_LEFT,
6998       MV_RIGHT,
6999     };
7000     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7001     int move_preference = -1000000;     /* start with very low preference */
7002     int new_move_dir = MV_NONE;
7003     int start_test = RND(4);
7004     int i;
7005
7006     for (i = 0; i < NUM_DIRECTIONS; i++)
7007     {
7008       int move_dir = test_dir[start_test + i];
7009       int move_dir_preference;
7010
7011       xx = x + test_xy[start_test + i][0];
7012       yy = y + test_xy[start_test + i][1];
7013
7014       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7015           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7016       {
7017         new_move_dir = move_dir;
7018
7019         break;
7020       }
7021
7022       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7023         continue;
7024
7025       move_dir_preference = -1 * RunnerVisit[xx][yy];
7026       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7027         move_dir_preference = PlayerVisit[xx][yy];
7028
7029       if (move_dir_preference > move_preference)
7030       {
7031         /* prefer field that has not been visited for the longest time */
7032         move_preference = move_dir_preference;
7033         new_move_dir = move_dir;
7034       }
7035       else if (move_dir_preference == move_preference &&
7036                move_dir == old_move_dir)
7037       {
7038         /* prefer last direction when all directions are preferred equally */
7039         move_preference = move_dir_preference;
7040         new_move_dir = move_dir;
7041       }
7042     }
7043
7044     MovDir[x][y] = new_move_dir;
7045     if (old_move_dir != new_move_dir)
7046       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7047   }
7048 }
7049
7050 static void TurnRound(int x, int y)
7051 {
7052   int direction = MovDir[x][y];
7053
7054   TurnRoundExt(x, y);
7055
7056   GfxDir[x][y] = MovDir[x][y];
7057
7058   if (direction != MovDir[x][y])
7059     GfxFrame[x][y] = 0;
7060
7061   if (MovDelay[x][y])
7062     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7063
7064   ResetGfxFrame(x, y);
7065 }
7066
7067 static boolean JustBeingPushed(int x, int y)
7068 {
7069   int i;
7070
7071   for (i = 0; i < MAX_PLAYERS; i++)
7072   {
7073     struct PlayerInfo *player = &stored_player[i];
7074
7075     if (player->active && player->is_pushing && player->MovPos)
7076     {
7077       int next_jx = player->jx + (player->jx - player->last_jx);
7078       int next_jy = player->jy + (player->jy - player->last_jy);
7079
7080       if (x == next_jx && y == next_jy)
7081         return TRUE;
7082     }
7083   }
7084
7085   return FALSE;
7086 }
7087
7088 void StartMoving(int x, int y)
7089 {
7090   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7091   int element = Feld[x][y];
7092
7093   if (Stop[x][y])
7094     return;
7095
7096   if (MovDelay[x][y] == 0)
7097     GfxAction[x][y] = ACTION_DEFAULT;
7098
7099   if (CAN_FALL(element) && y < lev_fieldy - 1)
7100   {
7101     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7102         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7103       if (JustBeingPushed(x, y))
7104         return;
7105
7106     if (element == EL_QUICKSAND_FULL)
7107     {
7108       if (IS_FREE(x, y + 1))
7109       {
7110         InitMovingField(x, y, MV_DOWN);
7111         started_moving = TRUE;
7112
7113         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7114 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7115         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7116           Store[x][y] = EL_ROCK;
7117 #else
7118         Store[x][y] = EL_ROCK;
7119 #endif
7120
7121         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7122       }
7123       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7124       {
7125         if (!MovDelay[x][y])
7126         {
7127           MovDelay[x][y] = TILEY + 1;
7128
7129           ResetGfxAnimation(x, y);
7130           ResetGfxAnimation(x, y + 1);
7131         }
7132
7133         if (MovDelay[x][y])
7134         {
7135           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7136           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7137
7138           MovDelay[x][y]--;
7139           if (MovDelay[x][y])
7140             return;
7141         }
7142
7143         Feld[x][y] = EL_QUICKSAND_EMPTY;
7144         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7145         Store[x][y + 1] = Store[x][y];
7146         Store[x][y] = 0;
7147
7148         PlayLevelSoundAction(x, y, ACTION_FILLING);
7149       }
7150       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7151       {
7152         if (!MovDelay[x][y])
7153         {
7154           MovDelay[x][y] = TILEY + 1;
7155
7156           ResetGfxAnimation(x, y);
7157           ResetGfxAnimation(x, y + 1);
7158         }
7159
7160         if (MovDelay[x][y])
7161         {
7162           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7163           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7164
7165           MovDelay[x][y]--;
7166           if (MovDelay[x][y])
7167             return;
7168         }
7169
7170         Feld[x][y] = EL_QUICKSAND_EMPTY;
7171         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7172         Store[x][y + 1] = Store[x][y];
7173         Store[x][y] = 0;
7174
7175         PlayLevelSoundAction(x, y, ACTION_FILLING);
7176       }
7177     }
7178     else if (element == EL_QUICKSAND_FAST_FULL)
7179     {
7180       if (IS_FREE(x, y + 1))
7181       {
7182         InitMovingField(x, y, MV_DOWN);
7183         started_moving = TRUE;
7184
7185         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7186 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7187         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7188           Store[x][y] = EL_ROCK;
7189 #else
7190         Store[x][y] = EL_ROCK;
7191 #endif
7192
7193         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7194       }
7195       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7196       {
7197         if (!MovDelay[x][y])
7198         {
7199           MovDelay[x][y] = TILEY + 1;
7200
7201           ResetGfxAnimation(x, y);
7202           ResetGfxAnimation(x, y + 1);
7203         }
7204
7205         if (MovDelay[x][y])
7206         {
7207           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7208           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7209
7210           MovDelay[x][y]--;
7211           if (MovDelay[x][y])
7212             return;
7213         }
7214
7215         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7216         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7217         Store[x][y + 1] = Store[x][y];
7218         Store[x][y] = 0;
7219
7220         PlayLevelSoundAction(x, y, ACTION_FILLING);
7221       }
7222       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7223       {
7224         if (!MovDelay[x][y])
7225         {
7226           MovDelay[x][y] = TILEY + 1;
7227
7228           ResetGfxAnimation(x, y);
7229           ResetGfxAnimation(x, y + 1);
7230         }
7231
7232         if (MovDelay[x][y])
7233         {
7234           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7235           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7236
7237           MovDelay[x][y]--;
7238           if (MovDelay[x][y])
7239             return;
7240         }
7241
7242         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7243         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7244         Store[x][y + 1] = Store[x][y];
7245         Store[x][y] = 0;
7246
7247         PlayLevelSoundAction(x, y, ACTION_FILLING);
7248       }
7249     }
7250     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7251              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7252     {
7253       InitMovingField(x, y, MV_DOWN);
7254       started_moving = TRUE;
7255
7256       Feld[x][y] = EL_QUICKSAND_FILLING;
7257       Store[x][y] = element;
7258
7259       PlayLevelSoundAction(x, y, ACTION_FILLING);
7260     }
7261     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7262              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7263     {
7264       InitMovingField(x, y, MV_DOWN);
7265       started_moving = TRUE;
7266
7267       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7268       Store[x][y] = element;
7269
7270       PlayLevelSoundAction(x, y, ACTION_FILLING);
7271     }
7272     else if (element == EL_MAGIC_WALL_FULL)
7273     {
7274       if (IS_FREE(x, y + 1))
7275       {
7276         InitMovingField(x, y, MV_DOWN);
7277         started_moving = TRUE;
7278
7279         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7280         Store[x][y] = EL_CHANGED(Store[x][y]);
7281       }
7282       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7283       {
7284         if (!MovDelay[x][y])
7285           MovDelay[x][y] = TILEY / 4 + 1;
7286
7287         if (MovDelay[x][y])
7288         {
7289           MovDelay[x][y]--;
7290           if (MovDelay[x][y])
7291             return;
7292         }
7293
7294         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7295         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7296         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7297         Store[x][y] = 0;
7298       }
7299     }
7300     else if (element == EL_BD_MAGIC_WALL_FULL)
7301     {
7302       if (IS_FREE(x, y + 1))
7303       {
7304         InitMovingField(x, y, MV_DOWN);
7305         started_moving = TRUE;
7306
7307         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7308         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7309       }
7310       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7311       {
7312         if (!MovDelay[x][y])
7313           MovDelay[x][y] = TILEY / 4 + 1;
7314
7315         if (MovDelay[x][y])
7316         {
7317           MovDelay[x][y]--;
7318           if (MovDelay[x][y])
7319             return;
7320         }
7321
7322         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7323         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7324         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7325         Store[x][y] = 0;
7326       }
7327     }
7328     else if (element == EL_DC_MAGIC_WALL_FULL)
7329     {
7330       if (IS_FREE(x, y + 1))
7331       {
7332         InitMovingField(x, y, MV_DOWN);
7333         started_moving = TRUE;
7334
7335         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7336         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7337       }
7338       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7339       {
7340         if (!MovDelay[x][y])
7341           MovDelay[x][y] = TILEY / 4 + 1;
7342
7343         if (MovDelay[x][y])
7344         {
7345           MovDelay[x][y]--;
7346           if (MovDelay[x][y])
7347             return;
7348         }
7349
7350         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7351         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7352         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7353         Store[x][y] = 0;
7354       }
7355     }
7356     else if ((CAN_PASS_MAGIC_WALL(element) &&
7357               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7358                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7359              (CAN_PASS_DC_MAGIC_WALL(element) &&
7360               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7361
7362     {
7363       InitMovingField(x, y, MV_DOWN);
7364       started_moving = TRUE;
7365
7366       Feld[x][y] =
7367         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7368          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7369          EL_DC_MAGIC_WALL_FILLING);
7370       Store[x][y] = element;
7371     }
7372     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7373     {
7374       SplashAcid(x, y + 1);
7375
7376       InitMovingField(x, y, MV_DOWN);
7377       started_moving = TRUE;
7378
7379       Store[x][y] = EL_ACID;
7380     }
7381     else if (
7382              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7383               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7384              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7385               CAN_FALL(element) && WasJustFalling[x][y] &&
7386               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7387
7388              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7389               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7390               (Feld[x][y + 1] == EL_BLOCKED)))
7391     {
7392       /* this is needed for a special case not covered by calling "Impact()"
7393          from "ContinueMoving()": if an element moves to a tile directly below
7394          another element which was just falling on that tile (which was empty
7395          in the previous frame), the falling element above would just stop
7396          instead of smashing the element below (in previous version, the above
7397          element was just checked for "moving" instead of "falling", resulting
7398          in incorrect smashes caused by horizontal movement of the above
7399          element; also, the case of the player being the element to smash was
7400          simply not covered here... :-/ ) */
7401
7402       CheckCollision[x][y] = 0;
7403       CheckImpact[x][y] = 0;
7404
7405       Impact(x, y);
7406     }
7407     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7408     {
7409       if (MovDir[x][y] == MV_NONE)
7410       {
7411         InitMovingField(x, y, MV_DOWN);
7412         started_moving = TRUE;
7413       }
7414     }
7415     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7416     {
7417       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7418         MovDir[x][y] = MV_DOWN;
7419
7420       InitMovingField(x, y, MV_DOWN);
7421       started_moving = TRUE;
7422     }
7423     else if (element == EL_AMOEBA_DROP)
7424     {
7425       Feld[x][y] = EL_AMOEBA_GROWING;
7426       Store[x][y] = EL_AMOEBA_WET;
7427     }
7428     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7429               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7430              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7431              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7432     {
7433       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7434                                 (IS_FREE(x - 1, y + 1) ||
7435                                  Feld[x - 1][y + 1] == EL_ACID));
7436       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7437                                 (IS_FREE(x + 1, y + 1) ||
7438                                  Feld[x + 1][y + 1] == EL_ACID));
7439       boolean can_fall_any  = (can_fall_left || can_fall_right);
7440       boolean can_fall_both = (can_fall_left && can_fall_right);
7441       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7442
7443       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7444       {
7445         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7446           can_fall_right = FALSE;
7447         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7448           can_fall_left = FALSE;
7449         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7450           can_fall_right = FALSE;
7451         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7452           can_fall_left = FALSE;
7453
7454         can_fall_any  = (can_fall_left || can_fall_right);
7455         can_fall_both = FALSE;
7456       }
7457
7458       if (can_fall_both)
7459       {
7460         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7461           can_fall_right = FALSE;       /* slip down on left side */
7462         else
7463           can_fall_left = !(can_fall_right = RND(2));
7464
7465         can_fall_both = FALSE;
7466       }
7467
7468       if (can_fall_any)
7469       {
7470         /* if not determined otherwise, prefer left side for slipping down */
7471         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7472         started_moving = TRUE;
7473       }
7474     }
7475     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7476     {
7477       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7478       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7479       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7480       int belt_dir = game.belt_dir[belt_nr];
7481
7482       if ((belt_dir == MV_LEFT  && left_is_free) ||
7483           (belt_dir == MV_RIGHT && right_is_free))
7484       {
7485         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7486
7487         InitMovingField(x, y, belt_dir);
7488         started_moving = TRUE;
7489
7490         Pushed[x][y] = TRUE;
7491         Pushed[nextx][y] = TRUE;
7492
7493         GfxAction[x][y] = ACTION_DEFAULT;
7494       }
7495       else
7496       {
7497         MovDir[x][y] = 0;       /* if element was moving, stop it */
7498       }
7499     }
7500   }
7501
7502   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7503   if (CAN_MOVE(element) && !started_moving)
7504   {
7505     int move_pattern = element_info[element].move_pattern;
7506     int newx, newy;
7507
7508     Moving2Blocked(x, y, &newx, &newy);
7509
7510     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7511       return;
7512
7513     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7514         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7515     {
7516       WasJustMoving[x][y] = 0;
7517       CheckCollision[x][y] = 0;
7518
7519       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7520
7521       if (Feld[x][y] != element)        /* element has changed */
7522         return;
7523     }
7524
7525     if (!MovDelay[x][y])        /* start new movement phase */
7526     {
7527       /* all objects that can change their move direction after each step
7528          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7529
7530       if (element != EL_YAMYAM &&
7531           element != EL_DARK_YAMYAM &&
7532           element != EL_PACMAN &&
7533           !(move_pattern & MV_ANY_DIRECTION) &&
7534           move_pattern != MV_TURNING_LEFT &&
7535           move_pattern != MV_TURNING_RIGHT &&
7536           move_pattern != MV_TURNING_LEFT_RIGHT &&
7537           move_pattern != MV_TURNING_RIGHT_LEFT &&
7538           move_pattern != MV_TURNING_RANDOM)
7539       {
7540         TurnRound(x, y);
7541
7542         if (MovDelay[x][y] && (element == EL_BUG ||
7543                                element == EL_SPACESHIP ||
7544                                element == EL_SP_SNIKSNAK ||
7545                                element == EL_SP_ELECTRON ||
7546                                element == EL_MOLE))
7547           TEST_DrawLevelField(x, y);
7548       }
7549     }
7550
7551     if (MovDelay[x][y])         /* wait some time before next movement */
7552     {
7553       MovDelay[x][y]--;
7554
7555       if (element == EL_ROBOT ||
7556           element == EL_YAMYAM ||
7557           element == EL_DARK_YAMYAM)
7558       {
7559         DrawLevelElementAnimationIfNeeded(x, y, element);
7560         PlayLevelSoundAction(x, y, ACTION_WAITING);
7561       }
7562       else if (element == EL_SP_ELECTRON)
7563         DrawLevelElementAnimationIfNeeded(x, y, element);
7564       else if (element == EL_DRAGON)
7565       {
7566         int i;
7567         int dir = MovDir[x][y];
7568         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7569         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7570         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7571                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7572                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7573                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7574         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7575
7576         GfxAction[x][y] = ACTION_ATTACKING;
7577
7578         if (IS_PLAYER(x, y))
7579           DrawPlayerField(x, y);
7580         else
7581           TEST_DrawLevelField(x, y);
7582
7583         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7584
7585         for (i = 1; i <= 3; i++)
7586         {
7587           int xx = x + i * dx;
7588           int yy = y + i * dy;
7589           int sx = SCREENX(xx);
7590           int sy = SCREENY(yy);
7591           int flame_graphic = graphic + (i - 1);
7592
7593           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7594             break;
7595
7596           if (MovDelay[x][y])
7597           {
7598             int flamed = MovingOrBlocked2Element(xx, yy);
7599
7600             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7601               Bang(xx, yy);
7602             else
7603               RemoveMovingField(xx, yy);
7604
7605             ChangeDelay[xx][yy] = 0;
7606
7607             Feld[xx][yy] = EL_FLAMES;
7608
7609             if (IN_SCR_FIELD(sx, sy))
7610             {
7611               TEST_DrawLevelFieldCrumbled(xx, yy);
7612               DrawGraphic(sx, sy, flame_graphic, frame);
7613             }
7614           }
7615           else
7616           {
7617             if (Feld[xx][yy] == EL_FLAMES)
7618               Feld[xx][yy] = EL_EMPTY;
7619             TEST_DrawLevelField(xx, yy);
7620           }
7621         }
7622       }
7623
7624       if (MovDelay[x][y])       /* element still has to wait some time */
7625       {
7626         PlayLevelSoundAction(x, y, ACTION_WAITING);
7627
7628         return;
7629       }
7630     }
7631
7632     /* now make next step */
7633
7634     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7635
7636     if (DONT_COLLIDE_WITH(element) &&
7637         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7638         !PLAYER_ENEMY_PROTECTED(newx, newy))
7639     {
7640       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7641
7642       return;
7643     }
7644
7645     else if (CAN_MOVE_INTO_ACID(element) &&
7646              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7647              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7648              (MovDir[x][y] == MV_DOWN ||
7649               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7650     {
7651       SplashAcid(newx, newy);
7652       Store[x][y] = EL_ACID;
7653     }
7654     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7655     {
7656       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7657           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7658           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7659           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7660       {
7661         RemoveField(x, y);
7662         TEST_DrawLevelField(x, y);
7663
7664         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7665         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7666           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7667
7668         local_player->friends_still_needed--;
7669         if (!local_player->friends_still_needed &&
7670             !local_player->GameOver && AllPlayersGone)
7671           PlayerWins(local_player);
7672
7673         return;
7674       }
7675       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7676       {
7677         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7678           TEST_DrawLevelField(newx, newy);
7679         else
7680           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7681       }
7682       else if (!IS_FREE(newx, newy))
7683       {
7684         GfxAction[x][y] = ACTION_WAITING;
7685
7686         if (IS_PLAYER(x, y))
7687           DrawPlayerField(x, y);
7688         else
7689           TEST_DrawLevelField(x, y);
7690
7691         return;
7692       }
7693     }
7694     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7695     {
7696       if (IS_FOOD_PIG(Feld[newx][newy]))
7697       {
7698         if (IS_MOVING(newx, newy))
7699           RemoveMovingField(newx, newy);
7700         else
7701         {
7702           Feld[newx][newy] = EL_EMPTY;
7703           TEST_DrawLevelField(newx, newy);
7704         }
7705
7706         PlayLevelSound(x, y, SND_PIG_DIGGING);
7707       }
7708       else if (!IS_FREE(newx, newy))
7709       {
7710         if (IS_PLAYER(x, y))
7711           DrawPlayerField(x, y);
7712         else
7713           TEST_DrawLevelField(x, y);
7714
7715         return;
7716       }
7717     }
7718     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7719     {
7720       if (Store[x][y] != EL_EMPTY)
7721       {
7722         boolean can_clone = FALSE;
7723         int xx, yy;
7724
7725         /* check if element to clone is still there */
7726         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7727         {
7728           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7729           {
7730             can_clone = TRUE;
7731
7732             break;
7733           }
7734         }
7735
7736         /* cannot clone or target field not free anymore -- do not clone */
7737         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7738           Store[x][y] = EL_EMPTY;
7739       }
7740
7741       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7742       {
7743         if (IS_MV_DIAGONAL(MovDir[x][y]))
7744         {
7745           int diagonal_move_dir = MovDir[x][y];
7746           int stored = Store[x][y];
7747           int change_delay = 8;
7748           int graphic;
7749
7750           /* android is moving diagonally */
7751
7752           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7753
7754           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7755           GfxElement[x][y] = EL_EMC_ANDROID;
7756           GfxAction[x][y] = ACTION_SHRINKING;
7757           GfxDir[x][y] = diagonal_move_dir;
7758           ChangeDelay[x][y] = change_delay;
7759
7760           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7761                                    GfxDir[x][y]);
7762
7763           DrawLevelGraphicAnimation(x, y, graphic);
7764           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7765
7766           if (Feld[newx][newy] == EL_ACID)
7767           {
7768             SplashAcid(newx, newy);
7769
7770             return;
7771           }
7772
7773           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7774
7775           Store[newx][newy] = EL_EMC_ANDROID;
7776           GfxElement[newx][newy] = EL_EMC_ANDROID;
7777           GfxAction[newx][newy] = ACTION_GROWING;
7778           GfxDir[newx][newy] = diagonal_move_dir;
7779           ChangeDelay[newx][newy] = change_delay;
7780
7781           graphic = el_act_dir2img(GfxElement[newx][newy],
7782                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7783
7784           DrawLevelGraphicAnimation(newx, newy, graphic);
7785           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7786
7787           return;
7788         }
7789         else
7790         {
7791           Feld[newx][newy] = EL_EMPTY;
7792           TEST_DrawLevelField(newx, newy);
7793
7794           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7795         }
7796       }
7797       else if (!IS_FREE(newx, newy))
7798       {
7799         return;
7800       }
7801     }
7802     else if (IS_CUSTOM_ELEMENT(element) &&
7803              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7804     {
7805       if (!DigFieldByCE(newx, newy, element))
7806         return;
7807
7808       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7809       {
7810         RunnerVisit[x][y] = FrameCounter;
7811         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7812       }
7813     }
7814     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7815     {
7816       if (!IS_FREE(newx, newy))
7817       {
7818         if (IS_PLAYER(x, y))
7819           DrawPlayerField(x, y);
7820         else
7821           TEST_DrawLevelField(x, y);
7822
7823         return;
7824       }
7825       else
7826       {
7827         boolean wanna_flame = !RND(10);
7828         int dx = newx - x, dy = newy - y;
7829         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7830         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7831         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7832                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7833         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7834                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7835
7836         if ((wanna_flame ||
7837              IS_CLASSIC_ENEMY(element1) ||
7838              IS_CLASSIC_ENEMY(element2)) &&
7839             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7840             element1 != EL_FLAMES && element2 != EL_FLAMES)
7841         {
7842           ResetGfxAnimation(x, y);
7843           GfxAction[x][y] = ACTION_ATTACKING;
7844
7845           if (IS_PLAYER(x, y))
7846             DrawPlayerField(x, y);
7847           else
7848             TEST_DrawLevelField(x, y);
7849
7850           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7851
7852           MovDelay[x][y] = 50;
7853
7854           Feld[newx][newy] = EL_FLAMES;
7855           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7856             Feld[newx1][newy1] = EL_FLAMES;
7857           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7858             Feld[newx2][newy2] = EL_FLAMES;
7859
7860           return;
7861         }
7862       }
7863     }
7864     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7865              Feld[newx][newy] == EL_DIAMOND)
7866     {
7867       if (IS_MOVING(newx, newy))
7868         RemoveMovingField(newx, newy);
7869       else
7870       {
7871         Feld[newx][newy] = EL_EMPTY;
7872         TEST_DrawLevelField(newx, newy);
7873       }
7874
7875       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7876     }
7877     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7878              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7879     {
7880       if (AmoebaNr[newx][newy])
7881       {
7882         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7883         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7884             Feld[newx][newy] == EL_BD_AMOEBA)
7885           AmoebaCnt[AmoebaNr[newx][newy]]--;
7886       }
7887
7888       if (IS_MOVING(newx, newy))
7889       {
7890         RemoveMovingField(newx, newy);
7891       }
7892       else
7893       {
7894         Feld[newx][newy] = EL_EMPTY;
7895         TEST_DrawLevelField(newx, newy);
7896       }
7897
7898       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7899     }
7900     else if ((element == EL_PACMAN || element == EL_MOLE)
7901              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7902     {
7903       if (AmoebaNr[newx][newy])
7904       {
7905         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7906         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7907             Feld[newx][newy] == EL_BD_AMOEBA)
7908           AmoebaCnt[AmoebaNr[newx][newy]]--;
7909       }
7910
7911       if (element == EL_MOLE)
7912       {
7913         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7914         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7915
7916         ResetGfxAnimation(x, y);
7917         GfxAction[x][y] = ACTION_DIGGING;
7918         TEST_DrawLevelField(x, y);
7919
7920         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7921
7922         return;                         /* wait for shrinking amoeba */
7923       }
7924       else      /* element == EL_PACMAN */
7925       {
7926         Feld[newx][newy] = EL_EMPTY;
7927         TEST_DrawLevelField(newx, newy);
7928         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7929       }
7930     }
7931     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7932              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7933               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7934     {
7935       /* wait for shrinking amoeba to completely disappear */
7936       return;
7937     }
7938     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7939     {
7940       /* object was running against a wall */
7941
7942       TurnRound(x, y);
7943
7944       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7945         DrawLevelElementAnimation(x, y, element);
7946
7947       if (DONT_TOUCH(element))
7948         TestIfBadThingTouchesPlayer(x, y);
7949
7950       return;
7951     }
7952
7953     InitMovingField(x, y, MovDir[x][y]);
7954
7955     PlayLevelSoundAction(x, y, ACTION_MOVING);
7956   }
7957
7958   if (MovDir[x][y])
7959     ContinueMoving(x, y);
7960 }
7961
7962 void ContinueMoving(int x, int y)
7963 {
7964   int element = Feld[x][y];
7965   struct ElementInfo *ei = &element_info[element];
7966   int direction = MovDir[x][y];
7967   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7968   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7969   int newx = x + dx, newy = y + dy;
7970   int stored = Store[x][y];
7971   int stored_new = Store[newx][newy];
7972   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7973   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7974   boolean last_line = (newy == lev_fieldy - 1);
7975
7976   MovPos[x][y] += getElementMoveStepsize(x, y);
7977
7978   if (pushed_by_player) /* special case: moving object pushed by player */
7979     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7980
7981   if (ABS(MovPos[x][y]) < TILEX)
7982   {
7983     TEST_DrawLevelField(x, y);
7984
7985     return;     /* element is still moving */
7986   }
7987
7988   /* element reached destination field */
7989
7990   Feld[x][y] = EL_EMPTY;
7991   Feld[newx][newy] = element;
7992   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7993
7994   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7995   {
7996     element = Feld[newx][newy] = EL_ACID;
7997   }
7998   else if (element == EL_MOLE)
7999   {
8000     Feld[x][y] = EL_SAND;
8001
8002     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8003   }
8004   else if (element == EL_QUICKSAND_FILLING)
8005   {
8006     element = Feld[newx][newy] = get_next_element(element);
8007     Store[newx][newy] = Store[x][y];
8008   }
8009   else if (element == EL_QUICKSAND_EMPTYING)
8010   {
8011     Feld[x][y] = get_next_element(element);
8012     element = Feld[newx][newy] = Store[x][y];
8013   }
8014   else if (element == EL_QUICKSAND_FAST_FILLING)
8015   {
8016     element = Feld[newx][newy] = get_next_element(element);
8017     Store[newx][newy] = Store[x][y];
8018   }
8019   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8020   {
8021     Feld[x][y] = get_next_element(element);
8022     element = Feld[newx][newy] = Store[x][y];
8023   }
8024   else if (element == EL_MAGIC_WALL_FILLING)
8025   {
8026     element = Feld[newx][newy] = get_next_element(element);
8027     if (!game.magic_wall_active)
8028       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8029     Store[newx][newy] = Store[x][y];
8030   }
8031   else if (element == EL_MAGIC_WALL_EMPTYING)
8032   {
8033     Feld[x][y] = get_next_element(element);
8034     if (!game.magic_wall_active)
8035       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8036     element = Feld[newx][newy] = Store[x][y];
8037
8038     InitField(newx, newy, FALSE);
8039   }
8040   else if (element == EL_BD_MAGIC_WALL_FILLING)
8041   {
8042     element = Feld[newx][newy] = get_next_element(element);
8043     if (!game.magic_wall_active)
8044       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8045     Store[newx][newy] = Store[x][y];
8046   }
8047   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8048   {
8049     Feld[x][y] = get_next_element(element);
8050     if (!game.magic_wall_active)
8051       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8052     element = Feld[newx][newy] = Store[x][y];
8053
8054     InitField(newx, newy, FALSE);
8055   }
8056   else if (element == EL_DC_MAGIC_WALL_FILLING)
8057   {
8058     element = Feld[newx][newy] = get_next_element(element);
8059     if (!game.magic_wall_active)
8060       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8061     Store[newx][newy] = Store[x][y];
8062   }
8063   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8064   {
8065     Feld[x][y] = get_next_element(element);
8066     if (!game.magic_wall_active)
8067       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8068     element = Feld[newx][newy] = Store[x][y];
8069
8070     InitField(newx, newy, FALSE);
8071   }
8072   else if (element == EL_AMOEBA_DROPPING)
8073   {
8074     Feld[x][y] = get_next_element(element);
8075     element = Feld[newx][newy] = Store[x][y];
8076   }
8077   else if (element == EL_SOKOBAN_OBJECT)
8078   {
8079     if (Back[x][y])
8080       Feld[x][y] = Back[x][y];
8081
8082     if (Back[newx][newy])
8083       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8084
8085     Back[x][y] = Back[newx][newy] = 0;
8086   }
8087
8088   Store[x][y] = EL_EMPTY;
8089   MovPos[x][y] = 0;
8090   MovDir[x][y] = 0;
8091   MovDelay[x][y] = 0;
8092
8093   MovDelay[newx][newy] = 0;
8094
8095   if (CAN_CHANGE_OR_HAS_ACTION(element))
8096   {
8097     /* copy element change control values to new field */
8098     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8099     ChangePage[newx][newy]  = ChangePage[x][y];
8100     ChangeCount[newx][newy] = ChangeCount[x][y];
8101     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8102   }
8103
8104   CustomValue[newx][newy] = CustomValue[x][y];
8105
8106   ChangeDelay[x][y] = 0;
8107   ChangePage[x][y] = -1;
8108   ChangeCount[x][y] = 0;
8109   ChangeEvent[x][y] = -1;
8110
8111   CustomValue[x][y] = 0;
8112
8113   /* copy animation control values to new field */
8114   GfxFrame[newx][newy]  = GfxFrame[x][y];
8115   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8116   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8117   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8118
8119   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8120
8121   /* some elements can leave other elements behind after moving */
8122   if (ei->move_leave_element != EL_EMPTY &&
8123       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8124       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8125   {
8126     int move_leave_element = ei->move_leave_element;
8127
8128     /* this makes it possible to leave the removed element again */
8129     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8130       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8131
8132     Feld[x][y] = move_leave_element;
8133
8134     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8135       MovDir[x][y] = direction;
8136
8137     InitField(x, y, FALSE);
8138
8139     if (GFX_CRUMBLED(Feld[x][y]))
8140       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8141
8142     if (ELEM_IS_PLAYER(move_leave_element))
8143       RelocatePlayer(x, y, move_leave_element);
8144   }
8145
8146   /* do this after checking for left-behind element */
8147   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8148
8149   if (!CAN_MOVE(element) ||
8150       (CAN_FALL(element) && direction == MV_DOWN &&
8151        (element == EL_SPRING ||
8152         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8153         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8154     GfxDir[x][y] = MovDir[newx][newy] = 0;
8155
8156   TEST_DrawLevelField(x, y);
8157   TEST_DrawLevelField(newx, newy);
8158
8159   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8160
8161   /* prevent pushed element from moving on in pushed direction */
8162   if (pushed_by_player && CAN_MOVE(element) &&
8163       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8164       !(element_info[element].move_pattern & direction))
8165     TurnRound(newx, newy);
8166
8167   /* prevent elements on conveyor belt from moving on in last direction */
8168   if (pushed_by_conveyor && CAN_FALL(element) &&
8169       direction & MV_HORIZONTAL)
8170     MovDir[newx][newy] = 0;
8171
8172   if (!pushed_by_player)
8173   {
8174     int nextx = newx + dx, nexty = newy + dy;
8175     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8176
8177     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8178
8179     if (CAN_FALL(element) && direction == MV_DOWN)
8180       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8181
8182     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8183       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8184
8185     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8186       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8187   }
8188
8189   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8190   {
8191     TestIfBadThingTouchesPlayer(newx, newy);
8192     TestIfBadThingTouchesFriend(newx, newy);
8193
8194     if (!IS_CUSTOM_ELEMENT(element))
8195       TestIfBadThingTouchesOtherBadThing(newx, newy);
8196   }
8197   else if (element == EL_PENGUIN)
8198     TestIfFriendTouchesBadThing(newx, newy);
8199
8200   if (DONT_GET_HIT_BY(element))
8201   {
8202     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8203   }
8204
8205   /* give the player one last chance (one more frame) to move away */
8206   if (CAN_FALL(element) && direction == MV_DOWN &&
8207       (last_line || (!IS_FREE(x, newy + 1) &&
8208                      (!IS_PLAYER(x, newy + 1) ||
8209                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8210     Impact(x, newy);
8211
8212   if (pushed_by_player && !game.use_change_when_pushing_bug)
8213   {
8214     int push_side = MV_DIR_OPPOSITE(direction);
8215     struct PlayerInfo *player = PLAYERINFO(x, y);
8216
8217     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8218                                player->index_bit, push_side);
8219     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8220                                         player->index_bit, push_side);
8221   }
8222
8223   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8224     MovDelay[newx][newy] = 1;
8225
8226   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8227
8228   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8229   TestIfElementHitsCustomElement(newx, newy, direction);
8230   TestIfPlayerTouchesCustomElement(newx, newy);
8231   TestIfElementTouchesCustomElement(newx, newy);
8232
8233   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8234       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8235     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8236                              MV_DIR_OPPOSITE(direction));
8237 }
8238
8239 int AmoebeNachbarNr(int ax, int ay)
8240 {
8241   int i;
8242   int element = Feld[ax][ay];
8243   int group_nr = 0;
8244   static int xy[4][2] =
8245   {
8246     { 0, -1 },
8247     { -1, 0 },
8248     { +1, 0 },
8249     { 0, +1 }
8250   };
8251
8252   for (i = 0; i < NUM_DIRECTIONS; i++)
8253   {
8254     int x = ax + xy[i][0];
8255     int y = ay + xy[i][1];
8256
8257     if (!IN_LEV_FIELD(x, y))
8258       continue;
8259
8260     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8261       group_nr = AmoebaNr[x][y];
8262   }
8263
8264   return group_nr;
8265 }
8266
8267 void AmoebenVereinigen(int ax, int ay)
8268 {
8269   int i, x, y, xx, yy;
8270   int new_group_nr = AmoebaNr[ax][ay];
8271   static int xy[4][2] =
8272   {
8273     { 0, -1 },
8274     { -1, 0 },
8275     { +1, 0 },
8276     { 0, +1 }
8277   };
8278
8279   if (new_group_nr == 0)
8280     return;
8281
8282   for (i = 0; i < NUM_DIRECTIONS; i++)
8283   {
8284     x = ax + xy[i][0];
8285     y = ay + xy[i][1];
8286
8287     if (!IN_LEV_FIELD(x, y))
8288       continue;
8289
8290     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8291          Feld[x][y] == EL_BD_AMOEBA ||
8292          Feld[x][y] == EL_AMOEBA_DEAD) &&
8293         AmoebaNr[x][y] != new_group_nr)
8294     {
8295       int old_group_nr = AmoebaNr[x][y];
8296
8297       if (old_group_nr == 0)
8298         return;
8299
8300       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8301       AmoebaCnt[old_group_nr] = 0;
8302       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8303       AmoebaCnt2[old_group_nr] = 0;
8304
8305       SCAN_PLAYFIELD(xx, yy)
8306       {
8307         if (AmoebaNr[xx][yy] == old_group_nr)
8308           AmoebaNr[xx][yy] = new_group_nr;
8309       }
8310     }
8311   }
8312 }
8313
8314 void AmoebeUmwandeln(int ax, int ay)
8315 {
8316   int i, x, y;
8317
8318   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8319   {
8320     int group_nr = AmoebaNr[ax][ay];
8321
8322 #ifdef DEBUG
8323     if (group_nr == 0)
8324     {
8325       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8326       printf("AmoebeUmwandeln(): This should never happen!\n");
8327       return;
8328     }
8329 #endif
8330
8331     SCAN_PLAYFIELD(x, y)
8332     {
8333       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8334       {
8335         AmoebaNr[x][y] = 0;
8336         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8337       }
8338     }
8339
8340     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8341                             SND_AMOEBA_TURNING_TO_GEM :
8342                             SND_AMOEBA_TURNING_TO_ROCK));
8343     Bang(ax, ay);
8344   }
8345   else
8346   {
8347     static int xy[4][2] =
8348     {
8349       { 0, -1 },
8350       { -1, 0 },
8351       { +1, 0 },
8352       { 0, +1 }
8353     };
8354
8355     for (i = 0; i < NUM_DIRECTIONS; i++)
8356     {
8357       x = ax + xy[i][0];
8358       y = ay + xy[i][1];
8359
8360       if (!IN_LEV_FIELD(x, y))
8361         continue;
8362
8363       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8364       {
8365         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8366                               SND_AMOEBA_TURNING_TO_GEM :
8367                               SND_AMOEBA_TURNING_TO_ROCK));
8368         Bang(x, y);
8369       }
8370     }
8371   }
8372 }
8373
8374 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8375 {
8376   int x, y;
8377   int group_nr = AmoebaNr[ax][ay];
8378   boolean done = FALSE;
8379
8380 #ifdef DEBUG
8381   if (group_nr == 0)
8382   {
8383     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8384     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8385     return;
8386   }
8387 #endif
8388
8389   SCAN_PLAYFIELD(x, y)
8390   {
8391     if (AmoebaNr[x][y] == group_nr &&
8392         (Feld[x][y] == EL_AMOEBA_DEAD ||
8393          Feld[x][y] == EL_BD_AMOEBA ||
8394          Feld[x][y] == EL_AMOEBA_GROWING))
8395     {
8396       AmoebaNr[x][y] = 0;
8397       Feld[x][y] = new_element;
8398       InitField(x, y, FALSE);
8399       TEST_DrawLevelField(x, y);
8400       done = TRUE;
8401     }
8402   }
8403
8404   if (done)
8405     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8406                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8407                             SND_BD_AMOEBA_TURNING_TO_GEM));
8408 }
8409
8410 void AmoebeWaechst(int x, int y)
8411 {
8412   static unsigned int sound_delay = 0;
8413   static unsigned int sound_delay_value = 0;
8414
8415   if (!MovDelay[x][y])          /* start new growing cycle */
8416   {
8417     MovDelay[x][y] = 7;
8418
8419     if (DelayReached(&sound_delay, sound_delay_value))
8420     {
8421       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8422       sound_delay_value = 30;
8423     }
8424   }
8425
8426   if (MovDelay[x][y])           /* wait some time before growing bigger */
8427   {
8428     MovDelay[x][y]--;
8429     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8430     {
8431       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8432                                            6 - MovDelay[x][y]);
8433
8434       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8435     }
8436
8437     if (!MovDelay[x][y])
8438     {
8439       Feld[x][y] = Store[x][y];
8440       Store[x][y] = 0;
8441       TEST_DrawLevelField(x, y);
8442     }
8443   }
8444 }
8445
8446 void AmoebaDisappearing(int x, int y)
8447 {
8448   static unsigned int sound_delay = 0;
8449   static unsigned int sound_delay_value = 0;
8450
8451   if (!MovDelay[x][y])          /* start new shrinking cycle */
8452   {
8453     MovDelay[x][y] = 7;
8454
8455     if (DelayReached(&sound_delay, sound_delay_value))
8456       sound_delay_value = 30;
8457   }
8458
8459   if (MovDelay[x][y])           /* wait some time before shrinking */
8460   {
8461     MovDelay[x][y]--;
8462     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8463     {
8464       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8465                                            6 - MovDelay[x][y]);
8466
8467       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8468     }
8469
8470     if (!MovDelay[x][y])
8471     {
8472       Feld[x][y] = EL_EMPTY;
8473       TEST_DrawLevelField(x, y);
8474
8475       /* don't let mole enter this field in this cycle;
8476          (give priority to objects falling to this field from above) */
8477       Stop[x][y] = TRUE;
8478     }
8479   }
8480 }
8481
8482 void AmoebeAbleger(int ax, int ay)
8483 {
8484   int i;
8485   int element = Feld[ax][ay];
8486   int graphic = el2img(element);
8487   int newax = ax, neway = ay;
8488   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8489   static int xy[4][2] =
8490   {
8491     { 0, -1 },
8492     { -1, 0 },
8493     { +1, 0 },
8494     { 0, +1 }
8495   };
8496
8497   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8498   {
8499     Feld[ax][ay] = EL_AMOEBA_DEAD;
8500     TEST_DrawLevelField(ax, ay);
8501     return;
8502   }
8503
8504   if (IS_ANIMATED(graphic))
8505     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8506
8507   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8508     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8509
8510   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8511   {
8512     MovDelay[ax][ay]--;
8513     if (MovDelay[ax][ay])
8514       return;
8515   }
8516
8517   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8518   {
8519     int start = RND(4);
8520     int x = ax + xy[start][0];
8521     int y = ay + xy[start][1];
8522
8523     if (!IN_LEV_FIELD(x, y))
8524       return;
8525
8526     if (IS_FREE(x, y) ||
8527         CAN_GROW_INTO(Feld[x][y]) ||
8528         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8529         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8530     {
8531       newax = x;
8532       neway = y;
8533     }
8534
8535     if (newax == ax && neway == ay)
8536       return;
8537   }
8538   else                          /* normal or "filled" (BD style) amoeba */
8539   {
8540     int start = RND(4);
8541     boolean waiting_for_player = FALSE;
8542
8543     for (i = 0; i < NUM_DIRECTIONS; i++)
8544     {
8545       int j = (start + i) % 4;
8546       int x = ax + xy[j][0];
8547       int y = ay + xy[j][1];
8548
8549       if (!IN_LEV_FIELD(x, y))
8550         continue;
8551
8552       if (IS_FREE(x, y) ||
8553           CAN_GROW_INTO(Feld[x][y]) ||
8554           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8555           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8556       {
8557         newax = x;
8558         neway = y;
8559         break;
8560       }
8561       else if (IS_PLAYER(x, y))
8562         waiting_for_player = TRUE;
8563     }
8564
8565     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8566     {
8567       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8568       {
8569         Feld[ax][ay] = EL_AMOEBA_DEAD;
8570         TEST_DrawLevelField(ax, ay);
8571         AmoebaCnt[AmoebaNr[ax][ay]]--;
8572
8573         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8574         {
8575           if (element == EL_AMOEBA_FULL)
8576             AmoebeUmwandeln(ax, ay);
8577           else if (element == EL_BD_AMOEBA)
8578             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8579         }
8580       }
8581       return;
8582     }
8583     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8584     {
8585       /* amoeba gets larger by growing in some direction */
8586
8587       int new_group_nr = AmoebaNr[ax][ay];
8588
8589 #ifdef DEBUG
8590   if (new_group_nr == 0)
8591   {
8592     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8593     printf("AmoebeAbleger(): This should never happen!\n");
8594     return;
8595   }
8596 #endif
8597
8598       AmoebaNr[newax][neway] = new_group_nr;
8599       AmoebaCnt[new_group_nr]++;
8600       AmoebaCnt2[new_group_nr]++;
8601
8602       /* if amoeba touches other amoeba(s) after growing, unify them */
8603       AmoebenVereinigen(newax, neway);
8604
8605       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8606       {
8607         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8608         return;
8609       }
8610     }
8611   }
8612
8613   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8614       (neway == lev_fieldy - 1 && newax != ax))
8615   {
8616     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8617     Store[newax][neway] = element;
8618   }
8619   else if (neway == ay || element == EL_EMC_DRIPPER)
8620   {
8621     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8622
8623     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8624   }
8625   else
8626   {
8627     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8628     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8629     Store[ax][ay] = EL_AMOEBA_DROP;
8630     ContinueMoving(ax, ay);
8631     return;
8632   }
8633
8634   TEST_DrawLevelField(newax, neway);
8635 }
8636
8637 void Life(int ax, int ay)
8638 {
8639   int x1, y1, x2, y2;
8640   int life_time = 40;
8641   int element = Feld[ax][ay];
8642   int graphic = el2img(element);
8643   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8644                          level.biomaze);
8645   boolean changed = FALSE;
8646
8647   if (IS_ANIMATED(graphic))
8648     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8649
8650   if (Stop[ax][ay])
8651     return;
8652
8653   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8654     MovDelay[ax][ay] = life_time;
8655
8656   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8657   {
8658     MovDelay[ax][ay]--;
8659     if (MovDelay[ax][ay])
8660       return;
8661   }
8662
8663   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8664   {
8665     int xx = ax+x1, yy = ay+y1;
8666     int nachbarn = 0;
8667
8668     if (!IN_LEV_FIELD(xx, yy))
8669       continue;
8670
8671     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8672     {
8673       int x = xx+x2, y = yy+y2;
8674
8675       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8676         continue;
8677
8678       if (((Feld[x][y] == element ||
8679             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8680            !Stop[x][y]) ||
8681           (IS_FREE(x, y) && Stop[x][y]))
8682         nachbarn++;
8683     }
8684
8685     if (xx == ax && yy == ay)           /* field in the middle */
8686     {
8687       if (nachbarn < life_parameter[0] ||
8688           nachbarn > life_parameter[1])
8689       {
8690         Feld[xx][yy] = EL_EMPTY;
8691         if (!Stop[xx][yy])
8692           TEST_DrawLevelField(xx, yy);
8693         Stop[xx][yy] = TRUE;
8694         changed = TRUE;
8695       }
8696     }
8697     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8698     {                                   /* free border field */
8699       if (nachbarn >= life_parameter[2] &&
8700           nachbarn <= life_parameter[3])
8701       {
8702         Feld[xx][yy] = element;
8703         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8704         if (!Stop[xx][yy])
8705           TEST_DrawLevelField(xx, yy);
8706         Stop[xx][yy] = TRUE;
8707         changed = TRUE;
8708       }
8709     }
8710   }
8711
8712   if (changed)
8713     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8714                    SND_GAME_OF_LIFE_GROWING);
8715 }
8716
8717 static void InitRobotWheel(int x, int y)
8718 {
8719   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8720 }
8721
8722 static void RunRobotWheel(int x, int y)
8723 {
8724   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8725 }
8726
8727 static void StopRobotWheel(int x, int y)
8728 {
8729   if (ZX == x && ZY == y)
8730   {
8731     ZX = ZY = -1;
8732
8733     game.robot_wheel_active = FALSE;
8734   }
8735 }
8736
8737 static void InitTimegateWheel(int x, int y)
8738 {
8739   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8740 }
8741
8742 static void RunTimegateWheel(int x, int y)
8743 {
8744   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8745 }
8746
8747 static void InitMagicBallDelay(int x, int y)
8748 {
8749   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8750 }
8751
8752 static void ActivateMagicBall(int bx, int by)
8753 {
8754   int x, y;
8755
8756   if (level.ball_random)
8757   {
8758     int pos_border = RND(8);    /* select one of the eight border elements */
8759     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8760     int xx = pos_content % 3;
8761     int yy = pos_content / 3;
8762
8763     x = bx - 1 + xx;
8764     y = by - 1 + yy;
8765
8766     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8767       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8768   }
8769   else
8770   {
8771     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8772     {
8773       int xx = x - bx + 1;
8774       int yy = y - by + 1;
8775
8776       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8777         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8778     }
8779   }
8780
8781   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8782 }
8783
8784 void CheckExit(int x, int y)
8785 {
8786   if (local_player->gems_still_needed > 0 ||
8787       local_player->sokobanfields_still_needed > 0 ||
8788       local_player->lights_still_needed > 0)
8789   {
8790     int element = Feld[x][y];
8791     int graphic = el2img(element);
8792
8793     if (IS_ANIMATED(graphic))
8794       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8795
8796     return;
8797   }
8798
8799   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8800     return;
8801
8802   Feld[x][y] = EL_EXIT_OPENING;
8803
8804   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8805 }
8806
8807 void CheckExitEM(int x, int y)
8808 {
8809   if (local_player->gems_still_needed > 0 ||
8810       local_player->sokobanfields_still_needed > 0 ||
8811       local_player->lights_still_needed > 0)
8812   {
8813     int element = Feld[x][y];
8814     int graphic = el2img(element);
8815
8816     if (IS_ANIMATED(graphic))
8817       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8818
8819     return;
8820   }
8821
8822   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8823     return;
8824
8825   Feld[x][y] = EL_EM_EXIT_OPENING;
8826
8827   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8828 }
8829
8830 void CheckExitSteel(int x, int y)
8831 {
8832   if (local_player->gems_still_needed > 0 ||
8833       local_player->sokobanfields_still_needed > 0 ||
8834       local_player->lights_still_needed > 0)
8835   {
8836     int element = Feld[x][y];
8837     int graphic = el2img(element);
8838
8839     if (IS_ANIMATED(graphic))
8840       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8841
8842     return;
8843   }
8844
8845   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8846     return;
8847
8848   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8849
8850   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8851 }
8852
8853 void CheckExitSteelEM(int x, int y)
8854 {
8855   if (local_player->gems_still_needed > 0 ||
8856       local_player->sokobanfields_still_needed > 0 ||
8857       local_player->lights_still_needed > 0)
8858   {
8859     int element = Feld[x][y];
8860     int graphic = el2img(element);
8861
8862     if (IS_ANIMATED(graphic))
8863       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8864
8865     return;
8866   }
8867
8868   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8869     return;
8870
8871   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8872
8873   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8874 }
8875
8876 void CheckExitSP(int x, int y)
8877 {
8878   if (local_player->gems_still_needed > 0)
8879   {
8880     int element = Feld[x][y];
8881     int graphic = el2img(element);
8882
8883     if (IS_ANIMATED(graphic))
8884       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8885
8886     return;
8887   }
8888
8889   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8890     return;
8891
8892   Feld[x][y] = EL_SP_EXIT_OPENING;
8893
8894   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8895 }
8896
8897 static void CloseAllOpenTimegates()
8898 {
8899   int x, y;
8900
8901   SCAN_PLAYFIELD(x, y)
8902   {
8903     int element = Feld[x][y];
8904
8905     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8906     {
8907       Feld[x][y] = EL_TIMEGATE_CLOSING;
8908
8909       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8910     }
8911   }
8912 }
8913
8914 void DrawTwinkleOnField(int x, int y)
8915 {
8916   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8917     return;
8918
8919   if (Feld[x][y] == EL_BD_DIAMOND)
8920     return;
8921
8922   if (MovDelay[x][y] == 0)      /* next animation frame */
8923     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8924
8925   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8926   {
8927     MovDelay[x][y]--;
8928
8929     DrawLevelElementAnimation(x, y, Feld[x][y]);
8930
8931     if (MovDelay[x][y] != 0)
8932     {
8933       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8934                                            10 - MovDelay[x][y]);
8935
8936       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8937     }
8938   }
8939 }
8940
8941 void MauerWaechst(int x, int y)
8942 {
8943   int delay = 6;
8944
8945   if (!MovDelay[x][y])          /* next animation frame */
8946     MovDelay[x][y] = 3 * delay;
8947
8948   if (MovDelay[x][y])           /* wait some time before next frame */
8949   {
8950     MovDelay[x][y]--;
8951
8952     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8953     {
8954       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8955       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8956
8957       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8958     }
8959
8960     if (!MovDelay[x][y])
8961     {
8962       if (MovDir[x][y] == MV_LEFT)
8963       {
8964         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8965           TEST_DrawLevelField(x - 1, y);
8966       }
8967       else if (MovDir[x][y] == MV_RIGHT)
8968       {
8969         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8970           TEST_DrawLevelField(x + 1, y);
8971       }
8972       else if (MovDir[x][y] == MV_UP)
8973       {
8974         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8975           TEST_DrawLevelField(x, y - 1);
8976       }
8977       else
8978       {
8979         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8980           TEST_DrawLevelField(x, y + 1);
8981       }
8982
8983       Feld[x][y] = Store[x][y];
8984       Store[x][y] = 0;
8985       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8986       TEST_DrawLevelField(x, y);
8987     }
8988   }
8989 }
8990
8991 void MauerAbleger(int ax, int ay)
8992 {
8993   int element = Feld[ax][ay];
8994   int graphic = el2img(element);
8995   boolean oben_frei = FALSE, unten_frei = FALSE;
8996   boolean links_frei = FALSE, rechts_frei = FALSE;
8997   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8998   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8999   boolean new_wall = FALSE;
9000
9001   if (IS_ANIMATED(graphic))
9002     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9003
9004   if (!MovDelay[ax][ay])        /* start building new wall */
9005     MovDelay[ax][ay] = 6;
9006
9007   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9008   {
9009     MovDelay[ax][ay]--;
9010     if (MovDelay[ax][ay])
9011       return;
9012   }
9013
9014   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9015     oben_frei = TRUE;
9016   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9017     unten_frei = TRUE;
9018   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9019     links_frei = TRUE;
9020   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9021     rechts_frei = TRUE;
9022
9023   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9024       element == EL_EXPANDABLE_WALL_ANY)
9025   {
9026     if (oben_frei)
9027     {
9028       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9029       Store[ax][ay-1] = element;
9030       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9031       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9032         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9033                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9034       new_wall = TRUE;
9035     }
9036     if (unten_frei)
9037     {
9038       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9039       Store[ax][ay+1] = element;
9040       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9041       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9042         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9043                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9044       new_wall = TRUE;
9045     }
9046   }
9047
9048   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9049       element == EL_EXPANDABLE_WALL_ANY ||
9050       element == EL_EXPANDABLE_WALL ||
9051       element == EL_BD_EXPANDABLE_WALL)
9052   {
9053     if (links_frei)
9054     {
9055       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9056       Store[ax-1][ay] = element;
9057       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9058       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9059         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9060                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9061       new_wall = TRUE;
9062     }
9063
9064     if (rechts_frei)
9065     {
9066       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9067       Store[ax+1][ay] = element;
9068       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9069       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9070         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9071                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9072       new_wall = TRUE;
9073     }
9074   }
9075
9076   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9077     TEST_DrawLevelField(ax, ay);
9078
9079   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9080     oben_massiv = TRUE;
9081   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9082     unten_massiv = TRUE;
9083   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9084     links_massiv = TRUE;
9085   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9086     rechts_massiv = TRUE;
9087
9088   if (((oben_massiv && unten_massiv) ||
9089        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9090        element == EL_EXPANDABLE_WALL) &&
9091       ((links_massiv && rechts_massiv) ||
9092        element == EL_EXPANDABLE_WALL_VERTICAL))
9093     Feld[ax][ay] = EL_WALL;
9094
9095   if (new_wall)
9096     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9097 }
9098
9099 void MauerAblegerStahl(int ax, int ay)
9100 {
9101   int element = Feld[ax][ay];
9102   int graphic = el2img(element);
9103   boolean oben_frei = FALSE, unten_frei = FALSE;
9104   boolean links_frei = FALSE, rechts_frei = FALSE;
9105   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9106   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9107   boolean new_wall = FALSE;
9108
9109   if (IS_ANIMATED(graphic))
9110     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9111
9112   if (!MovDelay[ax][ay])        /* start building new wall */
9113     MovDelay[ax][ay] = 6;
9114
9115   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9116   {
9117     MovDelay[ax][ay]--;
9118     if (MovDelay[ax][ay])
9119       return;
9120   }
9121
9122   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9123     oben_frei = TRUE;
9124   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9125     unten_frei = TRUE;
9126   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9127     links_frei = TRUE;
9128   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9129     rechts_frei = TRUE;
9130
9131   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9132       element == EL_EXPANDABLE_STEELWALL_ANY)
9133   {
9134     if (oben_frei)
9135     {
9136       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9137       Store[ax][ay-1] = element;
9138       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9139       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9140         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9141                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9142       new_wall = TRUE;
9143     }
9144     if (unten_frei)
9145     {
9146       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9147       Store[ax][ay+1] = element;
9148       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9149       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9150         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9151                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9152       new_wall = TRUE;
9153     }
9154   }
9155
9156   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9157       element == EL_EXPANDABLE_STEELWALL_ANY)
9158   {
9159     if (links_frei)
9160     {
9161       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9162       Store[ax-1][ay] = element;
9163       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9164       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9165         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9166                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9167       new_wall = TRUE;
9168     }
9169
9170     if (rechts_frei)
9171     {
9172       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9173       Store[ax+1][ay] = element;
9174       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9175       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9176         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9177                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9178       new_wall = TRUE;
9179     }
9180   }
9181
9182   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9183     oben_massiv = TRUE;
9184   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9185     unten_massiv = TRUE;
9186   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9187     links_massiv = TRUE;
9188   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9189     rechts_massiv = TRUE;
9190
9191   if (((oben_massiv && unten_massiv) ||
9192        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9193       ((links_massiv && rechts_massiv) ||
9194        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9195     Feld[ax][ay] = EL_STEELWALL;
9196
9197   if (new_wall)
9198     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9199 }
9200
9201 void CheckForDragon(int x, int y)
9202 {
9203   int i, j;
9204   boolean dragon_found = FALSE;
9205   static int xy[4][2] =
9206   {
9207     { 0, -1 },
9208     { -1, 0 },
9209     { +1, 0 },
9210     { 0, +1 }
9211   };
9212
9213   for (i = 0; i < NUM_DIRECTIONS; i++)
9214   {
9215     for (j = 0; j < 4; j++)
9216     {
9217       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9218
9219       if (IN_LEV_FIELD(xx, yy) &&
9220           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9221       {
9222         if (Feld[xx][yy] == EL_DRAGON)
9223           dragon_found = TRUE;
9224       }
9225       else
9226         break;
9227     }
9228   }
9229
9230   if (!dragon_found)
9231   {
9232     for (i = 0; i < NUM_DIRECTIONS; i++)
9233     {
9234       for (j = 0; j < 3; j++)
9235       {
9236         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9237   
9238         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9239         {
9240           Feld[xx][yy] = EL_EMPTY;
9241           TEST_DrawLevelField(xx, yy);
9242         }
9243         else
9244           break;
9245       }
9246     }
9247   }
9248 }
9249
9250 static void InitBuggyBase(int x, int y)
9251 {
9252   int element = Feld[x][y];
9253   int activating_delay = FRAMES_PER_SECOND / 4;
9254
9255   ChangeDelay[x][y] =
9256     (element == EL_SP_BUGGY_BASE ?
9257      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9258      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9259      activating_delay :
9260      element == EL_SP_BUGGY_BASE_ACTIVE ?
9261      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9262 }
9263
9264 static void WarnBuggyBase(int x, int y)
9265 {
9266   int i;
9267   static int xy[4][2] =
9268   {
9269     { 0, -1 },
9270     { -1, 0 },
9271     { +1, 0 },
9272     { 0, +1 }
9273   };
9274
9275   for (i = 0; i < NUM_DIRECTIONS; i++)
9276   {
9277     int xx = x + xy[i][0];
9278     int yy = y + xy[i][1];
9279
9280     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9281     {
9282       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9283
9284       break;
9285     }
9286   }
9287 }
9288
9289 static void InitTrap(int x, int y)
9290 {
9291   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9292 }
9293
9294 static void ActivateTrap(int x, int y)
9295 {
9296   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9297 }
9298
9299 static void ChangeActiveTrap(int x, int y)
9300 {
9301   int graphic = IMG_TRAP_ACTIVE;
9302
9303   /* if new animation frame was drawn, correct crumbled sand border */
9304   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9305     TEST_DrawLevelFieldCrumbled(x, y);
9306 }
9307
9308 static int getSpecialActionElement(int element, int number, int base_element)
9309 {
9310   return (element != EL_EMPTY ? element :
9311           number != -1 ? base_element + number - 1 :
9312           EL_EMPTY);
9313 }
9314
9315 static int getModifiedActionNumber(int value_old, int operator, int operand,
9316                                    int value_min, int value_max)
9317 {
9318   int value_new = (operator == CA_MODE_SET      ? operand :
9319                    operator == CA_MODE_ADD      ? value_old + operand :
9320                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9321                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9322                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9323                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9324                    value_old);
9325
9326   return (value_new < value_min ? value_min :
9327           value_new > value_max ? value_max :
9328           value_new);
9329 }
9330
9331 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9332 {
9333   struct ElementInfo *ei = &element_info[element];
9334   struct ElementChangeInfo *change = &ei->change_page[page];
9335   int target_element = change->target_element;
9336   int action_type = change->action_type;
9337   int action_mode = change->action_mode;
9338   int action_arg = change->action_arg;
9339   int action_element = change->action_element;
9340   int i;
9341
9342   if (!change->has_action)
9343     return;
9344
9345   /* ---------- determine action paramater values -------------------------- */
9346
9347   int level_time_value =
9348     (level.time > 0 ? TimeLeft :
9349      TimePlayed);
9350
9351   int action_arg_element_raw =
9352     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9353      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9354      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9355      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9356      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9357      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9358      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9359      EL_EMPTY);
9360   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9361
9362   int action_arg_direction =
9363     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9364      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9365      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9366      change->actual_trigger_side :
9367      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9368      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9369      MV_NONE);
9370
9371   int action_arg_number_min =
9372     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9373      CA_ARG_MIN);
9374
9375   int action_arg_number_max =
9376     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9377      action_type == CA_SET_LEVEL_GEMS ? 999 :
9378      action_type == CA_SET_LEVEL_TIME ? 9999 :
9379      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9380      action_type == CA_SET_CE_VALUE ? 9999 :
9381      action_type == CA_SET_CE_SCORE ? 9999 :
9382      CA_ARG_MAX);
9383
9384   int action_arg_number_reset =
9385     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9386      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9387      action_type == CA_SET_LEVEL_TIME ? level.time :
9388      action_type == CA_SET_LEVEL_SCORE ? 0 :
9389      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9390      action_type == CA_SET_CE_SCORE ? 0 :
9391      0);
9392
9393   int action_arg_number =
9394     (action_arg <= CA_ARG_MAX ? action_arg :
9395      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9396      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9397      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9398      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9399      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9400      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9401      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9402      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9403      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9404      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9405      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9406      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9407      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9408      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9409      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9410      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9411      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9412      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9413      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9414      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9415      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9416      -1);
9417
9418   int action_arg_number_old =
9419     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9420      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9421      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9422      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9423      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9424      0);
9425
9426   int action_arg_number_new =
9427     getModifiedActionNumber(action_arg_number_old,
9428                             action_mode, action_arg_number,
9429                             action_arg_number_min, action_arg_number_max);
9430
9431   int trigger_player_bits =
9432     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9433      change->actual_trigger_player_bits : change->trigger_player);
9434
9435   int action_arg_player_bits =
9436     (action_arg >= CA_ARG_PLAYER_1 &&
9437      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9438      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9439      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9440      PLAYER_BITS_ANY);
9441
9442   /* ---------- execute action  -------------------------------------------- */
9443
9444   switch (action_type)
9445   {
9446     case CA_NO_ACTION:
9447     {
9448       return;
9449     }
9450
9451     /* ---------- level actions  ------------------------------------------- */
9452
9453     case CA_RESTART_LEVEL:
9454     {
9455       game.restart_level = TRUE;
9456
9457       break;
9458     }
9459
9460     case CA_SHOW_ENVELOPE:
9461     {
9462       int element = getSpecialActionElement(action_arg_element,
9463                                             action_arg_number, EL_ENVELOPE_1);
9464
9465       if (IS_ENVELOPE(element))
9466         local_player->show_envelope = element;
9467
9468       break;
9469     }
9470
9471     case CA_SET_LEVEL_TIME:
9472     {
9473       if (level.time > 0)       /* only modify limited time value */
9474       {
9475         TimeLeft = action_arg_number_new;
9476
9477         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9478
9479         DisplayGameControlValues();
9480
9481         if (!TimeLeft && setup.time_limit)
9482           for (i = 0; i < MAX_PLAYERS; i++)
9483             KillPlayer(&stored_player[i]);
9484       }
9485
9486       break;
9487     }
9488
9489     case CA_SET_LEVEL_SCORE:
9490     {
9491       local_player->score = action_arg_number_new;
9492
9493       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9494
9495       DisplayGameControlValues();
9496
9497       break;
9498     }
9499
9500     case CA_SET_LEVEL_GEMS:
9501     {
9502       local_player->gems_still_needed = action_arg_number_new;
9503
9504       game.snapshot.collected_item = TRUE;
9505
9506       game_panel_controls[GAME_PANEL_GEMS].value =
9507         local_player->gems_still_needed;
9508
9509       DisplayGameControlValues();
9510
9511       break;
9512     }
9513
9514     case CA_SET_LEVEL_WIND:
9515     {
9516       game.wind_direction = action_arg_direction;
9517
9518       break;
9519     }
9520
9521     case CA_SET_LEVEL_RANDOM_SEED:
9522     {
9523       /* ensure that setting a new random seed while playing is predictable */
9524       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9525
9526       break;
9527     }
9528
9529     /* ---------- player actions  ------------------------------------------ */
9530
9531     case CA_MOVE_PLAYER:
9532     {
9533       /* automatically move to the next field in specified direction */
9534       for (i = 0; i < MAX_PLAYERS; i++)
9535         if (trigger_player_bits & (1 << i))
9536           stored_player[i].programmed_action = action_arg_direction;
9537
9538       break;
9539     }
9540
9541     case CA_EXIT_PLAYER:
9542     {
9543       for (i = 0; i < MAX_PLAYERS; i++)
9544         if (action_arg_player_bits & (1 << i))
9545           PlayerWins(&stored_player[i]);
9546
9547       break;
9548     }
9549
9550     case CA_KILL_PLAYER:
9551     {
9552       for (i = 0; i < MAX_PLAYERS; i++)
9553         if (action_arg_player_bits & (1 << i))
9554           KillPlayer(&stored_player[i]);
9555
9556       break;
9557     }
9558
9559     case CA_SET_PLAYER_KEYS:
9560     {
9561       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9562       int element = getSpecialActionElement(action_arg_element,
9563                                             action_arg_number, EL_KEY_1);
9564
9565       if (IS_KEY(element))
9566       {
9567         for (i = 0; i < MAX_PLAYERS; i++)
9568         {
9569           if (trigger_player_bits & (1 << i))
9570           {
9571             stored_player[i].key[KEY_NR(element)] = key_state;
9572
9573             DrawGameDoorValues();
9574           }
9575         }
9576       }
9577
9578       break;
9579     }
9580
9581     case CA_SET_PLAYER_SPEED:
9582     {
9583       for (i = 0; i < MAX_PLAYERS; i++)
9584       {
9585         if (trigger_player_bits & (1 << i))
9586         {
9587           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9588
9589           if (action_arg == CA_ARG_SPEED_FASTER &&
9590               stored_player[i].cannot_move)
9591           {
9592             action_arg_number = STEPSIZE_VERY_SLOW;
9593           }
9594           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9595                    action_arg == CA_ARG_SPEED_FASTER)
9596           {
9597             action_arg_number = 2;
9598             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9599                            CA_MODE_MULTIPLY);
9600           }
9601           else if (action_arg == CA_ARG_NUMBER_RESET)
9602           {
9603             action_arg_number = level.initial_player_stepsize[i];
9604           }
9605
9606           move_stepsize =
9607             getModifiedActionNumber(move_stepsize,
9608                                     action_mode,
9609                                     action_arg_number,
9610                                     action_arg_number_min,
9611                                     action_arg_number_max);
9612
9613           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9614         }
9615       }
9616
9617       break;
9618     }
9619
9620     case CA_SET_PLAYER_SHIELD:
9621     {
9622       for (i = 0; i < MAX_PLAYERS; i++)
9623       {
9624         if (trigger_player_bits & (1 << i))
9625         {
9626           if (action_arg == CA_ARG_SHIELD_OFF)
9627           {
9628             stored_player[i].shield_normal_time_left = 0;
9629             stored_player[i].shield_deadly_time_left = 0;
9630           }
9631           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9632           {
9633             stored_player[i].shield_normal_time_left = 999999;
9634           }
9635           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9636           {
9637             stored_player[i].shield_normal_time_left = 999999;
9638             stored_player[i].shield_deadly_time_left = 999999;
9639           }
9640         }
9641       }
9642
9643       break;
9644     }
9645
9646     case CA_SET_PLAYER_GRAVITY:
9647     {
9648       for (i = 0; i < MAX_PLAYERS; i++)
9649       {
9650         if (trigger_player_bits & (1 << i))
9651         {
9652           stored_player[i].gravity =
9653             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9654              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9655              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9656              stored_player[i].gravity);
9657         }
9658       }
9659
9660       break;
9661     }
9662
9663     case CA_SET_PLAYER_ARTWORK:
9664     {
9665       for (i = 0; i < MAX_PLAYERS; i++)
9666       {
9667         if (trigger_player_bits & (1 << i))
9668         {
9669           int artwork_element = action_arg_element;
9670
9671           if (action_arg == CA_ARG_ELEMENT_RESET)
9672             artwork_element =
9673               (level.use_artwork_element[i] ? level.artwork_element[i] :
9674                stored_player[i].element_nr);
9675
9676           if (stored_player[i].artwork_element != artwork_element)
9677             stored_player[i].Frame = 0;
9678
9679           stored_player[i].artwork_element = artwork_element;
9680
9681           SetPlayerWaiting(&stored_player[i], FALSE);
9682
9683           /* set number of special actions for bored and sleeping animation */
9684           stored_player[i].num_special_action_bored =
9685             get_num_special_action(artwork_element,
9686                                    ACTION_BORING_1, ACTION_BORING_LAST);
9687           stored_player[i].num_special_action_sleeping =
9688             get_num_special_action(artwork_element,
9689                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9690         }
9691       }
9692
9693       break;
9694     }
9695
9696     case CA_SET_PLAYER_INVENTORY:
9697     {
9698       for (i = 0; i < MAX_PLAYERS; i++)
9699       {
9700         struct PlayerInfo *player = &stored_player[i];
9701         int j, k;
9702
9703         if (trigger_player_bits & (1 << i))
9704         {
9705           int inventory_element = action_arg_element;
9706
9707           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9708               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9709               action_arg == CA_ARG_ELEMENT_ACTION)
9710           {
9711             int element = inventory_element;
9712             int collect_count = element_info[element].collect_count_initial;
9713
9714             if (!IS_CUSTOM_ELEMENT(element))
9715               collect_count = 1;
9716
9717             if (collect_count == 0)
9718               player->inventory_infinite_element = element;
9719             else
9720               for (k = 0; k < collect_count; k++)
9721                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9722                   player->inventory_element[player->inventory_size++] =
9723                     element;
9724           }
9725           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9726                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9727                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9728           {
9729             if (player->inventory_infinite_element != EL_UNDEFINED &&
9730                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9731                                      action_arg_element_raw))
9732               player->inventory_infinite_element = EL_UNDEFINED;
9733
9734             for (k = 0, j = 0; j < player->inventory_size; j++)
9735             {
9736               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9737                                         action_arg_element_raw))
9738                 player->inventory_element[k++] = player->inventory_element[j];
9739             }
9740
9741             player->inventory_size = k;
9742           }
9743           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9744           {
9745             if (player->inventory_size > 0)
9746             {
9747               for (j = 0; j < player->inventory_size - 1; j++)
9748                 player->inventory_element[j] = player->inventory_element[j + 1];
9749
9750               player->inventory_size--;
9751             }
9752           }
9753           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9754           {
9755             if (player->inventory_size > 0)
9756               player->inventory_size--;
9757           }
9758           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9759           {
9760             player->inventory_infinite_element = EL_UNDEFINED;
9761             player->inventory_size = 0;
9762           }
9763           else if (action_arg == CA_ARG_INVENTORY_RESET)
9764           {
9765             player->inventory_infinite_element = EL_UNDEFINED;
9766             player->inventory_size = 0;
9767
9768             if (level.use_initial_inventory[i])
9769             {
9770               for (j = 0; j < level.initial_inventory_size[i]; j++)
9771               {
9772                 int element = level.initial_inventory_content[i][j];
9773                 int collect_count = element_info[element].collect_count_initial;
9774
9775                 if (!IS_CUSTOM_ELEMENT(element))
9776                   collect_count = 1;
9777
9778                 if (collect_count == 0)
9779                   player->inventory_infinite_element = element;
9780                 else
9781                   for (k = 0; k < collect_count; k++)
9782                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9783                       player->inventory_element[player->inventory_size++] =
9784                         element;
9785               }
9786             }
9787           }
9788         }
9789       }
9790
9791       break;
9792     }
9793
9794     /* ---------- CE actions  ---------------------------------------------- */
9795
9796     case CA_SET_CE_VALUE:
9797     {
9798       int last_ce_value = CustomValue[x][y];
9799
9800       CustomValue[x][y] = action_arg_number_new;
9801
9802       if (CustomValue[x][y] != last_ce_value)
9803       {
9804         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9805         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9806
9807         if (CustomValue[x][y] == 0)
9808         {
9809           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9810           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9811         }
9812       }
9813
9814       break;
9815     }
9816
9817     case CA_SET_CE_SCORE:
9818     {
9819       int last_ce_score = ei->collect_score;
9820
9821       ei->collect_score = action_arg_number_new;
9822
9823       if (ei->collect_score != last_ce_score)
9824       {
9825         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9826         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9827
9828         if (ei->collect_score == 0)
9829         {
9830           int xx, yy;
9831
9832           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9833           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9834
9835           /*
9836             This is a very special case that seems to be a mixture between
9837             CheckElementChange() and CheckTriggeredElementChange(): while
9838             the first one only affects single elements that are triggered
9839             directly, the second one affects multiple elements in the playfield
9840             that are triggered indirectly by another element. This is a third
9841             case: Changing the CE score always affects multiple identical CEs,
9842             so every affected CE must be checked, not only the single CE for
9843             which the CE score was changed in the first place (as every instance
9844             of that CE shares the same CE score, and therefore also can change)!
9845           */
9846           SCAN_PLAYFIELD(xx, yy)
9847           {
9848             if (Feld[xx][yy] == element)
9849               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9850                                  CE_SCORE_GETS_ZERO);
9851           }
9852         }
9853       }
9854
9855       break;
9856     }
9857
9858     case CA_SET_CE_ARTWORK:
9859     {
9860       int artwork_element = action_arg_element;
9861       boolean reset_frame = FALSE;
9862       int xx, yy;
9863
9864       if (action_arg == CA_ARG_ELEMENT_RESET)
9865         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9866                            element);
9867
9868       if (ei->gfx_element != artwork_element)
9869         reset_frame = TRUE;
9870
9871       ei->gfx_element = artwork_element;
9872
9873       SCAN_PLAYFIELD(xx, yy)
9874       {
9875         if (Feld[xx][yy] == element)
9876         {
9877           if (reset_frame)
9878           {
9879             ResetGfxAnimation(xx, yy);
9880             ResetRandomAnimationValue(xx, yy);
9881           }
9882
9883           TEST_DrawLevelField(xx, yy);
9884         }
9885       }
9886
9887       break;
9888     }
9889
9890     /* ---------- engine actions  ------------------------------------------ */
9891
9892     case CA_SET_ENGINE_SCAN_MODE:
9893     {
9894       InitPlayfieldScanMode(action_arg);
9895
9896       break;
9897     }
9898
9899     default:
9900       break;
9901   }
9902 }
9903
9904 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9905 {
9906   int old_element = Feld[x][y];
9907   int new_element = GetElementFromGroupElement(element);
9908   int previous_move_direction = MovDir[x][y];
9909   int last_ce_value = CustomValue[x][y];
9910   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9911   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9912   boolean add_player_onto_element = (new_element_is_player &&
9913                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9914                                      IS_WALKABLE(old_element));
9915
9916   if (!add_player_onto_element)
9917   {
9918     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9919       RemoveMovingField(x, y);
9920     else
9921       RemoveField(x, y);
9922
9923     Feld[x][y] = new_element;
9924
9925     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9926       MovDir[x][y] = previous_move_direction;
9927
9928     if (element_info[new_element].use_last_ce_value)
9929       CustomValue[x][y] = last_ce_value;
9930
9931     InitField_WithBug1(x, y, FALSE);
9932
9933     new_element = Feld[x][y];   /* element may have changed */
9934
9935     ResetGfxAnimation(x, y);
9936     ResetRandomAnimationValue(x, y);
9937
9938     TEST_DrawLevelField(x, y);
9939
9940     if (GFX_CRUMBLED(new_element))
9941       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9942   }
9943
9944   /* check if element under the player changes from accessible to unaccessible
9945      (needed for special case of dropping element which then changes) */
9946   /* (must be checked after creating new element for walkable group elements) */
9947   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9948       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9949   {
9950     Bang(x, y);
9951
9952     return;
9953   }
9954
9955   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9956   if (new_element_is_player)
9957     RelocatePlayer(x, y, new_element);
9958
9959   if (is_change)
9960     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9961
9962   TestIfBadThingTouchesPlayer(x, y);
9963   TestIfPlayerTouchesCustomElement(x, y);
9964   TestIfElementTouchesCustomElement(x, y);
9965 }
9966
9967 static void CreateField(int x, int y, int element)
9968 {
9969   CreateFieldExt(x, y, element, FALSE);
9970 }
9971
9972 static void CreateElementFromChange(int x, int y, int element)
9973 {
9974   element = GET_VALID_RUNTIME_ELEMENT(element);
9975
9976   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9977   {
9978     int old_element = Feld[x][y];
9979
9980     /* prevent changed element from moving in same engine frame
9981        unless both old and new element can either fall or move */
9982     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9983         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9984       Stop[x][y] = TRUE;
9985   }
9986
9987   CreateFieldExt(x, y, element, TRUE);
9988 }
9989
9990 static boolean ChangeElement(int x, int y, int element, int page)
9991 {
9992   struct ElementInfo *ei = &element_info[element];
9993   struct ElementChangeInfo *change = &ei->change_page[page];
9994   int ce_value = CustomValue[x][y];
9995   int ce_score = ei->collect_score;
9996   int target_element;
9997   int old_element = Feld[x][y];
9998
9999   /* always use default change event to prevent running into a loop */
10000   if (ChangeEvent[x][y] == -1)
10001     ChangeEvent[x][y] = CE_DELAY;
10002
10003   if (ChangeEvent[x][y] == CE_DELAY)
10004   {
10005     /* reset actual trigger element, trigger player and action element */
10006     change->actual_trigger_element = EL_EMPTY;
10007     change->actual_trigger_player = EL_EMPTY;
10008     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10009     change->actual_trigger_side = CH_SIDE_NONE;
10010     change->actual_trigger_ce_value = 0;
10011     change->actual_trigger_ce_score = 0;
10012   }
10013
10014   /* do not change elements more than a specified maximum number of changes */
10015   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10016     return FALSE;
10017
10018   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10019
10020   if (change->explode)
10021   {
10022     Bang(x, y);
10023
10024     return TRUE;
10025   }
10026
10027   if (change->use_target_content)
10028   {
10029     boolean complete_replace = TRUE;
10030     boolean can_replace[3][3];
10031     int xx, yy;
10032
10033     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10034     {
10035       boolean is_empty;
10036       boolean is_walkable;
10037       boolean is_diggable;
10038       boolean is_collectible;
10039       boolean is_removable;
10040       boolean is_destructible;
10041       int ex = x + xx - 1;
10042       int ey = y + yy - 1;
10043       int content_element = change->target_content.e[xx][yy];
10044       int e;
10045
10046       can_replace[xx][yy] = TRUE;
10047
10048       if (ex == x && ey == y)   /* do not check changing element itself */
10049         continue;
10050
10051       if (content_element == EL_EMPTY_SPACE)
10052       {
10053         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10054
10055         continue;
10056       }
10057
10058       if (!IN_LEV_FIELD(ex, ey))
10059       {
10060         can_replace[xx][yy] = FALSE;
10061         complete_replace = FALSE;
10062
10063         continue;
10064       }
10065
10066       e = Feld[ex][ey];
10067
10068       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10069         e = MovingOrBlocked2Element(ex, ey);
10070
10071       is_empty = (IS_FREE(ex, ey) ||
10072                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10073
10074       is_walkable     = (is_empty || IS_WALKABLE(e));
10075       is_diggable     = (is_empty || IS_DIGGABLE(e));
10076       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10077       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10078       is_removable    = (is_diggable || is_collectible);
10079
10080       can_replace[xx][yy] =
10081         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10082           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10083           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10084           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10085           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10086           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10087          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10088
10089       if (!can_replace[xx][yy])
10090         complete_replace = FALSE;
10091     }
10092
10093     if (!change->only_if_complete || complete_replace)
10094     {
10095       boolean something_has_changed = FALSE;
10096
10097       if (change->only_if_complete && change->use_random_replace &&
10098           RND(100) < change->random_percentage)
10099         return FALSE;
10100
10101       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10102       {
10103         int ex = x + xx - 1;
10104         int ey = y + yy - 1;
10105         int content_element;
10106
10107         if (can_replace[xx][yy] && (!change->use_random_replace ||
10108                                     RND(100) < change->random_percentage))
10109         {
10110           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10111             RemoveMovingField(ex, ey);
10112
10113           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10114
10115           content_element = change->target_content.e[xx][yy];
10116           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10117                                               ce_value, ce_score);
10118
10119           CreateElementFromChange(ex, ey, target_element);
10120
10121           something_has_changed = TRUE;
10122
10123           /* for symmetry reasons, freeze newly created border elements */
10124           if (ex != x || ey != y)
10125             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10126         }
10127       }
10128
10129       if (something_has_changed)
10130       {
10131         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10132         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10133       }
10134     }
10135   }
10136   else
10137   {
10138     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10139                                         ce_value, ce_score);
10140
10141     if (element == EL_DIAGONAL_GROWING ||
10142         element == EL_DIAGONAL_SHRINKING)
10143     {
10144       target_element = Store[x][y];
10145
10146       Store[x][y] = EL_EMPTY;
10147     }
10148
10149     CreateElementFromChange(x, y, target_element);
10150
10151     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10152     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10153   }
10154
10155   /* this uses direct change before indirect change */
10156   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10157
10158   return TRUE;
10159 }
10160
10161 static void HandleElementChange(int x, int y, int page)
10162 {
10163   int element = MovingOrBlocked2Element(x, y);
10164   struct ElementInfo *ei = &element_info[element];
10165   struct ElementChangeInfo *change = &ei->change_page[page];
10166   boolean handle_action_before_change = FALSE;
10167
10168 #ifdef DEBUG
10169   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10170       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10171   {
10172     printf("\n\n");
10173     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10174            x, y, element, element_info[element].token_name);
10175     printf("HandleElementChange(): This should never happen!\n");
10176     printf("\n\n");
10177   }
10178 #endif
10179
10180   /* this can happen with classic bombs on walkable, changing elements */
10181   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10182   {
10183     return;
10184   }
10185
10186   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10187   {
10188     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10189
10190     if (change->can_change)
10191     {
10192       /* !!! not clear why graphic animation should be reset at all here !!! */
10193       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10194       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10195
10196       /*
10197         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10198
10199         When using an animation frame delay of 1 (this only happens with
10200         "sp_zonk.moving.left/right" in the classic graphics), the default
10201         (non-moving) animation shows wrong animation frames (while the
10202         moving animation, like "sp_zonk.moving.left/right", is correct,
10203         so this graphical bug never shows up with the classic graphics).
10204         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10205         be drawn instead of the correct frames 0,1,2,3. This is caused by
10206         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10207         an element change: First when the change delay ("ChangeDelay[][]")
10208         counter has reached zero after decrementing, then a second time in
10209         the next frame (after "GfxFrame[][]" was already incremented) when
10210         "ChangeDelay[][]" is reset to the initial delay value again.
10211
10212         This causes frame 0 to be drawn twice, while the last frame won't
10213         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10214
10215         As some animations may already be cleverly designed around this bug
10216         (at least the "Snake Bite" snake tail animation does this), it cannot
10217         simply be fixed here without breaking such existing animations.
10218         Unfortunately, it cannot easily be detected if a graphics set was
10219         designed "before" or "after" the bug was fixed. As a workaround,
10220         a new graphics set option "game.graphics_engine_version" was added
10221         to be able to specify the game's major release version for which the
10222         graphics set was designed, which can then be used to decide if the
10223         bugfix should be used (version 4 and above) or not (version 3 or
10224         below, or if no version was specified at all, as with old sets).
10225
10226         (The wrong/fixed animation frames can be tested with the test level set
10227         "test_gfxframe" and level "000", which contains a specially prepared
10228         custom element at level position (x/y) == (11/9) which uses the zonk
10229         animation mentioned above. Using "game.graphics_engine_version: 4"
10230         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10231         This can also be seen from the debug output for this test element.)
10232       */
10233
10234       /* when a custom element is about to change (for example by change delay),
10235          do not reset graphic animation when the custom element is moving */
10236       if (game.graphics_engine_version < 4 &&
10237           !IS_MOVING(x, y))
10238       {
10239         ResetGfxAnimation(x, y);
10240         ResetRandomAnimationValue(x, y);
10241       }
10242
10243       if (change->pre_change_function)
10244         change->pre_change_function(x, y);
10245     }
10246   }
10247
10248   ChangeDelay[x][y]--;
10249
10250   if (ChangeDelay[x][y] != 0)           /* continue element change */
10251   {
10252     if (change->can_change)
10253     {
10254       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10255
10256       if (IS_ANIMATED(graphic))
10257         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10258
10259       if (change->change_function)
10260         change->change_function(x, y);
10261     }
10262   }
10263   else                                  /* finish element change */
10264   {
10265     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10266     {
10267       page = ChangePage[x][y];
10268       ChangePage[x][y] = -1;
10269
10270       change = &ei->change_page[page];
10271     }
10272
10273     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10274     {
10275       ChangeDelay[x][y] = 1;            /* try change after next move step */
10276       ChangePage[x][y] = page;          /* remember page to use for change */
10277
10278       return;
10279     }
10280
10281     /* special case: set new level random seed before changing element */
10282     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10283       handle_action_before_change = TRUE;
10284
10285     if (change->has_action && handle_action_before_change)
10286       ExecuteCustomElementAction(x, y, element, page);
10287
10288     if (change->can_change)
10289     {
10290       if (ChangeElement(x, y, element, page))
10291       {
10292         if (change->post_change_function)
10293           change->post_change_function(x, y);
10294       }
10295     }
10296
10297     if (change->has_action && !handle_action_before_change)
10298       ExecuteCustomElementAction(x, y, element, page);
10299   }
10300 }
10301
10302 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10303                                               int trigger_element,
10304                                               int trigger_event,
10305                                               int trigger_player,
10306                                               int trigger_side,
10307                                               int trigger_page)
10308 {
10309   boolean change_done_any = FALSE;
10310   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10311   int i;
10312
10313   if (!(trigger_events[trigger_element][trigger_event]))
10314     return FALSE;
10315
10316   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10317
10318   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10319   {
10320     int element = EL_CUSTOM_START + i;
10321     boolean change_done = FALSE;
10322     int p;
10323
10324     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10325         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10326       continue;
10327
10328     for (p = 0; p < element_info[element].num_change_pages; p++)
10329     {
10330       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10331
10332       if (change->can_change_or_has_action &&
10333           change->has_event[trigger_event] &&
10334           change->trigger_side & trigger_side &&
10335           change->trigger_player & trigger_player &&
10336           change->trigger_page & trigger_page_bits &&
10337           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10338       {
10339         change->actual_trigger_element = trigger_element;
10340         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10341         change->actual_trigger_player_bits = trigger_player;
10342         change->actual_trigger_side = trigger_side;
10343         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10344         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10345
10346         if ((change->can_change && !change_done) || change->has_action)
10347         {
10348           int x, y;
10349
10350           SCAN_PLAYFIELD(x, y)
10351           {
10352             if (Feld[x][y] == element)
10353             {
10354               if (change->can_change && !change_done)
10355               {
10356                 /* if element already changed in this frame, not only prevent
10357                    another element change (checked in ChangeElement()), but
10358                    also prevent additional element actions for this element */
10359
10360                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10361                     !level.use_action_after_change_bug)
10362                   continue;
10363
10364                 ChangeDelay[x][y] = 1;
10365                 ChangeEvent[x][y] = trigger_event;
10366
10367                 HandleElementChange(x, y, p);
10368               }
10369               else if (change->has_action)
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                 ExecuteCustomElementAction(x, y, element, p);
10380                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10381               }
10382             }
10383           }
10384
10385           if (change->can_change)
10386           {
10387             change_done = TRUE;
10388             change_done_any = TRUE;
10389           }
10390         }
10391       }
10392     }
10393   }
10394
10395   RECURSION_LOOP_DETECTION_END();
10396
10397   return change_done_any;
10398 }
10399
10400 static boolean CheckElementChangeExt(int x, int y,
10401                                      int element,
10402                                      int trigger_element,
10403                                      int trigger_event,
10404                                      int trigger_player,
10405                                      int trigger_side)
10406 {
10407   boolean change_done = FALSE;
10408   int p;
10409
10410   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10411       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10412     return FALSE;
10413
10414   if (Feld[x][y] == EL_BLOCKED)
10415   {
10416     Blocked2Moving(x, y, &x, &y);
10417     element = Feld[x][y];
10418   }
10419
10420   /* check if element has already changed or is about to change after moving */
10421   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10422        Feld[x][y] != element) ||
10423
10424       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10425        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10426         ChangePage[x][y] != -1)))
10427     return FALSE;
10428
10429   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10430
10431   for (p = 0; p < element_info[element].num_change_pages; p++)
10432   {
10433     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10434
10435     /* check trigger element for all events where the element that is checked
10436        for changing interacts with a directly adjacent element -- this is
10437        different to element changes that affect other elements to change on the
10438        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10439     boolean check_trigger_element =
10440       (trigger_event == CE_TOUCHING_X ||
10441        trigger_event == CE_HITTING_X ||
10442        trigger_event == CE_HIT_BY_X ||
10443        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10444
10445     if (change->can_change_or_has_action &&
10446         change->has_event[trigger_event] &&
10447         change->trigger_side & trigger_side &&
10448         change->trigger_player & trigger_player &&
10449         (!check_trigger_element ||
10450          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10451     {
10452       change->actual_trigger_element = trigger_element;
10453       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10454       change->actual_trigger_player_bits = trigger_player;
10455       change->actual_trigger_side = trigger_side;
10456       change->actual_trigger_ce_value = CustomValue[x][y];
10457       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10458
10459       /* special case: trigger element not at (x,y) position for some events */
10460       if (check_trigger_element)
10461       {
10462         static struct
10463         {
10464           int dx, dy;
10465         } move_xy[] =
10466           {
10467             {  0,  0 },
10468             { -1,  0 },
10469             { +1,  0 },
10470             {  0,  0 },
10471             {  0, -1 },
10472             {  0,  0 }, { 0, 0 }, { 0, 0 },
10473             {  0, +1 }
10474           };
10475
10476         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10477         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10478
10479         change->actual_trigger_ce_value = CustomValue[xx][yy];
10480         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10481       }
10482
10483       if (change->can_change && !change_done)
10484       {
10485         ChangeDelay[x][y] = 1;
10486         ChangeEvent[x][y] = trigger_event;
10487
10488         HandleElementChange(x, y, p);
10489
10490         change_done = TRUE;
10491       }
10492       else if (change->has_action)
10493       {
10494         ExecuteCustomElementAction(x, y, element, p);
10495         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10496       }
10497     }
10498   }
10499
10500   RECURSION_LOOP_DETECTION_END();
10501
10502   return change_done;
10503 }
10504
10505 static void PlayPlayerSound(struct PlayerInfo *player)
10506 {
10507   int jx = player->jx, jy = player->jy;
10508   int sound_element = player->artwork_element;
10509   int last_action = player->last_action_waiting;
10510   int action = player->action_waiting;
10511
10512   if (player->is_waiting)
10513   {
10514     if (action != last_action)
10515       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10516     else
10517       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10518   }
10519   else
10520   {
10521     if (action != last_action)
10522       StopSound(element_info[sound_element].sound[last_action]);
10523
10524     if (last_action == ACTION_SLEEPING)
10525       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10526   }
10527 }
10528
10529 static void PlayAllPlayersSound()
10530 {
10531   int i;
10532
10533   for (i = 0; i < MAX_PLAYERS; i++)
10534     if (stored_player[i].active)
10535       PlayPlayerSound(&stored_player[i]);
10536 }
10537
10538 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10539 {
10540   boolean last_waiting = player->is_waiting;
10541   int move_dir = player->MovDir;
10542
10543   player->dir_waiting = move_dir;
10544   player->last_action_waiting = player->action_waiting;
10545
10546   if (is_waiting)
10547   {
10548     if (!last_waiting)          /* not waiting -> waiting */
10549     {
10550       player->is_waiting = TRUE;
10551
10552       player->frame_counter_bored =
10553         FrameCounter +
10554         game.player_boring_delay_fixed +
10555         GetSimpleRandom(game.player_boring_delay_random);
10556       player->frame_counter_sleeping =
10557         FrameCounter +
10558         game.player_sleeping_delay_fixed +
10559         GetSimpleRandom(game.player_sleeping_delay_random);
10560
10561       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10562     }
10563
10564     if (game.player_sleeping_delay_fixed +
10565         game.player_sleeping_delay_random > 0 &&
10566         player->anim_delay_counter == 0 &&
10567         player->post_delay_counter == 0 &&
10568         FrameCounter >= player->frame_counter_sleeping)
10569       player->is_sleeping = TRUE;
10570     else if (game.player_boring_delay_fixed +
10571              game.player_boring_delay_random > 0 &&
10572              FrameCounter >= player->frame_counter_bored)
10573       player->is_bored = TRUE;
10574
10575     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10576                               player->is_bored ? ACTION_BORING :
10577                               ACTION_WAITING);
10578
10579     if (player->is_sleeping && player->use_murphy)
10580     {
10581       /* special case for sleeping Murphy when leaning against non-free tile */
10582
10583       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10584           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10585            !IS_MOVING(player->jx - 1, player->jy)))
10586         move_dir = MV_LEFT;
10587       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10588                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10589                 !IS_MOVING(player->jx + 1, player->jy)))
10590         move_dir = MV_RIGHT;
10591       else
10592         player->is_sleeping = FALSE;
10593
10594       player->dir_waiting = move_dir;
10595     }
10596
10597     if (player->is_sleeping)
10598     {
10599       if (player->num_special_action_sleeping > 0)
10600       {
10601         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10602         {
10603           int last_special_action = player->special_action_sleeping;
10604           int num_special_action = player->num_special_action_sleeping;
10605           int special_action =
10606             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10607              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10608              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10609              last_special_action + 1 : ACTION_SLEEPING);
10610           int special_graphic =
10611             el_act_dir2img(player->artwork_element, special_action, move_dir);
10612
10613           player->anim_delay_counter =
10614             graphic_info[special_graphic].anim_delay_fixed +
10615             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10616           player->post_delay_counter =
10617             graphic_info[special_graphic].post_delay_fixed +
10618             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10619
10620           player->special_action_sleeping = special_action;
10621         }
10622
10623         if (player->anim_delay_counter > 0)
10624         {
10625           player->action_waiting = player->special_action_sleeping;
10626           player->anim_delay_counter--;
10627         }
10628         else if (player->post_delay_counter > 0)
10629         {
10630           player->post_delay_counter--;
10631         }
10632       }
10633     }
10634     else if (player->is_bored)
10635     {
10636       if (player->num_special_action_bored > 0)
10637       {
10638         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10639         {
10640           int special_action =
10641             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10642           int special_graphic =
10643             el_act_dir2img(player->artwork_element, special_action, move_dir);
10644
10645           player->anim_delay_counter =
10646             graphic_info[special_graphic].anim_delay_fixed +
10647             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10648           player->post_delay_counter =
10649             graphic_info[special_graphic].post_delay_fixed +
10650             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10651
10652           player->special_action_bored = special_action;
10653         }
10654
10655         if (player->anim_delay_counter > 0)
10656         {
10657           player->action_waiting = player->special_action_bored;
10658           player->anim_delay_counter--;
10659         }
10660         else if (player->post_delay_counter > 0)
10661         {
10662           player->post_delay_counter--;
10663         }
10664       }
10665     }
10666   }
10667   else if (last_waiting)        /* waiting -> not waiting */
10668   {
10669     player->is_waiting = FALSE;
10670     player->is_bored = FALSE;
10671     player->is_sleeping = FALSE;
10672
10673     player->frame_counter_bored = -1;
10674     player->frame_counter_sleeping = -1;
10675
10676     player->anim_delay_counter = 0;
10677     player->post_delay_counter = 0;
10678
10679     player->dir_waiting = player->MovDir;
10680     player->action_waiting = ACTION_DEFAULT;
10681
10682     player->special_action_bored = ACTION_DEFAULT;
10683     player->special_action_sleeping = ACTION_DEFAULT;
10684   }
10685 }
10686
10687 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10688 {
10689   if ((!player->is_moving  && player->was_moving) ||
10690       (player->MovPos == 0 && player->was_moving) ||
10691       (player->is_snapping && !player->was_snapping) ||
10692       (player->is_dropping && !player->was_dropping))
10693   {
10694     if (!CheckSaveEngineSnapshotToList())
10695       return;
10696
10697     player->was_moving = FALSE;
10698     player->was_snapping = TRUE;
10699     player->was_dropping = TRUE;
10700   }
10701   else
10702   {
10703     if (player->is_moving)
10704       player->was_moving = TRUE;
10705
10706     if (!player->is_snapping)
10707       player->was_snapping = FALSE;
10708
10709     if (!player->is_dropping)
10710       player->was_dropping = FALSE;
10711   }
10712 }
10713
10714 static void CheckSingleStepMode(struct PlayerInfo *player)
10715 {
10716   if (tape.single_step && tape.recording && !tape.pausing)
10717   {
10718     /* as it is called "single step mode", just return to pause mode when the
10719        player stopped moving after one tile (or never starts moving at all) */
10720     if (!player->is_moving &&
10721         !player->is_pushing &&
10722         !player->is_dropping_pressed)
10723     {
10724       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10725       SnapField(player, 0, 0);                  /* stop snapping */
10726     }
10727   }
10728
10729   CheckSaveEngineSnapshot(player);
10730 }
10731
10732 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10733 {
10734   int left      = player_action & JOY_LEFT;
10735   int right     = player_action & JOY_RIGHT;
10736   int up        = player_action & JOY_UP;
10737   int down      = player_action & JOY_DOWN;
10738   int button1   = player_action & JOY_BUTTON_1;
10739   int button2   = player_action & JOY_BUTTON_2;
10740   int dx        = (left ? -1 : right ? 1 : 0);
10741   int dy        = (up   ? -1 : down  ? 1 : 0);
10742
10743   if (!player->active || tape.pausing)
10744     return 0;
10745
10746   if (player_action)
10747   {
10748     if (button1)
10749       SnapField(player, dx, dy);
10750     else
10751     {
10752       if (button2)
10753         DropElement(player);
10754
10755       MovePlayer(player, dx, dy);
10756     }
10757
10758     CheckSingleStepMode(player);
10759
10760     SetPlayerWaiting(player, FALSE);
10761
10762     return player_action;
10763   }
10764   else
10765   {
10766     /* no actions for this player (no input at player's configured device) */
10767
10768     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10769     SnapField(player, 0, 0);
10770     CheckGravityMovementWhenNotMoving(player);
10771
10772     if (player->MovPos == 0)
10773       SetPlayerWaiting(player, TRUE);
10774
10775     if (player->MovPos == 0)    /* needed for tape.playing */
10776       player->is_moving = FALSE;
10777
10778     player->is_dropping = FALSE;
10779     player->is_dropping_pressed = FALSE;
10780     player->drop_pressed_delay = 0;
10781
10782     CheckSingleStepMode(player);
10783
10784     return 0;
10785   }
10786 }
10787
10788 static void CheckLevelTime()
10789 {
10790   int i;
10791
10792   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10793   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10794   {
10795     if (level.native_em_level->lev->home == 0)  /* all players at home */
10796     {
10797       PlayerWins(local_player);
10798
10799       AllPlayersGone = TRUE;
10800
10801       level.native_em_level->lev->home = -1;
10802     }
10803
10804     if (level.native_em_level->ply[0]->alive == 0 &&
10805         level.native_em_level->ply[1]->alive == 0 &&
10806         level.native_em_level->ply[2]->alive == 0 &&
10807         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10808       AllPlayersGone = TRUE;
10809   }
10810   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10811   {
10812     if (game_sp.LevelSolved &&
10813         !game_sp.GameOver)                              /* game won */
10814     {
10815       PlayerWins(local_player);
10816
10817       game_sp.GameOver = TRUE;
10818
10819       AllPlayersGone = TRUE;
10820     }
10821
10822     if (game_sp.GameOver)                               /* game lost */
10823       AllPlayersGone = TRUE;
10824   }
10825
10826   if (TimeFrames >= FRAMES_PER_SECOND)
10827   {
10828     TimeFrames = 0;
10829     TapeTime++;
10830
10831     for (i = 0; i < MAX_PLAYERS; i++)
10832     {
10833       struct PlayerInfo *player = &stored_player[i];
10834
10835       if (SHIELD_ON(player))
10836       {
10837         player->shield_normal_time_left--;
10838
10839         if (player->shield_deadly_time_left > 0)
10840           player->shield_deadly_time_left--;
10841       }
10842     }
10843
10844     if (!local_player->LevelSolved && !level.use_step_counter)
10845     {
10846       TimePlayed++;
10847
10848       if (TimeLeft > 0)
10849       {
10850         TimeLeft--;
10851
10852         if (TimeLeft <= 10 && setup.time_limit)
10853           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10854
10855         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10856            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10857
10858         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10859
10860         if (!TimeLeft && setup.time_limit)
10861         {
10862           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10863             level.native_em_level->lev->killed_out_of_time = TRUE;
10864           else
10865             for (i = 0; i < MAX_PLAYERS; i++)
10866               KillPlayer(&stored_player[i]);
10867         }
10868       }
10869       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10870       {
10871         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10872       }
10873
10874       level.native_em_level->lev->time =
10875         (game.no_time_limit ? TimePlayed : TimeLeft);
10876     }
10877
10878     if (tape.recording || tape.playing)
10879       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10880   }
10881
10882   if (tape.recording || tape.playing)
10883     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10884
10885   UpdateAndDisplayGameControlValues();
10886 }
10887
10888 void AdvanceFrameAndPlayerCounters(int player_nr)
10889 {
10890   int i;
10891
10892   /* advance frame counters (global frame counter and time frame counter) */
10893   FrameCounter++;
10894   TimeFrames++;
10895
10896   /* advance player counters (counters for move delay, move animation etc.) */
10897   for (i = 0; i < MAX_PLAYERS; i++)
10898   {
10899     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10900     int move_delay_value = stored_player[i].move_delay_value;
10901     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10902
10903     if (!advance_player_counters)       /* not all players may be affected */
10904       continue;
10905
10906     if (move_frames == 0)       /* less than one move per game frame */
10907     {
10908       int stepsize = TILEX / move_delay_value;
10909       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10910       int count = (stored_player[i].is_moving ?
10911                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10912
10913       if (count % delay == 0)
10914         move_frames = 1;
10915     }
10916
10917     stored_player[i].Frame += move_frames;
10918
10919     if (stored_player[i].MovPos != 0)
10920       stored_player[i].StepFrame += move_frames;
10921
10922     if (stored_player[i].move_delay > 0)
10923       stored_player[i].move_delay--;
10924
10925     /* due to bugs in previous versions, counter must count up, not down */
10926     if (stored_player[i].push_delay != -1)
10927       stored_player[i].push_delay++;
10928
10929     if (stored_player[i].drop_delay > 0)
10930       stored_player[i].drop_delay--;
10931
10932     if (stored_player[i].is_dropping_pressed)
10933       stored_player[i].drop_pressed_delay++;
10934   }
10935 }
10936
10937 void StartGameActions(boolean init_network_game, boolean record_tape,
10938                       int random_seed)
10939 {
10940   unsigned int new_random_seed = InitRND(random_seed);
10941
10942   if (record_tape)
10943     TapeStartRecording(new_random_seed);
10944
10945 #if defined(NETWORK_AVALIABLE)
10946   if (init_network_game)
10947   {
10948     SendToServer_StartPlaying();
10949
10950     return;
10951   }
10952 #endif
10953
10954   InitGame();
10955 }
10956
10957 void GameActionsExt()
10958 {
10959 #if 0
10960   static unsigned int game_frame_delay = 0;
10961 #endif
10962   unsigned int game_frame_delay_value;
10963   byte *recorded_player_action;
10964   byte summarized_player_action = 0;
10965   byte tape_action[MAX_PLAYERS];
10966   int i;
10967
10968   /* detect endless loops, caused by custom element programming */
10969   if (recursion_loop_detected && recursion_loop_depth == 0)
10970   {
10971     char *message = getStringCat3("Internal Error! Element ",
10972                                   EL_NAME(recursion_loop_element),
10973                                   " caused endless loop! Quit the game?");
10974
10975     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10976           EL_NAME(recursion_loop_element));
10977
10978     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10979
10980     recursion_loop_detected = FALSE;    /* if game should be continued */
10981
10982     free(message);
10983
10984     return;
10985   }
10986
10987   if (game.restart_level)
10988     StartGameActions(options.network, setup.autorecord, level.random_seed);
10989
10990   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10991   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10992   {
10993     if (level.native_em_level->lev->home == 0)  /* all players at home */
10994     {
10995       PlayerWins(local_player);
10996
10997       AllPlayersGone = TRUE;
10998
10999       level.native_em_level->lev->home = -1;
11000     }
11001
11002     if (level.native_em_level->ply[0]->alive == 0 &&
11003         level.native_em_level->ply[1]->alive == 0 &&
11004         level.native_em_level->ply[2]->alive == 0 &&
11005         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11006       AllPlayersGone = TRUE;
11007   }
11008   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11009   {
11010     if (game_sp.LevelSolved &&
11011         !game_sp.GameOver)                              /* game won */
11012     {
11013       PlayerWins(local_player);
11014
11015       game_sp.GameOver = TRUE;
11016
11017       AllPlayersGone = TRUE;
11018     }
11019
11020     if (game_sp.GameOver)                               /* game lost */
11021       AllPlayersGone = TRUE;
11022   }
11023
11024   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11025     GameWon();
11026
11027   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11028     TapeStop();
11029
11030   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11031     return;
11032
11033   game_frame_delay_value =
11034     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11035
11036   if (tape.playing && tape.warp_forward && !tape.pausing)
11037     game_frame_delay_value = 0;
11038
11039   SetVideoFrameDelay(game_frame_delay_value);
11040
11041 #if 0
11042 #if 0
11043   /* ---------- main game synchronization point ---------- */
11044
11045   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11046
11047   printf("::: skip == %d\n", skip);
11048
11049 #else
11050   /* ---------- main game synchronization point ---------- */
11051
11052   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11053 #endif
11054 #endif
11055
11056   if (network_playing && !network_player_action_received)
11057   {
11058     /* try to get network player actions in time */
11059
11060 #if defined(NETWORK_AVALIABLE)
11061     /* last chance to get network player actions without main loop delay */
11062     HandleNetworking();
11063 #endif
11064
11065     /* game was quit by network peer */
11066     if (game_status != GAME_MODE_PLAYING)
11067       return;
11068
11069     if (!network_player_action_received)
11070       return;           /* failed to get network player actions in time */
11071
11072     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11073   }
11074
11075   if (tape.pausing)
11076     return;
11077
11078   /* at this point we know that we really continue executing the game */
11079
11080   network_player_action_received = FALSE;
11081
11082   /* when playing tape, read previously recorded player input from tape data */
11083   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11084
11085   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11086   if (tape.pausing)
11087     return;
11088
11089   if (tape.set_centered_player)
11090   {
11091     game.centered_player_nr_next = tape.centered_player_nr_next;
11092     game.set_centered_player = TRUE;
11093   }
11094
11095   for (i = 0; i < MAX_PLAYERS; i++)
11096   {
11097     summarized_player_action |= stored_player[i].action;
11098
11099     if (!network_playing && (game.team_mode || tape.playing))
11100       stored_player[i].effective_action = stored_player[i].action;
11101   }
11102
11103 #if defined(NETWORK_AVALIABLE)
11104   if (network_playing)
11105     SendToServer_MovePlayer(summarized_player_action);
11106 #endif
11107
11108   // summarize all actions at local players mapped input device position
11109   // (this allows using different input devices in single player mode)
11110   if (!options.network && !game.team_mode)
11111     stored_player[map_player_action[local_player->index_nr]].effective_action =
11112       summarized_player_action;
11113
11114   if (tape.recording &&
11115       setup.team_mode &&
11116       setup.input_on_focus &&
11117       game.centered_player_nr != -1)
11118   {
11119     for (i = 0; i < MAX_PLAYERS; i++)
11120       stored_player[i].effective_action =
11121         (i == game.centered_player_nr ? summarized_player_action : 0);
11122   }
11123
11124   if (recorded_player_action != NULL)
11125     for (i = 0; i < MAX_PLAYERS; i++)
11126       stored_player[i].effective_action = recorded_player_action[i];
11127
11128   for (i = 0; i < MAX_PLAYERS; i++)
11129   {
11130     tape_action[i] = stored_player[i].effective_action;
11131
11132     /* (this may happen in the RND game engine if a player was not present on
11133        the playfield on level start, but appeared later from a custom element */
11134     if (setup.team_mode &&
11135         tape.recording &&
11136         tape_action[i] &&
11137         !tape.player_participates[i])
11138       tape.player_participates[i] = TRUE;
11139   }
11140
11141   /* only record actions from input devices, but not programmed actions */
11142   if (tape.recording)
11143     TapeRecordAction(tape_action);
11144
11145 #if USE_NEW_PLAYER_ASSIGNMENTS
11146   // !!! also map player actions in single player mode !!!
11147   // if (game.team_mode)
11148   if (1)
11149   {
11150     byte mapped_action[MAX_PLAYERS];
11151
11152 #if DEBUG_PLAYER_ACTIONS
11153     printf(":::");
11154     for (i = 0; i < MAX_PLAYERS; i++)
11155       printf(" %d, ", stored_player[i].effective_action);
11156 #endif
11157
11158     for (i = 0; i < MAX_PLAYERS; i++)
11159       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11160
11161     for (i = 0; i < MAX_PLAYERS; i++)
11162       stored_player[i].effective_action = mapped_action[i];
11163
11164 #if DEBUG_PLAYER_ACTIONS
11165     printf(" =>");
11166     for (i = 0; i < MAX_PLAYERS; i++)
11167       printf(" %d, ", stored_player[i].effective_action);
11168     printf("\n");
11169 #endif
11170   }
11171 #if DEBUG_PLAYER_ACTIONS
11172   else
11173   {
11174     printf(":::");
11175     for (i = 0; i < MAX_PLAYERS; i++)
11176       printf(" %d, ", stored_player[i].effective_action);
11177     printf("\n");
11178   }
11179 #endif
11180 #endif
11181
11182   for (i = 0; i < MAX_PLAYERS; i++)
11183   {
11184     // allow engine snapshot in case of changed movement attempt
11185     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11186         (stored_player[i].effective_action & KEY_MOTION))
11187       game.snapshot.changed_action = TRUE;
11188
11189     // allow engine snapshot in case of snapping/dropping attempt
11190     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11191         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11192       game.snapshot.changed_action = TRUE;
11193
11194     game.snapshot.last_action[i] = stored_player[i].effective_action;
11195   }
11196
11197   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11198   {
11199     GameActions_EM_Main();
11200   }
11201   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11202   {
11203     GameActions_SP_Main();
11204   }
11205   else
11206   {
11207     GameActions_RND_Main();
11208   }
11209
11210   BlitScreenToBitmap(backbuffer);
11211
11212   CheckLevelTime();
11213
11214   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11215
11216   if (global.show_frames_per_second)
11217   {
11218     static unsigned int fps_counter = 0;
11219     static int fps_frames = 0;
11220     unsigned int fps_delay_ms = Counter() - fps_counter;
11221
11222     fps_frames++;
11223
11224     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11225     {
11226       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11227
11228       fps_frames = 0;
11229       fps_counter = Counter();
11230
11231       /* always draw FPS to screen after FPS value was updated */
11232       redraw_mask |= REDRAW_FPS;
11233     }
11234
11235     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11236     if (GetDrawDeactivationMask() == REDRAW_NONE)
11237       redraw_mask |= REDRAW_FPS;
11238   }
11239 }
11240
11241 static void GameActions_CheckSaveEngineSnapshot()
11242 {
11243   if (!game.snapshot.save_snapshot)
11244     return;
11245
11246   // clear flag for saving snapshot _before_ saving snapshot
11247   game.snapshot.save_snapshot = FALSE;
11248
11249   SaveEngineSnapshotToList();
11250 }
11251
11252 void GameActions()
11253 {
11254   GameActionsExt();
11255
11256   GameActions_CheckSaveEngineSnapshot();
11257 }
11258
11259 void GameActions_EM_Main()
11260 {
11261   byte effective_action[MAX_PLAYERS];
11262   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11263   int i;
11264
11265   for (i = 0; i < MAX_PLAYERS; i++)
11266     effective_action[i] = stored_player[i].effective_action;
11267
11268   GameActions_EM(effective_action, warp_mode);
11269 }
11270
11271 void GameActions_SP_Main()
11272 {
11273   byte effective_action[MAX_PLAYERS];
11274   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11275   int i;
11276
11277   for (i = 0; i < MAX_PLAYERS; i++)
11278     effective_action[i] = stored_player[i].effective_action;
11279
11280   GameActions_SP(effective_action, warp_mode);
11281
11282   for (i = 0; i < MAX_PLAYERS; i++)
11283   {
11284     if (stored_player[i].force_dropping)
11285       stored_player[i].action |= KEY_BUTTON_DROP;
11286
11287     stored_player[i].force_dropping = FALSE;
11288   }
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, last_gfx_frame;
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     last_gfx_frame = GfxFrame[x][y];
11487
11488     ResetGfxFrame(x, y);
11489
11490     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11491       DrawLevelGraphicAnimation(x, y, graphic);
11492
11493     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11494         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11495       ResetRandomAnimationValue(x, y);
11496
11497     SetRandomAnimationValue(x, y);
11498
11499     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11500
11501     if (IS_INACTIVE(element))
11502     {
11503       if (IS_ANIMATED(graphic))
11504         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11505
11506       continue;
11507     }
11508
11509     /* this may take place after moving, so 'element' may have changed */
11510     if (IS_CHANGING(x, y) &&
11511         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11512     {
11513       int page = element_info[element].event_page_nr[CE_DELAY];
11514
11515       HandleElementChange(x, y, page);
11516
11517       element = Feld[x][y];
11518       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11519     }
11520
11521     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11522     {
11523       StartMoving(x, y);
11524
11525       element = Feld[x][y];
11526       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11527
11528       if (IS_ANIMATED(graphic) &&
11529           !IS_MOVING(x, y) &&
11530           !Stop[x][y])
11531         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11532
11533       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11534         TEST_DrawTwinkleOnField(x, y);
11535     }
11536     else if (element == EL_ACID)
11537     {
11538       if (!Stop[x][y])
11539         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11540     }
11541     else if ((element == EL_EXIT_OPEN ||
11542               element == EL_EM_EXIT_OPEN ||
11543               element == EL_SP_EXIT_OPEN ||
11544               element == EL_STEEL_EXIT_OPEN ||
11545               element == EL_EM_STEEL_EXIT_OPEN ||
11546               element == EL_SP_TERMINAL ||
11547               element == EL_SP_TERMINAL_ACTIVE ||
11548               element == EL_EXTRA_TIME ||
11549               element == EL_SHIELD_NORMAL ||
11550               element == EL_SHIELD_DEADLY) &&
11551              IS_ANIMATED(graphic))
11552       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11553     else if (IS_MOVING(x, y))
11554       ContinueMoving(x, y);
11555     else if (IS_ACTIVE_BOMB(element))
11556       CheckDynamite(x, y);
11557     else if (element == EL_AMOEBA_GROWING)
11558       AmoebeWaechst(x, y);
11559     else if (element == EL_AMOEBA_SHRINKING)
11560       AmoebaDisappearing(x, y);
11561
11562 #if !USE_NEW_AMOEBA_CODE
11563     else if (IS_AMOEBALIVE(element))
11564       AmoebeAbleger(x, y);
11565 #endif
11566
11567     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11568       Life(x, y);
11569     else if (element == EL_EXIT_CLOSED)
11570       CheckExit(x, y);
11571     else if (element == EL_EM_EXIT_CLOSED)
11572       CheckExitEM(x, y);
11573     else if (element == EL_STEEL_EXIT_CLOSED)
11574       CheckExitSteel(x, y);
11575     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11576       CheckExitSteelEM(x, y);
11577     else if (element == EL_SP_EXIT_CLOSED)
11578       CheckExitSP(x, y);
11579     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11580              element == EL_EXPANDABLE_STEELWALL_GROWING)
11581       MauerWaechst(x, y);
11582     else if (element == EL_EXPANDABLE_WALL ||
11583              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11584              element == EL_EXPANDABLE_WALL_VERTICAL ||
11585              element == EL_EXPANDABLE_WALL_ANY ||
11586              element == EL_BD_EXPANDABLE_WALL)
11587       MauerAbleger(x, y);
11588     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11589              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11590              element == EL_EXPANDABLE_STEELWALL_ANY)
11591       MauerAblegerStahl(x, y);
11592     else if (element == EL_FLAMES)
11593       CheckForDragon(x, y);
11594     else if (element == EL_EXPLOSION)
11595       ; /* drawing of correct explosion animation is handled separately */
11596     else if (element == EL_ELEMENT_SNAPPING ||
11597              element == EL_DIAGONAL_SHRINKING ||
11598              element == EL_DIAGONAL_GROWING)
11599     {
11600       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11601
11602       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11603     }
11604     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11605       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11606
11607     if (IS_BELT_ACTIVE(element))
11608       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11609
11610     if (game.magic_wall_active)
11611     {
11612       int jx = local_player->jx, jy = local_player->jy;
11613
11614       /* play the element sound at the position nearest to the player */
11615       if ((element == EL_MAGIC_WALL_FULL ||
11616            element == EL_MAGIC_WALL_ACTIVE ||
11617            element == EL_MAGIC_WALL_EMPTYING ||
11618            element == EL_BD_MAGIC_WALL_FULL ||
11619            element == EL_BD_MAGIC_WALL_ACTIVE ||
11620            element == EL_BD_MAGIC_WALL_EMPTYING ||
11621            element == EL_DC_MAGIC_WALL_FULL ||
11622            element == EL_DC_MAGIC_WALL_ACTIVE ||
11623            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11624           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11625       {
11626         magic_wall_x = x;
11627         magic_wall_y = y;
11628       }
11629     }
11630   }
11631
11632 #if USE_NEW_AMOEBA_CODE
11633   /* new experimental amoeba growth stuff */
11634   if (!(FrameCounter % 8))
11635   {
11636     static unsigned int random = 1684108901;
11637
11638     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11639     {
11640       x = RND(lev_fieldx);
11641       y = RND(lev_fieldy);
11642       element = Feld[x][y];
11643
11644       if (!IS_PLAYER(x,y) &&
11645           (element == EL_EMPTY ||
11646            CAN_GROW_INTO(element) ||
11647            element == EL_QUICKSAND_EMPTY ||
11648            element == EL_QUICKSAND_FAST_EMPTY ||
11649            element == EL_ACID_SPLASH_LEFT ||
11650            element == EL_ACID_SPLASH_RIGHT))
11651       {
11652         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11653             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11654             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11655             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11656           Feld[x][y] = EL_AMOEBA_DROP;
11657       }
11658
11659       random = random * 129 + 1;
11660     }
11661   }
11662 #endif
11663
11664   game.explosions_delayed = FALSE;
11665
11666   SCAN_PLAYFIELD(x, y)
11667   {
11668     element = Feld[x][y];
11669
11670     if (ExplodeField[x][y])
11671       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11672     else if (element == EL_EXPLOSION)
11673       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11674
11675     ExplodeField[x][y] = EX_TYPE_NONE;
11676   }
11677
11678   game.explosions_delayed = TRUE;
11679
11680   if (game.magic_wall_active)
11681   {
11682     if (!(game.magic_wall_time_left % 4))
11683     {
11684       int element = Feld[magic_wall_x][magic_wall_y];
11685
11686       if (element == EL_BD_MAGIC_WALL_FULL ||
11687           element == EL_BD_MAGIC_WALL_ACTIVE ||
11688           element == EL_BD_MAGIC_WALL_EMPTYING)
11689         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11690       else if (element == EL_DC_MAGIC_WALL_FULL ||
11691                element == EL_DC_MAGIC_WALL_ACTIVE ||
11692                element == EL_DC_MAGIC_WALL_EMPTYING)
11693         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11694       else
11695         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11696     }
11697
11698     if (game.magic_wall_time_left > 0)
11699     {
11700       game.magic_wall_time_left--;
11701
11702       if (!game.magic_wall_time_left)
11703       {
11704         SCAN_PLAYFIELD(x, y)
11705         {
11706           element = Feld[x][y];
11707
11708           if (element == EL_MAGIC_WALL_ACTIVE ||
11709               element == EL_MAGIC_WALL_FULL)
11710           {
11711             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11712             TEST_DrawLevelField(x, y);
11713           }
11714           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11715                    element == EL_BD_MAGIC_WALL_FULL)
11716           {
11717             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11718             TEST_DrawLevelField(x, y);
11719           }
11720           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11721                    element == EL_DC_MAGIC_WALL_FULL)
11722           {
11723             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11724             TEST_DrawLevelField(x, y);
11725           }
11726         }
11727
11728         game.magic_wall_active = FALSE;
11729       }
11730     }
11731   }
11732
11733   if (game.light_time_left > 0)
11734   {
11735     game.light_time_left--;
11736
11737     if (game.light_time_left == 0)
11738       RedrawAllLightSwitchesAndInvisibleElements();
11739   }
11740
11741   if (game.timegate_time_left > 0)
11742   {
11743     game.timegate_time_left--;
11744
11745     if (game.timegate_time_left == 0)
11746       CloseAllOpenTimegates();
11747   }
11748
11749   if (game.lenses_time_left > 0)
11750   {
11751     game.lenses_time_left--;
11752
11753     if (game.lenses_time_left == 0)
11754       RedrawAllInvisibleElementsForLenses();
11755   }
11756
11757   if (game.magnify_time_left > 0)
11758   {
11759     game.magnify_time_left--;
11760
11761     if (game.magnify_time_left == 0)
11762       RedrawAllInvisibleElementsForMagnifier();
11763   }
11764
11765   for (i = 0; i < MAX_PLAYERS; i++)
11766   {
11767     struct PlayerInfo *player = &stored_player[i];
11768
11769     if (SHIELD_ON(player))
11770     {
11771       if (player->shield_deadly_time_left)
11772         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11773       else if (player->shield_normal_time_left)
11774         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11775     }
11776   }
11777
11778 #if USE_DELAYED_GFX_REDRAW
11779   SCAN_PLAYFIELD(x, y)
11780   {
11781     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11782     {
11783       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11784          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11785
11786       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11787         DrawLevelField(x, y);
11788
11789       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11790         DrawLevelFieldCrumbled(x, y);
11791
11792       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11793         DrawLevelFieldCrumbledNeighbours(x, y);
11794
11795       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11796         DrawTwinkleOnField(x, y);
11797     }
11798
11799     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11800   }
11801 #endif
11802
11803   DrawAllPlayers();
11804   PlayAllPlayersSound();
11805
11806   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11807   {
11808     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11809
11810     local_player->show_envelope = 0;
11811   }
11812
11813   /* use random number generator in every frame to make it less predictable */
11814   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11815     RND(1);
11816 }
11817
11818 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11819 {
11820   int min_x = x, min_y = y, max_x = x, max_y = y;
11821   int i;
11822
11823   for (i = 0; i < MAX_PLAYERS; i++)
11824   {
11825     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11826
11827     if (!stored_player[i].active || &stored_player[i] == player)
11828       continue;
11829
11830     min_x = MIN(min_x, jx);
11831     min_y = MIN(min_y, jy);
11832     max_x = MAX(max_x, jx);
11833     max_y = MAX(max_y, jy);
11834   }
11835
11836   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11837 }
11838
11839 static boolean AllPlayersInVisibleScreen()
11840 {
11841   int i;
11842
11843   for (i = 0; i < MAX_PLAYERS; i++)
11844   {
11845     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11846
11847     if (!stored_player[i].active)
11848       continue;
11849
11850     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11851       return FALSE;
11852   }
11853
11854   return TRUE;
11855 }
11856
11857 void ScrollLevel(int dx, int dy)
11858 {
11859   int scroll_offset = 2 * TILEX_VAR;
11860   int x, y;
11861
11862   BlitBitmap(drawto_field, drawto_field,
11863              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11864              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11865              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11866              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11867              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11868              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11869
11870   if (dx != 0)
11871   {
11872     x = (dx == 1 ? BX1 : BX2);
11873     for (y = BY1; y <= BY2; y++)
11874       DrawScreenField(x, y);
11875   }
11876
11877   if (dy != 0)
11878   {
11879     y = (dy == 1 ? BY1 : BY2);
11880     for (x = BX1; x <= BX2; x++)
11881       DrawScreenField(x, y);
11882   }
11883
11884   redraw_mask |= REDRAW_FIELD;
11885 }
11886
11887 static boolean canFallDown(struct PlayerInfo *player)
11888 {
11889   int jx = player->jx, jy = player->jy;
11890
11891   return (IN_LEV_FIELD(jx, jy + 1) &&
11892           (IS_FREE(jx, jy + 1) ||
11893            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11894           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11895           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11896 }
11897
11898 static boolean canPassField(int x, int y, int move_dir)
11899 {
11900   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11901   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11902   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11903   int nextx = x + dx;
11904   int nexty = y + dy;
11905   int element = Feld[x][y];
11906
11907   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11908           !CAN_MOVE(element) &&
11909           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11910           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11911           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11912 }
11913
11914 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11915 {
11916   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11917   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11918   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11919   int newx = x + dx;
11920   int newy = y + dy;
11921
11922   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11923           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11924           (IS_DIGGABLE(Feld[newx][newy]) ||
11925            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11926            canPassField(newx, newy, move_dir)));
11927 }
11928
11929 static void CheckGravityMovement(struct PlayerInfo *player)
11930 {
11931   if (player->gravity && !player->programmed_action)
11932   {
11933     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11934     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11935     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11936     int jx = player->jx, jy = player->jy;
11937     boolean player_is_moving_to_valid_field =
11938       (!player_is_snapping &&
11939        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11940         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11941     boolean player_can_fall_down = canFallDown(player);
11942
11943     if (player_can_fall_down &&
11944         !player_is_moving_to_valid_field)
11945       player->programmed_action = MV_DOWN;
11946   }
11947 }
11948
11949 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11950 {
11951   return CheckGravityMovement(player);
11952
11953   if (player->gravity && !player->programmed_action)
11954   {
11955     int jx = player->jx, jy = player->jy;
11956     boolean field_under_player_is_free =
11957       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11958     boolean player_is_standing_on_valid_field =
11959       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11960        (IS_WALKABLE(Feld[jx][jy]) &&
11961         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11962
11963     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11964       player->programmed_action = MV_DOWN;
11965   }
11966 }
11967
11968 /*
11969   MovePlayerOneStep()
11970   -----------------------------------------------------------------------------
11971   dx, dy:               direction (non-diagonal) to try to move the player to
11972   real_dx, real_dy:     direction as read from input device (can be diagonal)
11973 */
11974
11975 boolean MovePlayerOneStep(struct PlayerInfo *player,
11976                           int dx, int dy, int real_dx, int real_dy)
11977 {
11978   int jx = player->jx, jy = player->jy;
11979   int new_jx = jx + dx, new_jy = jy + dy;
11980   int can_move;
11981   boolean player_can_move = !player->cannot_move;
11982
11983   if (!player->active || (!dx && !dy))
11984     return MP_NO_ACTION;
11985
11986   player->MovDir = (dx < 0 ? MV_LEFT :
11987                     dx > 0 ? MV_RIGHT :
11988                     dy < 0 ? MV_UP :
11989                     dy > 0 ? MV_DOWN :  MV_NONE);
11990
11991   if (!IN_LEV_FIELD(new_jx, new_jy))
11992     return MP_NO_ACTION;
11993
11994   if (!player_can_move)
11995   {
11996     if (player->MovPos == 0)
11997     {
11998       player->is_moving = FALSE;
11999       player->is_digging = FALSE;
12000       player->is_collecting = FALSE;
12001       player->is_snapping = FALSE;
12002       player->is_pushing = FALSE;
12003     }
12004   }
12005
12006   if (!options.network && game.centered_player_nr == -1 &&
12007       !AllPlayersInSight(player, new_jx, new_jy))
12008     return MP_NO_ACTION;
12009
12010   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12011   if (can_move != MP_MOVING)
12012     return can_move;
12013
12014   /* check if DigField() has caused relocation of the player */
12015   if (player->jx != jx || player->jy != jy)
12016     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12017
12018   StorePlayer[jx][jy] = 0;
12019   player->last_jx = jx;
12020   player->last_jy = jy;
12021   player->jx = new_jx;
12022   player->jy = new_jy;
12023   StorePlayer[new_jx][new_jy] = player->element_nr;
12024
12025   if (player->move_delay_value_next != -1)
12026   {
12027     player->move_delay_value = player->move_delay_value_next;
12028     player->move_delay_value_next = -1;
12029   }
12030
12031   player->MovPos =
12032     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12033
12034   player->step_counter++;
12035
12036   PlayerVisit[jx][jy] = FrameCounter;
12037
12038   player->is_moving = TRUE;
12039
12040 #if 1
12041   /* should better be called in MovePlayer(), but this breaks some tapes */
12042   ScrollPlayer(player, SCROLL_INIT);
12043 #endif
12044
12045   return MP_MOVING;
12046 }
12047
12048 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12049 {
12050   int jx = player->jx, jy = player->jy;
12051   int old_jx = jx, old_jy = jy;
12052   int moved = MP_NO_ACTION;
12053
12054   if (!player->active)
12055     return FALSE;
12056
12057   if (!dx && !dy)
12058   {
12059     if (player->MovPos == 0)
12060     {
12061       player->is_moving = FALSE;
12062       player->is_digging = FALSE;
12063       player->is_collecting = FALSE;
12064       player->is_snapping = FALSE;
12065       player->is_pushing = FALSE;
12066     }
12067
12068     return FALSE;
12069   }
12070
12071   if (player->move_delay > 0)
12072     return FALSE;
12073
12074   player->move_delay = -1;              /* set to "uninitialized" value */
12075
12076   /* store if player is automatically moved to next field */
12077   player->is_auto_moving = (player->programmed_action != MV_NONE);
12078
12079   /* remove the last programmed player action */
12080   player->programmed_action = 0;
12081
12082   if (player->MovPos)
12083   {
12084     /* should only happen if pre-1.2 tape recordings are played */
12085     /* this is only for backward compatibility */
12086
12087     int original_move_delay_value = player->move_delay_value;
12088
12089 #if DEBUG
12090     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12091            tape.counter);
12092 #endif
12093
12094     /* scroll remaining steps with finest movement resolution */
12095     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12096
12097     while (player->MovPos)
12098     {
12099       ScrollPlayer(player, SCROLL_GO_ON);
12100       ScrollScreen(NULL, SCROLL_GO_ON);
12101
12102       AdvanceFrameAndPlayerCounters(player->index_nr);
12103
12104       DrawAllPlayers();
12105       BackToFront_WithFrameDelay(0);
12106     }
12107
12108     player->move_delay_value = original_move_delay_value;
12109   }
12110
12111   player->is_active = FALSE;
12112
12113   if (player->last_move_dir & MV_HORIZONTAL)
12114   {
12115     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12116       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12117   }
12118   else
12119   {
12120     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12121       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12122   }
12123
12124   if (!moved && !player->is_active)
12125   {
12126     player->is_moving = FALSE;
12127     player->is_digging = FALSE;
12128     player->is_collecting = FALSE;
12129     player->is_snapping = FALSE;
12130     player->is_pushing = FALSE;
12131   }
12132
12133   jx = player->jx;
12134   jy = player->jy;
12135
12136   if (moved & MP_MOVING && !ScreenMovPos &&
12137       (player->index_nr == game.centered_player_nr ||
12138        game.centered_player_nr == -1))
12139   {
12140     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12141     int offset = game.scroll_delay_value;
12142
12143     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12144     {
12145       /* actual player has left the screen -- scroll in that direction */
12146       if (jx != old_jx)         /* player has moved horizontally */
12147         scroll_x += (jx - old_jx);
12148       else                      /* player has moved vertically */
12149         scroll_y += (jy - old_jy);
12150     }
12151     else
12152     {
12153       if (jx != old_jx)         /* player has moved horizontally */
12154       {
12155         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12156             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12157           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12158
12159         /* don't scroll over playfield boundaries */
12160         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12161           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12162
12163         /* don't scroll more than one field at a time */
12164         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12165
12166         /* don't scroll against the player's moving direction */
12167         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12168             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12169           scroll_x = old_scroll_x;
12170       }
12171       else                      /* player has moved vertically */
12172       {
12173         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12174             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12175           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12176
12177         /* don't scroll over playfield boundaries */
12178         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12179           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12180
12181         /* don't scroll more than one field at a time */
12182         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12183
12184         /* don't scroll against the player's moving direction */
12185         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12186             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12187           scroll_y = old_scroll_y;
12188       }
12189     }
12190
12191     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12192     {
12193       if (!options.network && game.centered_player_nr == -1 &&
12194           !AllPlayersInVisibleScreen())
12195       {
12196         scroll_x = old_scroll_x;
12197         scroll_y = old_scroll_y;
12198       }
12199       else
12200       {
12201         ScrollScreen(player, SCROLL_INIT);
12202         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12203       }
12204     }
12205   }
12206
12207   player->StepFrame = 0;
12208
12209   if (moved & MP_MOVING)
12210   {
12211     if (old_jx != jx && old_jy == jy)
12212       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12213     else if (old_jx == jx && old_jy != jy)
12214       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12215
12216     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12217
12218     player->last_move_dir = player->MovDir;
12219     player->is_moving = TRUE;
12220     player->is_snapping = FALSE;
12221     player->is_switching = FALSE;
12222     player->is_dropping = FALSE;
12223     player->is_dropping_pressed = FALSE;
12224     player->drop_pressed_delay = 0;
12225
12226 #if 0
12227     /* should better be called here than above, but this breaks some tapes */
12228     ScrollPlayer(player, SCROLL_INIT);
12229 #endif
12230   }
12231   else
12232   {
12233     CheckGravityMovementWhenNotMoving(player);
12234
12235     player->is_moving = FALSE;
12236
12237     /* at this point, the player is allowed to move, but cannot move right now
12238        (e.g. because of something blocking the way) -- ensure that the player
12239        is also allowed to move in the next frame (in old versions before 3.1.1,
12240        the player was forced to wait again for eight frames before next try) */
12241
12242     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12243       player->move_delay = 0;   /* allow direct movement in the next frame */
12244   }
12245
12246   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12247     player->move_delay = player->move_delay_value;
12248
12249   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12250   {
12251     TestIfPlayerTouchesBadThing(jx, jy);
12252     TestIfPlayerTouchesCustomElement(jx, jy);
12253   }
12254
12255   if (!player->active)
12256     RemovePlayer(player);
12257
12258   return moved;
12259 }
12260
12261 void ScrollPlayer(struct PlayerInfo *player, int mode)
12262 {
12263   int jx = player->jx, jy = player->jy;
12264   int last_jx = player->last_jx, last_jy = player->last_jy;
12265   int move_stepsize = TILEX / player->move_delay_value;
12266
12267   if (!player->active)
12268     return;
12269
12270   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12271     return;
12272
12273   if (mode == SCROLL_INIT)
12274   {
12275     player->actual_frame_counter = FrameCounter;
12276     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12277
12278     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12279         Feld[last_jx][last_jy] == EL_EMPTY)
12280     {
12281       int last_field_block_delay = 0;   /* start with no blocking at all */
12282       int block_delay_adjustment = player->block_delay_adjustment;
12283
12284       /* if player blocks last field, add delay for exactly one move */
12285       if (player->block_last_field)
12286       {
12287         last_field_block_delay += player->move_delay_value;
12288
12289         /* when blocking enabled, prevent moving up despite gravity */
12290         if (player->gravity && player->MovDir == MV_UP)
12291           block_delay_adjustment = -1;
12292       }
12293
12294       /* add block delay adjustment (also possible when not blocking) */
12295       last_field_block_delay += block_delay_adjustment;
12296
12297       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12298       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12299     }
12300
12301     if (player->MovPos != 0)    /* player has not yet reached destination */
12302       return;
12303   }
12304   else if (!FrameReached(&player->actual_frame_counter, 1))
12305     return;
12306
12307   if (player->MovPos != 0)
12308   {
12309     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12310     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12311
12312     /* before DrawPlayer() to draw correct player graphic for this case */
12313     if (player->MovPos == 0)
12314       CheckGravityMovement(player);
12315   }
12316
12317   if (player->MovPos == 0)      /* player reached destination field */
12318   {
12319     if (player->move_delay_reset_counter > 0)
12320     {
12321       player->move_delay_reset_counter--;
12322
12323       if (player->move_delay_reset_counter == 0)
12324       {
12325         /* continue with normal speed after quickly moving through gate */
12326         HALVE_PLAYER_SPEED(player);
12327
12328         /* be able to make the next move without delay */
12329         player->move_delay = 0;
12330       }
12331     }
12332
12333     player->last_jx = jx;
12334     player->last_jy = jy;
12335
12336     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12337         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12338         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12339         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12340         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12341         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12342         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12343         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12344     {
12345       DrawPlayer(player);       /* needed here only to cleanup last field */
12346       RemovePlayer(player);
12347
12348       if (local_player->friends_still_needed == 0 ||
12349           IS_SP_ELEMENT(Feld[jx][jy]))
12350         PlayerWins(player);
12351     }
12352
12353     /* this breaks one level: "machine", level 000 */
12354     {
12355       int move_direction = player->MovDir;
12356       int enter_side = MV_DIR_OPPOSITE(move_direction);
12357       int leave_side = move_direction;
12358       int old_jx = last_jx;
12359       int old_jy = last_jy;
12360       int old_element = Feld[old_jx][old_jy];
12361       int new_element = Feld[jx][jy];
12362
12363       if (IS_CUSTOM_ELEMENT(old_element))
12364         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12365                                    CE_LEFT_BY_PLAYER,
12366                                    player->index_bit, leave_side);
12367
12368       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12369                                           CE_PLAYER_LEAVES_X,
12370                                           player->index_bit, leave_side);
12371
12372       if (IS_CUSTOM_ELEMENT(new_element))
12373         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12374                                    player->index_bit, enter_side);
12375
12376       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12377                                           CE_PLAYER_ENTERS_X,
12378                                           player->index_bit, enter_side);
12379
12380       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12381                                         CE_MOVE_OF_X, move_direction);
12382     }
12383
12384     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12385     {
12386       TestIfPlayerTouchesBadThing(jx, jy);
12387       TestIfPlayerTouchesCustomElement(jx, jy);
12388
12389       /* needed because pushed element has not yet reached its destination,
12390          so it would trigger a change event at its previous field location */
12391       if (!player->is_pushing)
12392         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12393
12394       if (!player->active)
12395         RemovePlayer(player);
12396     }
12397
12398     if (!local_player->LevelSolved && level.use_step_counter)
12399     {
12400       int i;
12401
12402       TimePlayed++;
12403
12404       if (TimeLeft > 0)
12405       {
12406         TimeLeft--;
12407
12408         if (TimeLeft <= 10 && setup.time_limit)
12409           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12410
12411         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12412
12413         DisplayGameControlValues();
12414
12415         if (!TimeLeft && setup.time_limit)
12416           for (i = 0; i < MAX_PLAYERS; i++)
12417             KillPlayer(&stored_player[i]);
12418       }
12419       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12420       {
12421         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12422
12423         DisplayGameControlValues();
12424       }
12425     }
12426
12427     if (tape.single_step && tape.recording && !tape.pausing &&
12428         !player->programmed_action)
12429       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12430
12431     if (!player->programmed_action)
12432       CheckSaveEngineSnapshot(player);
12433   }
12434 }
12435
12436 void ScrollScreen(struct PlayerInfo *player, int mode)
12437 {
12438   static unsigned int screen_frame_counter = 0;
12439
12440   if (mode == SCROLL_INIT)
12441   {
12442     /* set scrolling step size according to actual player's moving speed */
12443     ScrollStepSize = TILEX / player->move_delay_value;
12444
12445     screen_frame_counter = FrameCounter;
12446     ScreenMovDir = player->MovDir;
12447     ScreenMovPos = player->MovPos;
12448     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12449     return;
12450   }
12451   else if (!FrameReached(&screen_frame_counter, 1))
12452     return;
12453
12454   if (ScreenMovPos)
12455   {
12456     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12457     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12458     redraw_mask |= REDRAW_FIELD;
12459   }
12460   else
12461     ScreenMovDir = MV_NONE;
12462 }
12463
12464 void TestIfPlayerTouchesCustomElement(int x, int y)
12465 {
12466   static int xy[4][2] =
12467   {
12468     { 0, -1 },
12469     { -1, 0 },
12470     { +1, 0 },
12471     { 0, +1 }
12472   };
12473   static int trigger_sides[4][2] =
12474   {
12475     /* center side       border side */
12476     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12477     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12478     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12479     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12480   };
12481   static int touch_dir[4] =
12482   {
12483     MV_LEFT | MV_RIGHT,
12484     MV_UP   | MV_DOWN,
12485     MV_UP   | MV_DOWN,
12486     MV_LEFT | MV_RIGHT
12487   };
12488   int center_element = Feld[x][y];      /* should always be non-moving! */
12489   int i;
12490
12491   for (i = 0; i < NUM_DIRECTIONS; i++)
12492   {
12493     int xx = x + xy[i][0];
12494     int yy = y + xy[i][1];
12495     int center_side = trigger_sides[i][0];
12496     int border_side = trigger_sides[i][1];
12497     int border_element;
12498
12499     if (!IN_LEV_FIELD(xx, yy))
12500       continue;
12501
12502     if (IS_PLAYER(x, y))                /* player found at center element */
12503     {
12504       struct PlayerInfo *player = PLAYERINFO(x, y);
12505
12506       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12507         border_element = Feld[xx][yy];          /* may be moving! */
12508       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12509         border_element = Feld[xx][yy];
12510       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12511         border_element = MovingOrBlocked2Element(xx, yy);
12512       else
12513         continue;               /* center and border element do not touch */
12514
12515       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12516                                  player->index_bit, border_side);
12517       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12518                                           CE_PLAYER_TOUCHES_X,
12519                                           player->index_bit, border_side);
12520
12521       {
12522         /* use player element that is initially defined in the level playfield,
12523            not the player element that corresponds to the runtime player number
12524            (example: a level that contains EL_PLAYER_3 as the only player would
12525            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12526         int player_element = PLAYERINFO(x, y)->initial_element;
12527
12528         CheckElementChangeBySide(xx, yy, border_element, player_element,
12529                                  CE_TOUCHING_X, border_side);
12530       }
12531     }
12532     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12533     {
12534       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12535
12536       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12537       {
12538         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12539           continue;             /* center and border element do not touch */
12540       }
12541
12542       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12543                                  player->index_bit, center_side);
12544       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12545                                           CE_PLAYER_TOUCHES_X,
12546                                           player->index_bit, center_side);
12547
12548       {
12549         /* use player element that is initially defined in the level playfield,
12550            not the player element that corresponds to the runtime player number
12551            (example: a level that contains EL_PLAYER_3 as the only player would
12552            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12553         int player_element = PLAYERINFO(xx, yy)->initial_element;
12554
12555         CheckElementChangeBySide(x, y, center_element, player_element,
12556                                  CE_TOUCHING_X, center_side);
12557       }
12558
12559       break;
12560     }
12561   }
12562 }
12563
12564 void TestIfElementTouchesCustomElement(int x, int y)
12565 {
12566   static int xy[4][2] =
12567   {
12568     { 0, -1 },
12569     { -1, 0 },
12570     { +1, 0 },
12571     { 0, +1 }
12572   };
12573   static int trigger_sides[4][2] =
12574   {
12575     /* center side      border side */
12576     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12577     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12578     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12579     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12580   };
12581   static int touch_dir[4] =
12582   {
12583     MV_LEFT | MV_RIGHT,
12584     MV_UP   | MV_DOWN,
12585     MV_UP   | MV_DOWN,
12586     MV_LEFT | MV_RIGHT
12587   };
12588   boolean change_center_element = FALSE;
12589   int center_element = Feld[x][y];      /* should always be non-moving! */
12590   int border_element_old[NUM_DIRECTIONS];
12591   int i;
12592
12593   for (i = 0; i < NUM_DIRECTIONS; i++)
12594   {
12595     int xx = x + xy[i][0];
12596     int yy = y + xy[i][1];
12597     int border_element;
12598
12599     border_element_old[i] = -1;
12600
12601     if (!IN_LEV_FIELD(xx, yy))
12602       continue;
12603
12604     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12605       border_element = Feld[xx][yy];    /* may be moving! */
12606     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12607       border_element = Feld[xx][yy];
12608     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12609       border_element = MovingOrBlocked2Element(xx, yy);
12610     else
12611       continue;                 /* center and border element do not touch */
12612
12613     border_element_old[i] = border_element;
12614   }
12615
12616   for (i = 0; i < NUM_DIRECTIONS; i++)
12617   {
12618     int xx = x + xy[i][0];
12619     int yy = y + xy[i][1];
12620     int center_side = trigger_sides[i][0];
12621     int border_element = border_element_old[i];
12622
12623     if (border_element == -1)
12624       continue;
12625
12626     /* check for change of border element */
12627     CheckElementChangeBySide(xx, yy, border_element, center_element,
12628                              CE_TOUCHING_X, center_side);
12629
12630     /* (center element cannot be player, so we dont have to check this here) */
12631   }
12632
12633   for (i = 0; i < NUM_DIRECTIONS; i++)
12634   {
12635     int xx = x + xy[i][0];
12636     int yy = y + xy[i][1];
12637     int border_side = trigger_sides[i][1];
12638     int border_element = border_element_old[i];
12639
12640     if (border_element == -1)
12641       continue;
12642
12643     /* check for change of center element (but change it only once) */
12644     if (!change_center_element)
12645       change_center_element =
12646         CheckElementChangeBySide(x, y, center_element, border_element,
12647                                  CE_TOUCHING_X, border_side);
12648
12649     if (IS_PLAYER(xx, yy))
12650     {
12651       /* use player element that is initially defined in the level playfield,
12652          not the player element that corresponds to the runtime player number
12653          (example: a level that contains EL_PLAYER_3 as the only player would
12654          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12655       int player_element = PLAYERINFO(xx, yy)->initial_element;
12656
12657       CheckElementChangeBySide(x, y, center_element, player_element,
12658                                CE_TOUCHING_X, border_side);
12659     }
12660   }
12661 }
12662
12663 void TestIfElementHitsCustomElement(int x, int y, int direction)
12664 {
12665   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12666   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12667   int hitx = x + dx, hity = y + dy;
12668   int hitting_element = Feld[x][y];
12669   int touched_element;
12670
12671   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12672     return;
12673
12674   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12675                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12676
12677   if (IN_LEV_FIELD(hitx, hity))
12678   {
12679     int opposite_direction = MV_DIR_OPPOSITE(direction);
12680     int hitting_side = direction;
12681     int touched_side = opposite_direction;
12682     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12683                           MovDir[hitx][hity] != direction ||
12684                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12685
12686     object_hit = TRUE;
12687
12688     if (object_hit)
12689     {
12690       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12691                                CE_HITTING_X, touched_side);
12692
12693       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12694                                CE_HIT_BY_X, hitting_side);
12695
12696       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12697                                CE_HIT_BY_SOMETHING, opposite_direction);
12698
12699       if (IS_PLAYER(hitx, hity))
12700       {
12701         /* use player element that is initially defined in the level playfield,
12702            not the player element that corresponds to the runtime player number
12703            (example: a level that contains EL_PLAYER_3 as the only player would
12704            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12705         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12706
12707         CheckElementChangeBySide(x, y, hitting_element, player_element,
12708                                  CE_HITTING_X, touched_side);
12709       }
12710     }
12711   }
12712
12713   /* "hitting something" is also true when hitting the playfield border */
12714   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12715                            CE_HITTING_SOMETHING, direction);
12716 }
12717
12718 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12719 {
12720   int i, kill_x = -1, kill_y = -1;
12721
12722   int bad_element = -1;
12723   static int test_xy[4][2] =
12724   {
12725     { 0, -1 },
12726     { -1, 0 },
12727     { +1, 0 },
12728     { 0, +1 }
12729   };
12730   static int test_dir[4] =
12731   {
12732     MV_UP,
12733     MV_LEFT,
12734     MV_RIGHT,
12735     MV_DOWN
12736   };
12737
12738   for (i = 0; i < NUM_DIRECTIONS; i++)
12739   {
12740     int test_x, test_y, test_move_dir, test_element;
12741
12742     test_x = good_x + test_xy[i][0];
12743     test_y = good_y + test_xy[i][1];
12744
12745     if (!IN_LEV_FIELD(test_x, test_y))
12746       continue;
12747
12748     test_move_dir =
12749       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12750
12751     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12752
12753     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12754        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12755     */
12756     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12757         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12758     {
12759       kill_x = test_x;
12760       kill_y = test_y;
12761       bad_element = test_element;
12762
12763       break;
12764     }
12765   }
12766
12767   if (kill_x != -1 || kill_y != -1)
12768   {
12769     if (IS_PLAYER(good_x, good_y))
12770     {
12771       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12772
12773       if (player->shield_deadly_time_left > 0 &&
12774           !IS_INDESTRUCTIBLE(bad_element))
12775         Bang(kill_x, kill_y);
12776       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12777         KillPlayer(player);
12778     }
12779     else
12780       Bang(good_x, good_y);
12781   }
12782 }
12783
12784 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12785 {
12786   int i, kill_x = -1, kill_y = -1;
12787   int bad_element = Feld[bad_x][bad_y];
12788   static int test_xy[4][2] =
12789   {
12790     { 0, -1 },
12791     { -1, 0 },
12792     { +1, 0 },
12793     { 0, +1 }
12794   };
12795   static int touch_dir[4] =
12796   {
12797     MV_LEFT | MV_RIGHT,
12798     MV_UP   | MV_DOWN,
12799     MV_UP   | MV_DOWN,
12800     MV_LEFT | MV_RIGHT
12801   };
12802   static int test_dir[4] =
12803   {
12804     MV_UP,
12805     MV_LEFT,
12806     MV_RIGHT,
12807     MV_DOWN
12808   };
12809
12810   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12811     return;
12812
12813   for (i = 0; i < NUM_DIRECTIONS; i++)
12814   {
12815     int test_x, test_y, test_move_dir, test_element;
12816
12817     test_x = bad_x + test_xy[i][0];
12818     test_y = bad_y + test_xy[i][1];
12819
12820     if (!IN_LEV_FIELD(test_x, test_y))
12821       continue;
12822
12823     test_move_dir =
12824       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12825
12826     test_element = Feld[test_x][test_y];
12827
12828     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12829        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12830     */
12831     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12832         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12833     {
12834       /* good thing is player or penguin that does not move away */
12835       if (IS_PLAYER(test_x, test_y))
12836       {
12837         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12838
12839         if (bad_element == EL_ROBOT && player->is_moving)
12840           continue;     /* robot does not kill player if he is moving */
12841
12842         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12843         {
12844           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12845             continue;           /* center and border element do not touch */
12846         }
12847
12848         kill_x = test_x;
12849         kill_y = test_y;
12850
12851         break;
12852       }
12853       else if (test_element == EL_PENGUIN)
12854       {
12855         kill_x = test_x;
12856         kill_y = test_y;
12857
12858         break;
12859       }
12860     }
12861   }
12862
12863   if (kill_x != -1 || kill_y != -1)
12864   {
12865     if (IS_PLAYER(kill_x, kill_y))
12866     {
12867       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12868
12869       if (player->shield_deadly_time_left > 0 &&
12870           !IS_INDESTRUCTIBLE(bad_element))
12871         Bang(bad_x, bad_y);
12872       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12873         KillPlayer(player);
12874     }
12875     else
12876       Bang(kill_x, kill_y);
12877   }
12878 }
12879
12880 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12881 {
12882   int bad_element = Feld[bad_x][bad_y];
12883   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12884   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12885   int test_x = bad_x + dx, test_y = bad_y + dy;
12886   int test_move_dir, test_element;
12887   int kill_x = -1, kill_y = -1;
12888
12889   if (!IN_LEV_FIELD(test_x, test_y))
12890     return;
12891
12892   test_move_dir =
12893     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12894
12895   test_element = Feld[test_x][test_y];
12896
12897   if (test_move_dir != bad_move_dir)
12898   {
12899     /* good thing can be player or penguin that does not move away */
12900     if (IS_PLAYER(test_x, test_y))
12901     {
12902       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12903
12904       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12905          player as being hit when he is moving towards the bad thing, because
12906          the "get hit by" condition would be lost after the player stops) */
12907       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12908         return;         /* player moves away from bad thing */
12909
12910       kill_x = test_x;
12911       kill_y = test_y;
12912     }
12913     else if (test_element == EL_PENGUIN)
12914     {
12915       kill_x = test_x;
12916       kill_y = test_y;
12917     }
12918   }
12919
12920   if (kill_x != -1 || kill_y != -1)
12921   {
12922     if (IS_PLAYER(kill_x, kill_y))
12923     {
12924       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12925
12926       if (player->shield_deadly_time_left > 0 &&
12927           !IS_INDESTRUCTIBLE(bad_element))
12928         Bang(bad_x, bad_y);
12929       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12930         KillPlayer(player);
12931     }
12932     else
12933       Bang(kill_x, kill_y);
12934   }
12935 }
12936
12937 void TestIfPlayerTouchesBadThing(int x, int y)
12938 {
12939   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12940 }
12941
12942 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12943 {
12944   TestIfGoodThingHitsBadThing(x, y, move_dir);
12945 }
12946
12947 void TestIfBadThingTouchesPlayer(int x, int y)
12948 {
12949   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12950 }
12951
12952 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12953 {
12954   TestIfBadThingHitsGoodThing(x, y, move_dir);
12955 }
12956
12957 void TestIfFriendTouchesBadThing(int x, int y)
12958 {
12959   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12960 }
12961
12962 void TestIfBadThingTouchesFriend(int x, int y)
12963 {
12964   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12965 }
12966
12967 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12968 {
12969   int i, kill_x = bad_x, kill_y = bad_y;
12970   static int xy[4][2] =
12971   {
12972     { 0, -1 },
12973     { -1, 0 },
12974     { +1, 0 },
12975     { 0, +1 }
12976   };
12977
12978   for (i = 0; i < NUM_DIRECTIONS; i++)
12979   {
12980     int x, y, element;
12981
12982     x = bad_x + xy[i][0];
12983     y = bad_y + xy[i][1];
12984     if (!IN_LEV_FIELD(x, y))
12985       continue;
12986
12987     element = Feld[x][y];
12988     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12989         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12990     {
12991       kill_x = x;
12992       kill_y = y;
12993       break;
12994     }
12995   }
12996
12997   if (kill_x != bad_x || kill_y != bad_y)
12998     Bang(bad_x, bad_y);
12999 }
13000
13001 void KillPlayer(struct PlayerInfo *player)
13002 {
13003   int jx = player->jx, jy = player->jy;
13004
13005   if (!player->active)
13006     return;
13007
13008 #if 0
13009   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13010          player->killed, player->active, player->reanimated);
13011 #endif
13012
13013   /* the following code was introduced to prevent an infinite loop when calling
13014      -> Bang()
13015      -> CheckTriggeredElementChangeExt()
13016      -> ExecuteCustomElementAction()
13017      -> KillPlayer()
13018      -> (infinitely repeating the above sequence of function calls)
13019      which occurs when killing the player while having a CE with the setting
13020      "kill player X when explosion of <player X>"; the solution using a new
13021      field "player->killed" was chosen for backwards compatibility, although
13022      clever use of the fields "player->active" etc. would probably also work */
13023 #if 1
13024   if (player->killed)
13025     return;
13026 #endif
13027
13028   player->killed = TRUE;
13029
13030   /* remove accessible field at the player's position */
13031   Feld[jx][jy] = EL_EMPTY;
13032
13033   /* deactivate shield (else Bang()/Explode() would not work right) */
13034   player->shield_normal_time_left = 0;
13035   player->shield_deadly_time_left = 0;
13036
13037 #if 0
13038   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13039          player->killed, player->active, player->reanimated);
13040 #endif
13041
13042   Bang(jx, jy);
13043
13044 #if 0
13045   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13046          player->killed, player->active, player->reanimated);
13047 #endif
13048
13049   if (player->reanimated)       /* killed player may have been reanimated */
13050     player->killed = player->reanimated = FALSE;
13051   else
13052     BuryPlayer(player);
13053 }
13054
13055 static void KillPlayerUnlessEnemyProtected(int x, int y)
13056 {
13057   if (!PLAYER_ENEMY_PROTECTED(x, y))
13058     KillPlayer(PLAYERINFO(x, y));
13059 }
13060
13061 static void KillPlayerUnlessExplosionProtected(int x, int y)
13062 {
13063   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13064     KillPlayer(PLAYERINFO(x, y));
13065 }
13066
13067 void BuryPlayer(struct PlayerInfo *player)
13068 {
13069   int jx = player->jx, jy = player->jy;
13070
13071   if (!player->active)
13072     return;
13073
13074   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13075   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13076
13077   player->GameOver = TRUE;
13078   RemovePlayer(player);
13079 }
13080
13081 void RemovePlayer(struct PlayerInfo *player)
13082 {
13083   int jx = player->jx, jy = player->jy;
13084   int i, found = FALSE;
13085
13086   player->present = FALSE;
13087   player->active = FALSE;
13088
13089   if (!ExplodeField[jx][jy])
13090     StorePlayer[jx][jy] = 0;
13091
13092   if (player->is_moving)
13093     TEST_DrawLevelField(player->last_jx, player->last_jy);
13094
13095   for (i = 0; i < MAX_PLAYERS; i++)
13096     if (stored_player[i].active)
13097       found = TRUE;
13098
13099   if (!found)
13100     AllPlayersGone = TRUE;
13101
13102   ExitX = ZX = jx;
13103   ExitY = ZY = jy;
13104 }
13105
13106 static void setFieldForSnapping(int x, int y, int element, int direction)
13107 {
13108   struct ElementInfo *ei = &element_info[element];
13109   int direction_bit = MV_DIR_TO_BIT(direction);
13110   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13111   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13112                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13113
13114   Feld[x][y] = EL_ELEMENT_SNAPPING;
13115   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13116
13117   ResetGfxAnimation(x, y);
13118
13119   GfxElement[x][y] = element;
13120   GfxAction[x][y] = action;
13121   GfxDir[x][y] = direction;
13122   GfxFrame[x][y] = -1;
13123 }
13124
13125 /*
13126   =============================================================================
13127   checkDiagonalPushing()
13128   -----------------------------------------------------------------------------
13129   check if diagonal input device direction results in pushing of object
13130   (by checking if the alternative direction is walkable, diggable, ...)
13131   =============================================================================
13132 */
13133
13134 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13135                                     int x, int y, int real_dx, int real_dy)
13136 {
13137   int jx, jy, dx, dy, xx, yy;
13138
13139   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13140     return TRUE;
13141
13142   /* diagonal direction: check alternative direction */
13143   jx = player->jx;
13144   jy = player->jy;
13145   dx = x - jx;
13146   dy = y - jy;
13147   xx = jx + (dx == 0 ? real_dx : 0);
13148   yy = jy + (dy == 0 ? real_dy : 0);
13149
13150   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13151 }
13152
13153 /*
13154   =============================================================================
13155   DigField()
13156   -----------------------------------------------------------------------------
13157   x, y:                 field next to player (non-diagonal) to try to dig to
13158   real_dx, real_dy:     direction as read from input device (can be diagonal)
13159   =============================================================================
13160 */
13161
13162 static int DigField(struct PlayerInfo *player,
13163                     int oldx, int oldy, int x, int y,
13164                     int real_dx, int real_dy, int mode)
13165 {
13166   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13167   boolean player_was_pushing = player->is_pushing;
13168   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13169   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13170   int jx = oldx, jy = oldy;
13171   int dx = x - jx, dy = y - jy;
13172   int nextx = x + dx, nexty = y + dy;
13173   int move_direction = (dx == -1 ? MV_LEFT  :
13174                         dx == +1 ? MV_RIGHT :
13175                         dy == -1 ? MV_UP    :
13176                         dy == +1 ? MV_DOWN  : MV_NONE);
13177   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13178   int dig_side = MV_DIR_OPPOSITE(move_direction);
13179   int old_element = Feld[jx][jy];
13180   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13181   int collect_count;
13182
13183   if (is_player)                /* function can also be called by EL_PENGUIN */
13184   {
13185     if (player->MovPos == 0)
13186     {
13187       player->is_digging = FALSE;
13188       player->is_collecting = FALSE;
13189     }
13190
13191     if (player->MovPos == 0)    /* last pushing move finished */
13192       player->is_pushing = FALSE;
13193
13194     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13195     {
13196       player->is_switching = FALSE;
13197       player->push_delay = -1;
13198
13199       return MP_NO_ACTION;
13200     }
13201   }
13202
13203   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13204     old_element = Back[jx][jy];
13205
13206   /* in case of element dropped at player position, check background */
13207   else if (Back[jx][jy] != EL_EMPTY &&
13208            game.engine_version >= VERSION_IDENT(2,2,0,0))
13209     old_element = Back[jx][jy];
13210
13211   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13212     return MP_NO_ACTION;        /* field has no opening in this direction */
13213
13214   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13215     return MP_NO_ACTION;        /* field has no opening in this direction */
13216
13217   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13218   {
13219     SplashAcid(x, y);
13220
13221     Feld[jx][jy] = player->artwork_element;
13222     InitMovingField(jx, jy, MV_DOWN);
13223     Store[jx][jy] = EL_ACID;
13224     ContinueMoving(jx, jy);
13225     BuryPlayer(player);
13226
13227     return MP_DONT_RUN_INTO;
13228   }
13229
13230   if (player_can_move && DONT_RUN_INTO(element))
13231   {
13232     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13233
13234     return MP_DONT_RUN_INTO;
13235   }
13236
13237   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13238     return MP_NO_ACTION;
13239
13240   collect_count = element_info[element].collect_count_initial;
13241
13242   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13243     return MP_NO_ACTION;
13244
13245   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13246     player_can_move = player_can_move_or_snap;
13247
13248   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13249       game.engine_version >= VERSION_IDENT(2,2,0,0))
13250   {
13251     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13252                                player->index_bit, dig_side);
13253     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13254                                         player->index_bit, dig_side);
13255
13256     if (element == EL_DC_LANDMINE)
13257       Bang(x, y);
13258
13259     if (Feld[x][y] != element)          /* field changed by snapping */
13260       return MP_ACTION;
13261
13262     return MP_NO_ACTION;
13263   }
13264
13265   if (player->gravity && is_player && !player->is_auto_moving &&
13266       canFallDown(player) && move_direction != MV_DOWN &&
13267       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13268     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13269
13270   if (player_can_move &&
13271       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13272   {
13273     int sound_element = SND_ELEMENT(element);
13274     int sound_action = ACTION_WALKING;
13275
13276     if (IS_RND_GATE(element))
13277     {
13278       if (!player->key[RND_GATE_NR(element)])
13279         return MP_NO_ACTION;
13280     }
13281     else if (IS_RND_GATE_GRAY(element))
13282     {
13283       if (!player->key[RND_GATE_GRAY_NR(element)])
13284         return MP_NO_ACTION;
13285     }
13286     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13287     {
13288       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13289         return MP_NO_ACTION;
13290     }
13291     else if (element == EL_EXIT_OPEN ||
13292              element == EL_EM_EXIT_OPEN ||
13293              element == EL_EM_EXIT_OPENING ||
13294              element == EL_STEEL_EXIT_OPEN ||
13295              element == EL_EM_STEEL_EXIT_OPEN ||
13296              element == EL_EM_STEEL_EXIT_OPENING ||
13297              element == EL_SP_EXIT_OPEN ||
13298              element == EL_SP_EXIT_OPENING)
13299     {
13300       sound_action = ACTION_PASSING;    /* player is passing exit */
13301     }
13302     else if (element == EL_EMPTY)
13303     {
13304       sound_action = ACTION_MOVING;             /* nothing to walk on */
13305     }
13306
13307     /* play sound from background or player, whatever is available */
13308     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13309       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13310     else
13311       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13312   }
13313   else if (player_can_move &&
13314            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13315   {
13316     if (!ACCESS_FROM(element, opposite_direction))
13317       return MP_NO_ACTION;      /* field not accessible from this direction */
13318
13319     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13320       return MP_NO_ACTION;
13321
13322     if (IS_EM_GATE(element))
13323     {
13324       if (!player->key[EM_GATE_NR(element)])
13325         return MP_NO_ACTION;
13326     }
13327     else if (IS_EM_GATE_GRAY(element))
13328     {
13329       if (!player->key[EM_GATE_GRAY_NR(element)])
13330         return MP_NO_ACTION;
13331     }
13332     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13333     {
13334       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13335         return MP_NO_ACTION;
13336     }
13337     else if (IS_EMC_GATE(element))
13338     {
13339       if (!player->key[EMC_GATE_NR(element)])
13340         return MP_NO_ACTION;
13341     }
13342     else if (IS_EMC_GATE_GRAY(element))
13343     {
13344       if (!player->key[EMC_GATE_GRAY_NR(element)])
13345         return MP_NO_ACTION;
13346     }
13347     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13348     {
13349       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13350         return MP_NO_ACTION;
13351     }
13352     else if (element == EL_DC_GATE_WHITE ||
13353              element == EL_DC_GATE_WHITE_GRAY ||
13354              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13355     {
13356       if (player->num_white_keys == 0)
13357         return MP_NO_ACTION;
13358
13359       player->num_white_keys--;
13360     }
13361     else if (IS_SP_PORT(element))
13362     {
13363       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13364           element == EL_SP_GRAVITY_PORT_RIGHT ||
13365           element == EL_SP_GRAVITY_PORT_UP ||
13366           element == EL_SP_GRAVITY_PORT_DOWN)
13367         player->gravity = !player->gravity;
13368       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13369                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13370                element == EL_SP_GRAVITY_ON_PORT_UP ||
13371                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13372         player->gravity = TRUE;
13373       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13374                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13375                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13376                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13377         player->gravity = FALSE;
13378     }
13379
13380     /* automatically move to the next field with double speed */
13381     player->programmed_action = move_direction;
13382
13383     if (player->move_delay_reset_counter == 0)
13384     {
13385       player->move_delay_reset_counter = 2;     /* two double speed steps */
13386
13387       DOUBLE_PLAYER_SPEED(player);
13388     }
13389
13390     PlayLevelSoundAction(x, y, ACTION_PASSING);
13391   }
13392   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13393   {
13394     RemoveField(x, y);
13395
13396     if (mode != DF_SNAP)
13397     {
13398       GfxElement[x][y] = GFX_ELEMENT(element);
13399       player->is_digging = TRUE;
13400     }
13401
13402     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13403
13404     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13405                                         player->index_bit, dig_side);
13406
13407     if (mode == DF_SNAP)
13408     {
13409       if (level.block_snap_field)
13410         setFieldForSnapping(x, y, element, move_direction);
13411       else
13412         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13413
13414       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13415                                           player->index_bit, dig_side);
13416     }
13417   }
13418   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13419   {
13420     RemoveField(x, y);
13421
13422     if (is_player && mode != DF_SNAP)
13423     {
13424       GfxElement[x][y] = element;
13425       player->is_collecting = TRUE;
13426     }
13427
13428     if (element == EL_SPEED_PILL)
13429     {
13430       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13431     }
13432     else if (element == EL_EXTRA_TIME && level.time > 0)
13433     {
13434       TimeLeft += level.extra_time;
13435
13436       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13437
13438       DisplayGameControlValues();
13439     }
13440     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13441     {
13442       player->shield_normal_time_left += level.shield_normal_time;
13443       if (element == EL_SHIELD_DEADLY)
13444         player->shield_deadly_time_left += level.shield_deadly_time;
13445     }
13446     else if (element == EL_DYNAMITE ||
13447              element == EL_EM_DYNAMITE ||
13448              element == EL_SP_DISK_RED)
13449     {
13450       if (player->inventory_size < MAX_INVENTORY_SIZE)
13451         player->inventory_element[player->inventory_size++] = element;
13452
13453       DrawGameDoorValues();
13454     }
13455     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13456     {
13457       player->dynabomb_count++;
13458       player->dynabombs_left++;
13459     }
13460     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13461     {
13462       player->dynabomb_size++;
13463     }
13464     else if (element == EL_DYNABOMB_INCREASE_POWER)
13465     {
13466       player->dynabomb_xl = TRUE;
13467     }
13468     else if (IS_KEY(element))
13469     {
13470       player->key[KEY_NR(element)] = TRUE;
13471
13472       DrawGameDoorValues();
13473     }
13474     else if (element == EL_DC_KEY_WHITE)
13475     {
13476       player->num_white_keys++;
13477
13478       /* display white keys? */
13479       /* DrawGameDoorValues(); */
13480     }
13481     else if (IS_ENVELOPE(element))
13482     {
13483       player->show_envelope = element;
13484     }
13485     else if (element == EL_EMC_LENSES)
13486     {
13487       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13488
13489       RedrawAllInvisibleElementsForLenses();
13490     }
13491     else if (element == EL_EMC_MAGNIFIER)
13492     {
13493       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13494
13495       RedrawAllInvisibleElementsForMagnifier();
13496     }
13497     else if (IS_DROPPABLE(element) ||
13498              IS_THROWABLE(element))     /* can be collected and dropped */
13499     {
13500       int i;
13501
13502       if (collect_count == 0)
13503         player->inventory_infinite_element = element;
13504       else
13505         for (i = 0; i < collect_count; i++)
13506           if (player->inventory_size < MAX_INVENTORY_SIZE)
13507             player->inventory_element[player->inventory_size++] = element;
13508
13509       DrawGameDoorValues();
13510     }
13511     else if (collect_count > 0)
13512     {
13513       local_player->gems_still_needed -= collect_count;
13514       if (local_player->gems_still_needed < 0)
13515         local_player->gems_still_needed = 0;
13516
13517       game.snapshot.collected_item = TRUE;
13518
13519       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13520
13521       DisplayGameControlValues();
13522     }
13523
13524     RaiseScoreElement(element);
13525     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13526
13527     if (is_player)
13528       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13529                                           player->index_bit, dig_side);
13530
13531     if (mode == DF_SNAP)
13532     {
13533       if (level.block_snap_field)
13534         setFieldForSnapping(x, y, element, move_direction);
13535       else
13536         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13537
13538       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13539                                           player->index_bit, dig_side);
13540     }
13541   }
13542   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13543   {
13544     if (mode == DF_SNAP && element != EL_BD_ROCK)
13545       return MP_NO_ACTION;
13546
13547     if (CAN_FALL(element) && dy)
13548       return MP_NO_ACTION;
13549
13550     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13551         !(element == EL_SPRING && level.use_spring_bug))
13552       return MP_NO_ACTION;
13553
13554     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13555         ((move_direction & MV_VERTICAL &&
13556           ((element_info[element].move_pattern & MV_LEFT &&
13557             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13558            (element_info[element].move_pattern & MV_RIGHT &&
13559             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13560          (move_direction & MV_HORIZONTAL &&
13561           ((element_info[element].move_pattern & MV_UP &&
13562             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13563            (element_info[element].move_pattern & MV_DOWN &&
13564             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13565       return MP_NO_ACTION;
13566
13567     /* do not push elements already moving away faster than player */
13568     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13569         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13570       return MP_NO_ACTION;
13571
13572     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13573     {
13574       if (player->push_delay_value == -1 || !player_was_pushing)
13575         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13576     }
13577     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13578     {
13579       if (player->push_delay_value == -1)
13580         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13581     }
13582     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13583     {
13584       if (!player->is_pushing)
13585         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13586     }
13587
13588     player->is_pushing = TRUE;
13589     player->is_active = TRUE;
13590
13591     if (!(IN_LEV_FIELD(nextx, nexty) &&
13592           (IS_FREE(nextx, nexty) ||
13593            (IS_SB_ELEMENT(element) &&
13594             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13595            (IS_CUSTOM_ELEMENT(element) &&
13596             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13597       return MP_NO_ACTION;
13598
13599     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13600       return MP_NO_ACTION;
13601
13602     if (player->push_delay == -1)       /* new pushing; restart delay */
13603       player->push_delay = 0;
13604
13605     if (player->push_delay < player->push_delay_value &&
13606         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13607         element != EL_SPRING && element != EL_BALLOON)
13608     {
13609       /* make sure that there is no move delay before next try to push */
13610       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13611         player->move_delay = 0;
13612
13613       return MP_NO_ACTION;
13614     }
13615
13616     if (IS_CUSTOM_ELEMENT(element) &&
13617         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13618     {
13619       if (!DigFieldByCE(nextx, nexty, element))
13620         return MP_NO_ACTION;
13621     }
13622
13623     if (IS_SB_ELEMENT(element))
13624     {
13625       if (element == EL_SOKOBAN_FIELD_FULL)
13626       {
13627         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13628         local_player->sokobanfields_still_needed++;
13629       }
13630
13631       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13632       {
13633         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13634         local_player->sokobanfields_still_needed--;
13635       }
13636
13637       Feld[x][y] = EL_SOKOBAN_OBJECT;
13638
13639       if (Back[x][y] == Back[nextx][nexty])
13640         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13641       else if (Back[x][y] != 0)
13642         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13643                                     ACTION_EMPTYING);
13644       else
13645         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13646                                     ACTION_FILLING);
13647
13648       if (local_player->sokobanfields_still_needed == 0 &&
13649           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13650       {
13651         PlayerWins(player);
13652
13653         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13654       }
13655     }
13656     else
13657       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13658
13659     InitMovingField(x, y, move_direction);
13660     GfxAction[x][y] = ACTION_PUSHING;
13661
13662     if (mode == DF_SNAP)
13663       ContinueMoving(x, y);
13664     else
13665       MovPos[x][y] = (dx != 0 ? dx : dy);
13666
13667     Pushed[x][y] = TRUE;
13668     Pushed[nextx][nexty] = TRUE;
13669
13670     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13671       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13672     else
13673       player->push_delay_value = -1;    /* get new value later */
13674
13675     /* check for element change _after_ element has been pushed */
13676     if (game.use_change_when_pushing_bug)
13677     {
13678       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13679                                  player->index_bit, dig_side);
13680       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13681                                           player->index_bit, dig_side);
13682     }
13683   }
13684   else if (IS_SWITCHABLE(element))
13685   {
13686     if (PLAYER_SWITCHING(player, x, y))
13687     {
13688       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13689                                           player->index_bit, dig_side);
13690
13691       return MP_ACTION;
13692     }
13693
13694     player->is_switching = TRUE;
13695     player->switch_x = x;
13696     player->switch_y = y;
13697
13698     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13699
13700     if (element == EL_ROBOT_WHEEL)
13701     {
13702       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13703       ZX = x;
13704       ZY = y;
13705
13706       game.robot_wheel_active = TRUE;
13707
13708       TEST_DrawLevelField(x, y);
13709     }
13710     else if (element == EL_SP_TERMINAL)
13711     {
13712       int xx, yy;
13713
13714       SCAN_PLAYFIELD(xx, yy)
13715       {
13716         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13717         {
13718           Bang(xx, yy);
13719         }
13720         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13721         {
13722           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13723
13724           ResetGfxAnimation(xx, yy);
13725           TEST_DrawLevelField(xx, yy);
13726         }
13727       }
13728     }
13729     else if (IS_BELT_SWITCH(element))
13730     {
13731       ToggleBeltSwitch(x, y);
13732     }
13733     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13734              element == EL_SWITCHGATE_SWITCH_DOWN ||
13735              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13736              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13737     {
13738       ToggleSwitchgateSwitch(x, y);
13739     }
13740     else if (element == EL_LIGHT_SWITCH ||
13741              element == EL_LIGHT_SWITCH_ACTIVE)
13742     {
13743       ToggleLightSwitch(x, y);
13744     }
13745     else if (element == EL_TIMEGATE_SWITCH ||
13746              element == EL_DC_TIMEGATE_SWITCH)
13747     {
13748       ActivateTimegateSwitch(x, y);
13749     }
13750     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13751              element == EL_BALLOON_SWITCH_RIGHT ||
13752              element == EL_BALLOON_SWITCH_UP    ||
13753              element == EL_BALLOON_SWITCH_DOWN  ||
13754              element == EL_BALLOON_SWITCH_NONE  ||
13755              element == EL_BALLOON_SWITCH_ANY)
13756     {
13757       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13758                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13759                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13760                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13761                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13762                              move_direction);
13763     }
13764     else if (element == EL_LAMP)
13765     {
13766       Feld[x][y] = EL_LAMP_ACTIVE;
13767       local_player->lights_still_needed--;
13768
13769       ResetGfxAnimation(x, y);
13770       TEST_DrawLevelField(x, y);
13771     }
13772     else if (element == EL_TIME_ORB_FULL)
13773     {
13774       Feld[x][y] = EL_TIME_ORB_EMPTY;
13775
13776       if (level.time > 0 || level.use_time_orb_bug)
13777       {
13778         TimeLeft += level.time_orb_time;
13779         game.no_time_limit = FALSE;
13780
13781         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13782
13783         DisplayGameControlValues();
13784       }
13785
13786       ResetGfxAnimation(x, y);
13787       TEST_DrawLevelField(x, y);
13788     }
13789     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13790              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13791     {
13792       int xx, yy;
13793
13794       game.ball_state = !game.ball_state;
13795
13796       SCAN_PLAYFIELD(xx, yy)
13797       {
13798         int e = Feld[xx][yy];
13799
13800         if (game.ball_state)
13801         {
13802           if (e == EL_EMC_MAGIC_BALL)
13803             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13804           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13805             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13806         }
13807         else
13808         {
13809           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13810             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13811           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13812             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13813         }
13814       }
13815     }
13816
13817     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13818                                         player->index_bit, dig_side);
13819
13820     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13821                                         player->index_bit, dig_side);
13822
13823     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13824                                         player->index_bit, dig_side);
13825
13826     return MP_ACTION;
13827   }
13828   else
13829   {
13830     if (!PLAYER_SWITCHING(player, x, y))
13831     {
13832       player->is_switching = TRUE;
13833       player->switch_x = x;
13834       player->switch_y = y;
13835
13836       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13837                                  player->index_bit, dig_side);
13838       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13839                                           player->index_bit, dig_side);
13840
13841       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13842                                  player->index_bit, dig_side);
13843       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13844                                           player->index_bit, dig_side);
13845     }
13846
13847     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13848                                player->index_bit, dig_side);
13849     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13850                                         player->index_bit, dig_side);
13851
13852     return MP_NO_ACTION;
13853   }
13854
13855   player->push_delay = -1;
13856
13857   if (is_player)                /* function can also be called by EL_PENGUIN */
13858   {
13859     if (Feld[x][y] != element)          /* really digged/collected something */
13860     {
13861       player->is_collecting = !player->is_digging;
13862       player->is_active = TRUE;
13863     }
13864   }
13865
13866   return MP_MOVING;
13867 }
13868
13869 static boolean DigFieldByCE(int x, int y, int digging_element)
13870 {
13871   int element = Feld[x][y];
13872
13873   if (!IS_FREE(x, y))
13874   {
13875     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13876                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13877                   ACTION_BREAKING);
13878
13879     /* no element can dig solid indestructible elements */
13880     if (IS_INDESTRUCTIBLE(element) &&
13881         !IS_DIGGABLE(element) &&
13882         !IS_COLLECTIBLE(element))
13883       return FALSE;
13884
13885     if (AmoebaNr[x][y] &&
13886         (element == EL_AMOEBA_FULL ||
13887          element == EL_BD_AMOEBA ||
13888          element == EL_AMOEBA_GROWING))
13889     {
13890       AmoebaCnt[AmoebaNr[x][y]]--;
13891       AmoebaCnt2[AmoebaNr[x][y]]--;
13892     }
13893
13894     if (IS_MOVING(x, y))
13895       RemoveMovingField(x, y);
13896     else
13897     {
13898       RemoveField(x, y);
13899       TEST_DrawLevelField(x, y);
13900     }
13901
13902     /* if digged element was about to explode, prevent the explosion */
13903     ExplodeField[x][y] = EX_TYPE_NONE;
13904
13905     PlayLevelSoundAction(x, y, action);
13906   }
13907
13908   Store[x][y] = EL_EMPTY;
13909
13910   /* this makes it possible to leave the removed element again */
13911   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13912     Store[x][y] = element;
13913
13914   return TRUE;
13915 }
13916
13917 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13918 {
13919   int jx = player->jx, jy = player->jy;
13920   int x = jx + dx, y = jy + dy;
13921   int snap_direction = (dx == -1 ? MV_LEFT  :
13922                         dx == +1 ? MV_RIGHT :
13923                         dy == -1 ? MV_UP    :
13924                         dy == +1 ? MV_DOWN  : MV_NONE);
13925   boolean can_continue_snapping = (level.continuous_snapping &&
13926                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13927
13928   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13929     return FALSE;
13930
13931   if (!player->active || !IN_LEV_FIELD(x, y))
13932     return FALSE;
13933
13934   if (dx && dy)
13935     return FALSE;
13936
13937   if (!dx && !dy)
13938   {
13939     if (player->MovPos == 0)
13940       player->is_pushing = FALSE;
13941
13942     player->is_snapping = FALSE;
13943
13944     if (player->MovPos == 0)
13945     {
13946       player->is_moving = FALSE;
13947       player->is_digging = FALSE;
13948       player->is_collecting = FALSE;
13949     }
13950
13951     return FALSE;
13952   }
13953
13954   /* prevent snapping with already pressed snap key when not allowed */
13955   if (player->is_snapping && !can_continue_snapping)
13956     return FALSE;
13957
13958   player->MovDir = snap_direction;
13959
13960   if (player->MovPos == 0)
13961   {
13962     player->is_moving = FALSE;
13963     player->is_digging = FALSE;
13964     player->is_collecting = FALSE;
13965   }
13966
13967   player->is_dropping = FALSE;
13968   player->is_dropping_pressed = FALSE;
13969   player->drop_pressed_delay = 0;
13970
13971   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13972     return FALSE;
13973
13974   player->is_snapping = TRUE;
13975   player->is_active = TRUE;
13976
13977   if (player->MovPos == 0)
13978   {
13979     player->is_moving = FALSE;
13980     player->is_digging = FALSE;
13981     player->is_collecting = FALSE;
13982   }
13983
13984   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13985     TEST_DrawLevelField(player->last_jx, player->last_jy);
13986
13987   TEST_DrawLevelField(x, y);
13988
13989   return TRUE;
13990 }
13991
13992 static boolean DropElement(struct PlayerInfo *player)
13993 {
13994   int old_element, new_element;
13995   int dropx = player->jx, dropy = player->jy;
13996   int drop_direction = player->MovDir;
13997   int drop_side = drop_direction;
13998   int drop_element = get_next_dropped_element(player);
13999
14000   /* do not drop an element on top of another element; when holding drop key
14001      pressed without moving, dropped element must move away before the next
14002      element can be dropped (this is especially important if the next element
14003      is dynamite, which can be placed on background for historical reasons) */
14004   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14005     return MP_ACTION;
14006
14007   if (IS_THROWABLE(drop_element))
14008   {
14009     dropx += GET_DX_FROM_DIR(drop_direction);
14010     dropy += GET_DY_FROM_DIR(drop_direction);
14011
14012     if (!IN_LEV_FIELD(dropx, dropy))
14013       return FALSE;
14014   }
14015
14016   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14017   new_element = drop_element;           /* default: no change when dropping */
14018
14019   /* check if player is active, not moving and ready to drop */
14020   if (!player->active || player->MovPos || player->drop_delay > 0)
14021     return FALSE;
14022
14023   /* check if player has anything that can be dropped */
14024   if (new_element == EL_UNDEFINED)
14025     return FALSE;
14026
14027   /* only set if player has anything that can be dropped */
14028   player->is_dropping_pressed = TRUE;
14029
14030   /* check if drop key was pressed long enough for EM style dynamite */
14031   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14032     return FALSE;
14033
14034   /* check if anything can be dropped at the current position */
14035   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14036     return FALSE;
14037
14038   /* collected custom elements can only be dropped on empty fields */
14039   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14040     return FALSE;
14041
14042   if (old_element != EL_EMPTY)
14043     Back[dropx][dropy] = old_element;   /* store old element on this field */
14044
14045   ResetGfxAnimation(dropx, dropy);
14046   ResetRandomAnimationValue(dropx, dropy);
14047
14048   if (player->inventory_size > 0 ||
14049       player->inventory_infinite_element != EL_UNDEFINED)
14050   {
14051     if (player->inventory_size > 0)
14052     {
14053       player->inventory_size--;
14054
14055       DrawGameDoorValues();
14056
14057       if (new_element == EL_DYNAMITE)
14058         new_element = EL_DYNAMITE_ACTIVE;
14059       else if (new_element == EL_EM_DYNAMITE)
14060         new_element = EL_EM_DYNAMITE_ACTIVE;
14061       else if (new_element == EL_SP_DISK_RED)
14062         new_element = EL_SP_DISK_RED_ACTIVE;
14063     }
14064
14065     Feld[dropx][dropy] = new_element;
14066
14067     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14068       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14069                           el2img(Feld[dropx][dropy]), 0);
14070
14071     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14072
14073     /* needed if previous element just changed to "empty" in the last frame */
14074     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14075
14076     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14077                                player->index_bit, drop_side);
14078     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14079                                         CE_PLAYER_DROPS_X,
14080                                         player->index_bit, drop_side);
14081
14082     TestIfElementTouchesCustomElement(dropx, dropy);
14083   }
14084   else          /* player is dropping a dyna bomb */
14085   {
14086     player->dynabombs_left--;
14087
14088     Feld[dropx][dropy] = new_element;
14089
14090     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14091       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14092                           el2img(Feld[dropx][dropy]), 0);
14093
14094     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14095   }
14096
14097   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14098     InitField_WithBug1(dropx, dropy, FALSE);
14099
14100   new_element = Feld[dropx][dropy];     /* element might have changed */
14101
14102   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14103       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14104   {
14105     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14106       MovDir[dropx][dropy] = drop_direction;
14107
14108     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14109
14110     /* do not cause impact style collision by dropping elements that can fall */
14111     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14112   }
14113
14114   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14115   player->is_dropping = TRUE;
14116
14117   player->drop_pressed_delay = 0;
14118   player->is_dropping_pressed = FALSE;
14119
14120   player->drop_x = dropx;
14121   player->drop_y = dropy;
14122
14123   return TRUE;
14124 }
14125
14126 /* ------------------------------------------------------------------------- */
14127 /* game sound playing functions                                              */
14128 /* ------------------------------------------------------------------------- */
14129
14130 static int *loop_sound_frame = NULL;
14131 static int *loop_sound_volume = NULL;
14132
14133 void InitPlayLevelSound()
14134 {
14135   int num_sounds = getSoundListSize();
14136
14137   checked_free(loop_sound_frame);
14138   checked_free(loop_sound_volume);
14139
14140   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14141   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14142 }
14143
14144 static void PlayLevelSound(int x, int y, int nr)
14145 {
14146   int sx = SCREENX(x), sy = SCREENY(y);
14147   int volume, stereo_position;
14148   int max_distance = 8;
14149   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14150
14151   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14152       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14153     return;
14154
14155   if (!IN_LEV_FIELD(x, y) ||
14156       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14157       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14158     return;
14159
14160   volume = SOUND_MAX_VOLUME;
14161
14162   if (!IN_SCR_FIELD(sx, sy))
14163   {
14164     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14165     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14166
14167     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14168   }
14169
14170   stereo_position = (SOUND_MAX_LEFT +
14171                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14172                      (SCR_FIELDX + 2 * max_distance));
14173
14174   if (IS_LOOP_SOUND(nr))
14175   {
14176     /* This assures that quieter loop sounds do not overwrite louder ones,
14177        while restarting sound volume comparison with each new game frame. */
14178
14179     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14180       return;
14181
14182     loop_sound_volume[nr] = volume;
14183     loop_sound_frame[nr] = FrameCounter;
14184   }
14185
14186   PlaySoundExt(nr, volume, stereo_position, type);
14187 }
14188
14189 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14190 {
14191   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14192                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14193                  y < LEVELY(BY1) ? LEVELY(BY1) :
14194                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14195                  sound_action);
14196 }
14197
14198 static void PlayLevelSoundAction(int x, int y, int action)
14199 {
14200   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14201 }
14202
14203 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14204 {
14205   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14206
14207   if (sound_effect != SND_UNDEFINED)
14208     PlayLevelSound(x, y, sound_effect);
14209 }
14210
14211 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14212                                               int action)
14213 {
14214   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14215
14216   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14217     PlayLevelSound(x, y, sound_effect);
14218 }
14219
14220 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14221 {
14222   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14223
14224   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14225     PlayLevelSound(x, y, sound_effect);
14226 }
14227
14228 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14229 {
14230   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14231
14232   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14233     StopSound(sound_effect);
14234 }
14235
14236 static int getLevelMusicNr()
14237 {
14238   if (levelset.music[level_nr] != MUS_UNDEFINED)
14239     return levelset.music[level_nr];            /* from config file */
14240   else
14241     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14242 }
14243
14244 static void FadeLevelSounds()
14245 {
14246   FadeSounds();
14247 }
14248
14249 static void FadeLevelMusic()
14250 {
14251   int music_nr = getLevelMusicNr();
14252   char *curr_music = getCurrentlyPlayingMusicFilename();
14253   char *next_music = getMusicInfoEntryFilename(music_nr);
14254
14255   if (!strEqual(curr_music, next_music))
14256     FadeMusic();
14257 }
14258
14259 void FadeLevelSoundsAndMusic()
14260 {
14261   FadeLevelSounds();
14262   FadeLevelMusic();
14263 }
14264
14265 static void PlayLevelMusic()
14266 {
14267   int music_nr = getLevelMusicNr();
14268   char *curr_music = getCurrentlyPlayingMusicFilename();
14269   char *next_music = getMusicInfoEntryFilename(music_nr);
14270
14271   if (!strEqual(curr_music, next_music))
14272     PlayMusic(music_nr);
14273 }
14274
14275 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14276 {
14277   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14278   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14279   int x = xx - 1 - offset;
14280   int y = yy - 1 - offset;
14281
14282   switch (sample)
14283   {
14284     case SAMPLE_blank:
14285       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14286       break;
14287
14288     case SAMPLE_roll:
14289       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14290       break;
14291
14292     case SAMPLE_stone:
14293       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14294       break;
14295
14296     case SAMPLE_nut:
14297       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14298       break;
14299
14300     case SAMPLE_crack:
14301       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14302       break;
14303
14304     case SAMPLE_bug:
14305       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14306       break;
14307
14308     case SAMPLE_tank:
14309       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14310       break;
14311
14312     case SAMPLE_android_clone:
14313       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14314       break;
14315
14316     case SAMPLE_android_move:
14317       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14318       break;
14319
14320     case SAMPLE_spring:
14321       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14322       break;
14323
14324     case SAMPLE_slurp:
14325       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14326       break;
14327
14328     case SAMPLE_eater:
14329       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14330       break;
14331
14332     case SAMPLE_eater_eat:
14333       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14334       break;
14335
14336     case SAMPLE_alien:
14337       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14338       break;
14339
14340     case SAMPLE_collect:
14341       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14342       break;
14343
14344     case SAMPLE_diamond:
14345       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14346       break;
14347
14348     case SAMPLE_squash:
14349       /* !!! CHECK THIS !!! */
14350 #if 1
14351       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14352 #else
14353       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14354 #endif
14355       break;
14356
14357     case SAMPLE_wonderfall:
14358       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14359       break;
14360
14361     case SAMPLE_drip:
14362       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14363       break;
14364
14365     case SAMPLE_push:
14366       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14367       break;
14368
14369     case SAMPLE_dirt:
14370       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14371       break;
14372
14373     case SAMPLE_acid:
14374       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14375       break;
14376
14377     case SAMPLE_ball:
14378       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14379       break;
14380
14381     case SAMPLE_grow:
14382       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14383       break;
14384
14385     case SAMPLE_wonder:
14386       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14387       break;
14388
14389     case SAMPLE_door:
14390       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14391       break;
14392
14393     case SAMPLE_exit_open:
14394       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14395       break;
14396
14397     case SAMPLE_exit_leave:
14398       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14399       break;
14400
14401     case SAMPLE_dynamite:
14402       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14403       break;
14404
14405     case SAMPLE_tick:
14406       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14407       break;
14408
14409     case SAMPLE_press:
14410       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14411       break;
14412
14413     case SAMPLE_wheel:
14414       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14415       break;
14416
14417     case SAMPLE_boom:
14418       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14419       break;
14420
14421     case SAMPLE_die:
14422       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14423       break;
14424
14425     case SAMPLE_time:
14426       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14427       break;
14428
14429     default:
14430       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14431       break;
14432   }
14433 }
14434
14435 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14436 {
14437   int element = map_element_SP_to_RND(element_sp);
14438   int action = map_action_SP_to_RND(action_sp);
14439   int offset = (setup.sp_show_border_elements ? 0 : 1);
14440   int x = xx - offset;
14441   int y = yy - offset;
14442
14443   PlayLevelSoundElementAction(x, y, element, action);
14444 }
14445
14446 void RaiseScore(int value)
14447 {
14448   local_player->score += value;
14449
14450   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14451
14452   DisplayGameControlValues();
14453 }
14454
14455 void RaiseScoreElement(int element)
14456 {
14457   switch (element)
14458   {
14459     case EL_EMERALD:
14460     case EL_BD_DIAMOND:
14461     case EL_EMERALD_YELLOW:
14462     case EL_EMERALD_RED:
14463     case EL_EMERALD_PURPLE:
14464     case EL_SP_INFOTRON:
14465       RaiseScore(level.score[SC_EMERALD]);
14466       break;
14467     case EL_DIAMOND:
14468       RaiseScore(level.score[SC_DIAMOND]);
14469       break;
14470     case EL_CRYSTAL:
14471       RaiseScore(level.score[SC_CRYSTAL]);
14472       break;
14473     case EL_PEARL:
14474       RaiseScore(level.score[SC_PEARL]);
14475       break;
14476     case EL_BUG:
14477     case EL_BD_BUTTERFLY:
14478     case EL_SP_ELECTRON:
14479       RaiseScore(level.score[SC_BUG]);
14480       break;
14481     case EL_SPACESHIP:
14482     case EL_BD_FIREFLY:
14483     case EL_SP_SNIKSNAK:
14484       RaiseScore(level.score[SC_SPACESHIP]);
14485       break;
14486     case EL_YAMYAM:
14487     case EL_DARK_YAMYAM:
14488       RaiseScore(level.score[SC_YAMYAM]);
14489       break;
14490     case EL_ROBOT:
14491       RaiseScore(level.score[SC_ROBOT]);
14492       break;
14493     case EL_PACMAN:
14494       RaiseScore(level.score[SC_PACMAN]);
14495       break;
14496     case EL_NUT:
14497       RaiseScore(level.score[SC_NUT]);
14498       break;
14499     case EL_DYNAMITE:
14500     case EL_EM_DYNAMITE:
14501     case EL_SP_DISK_RED:
14502     case EL_DYNABOMB_INCREASE_NUMBER:
14503     case EL_DYNABOMB_INCREASE_SIZE:
14504     case EL_DYNABOMB_INCREASE_POWER:
14505       RaiseScore(level.score[SC_DYNAMITE]);
14506       break;
14507     case EL_SHIELD_NORMAL:
14508     case EL_SHIELD_DEADLY:
14509       RaiseScore(level.score[SC_SHIELD]);
14510       break;
14511     case EL_EXTRA_TIME:
14512       RaiseScore(level.extra_time_score);
14513       break;
14514     case EL_KEY_1:
14515     case EL_KEY_2:
14516     case EL_KEY_3:
14517     case EL_KEY_4:
14518     case EL_EM_KEY_1:
14519     case EL_EM_KEY_2:
14520     case EL_EM_KEY_3:
14521     case EL_EM_KEY_4:
14522     case EL_EMC_KEY_5:
14523     case EL_EMC_KEY_6:
14524     case EL_EMC_KEY_7:
14525     case EL_EMC_KEY_8:
14526     case EL_DC_KEY_WHITE:
14527       RaiseScore(level.score[SC_KEY]);
14528       break;
14529     default:
14530       RaiseScore(element_info[element].collect_score);
14531       break;
14532   }
14533 }
14534
14535 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14536 {
14537   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14538   {
14539     /* closing door required in case of envelope style request dialogs */
14540     if (!skip_request)
14541       CloseDoor(DOOR_CLOSE_1);
14542
14543 #if defined(NETWORK_AVALIABLE)
14544     if (options.network)
14545       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14546     else
14547 #endif
14548     {
14549       if (quick_quit)
14550         FadeSkipNextFadeIn();
14551
14552       SetGameStatus(GAME_MODE_MAIN);
14553
14554       DrawMainMenu();
14555     }
14556   }
14557   else          /* continue playing the game */
14558   {
14559     if (tape.playing && tape.deactivate_display)
14560       TapeDeactivateDisplayOff(TRUE);
14561
14562     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14563
14564     if (tape.playing && tape.deactivate_display)
14565       TapeDeactivateDisplayOn();
14566   }
14567 }
14568
14569 void RequestQuitGame(boolean ask_if_really_quit)
14570 {
14571   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14572   boolean skip_request = AllPlayersGone || quick_quit;
14573
14574   RequestQuitGameExt(skip_request, quick_quit,
14575                      "Do you really want to quit the game?");
14576 }
14577
14578
14579 /* ------------------------------------------------------------------------- */
14580 /* random generator functions                                                */
14581 /* ------------------------------------------------------------------------- */
14582
14583 unsigned int InitEngineRandom_RND(int seed)
14584 {
14585   game.num_random_calls = 0;
14586
14587   return InitEngineRandom(seed);
14588 }
14589
14590 unsigned int RND(int max)
14591 {
14592   if (max > 0)
14593   {
14594     game.num_random_calls++;
14595
14596     return GetEngineRandom(max);
14597   }
14598
14599   return 0;
14600 }
14601
14602
14603 /* ------------------------------------------------------------------------- */
14604 /* game engine snapshot handling functions                                   */
14605 /* ------------------------------------------------------------------------- */
14606
14607 struct EngineSnapshotInfo
14608 {
14609   /* runtime values for custom element collect score */
14610   int collect_score[NUM_CUSTOM_ELEMENTS];
14611
14612   /* runtime values for group element choice position */
14613   int choice_pos[NUM_GROUP_ELEMENTS];
14614
14615   /* runtime values for belt position animations */
14616   int belt_graphic[4][NUM_BELT_PARTS];
14617   int belt_anim_mode[4][NUM_BELT_PARTS];
14618 };
14619
14620 static struct EngineSnapshotInfo engine_snapshot_rnd;
14621 static char *snapshot_level_identifier = NULL;
14622 static int snapshot_level_nr = -1;
14623
14624 static void SaveEngineSnapshotValues_RND()
14625 {
14626   static int belt_base_active_element[4] =
14627   {
14628     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14629     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14630     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14631     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14632   };
14633   int i, j;
14634
14635   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14636   {
14637     int element = EL_CUSTOM_START + i;
14638
14639     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14640   }
14641
14642   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14643   {
14644     int element = EL_GROUP_START + i;
14645
14646     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14647   }
14648
14649   for (i = 0; i < 4; i++)
14650   {
14651     for (j = 0; j < NUM_BELT_PARTS; j++)
14652     {
14653       int element = belt_base_active_element[i] + j;
14654       int graphic = el2img(element);
14655       int anim_mode = graphic_info[graphic].anim_mode;
14656
14657       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14658       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14659     }
14660   }
14661 }
14662
14663 static void LoadEngineSnapshotValues_RND()
14664 {
14665   unsigned int num_random_calls = game.num_random_calls;
14666   int i, j;
14667
14668   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14669   {
14670     int element = EL_CUSTOM_START + i;
14671
14672     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14673   }
14674
14675   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14676   {
14677     int element = EL_GROUP_START + i;
14678
14679     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14680   }
14681
14682   for (i = 0; i < 4; i++)
14683   {
14684     for (j = 0; j < NUM_BELT_PARTS; j++)
14685     {
14686       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14687       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14688
14689       graphic_info[graphic].anim_mode = anim_mode;
14690     }
14691   }
14692
14693   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14694   {
14695     InitRND(tape.random_seed);
14696     for (i = 0; i < num_random_calls; i++)
14697       RND(1);
14698   }
14699
14700   if (game.num_random_calls != num_random_calls)
14701   {
14702     Error(ERR_INFO, "number of random calls out of sync");
14703     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14704     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14705     Error(ERR_EXIT, "this should not happen -- please debug");
14706   }
14707 }
14708
14709 void FreeEngineSnapshotSingle()
14710 {
14711   FreeSnapshotSingle();
14712
14713   setString(&snapshot_level_identifier, NULL);
14714   snapshot_level_nr = -1;
14715 }
14716
14717 void FreeEngineSnapshotList()
14718 {
14719   FreeSnapshotList();
14720 }
14721
14722 ListNode *SaveEngineSnapshotBuffers()
14723 {
14724   ListNode *buffers = NULL;
14725
14726   /* copy some special values to a structure better suited for the snapshot */
14727
14728   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14729     SaveEngineSnapshotValues_RND();
14730   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14731     SaveEngineSnapshotValues_EM();
14732   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14733     SaveEngineSnapshotValues_SP(&buffers);
14734
14735   /* save values stored in special snapshot structure */
14736
14737   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14738     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14739   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14740     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14741   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14742     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14743
14744   /* save further RND engine values */
14745
14746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14749
14750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14754
14755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14760
14761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14764
14765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14766
14767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14768
14769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14771
14772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14774   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14777   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14778   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14780   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14783   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14786   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14787   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14788   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14789   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14790
14791   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14792   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14793
14794   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14795   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14796   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14797
14798   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14799   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14800
14801   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14802   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14803   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14804   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14805   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14806
14807   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14808   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14809
14810 #if 0
14811   ListNode *node = engine_snapshot_list_rnd;
14812   int num_bytes = 0;
14813
14814   while (node != NULL)
14815   {
14816     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14817
14818     node = node->next;
14819   }
14820
14821   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14822 #endif
14823
14824   return buffers;
14825 }
14826
14827 void SaveEngineSnapshotSingle()
14828 {
14829   ListNode *buffers = SaveEngineSnapshotBuffers();
14830
14831   /* finally save all snapshot buffers to single snapshot */
14832   SaveSnapshotSingle(buffers);
14833
14834   /* save level identification information */
14835   setString(&snapshot_level_identifier, leveldir_current->identifier);
14836   snapshot_level_nr = level_nr;
14837 }
14838
14839 boolean CheckSaveEngineSnapshotToList()
14840 {
14841   boolean save_snapshot =
14842     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14843      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14844       game.snapshot.changed_action) ||
14845      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14846       game.snapshot.collected_item));
14847
14848   game.snapshot.changed_action = FALSE;
14849   game.snapshot.collected_item = FALSE;
14850   game.snapshot.save_snapshot = save_snapshot;
14851
14852   return save_snapshot;
14853 }
14854
14855 void SaveEngineSnapshotToList()
14856 {
14857   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14858       tape.quick_resume)
14859     return;
14860
14861   ListNode *buffers = SaveEngineSnapshotBuffers();
14862
14863   /* finally save all snapshot buffers to snapshot list */
14864   SaveSnapshotToList(buffers);
14865 }
14866
14867 void SaveEngineSnapshotToListInitial()
14868 {
14869   FreeEngineSnapshotList();
14870
14871   SaveEngineSnapshotToList();
14872 }
14873
14874 void LoadEngineSnapshotValues()
14875 {
14876   /* restore special values from snapshot structure */
14877
14878   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14879     LoadEngineSnapshotValues_RND();
14880   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14881     LoadEngineSnapshotValues_EM();
14882   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14883     LoadEngineSnapshotValues_SP();
14884 }
14885
14886 void LoadEngineSnapshotSingle()
14887 {
14888   LoadSnapshotSingle();
14889
14890   LoadEngineSnapshotValues();
14891 }
14892
14893 void LoadEngineSnapshot_Undo(int steps)
14894 {
14895   LoadSnapshotFromList_Older(steps);
14896
14897   LoadEngineSnapshotValues();
14898 }
14899
14900 void LoadEngineSnapshot_Redo(int steps)
14901 {
14902   LoadSnapshotFromList_Newer(steps);
14903
14904   LoadEngineSnapshotValues();
14905 }
14906
14907 boolean CheckEngineSnapshotSingle()
14908 {
14909   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14910           snapshot_level_nr == level_nr);
14911 }
14912
14913 boolean CheckEngineSnapshotList()
14914 {
14915   return CheckSnapshotList();
14916 }
14917
14918
14919 /* ---------- new game button stuff ---------------------------------------- */
14920
14921 static struct
14922 {
14923   int graphic;
14924   struct XY *pos;
14925   int gadget_id;
14926   char *infotext;
14927 } gamebutton_info[NUM_GAME_BUTTONS] =
14928 {
14929   {
14930     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14931     GAME_CTRL_ID_STOP,                  "stop game"
14932   },
14933   {
14934     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14935     GAME_CTRL_ID_PAUSE,                 "pause game"
14936   },
14937   {
14938     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14939     GAME_CTRL_ID_PLAY,                  "play game"
14940   },
14941   {
14942     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14943     GAME_CTRL_ID_UNDO,                  "undo step"
14944   },
14945   {
14946     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14947     GAME_CTRL_ID_REDO,                  "redo step"
14948   },
14949   {
14950     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14951     GAME_CTRL_ID_SAVE,                  "save game"
14952   },
14953   {
14954     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14955     GAME_CTRL_ID_PAUSE2,                "pause game"
14956   },
14957   {
14958     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14959     GAME_CTRL_ID_LOAD,                  "load game"
14960   },
14961   {
14962     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14963     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14964   },
14965   {
14966     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14967     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14968   },
14969   {
14970     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14971     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14972   }
14973 };
14974
14975 void CreateGameButtons()
14976 {
14977   int i;
14978
14979   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14980   {
14981     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14982     struct XY *pos = gamebutton_info[i].pos;
14983     struct GadgetInfo *gi;
14984     int button_type;
14985     boolean checked;
14986     unsigned int event_mask;
14987     int base_x = (tape.show_game_buttons ? VX : DX);
14988     int base_y = (tape.show_game_buttons ? VY : DY);
14989     int gd_x   = gfx->src_x;
14990     int gd_y   = gfx->src_y;
14991     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14992     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14993     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14994     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14995     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14996     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14997     int id = i;
14998
14999     if (gfx->bitmap == NULL)
15000     {
15001       game_gadget[id] = NULL;
15002
15003       continue;
15004     }
15005
15006     if (id == GAME_CTRL_ID_STOP ||
15007         id == GAME_CTRL_ID_PLAY ||
15008         id == GAME_CTRL_ID_SAVE ||
15009         id == GAME_CTRL_ID_LOAD)
15010     {
15011       button_type = GD_TYPE_NORMAL_BUTTON;
15012       checked = FALSE;
15013       event_mask = GD_EVENT_RELEASED;
15014     }
15015     else if (id == GAME_CTRL_ID_UNDO ||
15016              id == GAME_CTRL_ID_REDO)
15017     {
15018       button_type = GD_TYPE_NORMAL_BUTTON;
15019       checked = FALSE;
15020       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15021     }
15022     else
15023     {
15024       button_type = GD_TYPE_CHECK_BUTTON;
15025       checked =
15026         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15027          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15028          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15029       event_mask = GD_EVENT_PRESSED;
15030     }
15031
15032     gi = CreateGadget(GDI_CUSTOM_ID, id,
15033                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15034                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15035                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15036                       GDI_WIDTH, gfx->width,
15037                       GDI_HEIGHT, gfx->height,
15038                       GDI_TYPE, button_type,
15039                       GDI_STATE, GD_BUTTON_UNPRESSED,
15040                       GDI_CHECKED, checked,
15041                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15042                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15043                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15044                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15045                       GDI_DIRECT_DRAW, FALSE,
15046                       GDI_EVENT_MASK, event_mask,
15047                       GDI_CALLBACK_ACTION, HandleGameButtons,
15048                       GDI_END);
15049
15050     if (gi == NULL)
15051       Error(ERR_EXIT, "cannot create gadget");
15052
15053     game_gadget[id] = gi;
15054   }
15055 }
15056
15057 void FreeGameButtons()
15058 {
15059   int i;
15060
15061   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15062     FreeGadget(game_gadget[i]);
15063 }
15064
15065 static void UnmapGameButtonsAtSamePosition(int id)
15066 {
15067   int i;
15068
15069   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15070     if (i != id &&
15071         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15072         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15073       UnmapGadget(game_gadget[i]);
15074 }
15075
15076 static void UnmapGameButtonsAtSamePosition_All()
15077 {
15078   if (setup.show_snapshot_buttons)
15079   {
15080     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15081     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15082     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15083   }
15084   else
15085   {
15086     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15087     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15088     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15089   }
15090 }
15091
15092 static void MapGameButtonsAtSamePosition(int id)
15093 {
15094   int i;
15095
15096   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15097     if (i != id &&
15098         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15099         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15100       MapGadget(game_gadget[i]);
15101
15102   UnmapGameButtonsAtSamePosition_All();
15103 }
15104
15105 void MapUndoRedoButtons()
15106 {
15107   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15108   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15109
15110   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15111   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15112
15113   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15114 }
15115
15116 void UnmapUndoRedoButtons()
15117 {
15118   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15119   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15120
15121   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15122   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15123
15124   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15125 }
15126
15127 void MapGameButtons()
15128 {
15129   int i;
15130
15131   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15132     if (i != GAME_CTRL_ID_UNDO &&
15133         i != GAME_CTRL_ID_REDO)
15134       MapGadget(game_gadget[i]);
15135
15136   UnmapGameButtonsAtSamePosition_All();
15137
15138   RedrawGameButtons();
15139 }
15140
15141 void UnmapGameButtons()
15142 {
15143   int i;
15144
15145   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15146     UnmapGadget(game_gadget[i]);
15147 }
15148
15149 void RedrawGameButtons()
15150 {
15151   int i;
15152
15153   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15154     RedrawGadget(game_gadget[i]);
15155
15156   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15157   redraw_mask &= ~REDRAW_ALL;
15158 }
15159
15160 void GameUndoRedoExt()
15161 {
15162   ClearPlayerAction();
15163
15164   tape.pausing = TRUE;
15165
15166   RedrawPlayfield();
15167   UpdateAndDisplayGameControlValues();
15168
15169   DrawCompleteVideoDisplay();
15170   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15171   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15172   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15173
15174   BackToFront();
15175 }
15176
15177 void GameUndo(int steps)
15178 {
15179   if (!CheckEngineSnapshotList())
15180     return;
15181
15182   LoadEngineSnapshot_Undo(steps);
15183
15184   GameUndoRedoExt();
15185 }
15186
15187 void GameRedo(int steps)
15188 {
15189   if (!CheckEngineSnapshotList())
15190     return;
15191
15192   LoadEngineSnapshot_Redo(steps);
15193
15194   GameUndoRedoExt();
15195 }
15196
15197 static void HandleGameButtonsExt(int id, int button)
15198 {
15199   static boolean game_undo_executed = FALSE;
15200   int steps = BUTTON_STEPSIZE(button);
15201   boolean handle_game_buttons =
15202     (game_status == GAME_MODE_PLAYING ||
15203      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15204
15205   if (!handle_game_buttons)
15206     return;
15207
15208   switch (id)
15209   {
15210     case GAME_CTRL_ID_STOP:
15211       if (game_status == GAME_MODE_MAIN)
15212         break;
15213
15214       if (tape.playing)
15215         TapeStop();
15216       else
15217         RequestQuitGame(TRUE);
15218
15219       break;
15220
15221     case GAME_CTRL_ID_PAUSE:
15222     case GAME_CTRL_ID_PAUSE2:
15223       if (options.network && game_status == GAME_MODE_PLAYING)
15224       {
15225 #if defined(NETWORK_AVALIABLE)
15226         if (tape.pausing)
15227           SendToServer_ContinuePlaying();
15228         else
15229           SendToServer_PausePlaying();
15230 #endif
15231       }
15232       else
15233         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15234
15235       game_undo_executed = FALSE;
15236
15237       break;
15238
15239     case GAME_CTRL_ID_PLAY:
15240       if (game_status == GAME_MODE_MAIN)
15241       {
15242         StartGameActions(options.network, setup.autorecord, level.random_seed);
15243       }
15244       else if (tape.pausing)
15245       {
15246 #if defined(NETWORK_AVALIABLE)
15247         if (options.network)
15248           SendToServer_ContinuePlaying();
15249         else
15250 #endif
15251           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15252       }
15253       break;
15254
15255     case GAME_CTRL_ID_UNDO:
15256       // Important: When using "save snapshot when collecting an item" mode,
15257       // load last (current) snapshot for first "undo" after pressing "pause"
15258       // (else the last-but-one snapshot would be loaded, because the snapshot
15259       // pointer already points to the last snapshot when pressing "pause",
15260       // which is fine for "every step/move" mode, but not for "every collect")
15261       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15262           !game_undo_executed)
15263         steps--;
15264
15265       game_undo_executed = TRUE;
15266
15267       GameUndo(steps);
15268       break;
15269
15270     case GAME_CTRL_ID_REDO:
15271       GameRedo(steps);
15272       break;
15273
15274     case GAME_CTRL_ID_SAVE:
15275       TapeQuickSave();
15276       break;
15277
15278     case GAME_CTRL_ID_LOAD:
15279       TapeQuickLoad();
15280       break;
15281
15282     case SOUND_CTRL_ID_MUSIC:
15283       if (setup.sound_music)
15284       { 
15285         setup.sound_music = FALSE;
15286
15287         FadeMusic();
15288       }
15289       else if (audio.music_available)
15290       { 
15291         setup.sound = setup.sound_music = TRUE;
15292
15293         SetAudioMode(setup.sound);
15294
15295         PlayLevelMusic();
15296       }
15297       break;
15298
15299     case SOUND_CTRL_ID_LOOPS:
15300       if (setup.sound_loops)
15301         setup.sound_loops = FALSE;
15302       else if (audio.loops_available)
15303       {
15304         setup.sound = setup.sound_loops = TRUE;
15305
15306         SetAudioMode(setup.sound);
15307       }
15308       break;
15309
15310     case SOUND_CTRL_ID_SIMPLE:
15311       if (setup.sound_simple)
15312         setup.sound_simple = FALSE;
15313       else if (audio.sound_available)
15314       {
15315         setup.sound = setup.sound_simple = TRUE;
15316
15317         SetAudioMode(setup.sound);
15318       }
15319       break;
15320
15321     default:
15322       break;
15323   }
15324 }
15325
15326 static void HandleGameButtons(struct GadgetInfo *gi)
15327 {
15328   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15329 }
15330
15331 void HandleSoundButtonKeys(Key key)
15332 {
15333
15334   if (key == setup.shortcut.sound_simple)
15335     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15336   else if (key == setup.shortcut.sound_loops)
15337     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15338   else if (key == setup.shortcut.sound_music)
15339     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15340 }