3d2f055f27c88415dccb499d83d65932ad8a5f1e
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_FRAME                        35
126 #define GAME_PANEL_SHIELD_NORMAL                36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
128 #define GAME_PANEL_SHIELD_DEADLY                38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
130 #define GAME_PANEL_EXIT                         40
131 #define GAME_PANEL_EMC_MAGIC_BALL               41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
133 #define GAME_PANEL_LIGHT_SWITCH                 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
135 #define GAME_PANEL_TIMEGATE_SWITCH              45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
137 #define GAME_PANEL_SWITCHGATE_SWITCH            47
138 #define GAME_PANEL_EMC_LENSES                   48
139 #define GAME_PANEL_EMC_LENSES_TIME              49
140 #define GAME_PANEL_EMC_MAGNIFIER                50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
142 #define GAME_PANEL_BALLOON_SWITCH               52
143 #define GAME_PANEL_DYNABOMB_NUMBER              53
144 #define GAME_PANEL_DYNABOMB_SIZE                54
145 #define GAME_PANEL_DYNABOMB_POWER               55
146 #define GAME_PANEL_PENGUINS                     56
147 #define GAME_PANEL_SOKOBAN_OBJECTS              57
148 #define GAME_PANEL_SOKOBAN_FIELDS               58
149 #define GAME_PANEL_ROBOT_WHEEL                  59
150 #define GAME_PANEL_CONVEYOR_BELT_1              60
151 #define GAME_PANEL_CONVEYOR_BELT_2              61
152 #define GAME_PANEL_CONVEYOR_BELT_3              62
153 #define GAME_PANEL_CONVEYOR_BELT_4              63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
158 #define GAME_PANEL_MAGIC_WALL                   68
159 #define GAME_PANEL_MAGIC_WALL_TIME              69
160 #define GAME_PANEL_GRAVITY_STATE                70
161 #define GAME_PANEL_GRAPHIC_1                    71
162 #define GAME_PANEL_GRAPHIC_2                    72
163 #define GAME_PANEL_GRAPHIC_3                    73
164 #define GAME_PANEL_GRAPHIC_4                    74
165 #define GAME_PANEL_GRAPHIC_5                    75
166 #define GAME_PANEL_GRAPHIC_6                    76
167 #define GAME_PANEL_GRAPHIC_7                    77
168 #define GAME_PANEL_GRAPHIC_8                    78
169 #define GAME_PANEL_ELEMENT_1                    79
170 #define GAME_PANEL_ELEMENT_2                    80
171 #define GAME_PANEL_ELEMENT_3                    81
172 #define GAME_PANEL_ELEMENT_4                    82
173 #define GAME_PANEL_ELEMENT_5                    83
174 #define GAME_PANEL_ELEMENT_6                    84
175 #define GAME_PANEL_ELEMENT_7                    85
176 #define GAME_PANEL_ELEMENT_8                    86
177 #define GAME_PANEL_ELEMENT_COUNT_1              87
178 #define GAME_PANEL_ELEMENT_COUNT_2              88
179 #define GAME_PANEL_ELEMENT_COUNT_3              89
180 #define GAME_PANEL_ELEMENT_COUNT_4              90
181 #define GAME_PANEL_ELEMENT_COUNT_5              91
182 #define GAME_PANEL_ELEMENT_COUNT_6              92
183 #define GAME_PANEL_ELEMENT_COUNT_7              93
184 #define GAME_PANEL_ELEMENT_COUNT_8              94
185 #define GAME_PANEL_CE_SCORE_1                   95
186 #define GAME_PANEL_CE_SCORE_2                   96
187 #define GAME_PANEL_CE_SCORE_3                   97
188 #define GAME_PANEL_CE_SCORE_4                   98
189 #define GAME_PANEL_CE_SCORE_5                   99
190 #define GAME_PANEL_CE_SCORE_6                   100
191 #define GAME_PANEL_CE_SCORE_7                   101
192 #define GAME_PANEL_CE_SCORE_8                   102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
201 #define GAME_PANEL_PLAYER_NAME                  111
202 #define GAME_PANEL_LEVEL_NAME                   112
203 #define GAME_PANEL_LEVEL_AUTHOR                 113
204
205 #define NUM_GAME_PANEL_CONTROLS                 114
206
207 struct GamePanelOrderInfo
208 {
209   int nr;
210   int sort_priority;
211 };
212
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
214
215 struct GamePanelControlInfo
216 {
217   int nr;
218
219   struct TextPosInfo *pos;
220   int type;
221
222   int value, last_value;
223   int frame, last_frame;
224   int gfx_frame;
225   int gfx_random;
226 };
227
228 static struct GamePanelControlInfo game_panel_controls[] =
229 {
230   {
231     GAME_PANEL_LEVEL_NUMBER,
232     &game.panel.level_number,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_PANEL_GEMS,
237     &game.panel.gems,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_INVENTORY_COUNT,
242     &game.panel.inventory_count,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_FIRST_1,
247     &game.panel.inventory_first[0],
248     TYPE_ELEMENT,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_2,
252     &game.panel.inventory_first[1],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_3,
257     &game.panel.inventory_first[2],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_4,
262     &game.panel.inventory_first[3],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_5,
267     &game.panel.inventory_first[4],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_6,
272     &game.panel.inventory_first[5],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_7,
277     &game.panel.inventory_first[6],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_8,
282     &game.panel.inventory_first[7],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_LAST_1,
287     &game.panel.inventory_last[0],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_2,
292     &game.panel.inventory_last[1],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_3,
297     &game.panel.inventory_last[2],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_4,
302     &game.panel.inventory_last[3],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_5,
307     &game.panel.inventory_last[4],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_6,
312     &game.panel.inventory_last[5],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_7,
317     &game.panel.inventory_last[6],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_8,
322     &game.panel.inventory_last[7],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_KEY_1,
327     &game.panel.key[0],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_2,
332     &game.panel.key[1],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_3,
337     &game.panel.key[2],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_4,
342     &game.panel.key[3],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_5,
347     &game.panel.key[4],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_6,
352     &game.panel.key[5],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_7,
357     &game.panel.key[6],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_8,
362     &game.panel.key[7],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_WHITE,
367     &game.panel.key_white,
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE_COUNT,
372     &game.panel.key_white_count,
373     TYPE_INTEGER,
374   },
375   {
376     GAME_PANEL_SCORE,
377     &game.panel.score,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_HIGHSCORE,
382     &game.panel.highscore,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_TIME,
387     &game.panel.time,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME_HH,
392     &game.panel.time_hh,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_MM,
397     &game.panel.time_mm,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_SS,
402     &game.panel.time_ss,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_FRAME,
407     &game.panel.frame,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_SHIELD_NORMAL,
412     &game.panel.shield_normal,
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_PANEL_SHIELD_NORMAL_TIME,
417     &game.panel.shield_normal_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_PANEL_SHIELD_DEADLY,
422     &game.panel.shield_deadly,
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_PANEL_SHIELD_DEADLY_TIME,
427     &game.panel.shield_deadly_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_PANEL_EXIT,
432     &game.panel.exit,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_PANEL_EMC_MAGIC_BALL,
437     &game.panel.emc_magic_ball,
438     TYPE_ELEMENT,
439   },
440   {
441     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442     &game.panel.emc_magic_ball_switch,
443     TYPE_ELEMENT,
444   },
445   {
446     GAME_PANEL_LIGHT_SWITCH,
447     &game.panel.light_switch,
448     TYPE_ELEMENT,
449   },
450   {
451     GAME_PANEL_LIGHT_SWITCH_TIME,
452     &game.panel.light_switch_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_PANEL_TIMEGATE_SWITCH,
457     &game.panel.timegate_switch,
458     TYPE_ELEMENT,
459   },
460   {
461     GAME_PANEL_TIMEGATE_SWITCH_TIME,
462     &game.panel.timegate_switch_time,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_PANEL_SWITCHGATE_SWITCH,
467     &game.panel.switchgate_switch,
468     TYPE_ELEMENT,
469   },
470   {
471     GAME_PANEL_EMC_LENSES,
472     &game.panel.emc_lenses,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_PANEL_EMC_LENSES_TIME,
477     &game.panel.emc_lenses_time,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_PANEL_EMC_MAGNIFIER,
482     &game.panel.emc_magnifier,
483     TYPE_ELEMENT,
484   },
485   {
486     GAME_PANEL_EMC_MAGNIFIER_TIME,
487     &game.panel.emc_magnifier_time,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_PANEL_BALLOON_SWITCH,
492     &game.panel.balloon_switch,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_PANEL_DYNABOMB_NUMBER,
497     &game.panel.dynabomb_number,
498     TYPE_INTEGER,
499   },
500   {
501     GAME_PANEL_DYNABOMB_SIZE,
502     &game.panel.dynabomb_size,
503     TYPE_INTEGER,
504   },
505   {
506     GAME_PANEL_DYNABOMB_POWER,
507     &game.panel.dynabomb_power,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_PANEL_PENGUINS,
512     &game.panel.penguins,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_PANEL_SOKOBAN_OBJECTS,
517     &game.panel.sokoban_objects,
518     TYPE_INTEGER,
519   },
520   {
521     GAME_PANEL_SOKOBAN_FIELDS,
522     &game.panel.sokoban_fields,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_PANEL_ROBOT_WHEEL,
527     &game.panel.robot_wheel,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_PANEL_CONVEYOR_BELT_1,
532     &game.panel.conveyor_belt[0],
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_PANEL_CONVEYOR_BELT_2,
537     &game.panel.conveyor_belt[1],
538     TYPE_ELEMENT,
539   },
540   {
541     GAME_PANEL_CONVEYOR_BELT_3,
542     &game.panel.conveyor_belt[2],
543     TYPE_ELEMENT,
544   },
545   {
546     GAME_PANEL_CONVEYOR_BELT_4,
547     &game.panel.conveyor_belt[3],
548     TYPE_ELEMENT,
549   },
550   {
551     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552     &game.panel.conveyor_belt_switch[0],
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557     &game.panel.conveyor_belt_switch[1],
558     TYPE_ELEMENT,
559   },
560   {
561     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562     &game.panel.conveyor_belt_switch[2],
563     TYPE_ELEMENT,
564   },
565   {
566     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567     &game.panel.conveyor_belt_switch[3],
568     TYPE_ELEMENT,
569   },
570   {
571     GAME_PANEL_MAGIC_WALL,
572     &game.panel.magic_wall,
573     TYPE_ELEMENT,
574   },
575   {
576     GAME_PANEL_MAGIC_WALL_TIME,
577     &game.panel.magic_wall_time,
578     TYPE_INTEGER,
579   },
580   {
581     GAME_PANEL_GRAVITY_STATE,
582     &game.panel.gravity_state,
583     TYPE_STRING,
584   },
585   {
586     GAME_PANEL_GRAPHIC_1,
587     &game.panel.graphic[0],
588     TYPE_ELEMENT,
589   },
590   {
591     GAME_PANEL_GRAPHIC_2,
592     &game.panel.graphic[1],
593     TYPE_ELEMENT,
594   },
595   {
596     GAME_PANEL_GRAPHIC_3,
597     &game.panel.graphic[2],
598     TYPE_ELEMENT,
599   },
600   {
601     GAME_PANEL_GRAPHIC_4,
602     &game.panel.graphic[3],
603     TYPE_ELEMENT,
604   },
605   {
606     GAME_PANEL_GRAPHIC_5,
607     &game.panel.graphic[4],
608     TYPE_ELEMENT,
609   },
610   {
611     GAME_PANEL_GRAPHIC_6,
612     &game.panel.graphic[5],
613     TYPE_ELEMENT,
614   },
615   {
616     GAME_PANEL_GRAPHIC_7,
617     &game.panel.graphic[6],
618     TYPE_ELEMENT,
619   },
620   {
621     GAME_PANEL_GRAPHIC_8,
622     &game.panel.graphic[7],
623     TYPE_ELEMENT,
624   },
625   {
626     GAME_PANEL_ELEMENT_1,
627     &game.panel.element[0],
628     TYPE_ELEMENT,
629   },
630   {
631     GAME_PANEL_ELEMENT_2,
632     &game.panel.element[1],
633     TYPE_ELEMENT,
634   },
635   {
636     GAME_PANEL_ELEMENT_3,
637     &game.panel.element[2],
638     TYPE_ELEMENT,
639   },
640   {
641     GAME_PANEL_ELEMENT_4,
642     &game.panel.element[3],
643     TYPE_ELEMENT,
644   },
645   {
646     GAME_PANEL_ELEMENT_5,
647     &game.panel.element[4],
648     TYPE_ELEMENT,
649   },
650   {
651     GAME_PANEL_ELEMENT_6,
652     &game.panel.element[5],
653     TYPE_ELEMENT,
654   },
655   {
656     GAME_PANEL_ELEMENT_7,
657     &game.panel.element[6],
658     TYPE_ELEMENT,
659   },
660   {
661     GAME_PANEL_ELEMENT_8,
662     &game.panel.element[7],
663     TYPE_ELEMENT,
664   },
665   {
666     GAME_PANEL_ELEMENT_COUNT_1,
667     &game.panel.element_count[0],
668     TYPE_INTEGER,
669   },
670   {
671     GAME_PANEL_ELEMENT_COUNT_2,
672     &game.panel.element_count[1],
673     TYPE_INTEGER,
674   },
675   {
676     GAME_PANEL_ELEMENT_COUNT_3,
677     &game.panel.element_count[2],
678     TYPE_INTEGER,
679   },
680   {
681     GAME_PANEL_ELEMENT_COUNT_4,
682     &game.panel.element_count[3],
683     TYPE_INTEGER,
684   },
685   {
686     GAME_PANEL_ELEMENT_COUNT_5,
687     &game.panel.element_count[4],
688     TYPE_INTEGER,
689   },
690   {
691     GAME_PANEL_ELEMENT_COUNT_6,
692     &game.panel.element_count[5],
693     TYPE_INTEGER,
694   },
695   {
696     GAME_PANEL_ELEMENT_COUNT_7,
697     &game.panel.element_count[6],
698     TYPE_INTEGER,
699   },
700   {
701     GAME_PANEL_ELEMENT_COUNT_8,
702     &game.panel.element_count[7],
703     TYPE_INTEGER,
704   },
705   {
706     GAME_PANEL_CE_SCORE_1,
707     &game.panel.ce_score[0],
708     TYPE_INTEGER,
709   },
710   {
711     GAME_PANEL_CE_SCORE_2,
712     &game.panel.ce_score[1],
713     TYPE_INTEGER,
714   },
715   {
716     GAME_PANEL_CE_SCORE_3,
717     &game.panel.ce_score[2],
718     TYPE_INTEGER,
719   },
720   {
721     GAME_PANEL_CE_SCORE_4,
722     &game.panel.ce_score[3],
723     TYPE_INTEGER,
724   },
725   {
726     GAME_PANEL_CE_SCORE_5,
727     &game.panel.ce_score[4],
728     TYPE_INTEGER,
729   },
730   {
731     GAME_PANEL_CE_SCORE_6,
732     &game.panel.ce_score[5],
733     TYPE_INTEGER,
734   },
735   {
736     GAME_PANEL_CE_SCORE_7,
737     &game.panel.ce_score[6],
738     TYPE_INTEGER,
739   },
740   {
741     GAME_PANEL_CE_SCORE_8,
742     &game.panel.ce_score[7],
743     TYPE_INTEGER,
744   },
745   {
746     GAME_PANEL_CE_SCORE_1_ELEMENT,
747     &game.panel.ce_score_element[0],
748     TYPE_ELEMENT,
749   },
750   {
751     GAME_PANEL_CE_SCORE_2_ELEMENT,
752     &game.panel.ce_score_element[1],
753     TYPE_ELEMENT,
754   },
755   {
756     GAME_PANEL_CE_SCORE_3_ELEMENT,
757     &game.panel.ce_score_element[2],
758     TYPE_ELEMENT,
759   },
760   {
761     GAME_PANEL_CE_SCORE_4_ELEMENT,
762     &game.panel.ce_score_element[3],
763     TYPE_ELEMENT,
764   },
765   {
766     GAME_PANEL_CE_SCORE_5_ELEMENT,
767     &game.panel.ce_score_element[4],
768     TYPE_ELEMENT,
769   },
770   {
771     GAME_PANEL_CE_SCORE_6_ELEMENT,
772     &game.panel.ce_score_element[5],
773     TYPE_ELEMENT,
774   },
775   {
776     GAME_PANEL_CE_SCORE_7_ELEMENT,
777     &game.panel.ce_score_element[6],
778     TYPE_ELEMENT,
779   },
780   {
781     GAME_PANEL_CE_SCORE_8_ELEMENT,
782     &game.panel.ce_score_element[7],
783     TYPE_ELEMENT,
784   },
785   {
786     GAME_PANEL_PLAYER_NAME,
787     &game.panel.player_name,
788     TYPE_STRING,
789   },
790   {
791     GAME_PANEL_LEVEL_NAME,
792     &game.panel.level_name,
793     TYPE_STRING,
794   },
795   {
796     GAME_PANEL_LEVEL_AUTHOR,
797     &game.panel.level_author,
798     TYPE_STRING,
799   },
800
801   {
802     -1,
803     NULL,
804     -1,
805   }
806 };
807
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING      3
810 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION   2
812 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
813
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF  -1
816 #define INITIAL_MOVE_DELAY_ON   0
817
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED    32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED   4
822 #define MOVE_DELAY_MAX_SPEED    1
823
824 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
826
827 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
829
830 /* values for other actions */
831 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
832 #define MOVE_STEPSIZE_MIN       (1)
833 #define MOVE_STEPSIZE_MAX       (TILEX)
834
835 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
836 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
837
838 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
839
840 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
841                                  RND(element_info[e].push_delay_random))
842 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
843                                  RND(element_info[e].drop_delay_random))
844 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
845                                  RND(element_info[e].move_delay_random))
846 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
847                                     (element_info[e].move_delay_random))
848 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
849                                  RND(element_info[e].ce_value_random_initial))
850 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
851 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
852                                  RND((c)->delay_random * (c)->delay_frames))
853 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
854                                  RND((c)->delay_random))
855
856
857 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
858          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
859
860 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
861         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
862          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
863          (be) + (e) - EL_SELF)
864
865 #define GET_PLAYER_FROM_BITS(p)                                         \
866         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
867
868 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
869         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
870          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
871          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
872          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
873          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
874          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
875          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
876          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
877          (e))
878
879 #define CAN_GROW_INTO(e)                                                \
880         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
881
882 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
883                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
884                                         (condition)))
885
886 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
887                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
888                                         (CAN_MOVE_INTO_ACID(e) &&       \
889                                          Feld[x][y] == EL_ACID) ||      \
890                                         (condition)))
891
892 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
893                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
894                                         (CAN_MOVE_INTO_ACID(e) &&       \
895                                          Feld[x][y] == EL_ACID) ||      \
896                                         (condition)))
897
898 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
899                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
900                                         (condition) ||                  \
901                                         (CAN_MOVE_INTO_ACID(e) &&       \
902                                          Feld[x][y] == EL_ACID) ||      \
903                                         (DONT_COLLIDE_WITH(e) &&        \
904                                          IS_PLAYER(x, y) &&             \
905                                          !PLAYER_ENEMY_PROTECTED(x, y))))
906
907 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
908         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
909
910 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
911         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
912
913 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
914         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
915
916 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
917         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
918                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
919
920 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
921         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
922
923 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
924         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
925
926 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
927         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
928
929 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
930         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
931
932 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
933         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
934
935 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
936         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
939                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
940                                                  IS_FOOD_PENGUIN(Feld[x][y])))
941 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
942         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
943
944 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
946
947 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
949
950 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
951         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
952                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
953
954 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
955
956 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
957                 (!IS_PLAYER(x, y) &&                                    \
958                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
959
960 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
961         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
962
963 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
964 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
965
966 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
967 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
969 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
970
971 /* game button identifiers */
972 #define GAME_CTRL_ID_STOP               0
973 #define GAME_CTRL_ID_PAUSE              1
974 #define GAME_CTRL_ID_PLAY               2
975 #define GAME_CTRL_ID_UNDO               3
976 #define GAME_CTRL_ID_REDO               4
977 #define GAME_CTRL_ID_SAVE               5
978 #define GAME_CTRL_ID_PAUSE2             6
979 #define GAME_CTRL_ID_LOAD               7
980 #define SOUND_CTRL_ID_MUSIC             8
981 #define SOUND_CTRL_ID_LOOPS             9
982 #define SOUND_CTRL_ID_SIMPLE            10
983
984 #define NUM_GAME_BUTTONS                11
985
986
987 /* forward declaration for internal use */
988
989 static void CreateField(int, int, int);
990
991 static void ResetGfxAnimation(int, int);
992
993 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
994 static void AdvanceFrameAndPlayerCounters(int);
995
996 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
997 static boolean MovePlayer(struct PlayerInfo *, int, int);
998 static void ScrollPlayer(struct PlayerInfo *, int);
999 static void ScrollScreen(struct PlayerInfo *, int);
1000
1001 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1002 static boolean DigFieldByCE(int, int, int);
1003 static boolean SnapField(struct PlayerInfo *, int, int);
1004 static boolean DropElement(struct PlayerInfo *);
1005
1006 static void InitBeltMovement(void);
1007 static void CloseAllOpenTimegates(void);
1008 static void CheckGravityMovement(struct PlayerInfo *);
1009 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1010 static void KillPlayerUnlessEnemyProtected(int, int);
1011 static void KillPlayerUnlessExplosionProtected(int, int);
1012
1013 static void TestIfPlayerTouchesCustomElement(int, int);
1014 static void TestIfElementTouchesCustomElement(int, int);
1015 static void TestIfElementHitsCustomElement(int, int, int);
1016
1017 static void HandleElementChange(int, int, int);
1018 static void ExecuteCustomElementAction(int, int, int, int);
1019 static boolean ChangeElement(int, int, int, int);
1020
1021 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1022 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1023         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1024 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1026 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1027         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1028 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1029         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1030
1031 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1032 #define CheckElementChange(x, y, e, te, ev)                             \
1033         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1034 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1035         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1036 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1037         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1038
1039 static void PlayLevelSound(int, int, int);
1040 static void PlayLevelSoundNearest(int, int, int);
1041 static void PlayLevelSoundAction(int, int, int);
1042 static void PlayLevelSoundElementAction(int, int, int, int);
1043 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1044 static void PlayLevelSoundActionIfLoop(int, int, int);
1045 static void StopLevelSoundActionIfLoop(int, int, int);
1046 static void PlayLevelMusic();
1047
1048 static void HandleGameButtons(struct GadgetInfo *);
1049
1050 int AmoebeNachbarNr(int, int);
1051 void AmoebeUmwandeln(int, int);
1052 void ContinueMoving(int, int);
1053 void Bang(int, int);
1054 void InitMovDir(int, int);
1055 void InitAmoebaNr(int, int);
1056 int NewHiScore(void);
1057
1058 void TestIfGoodThingHitsBadThing(int, int, int);
1059 void TestIfBadThingHitsGoodThing(int, int, int);
1060 void TestIfPlayerTouchesBadThing(int, int);
1061 void TestIfPlayerRunsIntoBadThing(int, int, int);
1062 void TestIfBadThingTouchesPlayer(int, int);
1063 void TestIfBadThingRunsIntoPlayer(int, int, int);
1064 void TestIfFriendTouchesBadThing(int, int);
1065 void TestIfBadThingTouchesFriend(int, int);
1066 void TestIfBadThingTouchesOtherBadThing(int, int);
1067 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1068
1069 void KillPlayer(struct PlayerInfo *);
1070 void BuryPlayer(struct PlayerInfo *);
1071 void RemovePlayer(struct PlayerInfo *);
1072
1073 static int getInvisibleActiveFromInvisibleElement(int);
1074 static int getInvisibleFromInvisibleActiveElement(int);
1075
1076 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1077
1078 /* for detection of endless loops, caused by custom element programming */
1079 /* (using maximal playfield width x 10 is just a rough approximation) */
1080 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1081
1082 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1083 {                                                                       \
1084   if (recursion_loop_detected)                                          \
1085     return (rc);                                                        \
1086                                                                         \
1087   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1088   {                                                                     \
1089     recursion_loop_detected = TRUE;                                     \
1090     recursion_loop_element = (e);                                       \
1091   }                                                                     \
1092                                                                         \
1093   recursion_loop_depth++;                                               \
1094 }
1095
1096 #define RECURSION_LOOP_DETECTION_END()                                  \
1097 {                                                                       \
1098   recursion_loop_depth--;                                               \
1099 }
1100
1101 static int recursion_loop_depth;
1102 static boolean recursion_loop_detected;
1103 static boolean recursion_loop_element;
1104
1105 static int map_player_action[MAX_PLAYERS];
1106
1107
1108 /* ------------------------------------------------------------------------- */
1109 /* definition of elements that automatically change to other elements after  */
1110 /* a specified time, eventually calling a function when changing             */
1111 /* ------------------------------------------------------------------------- */
1112
1113 /* forward declaration for changer functions */
1114 static void InitBuggyBase(int, int);
1115 static void WarnBuggyBase(int, int);
1116
1117 static void InitTrap(int, int);
1118 static void ActivateTrap(int, int);
1119 static void ChangeActiveTrap(int, int);
1120
1121 static void InitRobotWheel(int, int);
1122 static void RunRobotWheel(int, int);
1123 static void StopRobotWheel(int, int);
1124
1125 static void InitTimegateWheel(int, int);
1126 static void RunTimegateWheel(int, int);
1127
1128 static void InitMagicBallDelay(int, int);
1129 static void ActivateMagicBall(int, int);
1130
1131 struct ChangingElementInfo
1132 {
1133   int element;
1134   int target_element;
1135   int change_delay;
1136   void (*pre_change_function)(int x, int y);
1137   void (*change_function)(int x, int y);
1138   void (*post_change_function)(int x, int y);
1139 };
1140
1141 static struct ChangingElementInfo change_delay_list[] =
1142 {
1143   {
1144     EL_NUT_BREAKING,
1145     EL_EMERALD,
1146     6,
1147     NULL,
1148     NULL,
1149     NULL
1150   },
1151   {
1152     EL_PEARL_BREAKING,
1153     EL_EMPTY,
1154     8,
1155     NULL,
1156     NULL,
1157     NULL
1158   },
1159   {
1160     EL_EXIT_OPENING,
1161     EL_EXIT_OPEN,
1162     29,
1163     NULL,
1164     NULL,
1165     NULL
1166   },
1167   {
1168     EL_EXIT_CLOSING,
1169     EL_EXIT_CLOSED,
1170     29,
1171     NULL,
1172     NULL,
1173     NULL
1174   },
1175   {
1176     EL_STEEL_EXIT_OPENING,
1177     EL_STEEL_EXIT_OPEN,
1178     29,
1179     NULL,
1180     NULL,
1181     NULL
1182   },
1183   {
1184     EL_STEEL_EXIT_CLOSING,
1185     EL_STEEL_EXIT_CLOSED,
1186     29,
1187     NULL,
1188     NULL,
1189     NULL
1190   },
1191   {
1192     EL_EM_EXIT_OPENING,
1193     EL_EM_EXIT_OPEN,
1194     29,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_EM_EXIT_CLOSING,
1201     EL_EMPTY,
1202     29,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EM_STEEL_EXIT_OPENING,
1209     EL_EM_STEEL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EM_STEEL_EXIT_CLOSING,
1217     EL_STEELWALL,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_SP_EXIT_OPENING,
1225     EL_SP_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_SP_EXIT_CLOSING,
1233     EL_SP_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_SWITCHGATE_OPENING,
1241     EL_SWITCHGATE_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_SWITCHGATE_CLOSING,
1249     EL_SWITCHGATE_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_TIMEGATE_OPENING,
1257     EL_TIMEGATE_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_TIMEGATE_CLOSING,
1265     EL_TIMEGATE_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271
1272   {
1273     EL_ACID_SPLASH_LEFT,
1274     EL_EMPTY,
1275     8,
1276     NULL,
1277     NULL,
1278     NULL
1279   },
1280   {
1281     EL_ACID_SPLASH_RIGHT,
1282     EL_EMPTY,
1283     8,
1284     NULL,
1285     NULL,
1286     NULL
1287   },
1288   {
1289     EL_SP_BUGGY_BASE,
1290     EL_SP_BUGGY_BASE_ACTIVATING,
1291     0,
1292     InitBuggyBase,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_SP_BUGGY_BASE_ACTIVATING,
1298     EL_SP_BUGGY_BASE_ACTIVE,
1299     0,
1300     InitBuggyBase,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_BUGGY_BASE_ACTIVE,
1306     EL_SP_BUGGY_BASE,
1307     0,
1308     InitBuggyBase,
1309     WarnBuggyBase,
1310     NULL
1311   },
1312   {
1313     EL_TRAP,
1314     EL_TRAP_ACTIVE,
1315     0,
1316     InitTrap,
1317     NULL,
1318     ActivateTrap
1319   },
1320   {
1321     EL_TRAP_ACTIVE,
1322     EL_TRAP,
1323     31,
1324     NULL,
1325     ChangeActiveTrap,
1326     NULL
1327   },
1328   {
1329     EL_ROBOT_WHEEL_ACTIVE,
1330     EL_ROBOT_WHEEL,
1331     0,
1332     InitRobotWheel,
1333     RunRobotWheel,
1334     StopRobotWheel
1335   },
1336   {
1337     EL_TIMEGATE_SWITCH_ACTIVE,
1338     EL_TIMEGATE_SWITCH,
1339     0,
1340     InitTimegateWheel,
1341     RunTimegateWheel,
1342     NULL
1343   },
1344   {
1345     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1346     EL_DC_TIMEGATE_SWITCH,
1347     0,
1348     InitTimegateWheel,
1349     RunTimegateWheel,
1350     NULL
1351   },
1352   {
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     EL_EMC_MAGIC_BALL_ACTIVE,
1355     0,
1356     InitMagicBallDelay,
1357     NULL,
1358     ActivateMagicBall
1359   },
1360   {
1361     EL_EMC_SPRING_BUMPER_ACTIVE,
1362     EL_EMC_SPRING_BUMPER,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_DIAGONAL_SHRINKING,
1370     EL_UNDEFINED,
1371     0,
1372     NULL,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_DIAGONAL_GROWING,
1378     EL_UNDEFINED,
1379     0,
1380     NULL,
1381     NULL,
1382     NULL,
1383   },
1384
1385   {
1386     EL_UNDEFINED,
1387     EL_UNDEFINED,
1388     -1,
1389     NULL,
1390     NULL,
1391     NULL
1392   }
1393 };
1394
1395 struct
1396 {
1397   int element;
1398   int push_delay_fixed, push_delay_random;
1399 }
1400 push_delay_list[] =
1401 {
1402   { EL_SPRING,                  0, 0 },
1403   { EL_BALLOON,                 0, 0 },
1404
1405   { EL_SOKOBAN_OBJECT,          2, 0 },
1406   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1407   { EL_SATELLITE,               2, 0 },
1408   { EL_SP_DISK_YELLOW,          2, 0 },
1409
1410   { EL_UNDEFINED,               0, 0 },
1411 };
1412
1413 struct
1414 {
1415   int element;
1416   int move_stepsize;
1417 }
1418 move_stepsize_list[] =
1419 {
1420   { EL_AMOEBA_DROP,             2 },
1421   { EL_AMOEBA_DROPPING,         2 },
1422   { EL_QUICKSAND_FILLING,       1 },
1423   { EL_QUICKSAND_EMPTYING,      1 },
1424   { EL_QUICKSAND_FAST_FILLING,  2 },
1425   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1426   { EL_MAGIC_WALL_FILLING,      2 },
1427   { EL_MAGIC_WALL_EMPTYING,     2 },
1428   { EL_BD_MAGIC_WALL_FILLING,   2 },
1429   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1430   { EL_DC_MAGIC_WALL_FILLING,   2 },
1431   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1432
1433   { EL_UNDEFINED,               0 },
1434 };
1435
1436 struct
1437 {
1438   int element;
1439   int count;
1440 }
1441 collect_count_list[] =
1442 {
1443   { EL_EMERALD,                 1 },
1444   { EL_BD_DIAMOND,              1 },
1445   { EL_EMERALD_YELLOW,          1 },
1446   { EL_EMERALD_RED,             1 },
1447   { EL_EMERALD_PURPLE,          1 },
1448   { EL_DIAMOND,                 3 },
1449   { EL_SP_INFOTRON,             1 },
1450   { EL_PEARL,                   5 },
1451   { EL_CRYSTAL,                 8 },
1452
1453   { EL_UNDEFINED,               0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int direction;
1460 }
1461 access_direction_list[] =
1462 {
1463   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1464   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1465   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1466   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1467   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1468   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1469   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1470   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1471   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1472   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1473   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1474
1475   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1476   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1477   { EL_SP_PORT_UP,                                                   MV_DOWN },
1478   { EL_SP_PORT_DOWN,                                         MV_UP           },
1479   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1480   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1481   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1482   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1483   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1484   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1485   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1486   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1487   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1488   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1489   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1490   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1491   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1492   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1493   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1494
1495   { EL_UNDEFINED,                       MV_NONE                              }
1496 };
1497
1498 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1499
1500 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1501 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1502 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1503                                  IS_JUST_CHANGING(x, y))
1504
1505 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1506
1507 /* static variables for playfield scan mode (scanning forward or backward) */
1508 static int playfield_scan_start_x = 0;
1509 static int playfield_scan_start_y = 0;
1510 static int playfield_scan_delta_x = 1;
1511 static int playfield_scan_delta_y = 1;
1512
1513 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1514                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1515                                      (y) += playfield_scan_delta_y)     \
1516                                 for ((x) = playfield_scan_start_x;      \
1517                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1518                                      (x) += playfield_scan_delta_x)
1519
1520 #ifdef DEBUG
1521 void DEBUG_SetMaximumDynamite()
1522 {
1523   int i;
1524
1525   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1526     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1527       local_player->inventory_element[local_player->inventory_size++] =
1528         EL_DYNAMITE;
1529 }
1530 #endif
1531
1532 static void InitPlayfieldScanModeVars()
1533 {
1534   if (game.use_reverse_scan_direction)
1535   {
1536     playfield_scan_start_x = lev_fieldx - 1;
1537     playfield_scan_start_y = lev_fieldy - 1;
1538
1539     playfield_scan_delta_x = -1;
1540     playfield_scan_delta_y = -1;
1541   }
1542   else
1543   {
1544     playfield_scan_start_x = 0;
1545     playfield_scan_start_y = 0;
1546
1547     playfield_scan_delta_x = 1;
1548     playfield_scan_delta_y = 1;
1549   }
1550 }
1551
1552 static void InitPlayfieldScanMode(int mode)
1553 {
1554   game.use_reverse_scan_direction =
1555     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1556
1557   InitPlayfieldScanModeVars();
1558 }
1559
1560 static int get_move_delay_from_stepsize(int move_stepsize)
1561 {
1562   move_stepsize =
1563     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1564
1565   /* make sure that stepsize value is always a power of 2 */
1566   move_stepsize = (1 << log_2(move_stepsize));
1567
1568   return TILEX / move_stepsize;
1569 }
1570
1571 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1572                                boolean init_game)
1573 {
1574   int player_nr = player->index_nr;
1575   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1576   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1577
1578   /* do no immediately change move delay -- the player might just be moving */
1579   player->move_delay_value_next = move_delay;
1580
1581   /* information if player can move must be set separately */
1582   player->cannot_move = cannot_move;
1583
1584   if (init_game)
1585   {
1586     player->move_delay       = game.initial_move_delay[player_nr];
1587     player->move_delay_value = game.initial_move_delay_value[player_nr];
1588
1589     player->move_delay_value_next = -1;
1590
1591     player->move_delay_reset_counter = 0;
1592   }
1593 }
1594
1595 void GetPlayerConfig()
1596 {
1597   GameFrameDelay = setup.game_frame_delay;
1598
1599   if (!audio.sound_available)
1600     setup.sound_simple = FALSE;
1601
1602   if (!audio.loops_available)
1603     setup.sound_loops = FALSE;
1604
1605   if (!audio.music_available)
1606     setup.sound_music = FALSE;
1607
1608   if (!video.fullscreen_available)
1609     setup.fullscreen = FALSE;
1610
1611   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1612
1613   SetAudioMode(setup.sound);
1614   InitJoysticks();
1615 }
1616
1617 int GetElementFromGroupElement(int element)
1618 {
1619   if (IS_GROUP_ELEMENT(element))
1620   {
1621     struct ElementGroupInfo *group = element_info[element].group;
1622     int last_anim_random_frame = gfx.anim_random_frame;
1623     int element_pos;
1624
1625     if (group->choice_mode == ANIM_RANDOM)
1626       gfx.anim_random_frame = RND(group->num_elements_resolved);
1627
1628     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1629                                     group->choice_mode, 0,
1630                                     group->choice_pos);
1631
1632     if (group->choice_mode == ANIM_RANDOM)
1633       gfx.anim_random_frame = last_anim_random_frame;
1634
1635     group->choice_pos++;
1636
1637     element = group->element_resolved[element_pos];
1638   }
1639
1640   return element;
1641 }
1642
1643 static void InitPlayerField(int x, int y, int element, boolean init_game)
1644 {
1645   if (element == EL_SP_MURPHY)
1646   {
1647     if (init_game)
1648     {
1649       if (stored_player[0].present)
1650       {
1651         Feld[x][y] = EL_SP_MURPHY_CLONE;
1652
1653         return;
1654       }
1655       else
1656       {
1657         stored_player[0].initial_element = element;
1658         stored_player[0].use_murphy = TRUE;
1659
1660         if (!level.use_artwork_element[0])
1661           stored_player[0].artwork_element = EL_SP_MURPHY;
1662       }
1663
1664       Feld[x][y] = EL_PLAYER_1;
1665     }
1666   }
1667
1668   if (init_game)
1669   {
1670     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1671     int jx = player->jx, jy = player->jy;
1672
1673     player->present = TRUE;
1674
1675     player->block_last_field = (element == EL_SP_MURPHY ?
1676                                 level.sp_block_last_field :
1677                                 level.block_last_field);
1678
1679     /* ---------- initialize player's last field block delay --------------- */
1680
1681     /* always start with reliable default value (no adjustment needed) */
1682     player->block_delay_adjustment = 0;
1683
1684     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1685     if (player->block_last_field && element == EL_SP_MURPHY)
1686       player->block_delay_adjustment = 1;
1687
1688     /* special case 2: in game engines before 3.1.1, blocking was different */
1689     if (game.use_block_last_field_bug)
1690       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1691
1692     if (!options.network || player->connected)
1693     {
1694       player->active = TRUE;
1695
1696       /* remove potentially duplicate players */
1697       if (StorePlayer[jx][jy] == Feld[x][y])
1698         StorePlayer[jx][jy] = 0;
1699
1700       StorePlayer[x][y] = Feld[x][y];
1701
1702 #if DEBUG_INIT_PLAYER
1703       if (options.debug)
1704       {
1705         printf("- player element %d activated", player->element_nr);
1706         printf(" (local player is %d and currently %s)\n",
1707                local_player->element_nr,
1708                local_player->active ? "active" : "not active");
1709       }
1710     }
1711 #endif
1712
1713     Feld[x][y] = EL_EMPTY;
1714
1715     player->jx = player->last_jx = x;
1716     player->jy = player->last_jy = y;
1717   }
1718
1719   if (!init_game)
1720   {
1721     int player_nr = GET_PLAYER_NR(element);
1722     struct PlayerInfo *player = &stored_player[player_nr];
1723
1724     if (player->active && player->killed)
1725       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1726   }
1727 }
1728
1729 static void InitField(int x, int y, boolean init_game)
1730 {
1731   int element = Feld[x][y];
1732
1733   switch (element)
1734   {
1735     case EL_SP_MURPHY:
1736     case EL_PLAYER_1:
1737     case EL_PLAYER_2:
1738     case EL_PLAYER_3:
1739     case EL_PLAYER_4:
1740       InitPlayerField(x, y, element, init_game);
1741       break;
1742
1743     case EL_SOKOBAN_FIELD_PLAYER:
1744       element = Feld[x][y] = EL_PLAYER_1;
1745       InitField(x, y, init_game);
1746
1747       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1748       InitField(x, y, init_game);
1749       break;
1750
1751     case EL_SOKOBAN_FIELD_EMPTY:
1752       local_player->sokobanfields_still_needed++;
1753       break;
1754
1755     case EL_STONEBLOCK:
1756       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1758       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1759         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1764       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1765         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1766       break;
1767
1768     case EL_BUG:
1769     case EL_BUG_RIGHT:
1770     case EL_BUG_UP:
1771     case EL_BUG_LEFT:
1772     case EL_BUG_DOWN:
1773     case EL_SPACESHIP:
1774     case EL_SPACESHIP_RIGHT:
1775     case EL_SPACESHIP_UP:
1776     case EL_SPACESHIP_LEFT:
1777     case EL_SPACESHIP_DOWN:
1778     case EL_BD_BUTTERFLY:
1779     case EL_BD_BUTTERFLY_RIGHT:
1780     case EL_BD_BUTTERFLY_UP:
1781     case EL_BD_BUTTERFLY_LEFT:
1782     case EL_BD_BUTTERFLY_DOWN:
1783     case EL_BD_FIREFLY:
1784     case EL_BD_FIREFLY_RIGHT:
1785     case EL_BD_FIREFLY_UP:
1786     case EL_BD_FIREFLY_LEFT:
1787     case EL_BD_FIREFLY_DOWN:
1788     case EL_PACMAN_RIGHT:
1789     case EL_PACMAN_UP:
1790     case EL_PACMAN_LEFT:
1791     case EL_PACMAN_DOWN:
1792     case EL_YAMYAM:
1793     case EL_YAMYAM_LEFT:
1794     case EL_YAMYAM_RIGHT:
1795     case EL_YAMYAM_UP:
1796     case EL_YAMYAM_DOWN:
1797     case EL_DARK_YAMYAM:
1798     case EL_ROBOT:
1799     case EL_PACMAN:
1800     case EL_SP_SNIKSNAK:
1801     case EL_SP_ELECTRON:
1802     case EL_MOLE:
1803     case EL_MOLE_LEFT:
1804     case EL_MOLE_RIGHT:
1805     case EL_MOLE_UP:
1806     case EL_MOLE_DOWN:
1807       InitMovDir(x, y);
1808       break;
1809
1810     case EL_AMOEBA_FULL:
1811     case EL_BD_AMOEBA:
1812       InitAmoebaNr(x, y);
1813       break;
1814
1815     case EL_AMOEBA_DROP:
1816       if (y == lev_fieldy - 1)
1817       {
1818         Feld[x][y] = EL_AMOEBA_GROWING;
1819         Store[x][y] = EL_AMOEBA_WET;
1820       }
1821       break;
1822
1823     case EL_DYNAMITE_ACTIVE:
1824     case EL_SP_DISK_RED_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1828     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1829       MovDelay[x][y] = 96;
1830       break;
1831
1832     case EL_EM_DYNAMITE_ACTIVE:
1833       MovDelay[x][y] = 32;
1834       break;
1835
1836     case EL_LAMP:
1837       local_player->lights_still_needed++;
1838       break;
1839
1840     case EL_PENGUIN:
1841       local_player->friends_still_needed++;
1842       break;
1843
1844     case EL_PIG:
1845     case EL_DRAGON:
1846       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1847       break;
1848
1849     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1850     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1851     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1853     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1854     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1856     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1857     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1859     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1860     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1861       if (init_game)
1862       {
1863         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1865         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1866
1867         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1868         {
1869           game.belt_dir[belt_nr] = belt_dir;
1870           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1871         }
1872         else    /* more than one switch -- set it like the first switch */
1873         {
1874           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1875         }
1876       }
1877       break;
1878
1879     case EL_LIGHT_SWITCH_ACTIVE:
1880       if (init_game)
1881         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1882       break;
1883
1884     case EL_INVISIBLE_STEELWALL:
1885     case EL_INVISIBLE_WALL:
1886     case EL_INVISIBLE_SAND:
1887       if (game.light_time_left > 0 ||
1888           game.lenses_time_left > 0)
1889         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1890       break;
1891
1892     case EL_EMC_MAGIC_BALL:
1893       if (game.ball_state)
1894         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1895       break;
1896
1897     case EL_EMC_MAGIC_BALL_SWITCH:
1898       if (game.ball_state)
1899         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1900       break;
1901
1902     case EL_TRIGGER_PLAYER:
1903     case EL_TRIGGER_ELEMENT:
1904     case EL_TRIGGER_CE_VALUE:
1905     case EL_TRIGGER_CE_SCORE:
1906     case EL_SELF:
1907     case EL_ANY_ELEMENT:
1908     case EL_CURRENT_CE_VALUE:
1909     case EL_CURRENT_CE_SCORE:
1910     case EL_PREV_CE_1:
1911     case EL_PREV_CE_2:
1912     case EL_PREV_CE_3:
1913     case EL_PREV_CE_4:
1914     case EL_PREV_CE_5:
1915     case EL_PREV_CE_6:
1916     case EL_PREV_CE_7:
1917     case EL_PREV_CE_8:
1918     case EL_NEXT_CE_1:
1919     case EL_NEXT_CE_2:
1920     case EL_NEXT_CE_3:
1921     case EL_NEXT_CE_4:
1922     case EL_NEXT_CE_5:
1923     case EL_NEXT_CE_6:
1924     case EL_NEXT_CE_7:
1925     case EL_NEXT_CE_8:
1926       /* reference elements should not be used on the playfield */
1927       Feld[x][y] = EL_EMPTY;
1928       break;
1929
1930     default:
1931       if (IS_CUSTOM_ELEMENT(element))
1932       {
1933         if (CAN_MOVE(element))
1934           InitMovDir(x, y);
1935
1936         if (!element_info[element].use_last_ce_value || init_game)
1937           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1938       }
1939       else if (IS_GROUP_ELEMENT(element))
1940       {
1941         Feld[x][y] = GetElementFromGroupElement(element);
1942
1943         InitField(x, y, init_game);
1944       }
1945
1946       break;
1947   }
1948
1949   if (!init_game)
1950     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1951 }
1952
1953 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1954 {
1955   InitField(x, y, init_game);
1956
1957   /* not needed to call InitMovDir() -- already done by InitField()! */
1958   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1959       CAN_MOVE(Feld[x][y]))
1960     InitMovDir(x, y);
1961 }
1962
1963 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1964 {
1965   int old_element = Feld[x][y];
1966
1967   InitField(x, y, init_game);
1968
1969   /* not needed to call InitMovDir() -- already done by InitField()! */
1970   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971       CAN_MOVE(old_element) &&
1972       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1973     InitMovDir(x, y);
1974
1975   /* this case is in fact a combination of not less than three bugs:
1976      first, it calls InitMovDir() for elements that can move, although this is
1977      already done by InitField(); then, it checks the element that was at this
1978      field _before_ the call to InitField() (which can change it); lastly, it
1979      was not called for "mole with direction" elements, which were treated as
1980      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1981   */
1982 }
1983
1984 static int get_key_element_from_nr(int key_nr)
1985 {
1986   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1987                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1988                           EL_EM_KEY_1 : EL_KEY_1);
1989
1990   return key_base_element + key_nr;
1991 }
1992
1993 static int get_next_dropped_element(struct PlayerInfo *player)
1994 {
1995   return (player->inventory_size > 0 ?
1996           player->inventory_element[player->inventory_size - 1] :
1997           player->inventory_infinite_element != EL_UNDEFINED ?
1998           player->inventory_infinite_element :
1999           player->dynabombs_left > 0 ?
2000           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2001           EL_UNDEFINED);
2002 }
2003
2004 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2005 {
2006   /* pos >= 0: get element from bottom of the stack;
2007      pos <  0: get element from top of the stack */
2008
2009   if (pos < 0)
2010   {
2011     int min_inventory_size = -pos;
2012     int inventory_pos = player->inventory_size - min_inventory_size;
2013     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2014
2015     return (player->inventory_size >= min_inventory_size ?
2016             player->inventory_element[inventory_pos] :
2017             player->inventory_infinite_element != EL_UNDEFINED ?
2018             player->inventory_infinite_element :
2019             player->dynabombs_left >= min_dynabombs_left ?
2020             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2021             EL_UNDEFINED);
2022   }
2023   else
2024   {
2025     int min_dynabombs_left = pos + 1;
2026     int min_inventory_size = pos + 1 - player->dynabombs_left;
2027     int inventory_pos = pos - player->dynabombs_left;
2028
2029     return (player->inventory_infinite_element != EL_UNDEFINED ?
2030             player->inventory_infinite_element :
2031             player->dynabombs_left >= min_dynabombs_left ?
2032             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2033             player->inventory_size >= min_inventory_size ?
2034             player->inventory_element[inventory_pos] :
2035             EL_UNDEFINED);
2036   }
2037 }
2038
2039 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2040 {
2041   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2042   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2043   int compare_result;
2044
2045   if (gpo1->sort_priority != gpo2->sort_priority)
2046     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2047   else
2048     compare_result = gpo1->nr - gpo2->nr;
2049
2050   return compare_result;
2051 }
2052
2053 void InitGameControlValues()
2054 {
2055   int i;
2056
2057   for (i = 0; game_panel_controls[i].nr != -1; i++)
2058   {
2059     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2060     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2061     struct TextPosInfo *pos = gpc->pos;
2062     int nr = gpc->nr;
2063     int type = gpc->type;
2064
2065     if (nr != i)
2066     {
2067       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2068       Error(ERR_EXIT, "this should not happen -- please debug");
2069     }
2070
2071     /* force update of game controls after initialization */
2072     gpc->value = gpc->last_value = -1;
2073     gpc->frame = gpc->last_frame = -1;
2074     gpc->gfx_frame = -1;
2075
2076     /* determine panel value width for later calculation of alignment */
2077     if (type == TYPE_INTEGER || type == TYPE_STRING)
2078     {
2079       pos->width = pos->size * getFontWidth(pos->font);
2080       pos->height = getFontHeight(pos->font);
2081     }
2082     else if (type == TYPE_ELEMENT)
2083     {
2084       pos->width = pos->size;
2085       pos->height = pos->size;
2086     }
2087
2088     /* fill structure for game panel draw order */
2089     gpo->nr = gpc->nr;
2090     gpo->sort_priority = pos->sort_priority;
2091   }
2092
2093   /* sort game panel controls according to sort_priority and control number */
2094   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2095         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2096 }
2097
2098 void UpdatePlayfieldElementCount()
2099 {
2100   boolean use_element_count = FALSE;
2101   int i, j, x, y;
2102
2103   /* first check if it is needed at all to calculate playfield element count */
2104   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2105     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2106       use_element_count = TRUE;
2107
2108   if (!use_element_count)
2109     return;
2110
2111   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2112     element_info[i].element_count = 0;
2113
2114   SCAN_PLAYFIELD(x, y)
2115   {
2116     element_info[Feld[x][y]].element_count++;
2117   }
2118
2119   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2120     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2121       if (IS_IN_GROUP(j, i))
2122         element_info[EL_GROUP_START + i].element_count +=
2123           element_info[j].element_count;
2124 }
2125
2126 void UpdateGameControlValues()
2127 {
2128   int i, k;
2129   int time = (local_player->LevelSolved ?
2130               local_player->LevelSolved_CountingTime :
2131               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2132               level.native_em_level->lev->time :
2133               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2134               level.native_sp_level->game_sp->time_played :
2135               game.no_time_limit ? TimePlayed : TimeLeft);
2136   int score = (local_player->LevelSolved ?
2137                local_player->LevelSolved_CountingScore :
2138                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2139                level.native_em_level->lev->score :
2140                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2141                level.native_sp_level->game_sp->score :
2142                local_player->score);
2143   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2144               level.native_em_level->lev->required :
2145               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2146               level.native_sp_level->game_sp->infotrons_still_needed :
2147               local_player->gems_still_needed);
2148   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149                      level.native_em_level->lev->required > 0 :
2150                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2152                      local_player->gems_still_needed > 0 ||
2153                      local_player->sokobanfields_still_needed > 0 ||
2154                      local_player->lights_still_needed > 0);
2155
2156   UpdatePlayfieldElementCount();
2157
2158   /* update game panel control values */
2159
2160   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2161   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2162
2163   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2164   for (i = 0; i < MAX_NUM_KEYS; i++)
2165     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2167   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2168
2169   if (game.centered_player_nr == -1)
2170   {
2171     for (i = 0; i < MAX_PLAYERS; i++)
2172     {
2173       /* only one player in Supaplex game engine */
2174       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2175         break;
2176
2177       for (k = 0; k < MAX_NUM_KEYS; k++)
2178       {
2179         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2180         {
2181           if (level.native_em_level->ply[i]->keys & (1 << k))
2182             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2183               get_key_element_from_nr(k);
2184         }
2185         else if (stored_player[i].key[k])
2186           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2187             get_key_element_from_nr(k);
2188       }
2189
2190       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2191         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2192           level.native_em_level->ply[i]->dynamite;
2193       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2194         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2195           level.native_sp_level->game_sp->red_disk_count;
2196       else
2197         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2198           stored_player[i].inventory_size;
2199
2200       if (stored_player[i].num_white_keys > 0)
2201         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2202           EL_DC_KEY_WHITE;
2203
2204       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2205         stored_player[i].num_white_keys;
2206     }
2207   }
2208   else
2209   {
2210     int player_nr = game.centered_player_nr;
2211
2212     for (k = 0; k < MAX_NUM_KEYS; k++)
2213     {
2214       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2215       {
2216         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2217           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2218             get_key_element_from_nr(k);
2219       }
2220       else if (stored_player[player_nr].key[k])
2221         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2222           get_key_element_from_nr(k);
2223     }
2224
2225     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2227         level.native_em_level->ply[player_nr]->dynamite;
2228     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2229       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2230         level.native_sp_level->game_sp->red_disk_count;
2231     else
2232       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2233         stored_player[player_nr].inventory_size;
2234
2235     if (stored_player[player_nr].num_white_keys > 0)
2236       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2237
2238     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2239       stored_player[player_nr].num_white_keys;
2240   }
2241
2242   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2243   {
2244     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2245       get_inventory_element_from_pos(local_player, i);
2246     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2247       get_inventory_element_from_pos(local_player, -i - 1);
2248   }
2249
2250   game_panel_controls[GAME_PANEL_SCORE].value = score;
2251   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2252
2253   game_panel_controls[GAME_PANEL_TIME].value = time;
2254
2255   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2256   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2257   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2258
2259   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2260
2261   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2262     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2263      EL_EMPTY);
2264   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2265     local_player->shield_normal_time_left;
2266   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2267     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2268      EL_EMPTY);
2269   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2270     local_player->shield_deadly_time_left;
2271
2272   game_panel_controls[GAME_PANEL_EXIT].value =
2273     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2274
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2277   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2278     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2279      EL_EMC_MAGIC_BALL_SWITCH);
2280
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2282     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2283   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2284     game.light_time_left;
2285
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2287     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2288   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2289     game.timegate_time_left;
2290
2291   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2292     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2293
2294   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2295     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2296   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2297     game.lenses_time_left;
2298
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2300     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2301   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2302     game.magnify_time_left;
2303
2304   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2305     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2306      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2307      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2308      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2309      EL_BALLOON_SWITCH_NONE);
2310
2311   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2312     local_player->dynabomb_count;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2314     local_player->dynabomb_size;
2315   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2316     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2317
2318   game_panel_controls[GAME_PANEL_PENGUINS].value =
2319     local_player->friends_still_needed;
2320
2321   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2322     local_player->sokobanfields_still_needed;
2323   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2324     local_player->sokobanfields_still_needed;
2325
2326   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2327     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2328
2329   for (i = 0; i < NUM_BELTS; i++)
2330   {
2331     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2332       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2333        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2334     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2335       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2336   }
2337
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2339     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2340   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2341     game.magic_wall_time_left;
2342
2343   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2344     local_player->gravity;
2345
2346   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2347     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2348
2349   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2350     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2351       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2352        game.panel.element[i].id : EL_UNDEFINED);
2353
2354   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2355     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2356       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2357        element_info[game.panel.element_count[i].id].element_count : 0);
2358
2359   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2360     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2361       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2362        element_info[game.panel.ce_score[i].id].collect_score : 0);
2363
2364   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2365     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2366       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2367        element_info[game.panel.ce_score_element[i].id].collect_score :
2368        EL_UNDEFINED);
2369
2370   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2372   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2373
2374   /* update game panel control frames */
2375
2376   for (i = 0; game_panel_controls[i].nr != -1; i++)
2377   {
2378     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2379
2380     if (gpc->type == TYPE_ELEMENT)
2381     {
2382       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2383       {
2384         int last_anim_random_frame = gfx.anim_random_frame;
2385         int element = gpc->value;
2386         int graphic = el2panelimg(element);
2387
2388         if (gpc->value != gpc->last_value)
2389         {
2390           gpc->gfx_frame = 0;
2391           gpc->gfx_random = INIT_GFX_RANDOM();
2392         }
2393         else
2394         {
2395           gpc->gfx_frame++;
2396
2397           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2398               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2399             gpc->gfx_random = INIT_GFX_RANDOM();
2400         }
2401
2402         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2403           gfx.anim_random_frame = gpc->gfx_random;
2404
2405         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2406           gpc->gfx_frame = element_info[element].collect_score;
2407
2408         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2409                                               gpc->gfx_frame);
2410
2411         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2412           gfx.anim_random_frame = last_anim_random_frame;
2413       }
2414     }
2415   }
2416 }
2417
2418 void DisplayGameControlValues()
2419 {
2420   boolean redraw_panel = FALSE;
2421   int i;
2422
2423   for (i = 0; game_panel_controls[i].nr != -1; i++)
2424   {
2425     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2426
2427     if (PANEL_DEACTIVATED(gpc->pos))
2428       continue;
2429
2430     if (gpc->value == gpc->last_value &&
2431         gpc->frame == gpc->last_frame)
2432       continue;
2433
2434     redraw_panel = TRUE;
2435   }
2436
2437   if (!redraw_panel)
2438     return;
2439
2440   /* copy default game door content to main double buffer */
2441
2442   /* !!! CHECK AGAIN !!! */
2443   SetPanelBackground();
2444   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2445   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2446
2447   /* redraw game control buttons */
2448   RedrawGameButtons();
2449
2450   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2451
2452   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2453   {
2454     int nr = game_panel_order[i].nr;
2455     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2456     struct TextPosInfo *pos = gpc->pos;
2457     int type = gpc->type;
2458     int value = gpc->value;
2459     int frame = gpc->frame;
2460     int size = pos->size;
2461     int font = pos->font;
2462     boolean draw_masked = pos->draw_masked;
2463     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2464
2465     if (PANEL_DEACTIVATED(pos))
2466       continue;
2467
2468     gpc->last_value = value;
2469     gpc->last_frame = frame;
2470
2471     if (type == TYPE_INTEGER)
2472     {
2473       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2474           nr == GAME_PANEL_TIME)
2475       {
2476         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2477
2478         if (use_dynamic_size)           /* use dynamic number of digits */
2479         {
2480           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2481           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2482           int size2 = size1 + 1;
2483           int font1 = pos->font;
2484           int font2 = pos->font_alt;
2485
2486           size = (value < value_change ? size1 : size2);
2487           font = (value < value_change ? font1 : font2);
2488         }
2489       }
2490
2491       /* correct text size if "digits" is zero or less */
2492       if (size <= 0)
2493         size = strlen(int2str(value, size));
2494
2495       /* dynamically correct text alignment */
2496       pos->width = size * getFontWidth(font);
2497
2498       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2499                   int2str(value, size), font, mask_mode);
2500     }
2501     else if (type == TYPE_ELEMENT)
2502     {
2503       int element, graphic;
2504       Bitmap *src_bitmap;
2505       int src_x, src_y;
2506       int width, height;
2507       int dst_x = PANEL_XPOS(pos);
2508       int dst_y = PANEL_YPOS(pos);
2509
2510       if (value != EL_UNDEFINED && value != EL_EMPTY)
2511       {
2512         element = value;
2513         graphic = el2panelimg(value);
2514
2515         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2516
2517         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2518           size = TILESIZE;
2519
2520         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2521                               &src_x, &src_y);
2522
2523         width  = graphic_info[graphic].width  * size / TILESIZE;
2524         height = graphic_info[graphic].height * size / TILESIZE;
2525
2526         if (draw_masked)
2527           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2528                            dst_x, dst_y);
2529         else
2530           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2531                      dst_x, dst_y);
2532       }
2533     }
2534     else if (type == TYPE_STRING)
2535     {
2536       boolean active = (value != 0);
2537       char *state_normal = "off";
2538       char *state_active = "on";
2539       char *state = (active ? state_active : state_normal);
2540       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2541                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2542                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2543                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2544
2545       if (nr == GAME_PANEL_GRAVITY_STATE)
2546       {
2547         int font1 = pos->font;          /* (used for normal state) */
2548         int font2 = pos->font_alt;      /* (used for active state) */
2549
2550         font = (active ? font2 : font1);
2551       }
2552
2553       if (s != NULL)
2554       {
2555         char *s_cut;
2556
2557         if (size <= 0)
2558         {
2559           /* don't truncate output if "chars" is zero or less */
2560           size = strlen(s);
2561
2562           /* dynamically correct text alignment */
2563           pos->width = size * getFontWidth(font);
2564         }
2565
2566         s_cut = getStringCopyN(s, size);
2567
2568         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2569                     s_cut, font, mask_mode);
2570
2571         free(s_cut);
2572       }
2573     }
2574
2575     redraw_mask |= REDRAW_DOOR_1;
2576   }
2577
2578   SetGameStatus(GAME_MODE_PLAYING);
2579 }
2580
2581 void UpdateAndDisplayGameControlValues()
2582 {
2583   if (tape.deactivate_display)
2584     return;
2585
2586   UpdateGameControlValues();
2587   DisplayGameControlValues();
2588 }
2589
2590 void UpdateGameDoorValues()
2591 {
2592   UpdateGameControlValues();
2593 }
2594
2595 void DrawGameDoorValues()
2596 {
2597   DisplayGameControlValues();
2598 }
2599
2600
2601 /*
2602   =============================================================================
2603   InitGameEngine()
2604   -----------------------------------------------------------------------------
2605   initialize game engine due to level / tape version number
2606   =============================================================================
2607 */
2608
2609 static void InitGameEngine()
2610 {
2611   int i, j, k, l, x, y;
2612
2613   /* set game engine from tape file when re-playing, else from level file */
2614   game.engine_version = (tape.playing ? tape.engine_version :
2615                          level.game_version);
2616
2617   /* set single or multi-player game mode (needed for re-playing tapes) */
2618   game.team_mode = setup.team_mode;
2619
2620   if (tape.playing)
2621   {
2622     int num_players = 0;
2623
2624     for (i = 0; i < MAX_PLAYERS; i++)
2625       if (tape.player_participates[i])
2626         num_players++;
2627
2628     /* multi-player tapes contain input data for more than one player */
2629     game.team_mode = (num_players > 1);
2630   }
2631
2632   /* ---------------------------------------------------------------------- */
2633   /* set flags for bugs and changes according to active game engine version */
2634   /* ---------------------------------------------------------------------- */
2635
2636   /*
2637     Summary of bugfix/change:
2638     Fixed handling for custom elements that change when pushed by the player.
2639
2640     Fixed/changed in version:
2641     3.1.0
2642
2643     Description:
2644     Before 3.1.0, custom elements that "change when pushing" changed directly
2645     after the player started pushing them (until then handled in "DigField()").
2646     Since 3.1.0, these custom elements are not changed until the "pushing"
2647     move of the element is finished (now handled in "ContinueMoving()").
2648
2649     Affected levels/tapes:
2650     The first condition is generally needed for all levels/tapes before version
2651     3.1.0, which might use the old behaviour before it was changed; known tapes
2652     that are affected are some tapes from the level set "Walpurgis Gardens" by
2653     Jamie Cullen.
2654     The second condition is an exception from the above case and is needed for
2655     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2656     above (including some development versions of 3.1.0), but before it was
2657     known that this change would break tapes like the above and was fixed in
2658     3.1.1, so that the changed behaviour was active although the engine version
2659     while recording maybe was before 3.1.0. There is at least one tape that is
2660     affected by this exception, which is the tape for the one-level set "Bug
2661     Machine" by Juergen Bonhagen.
2662   */
2663
2664   game.use_change_when_pushing_bug =
2665     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2666      !(tape.playing &&
2667        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2668        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2669
2670   /*
2671     Summary of bugfix/change:
2672     Fixed handling for blocking the field the player leaves when moving.
2673
2674     Fixed/changed in version:
2675     3.1.1
2676
2677     Description:
2678     Before 3.1.1, when "block last field when moving" was enabled, the field
2679     the player is leaving when moving was blocked for the time of the move,
2680     and was directly unblocked afterwards. This resulted in the last field
2681     being blocked for exactly one less than the number of frames of one player
2682     move. Additionally, even when blocking was disabled, the last field was
2683     blocked for exactly one frame.
2684     Since 3.1.1, due to changes in player movement handling, the last field
2685     is not blocked at all when blocking is disabled. When blocking is enabled,
2686     the last field is blocked for exactly the number of frames of one player
2687     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2688     last field is blocked for exactly one more than the number of frames of
2689     one player move.
2690
2691     Affected levels/tapes:
2692     (!!! yet to be determined -- probably many !!!)
2693   */
2694
2695   game.use_block_last_field_bug =
2696     (game.engine_version < VERSION_IDENT(3,1,1,0));
2697
2698   /* ---------------------------------------------------------------------- */
2699
2700   /* set maximal allowed number of custom element changes per game frame */
2701   game.max_num_changes_per_frame = 1;
2702
2703   /* default scan direction: scan playfield from top/left to bottom/right */
2704   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2705
2706   /* dynamically adjust element properties according to game engine version */
2707   InitElementPropertiesEngine(game.engine_version);
2708
2709 #if 0
2710   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2711   printf("          tape version == %06d [%s] [file: %06d]\n",
2712          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2713          tape.file_version);
2714   printf("       => game.engine_version == %06d\n", game.engine_version);
2715 #endif
2716
2717   /* ---------- initialize player's initial move delay --------------------- */
2718
2719   /* dynamically adjust player properties according to level information */
2720   for (i = 0; i < MAX_PLAYERS; i++)
2721     game.initial_move_delay_value[i] =
2722       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2723
2724   /* dynamically adjust player properties according to game engine version */
2725   for (i = 0; i < MAX_PLAYERS; i++)
2726     game.initial_move_delay[i] =
2727       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2728        game.initial_move_delay_value[i] : 0);
2729
2730   /* ---------- initialize player's initial push delay --------------------- */
2731
2732   /* dynamically adjust player properties according to game engine version */
2733   game.initial_push_delay_value =
2734     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2735
2736   /* ---------- initialize changing elements ------------------------------- */
2737
2738   /* initialize changing elements information */
2739   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2740   {
2741     struct ElementInfo *ei = &element_info[i];
2742
2743     /* this pointer might have been changed in the level editor */
2744     ei->change = &ei->change_page[0];
2745
2746     if (!IS_CUSTOM_ELEMENT(i))
2747     {
2748       ei->change->target_element = EL_EMPTY_SPACE;
2749       ei->change->delay_fixed = 0;
2750       ei->change->delay_random = 0;
2751       ei->change->delay_frames = 1;
2752     }
2753
2754     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2755     {
2756       ei->has_change_event[j] = FALSE;
2757
2758       ei->event_page_nr[j] = 0;
2759       ei->event_page[j] = &ei->change_page[0];
2760     }
2761   }
2762
2763   /* add changing elements from pre-defined list */
2764   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2765   {
2766     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2767     struct ElementInfo *ei = &element_info[ch_delay->element];
2768
2769     ei->change->target_element       = ch_delay->target_element;
2770     ei->change->delay_fixed          = ch_delay->change_delay;
2771
2772     ei->change->pre_change_function  = ch_delay->pre_change_function;
2773     ei->change->change_function      = ch_delay->change_function;
2774     ei->change->post_change_function = ch_delay->post_change_function;
2775
2776     ei->change->can_change = TRUE;
2777     ei->change->can_change_or_has_action = TRUE;
2778
2779     ei->has_change_event[CE_DELAY] = TRUE;
2780
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2782     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2783   }
2784
2785   /* ---------- initialize internal run-time variables --------------------- */
2786
2787   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2788   {
2789     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2790
2791     for (j = 0; j < ei->num_change_pages; j++)
2792     {
2793       ei->change_page[j].can_change_or_has_action =
2794         (ei->change_page[j].can_change |
2795          ei->change_page[j].has_action);
2796     }
2797   }
2798
2799   /* add change events from custom element configuration */
2800   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2801   {
2802     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2803
2804     for (j = 0; j < ei->num_change_pages; j++)
2805     {
2806       if (!ei->change_page[j].can_change_or_has_action)
2807         continue;
2808
2809       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2810       {
2811         /* only add event page for the first page found with this event */
2812         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2813         {
2814           ei->has_change_event[k] = TRUE;
2815
2816           ei->event_page_nr[k] = j;
2817           ei->event_page[k] = &ei->change_page[j];
2818         }
2819       }
2820     }
2821   }
2822
2823   /* ---------- initialize reference elements in change conditions --------- */
2824
2825   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2826   {
2827     int element = EL_CUSTOM_START + i;
2828     struct ElementInfo *ei = &element_info[element];
2829
2830     for (j = 0; j < ei->num_change_pages; j++)
2831     {
2832       int trigger_element = ei->change_page[j].initial_trigger_element;
2833
2834       if (trigger_element >= EL_PREV_CE_8 &&
2835           trigger_element <= EL_NEXT_CE_8)
2836         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2837
2838       ei->change_page[j].trigger_element = trigger_element;
2839     }
2840   }
2841
2842   /* ---------- initialize run-time trigger player and element ------------- */
2843
2844   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2845   {
2846     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2847
2848     for (j = 0; j < ei->num_change_pages; j++)
2849     {
2850       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2852       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2853       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2854       ei->change_page[j].actual_trigger_ce_value = 0;
2855       ei->change_page[j].actual_trigger_ce_score = 0;
2856     }
2857   }
2858
2859   /* ---------- initialize trigger events ---------------------------------- */
2860
2861   /* initialize trigger events information */
2862   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2863     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2864       trigger_events[i][j] = FALSE;
2865
2866   /* add trigger events from element change event properties */
2867   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2868   {
2869     struct ElementInfo *ei = &element_info[i];
2870
2871     for (j = 0; j < ei->num_change_pages; j++)
2872     {
2873       if (!ei->change_page[j].can_change_or_has_action)
2874         continue;
2875
2876       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2877       {
2878         int trigger_element = ei->change_page[j].trigger_element;
2879
2880         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2881         {
2882           if (ei->change_page[j].has_event[k])
2883           {
2884             if (IS_GROUP_ELEMENT(trigger_element))
2885             {
2886               struct ElementGroupInfo *group =
2887                 element_info[trigger_element].group;
2888
2889               for (l = 0; l < group->num_elements_resolved; l++)
2890                 trigger_events[group->element_resolved[l]][k] = TRUE;
2891             }
2892             else if (trigger_element == EL_ANY_ELEMENT)
2893               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2894                 trigger_events[l][k] = TRUE;
2895             else
2896               trigger_events[trigger_element][k] = TRUE;
2897           }
2898         }
2899       }
2900     }
2901   }
2902
2903   /* ---------- initialize push delay -------------------------------------- */
2904
2905   /* initialize push delay values to default */
2906   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2907   {
2908     if (!IS_CUSTOM_ELEMENT(i))
2909     {
2910       /* set default push delay values (corrected since version 3.0.7-1) */
2911       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2912       {
2913         element_info[i].push_delay_fixed = 2;
2914         element_info[i].push_delay_random = 8;
2915       }
2916       else
2917       {
2918         element_info[i].push_delay_fixed = 8;
2919         element_info[i].push_delay_random = 8;
2920       }
2921     }
2922   }
2923
2924   /* set push delay value for certain elements from pre-defined list */
2925   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2926   {
2927     int e = push_delay_list[i].element;
2928
2929     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2930     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2931   }
2932
2933   /* set push delay value for Supaplex elements for newer engine versions */
2934   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2935   {
2936     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2937     {
2938       if (IS_SP_ELEMENT(i))
2939       {
2940         /* set SP push delay to just enough to push under a falling zonk */
2941         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2942
2943         element_info[i].push_delay_fixed  = delay;
2944         element_info[i].push_delay_random = 0;
2945       }
2946     }
2947   }
2948
2949   /* ---------- initialize move stepsize ----------------------------------- */
2950
2951   /* initialize move stepsize values to default */
2952   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2953     if (!IS_CUSTOM_ELEMENT(i))
2954       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2955
2956   /* set move stepsize value for certain elements from pre-defined list */
2957   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2958   {
2959     int e = move_stepsize_list[i].element;
2960
2961     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2962   }
2963
2964   /* ---------- initialize collect score ----------------------------------- */
2965
2966   /* initialize collect score values for custom elements from initial value */
2967   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2968     if (IS_CUSTOM_ELEMENT(i))
2969       element_info[i].collect_score = element_info[i].collect_score_initial;
2970
2971   /* ---------- initialize collect count ----------------------------------- */
2972
2973   /* initialize collect count values for non-custom elements */
2974   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2975     if (!IS_CUSTOM_ELEMENT(i))
2976       element_info[i].collect_count_initial = 0;
2977
2978   /* add collect count values for all elements from pre-defined list */
2979   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2980     element_info[collect_count_list[i].element].collect_count_initial =
2981       collect_count_list[i].count;
2982
2983   /* ---------- initialize access direction -------------------------------- */
2984
2985   /* initialize access direction values to default (access from every side) */
2986   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2987     if (!IS_CUSTOM_ELEMENT(i))
2988       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2989
2990   /* set access direction value for certain elements from pre-defined list */
2991   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2992     element_info[access_direction_list[i].element].access_direction =
2993       access_direction_list[i].direction;
2994
2995   /* ---------- initialize explosion content ------------------------------- */
2996   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2997   {
2998     if (IS_CUSTOM_ELEMENT(i))
2999       continue;
3000
3001     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3002     {
3003       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3004
3005       element_info[i].content.e[x][y] =
3006         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3007          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3008          i == EL_PLAYER_3 ? EL_EMERALD :
3009          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3010          i == EL_MOLE ? EL_EMERALD_RED :
3011          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3012          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3013          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3014          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3015          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3016          i == EL_WALL_EMERALD ? EL_EMERALD :
3017          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3018          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3019          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3020          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3021          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3022          i == EL_WALL_PEARL ? EL_PEARL :
3023          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3024          EL_EMPTY);
3025     }
3026   }
3027
3028   /* ---------- initialize recursion detection ------------------------------ */
3029   recursion_loop_depth = 0;
3030   recursion_loop_detected = FALSE;
3031   recursion_loop_element = EL_UNDEFINED;
3032
3033   /* ---------- initialize graphics engine ---------------------------------- */
3034   game.scroll_delay_value =
3035     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3036      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3037   game.scroll_delay_value =
3038     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3039
3040   /* ---------- initialize game engine snapshots ---------------------------- */
3041   for (i = 0; i < MAX_PLAYERS; i++)
3042     game.snapshot.last_action[i] = 0;
3043   game.snapshot.changed_action = FALSE;
3044   game.snapshot.collected_item = FALSE;
3045   game.snapshot.mode =
3046     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3047      SNAPSHOT_MODE_EVERY_STEP :
3048      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3049      SNAPSHOT_MODE_EVERY_MOVE :
3050      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3051      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3052   game.snapshot.save_snapshot = FALSE;
3053 }
3054
3055 int get_num_special_action(int element, int action_first, int action_last)
3056 {
3057   int num_special_action = 0;
3058   int i, j;
3059
3060   for (i = action_first; i <= action_last; i++)
3061   {
3062     boolean found = FALSE;
3063
3064     for (j = 0; j < NUM_DIRECTIONS; j++)
3065       if (el_act_dir2img(element, i, j) !=
3066           el_act_dir2img(element, ACTION_DEFAULT, j))
3067         found = TRUE;
3068
3069     if (found)
3070       num_special_action++;
3071     else
3072       break;
3073   }
3074
3075   return num_special_action;
3076 }
3077
3078
3079 /*
3080   =============================================================================
3081   InitGame()
3082   -----------------------------------------------------------------------------
3083   initialize and start new game
3084   =============================================================================
3085 */
3086
3087 void InitGame()
3088 {
3089   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3090   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3091   int fade_mask = REDRAW_FIELD;
3092
3093   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3094   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3095   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3096   int initial_move_dir = MV_DOWN;
3097   int i, j, x, y;
3098
3099   // required here to update video display before fading (FIX THIS)
3100   DrawMaskedBorder(REDRAW_DOOR_2);
3101
3102   if (!game.restart_level)
3103     CloseDoor(DOOR_CLOSE_1);
3104
3105   SetGameStatus(GAME_MODE_PLAYING);
3106
3107   if (level_editor_test_game)
3108     FadeSkipNextFadeIn();
3109   else
3110     FadeSetEnterScreen();
3111
3112   if (CheckIfGlobalBorderHasChanged())
3113     fade_mask = REDRAW_ALL;
3114
3115   FadeSoundsAndMusic();
3116
3117   ExpireSoundLoops(TRUE);
3118
3119   FadeOut(fade_mask);
3120
3121   /* needed if different viewport properties defined for playing */
3122   ChangeViewportPropertiesIfNeeded();
3123
3124   ClearField();
3125
3126   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3127
3128   DrawCompleteVideoDisplay();
3129
3130   InitGameEngine();
3131   InitGameControlValues();
3132
3133   /* don't play tapes over network */
3134   network_playing = (options.network && !tape.playing);
3135
3136   for (i = 0; i < MAX_PLAYERS; i++)
3137   {
3138     struct PlayerInfo *player = &stored_player[i];
3139
3140     player->index_nr = i;
3141     player->index_bit = (1 << i);
3142     player->element_nr = EL_PLAYER_1 + i;
3143
3144     player->present = FALSE;
3145     player->active = FALSE;
3146     player->mapped = FALSE;
3147
3148     player->killed = FALSE;
3149     player->reanimated = FALSE;
3150
3151     player->action = 0;
3152     player->effective_action = 0;
3153     player->programmed_action = 0;
3154
3155     player->score = 0;
3156     player->score_final = 0;
3157
3158     player->gems_still_needed = level.gems_needed;
3159     player->sokobanfields_still_needed = 0;
3160     player->lights_still_needed = 0;
3161     player->friends_still_needed = 0;
3162
3163     for (j = 0; j < MAX_NUM_KEYS; j++)
3164       player->key[j] = FALSE;
3165
3166     player->num_white_keys = 0;
3167
3168     player->dynabomb_count = 0;
3169     player->dynabomb_size = 1;
3170     player->dynabombs_left = 0;
3171     player->dynabomb_xl = FALSE;
3172
3173     player->MovDir = initial_move_dir;
3174     player->MovPos = 0;
3175     player->GfxPos = 0;
3176     player->GfxDir = initial_move_dir;
3177     player->GfxAction = ACTION_DEFAULT;
3178     player->Frame = 0;
3179     player->StepFrame = 0;
3180
3181     player->initial_element = player->element_nr;
3182     player->artwork_element =
3183       (level.use_artwork_element[i] ? level.artwork_element[i] :
3184        player->element_nr);
3185     player->use_murphy = FALSE;
3186
3187     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3188     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3189
3190     player->gravity = level.initial_player_gravity[i];
3191
3192     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3193
3194     player->actual_frame_counter = 0;
3195
3196     player->step_counter = 0;
3197
3198     player->last_move_dir = initial_move_dir;
3199
3200     player->is_active = FALSE;
3201
3202     player->is_waiting = FALSE;
3203     player->is_moving = FALSE;
3204     player->is_auto_moving = FALSE;
3205     player->is_digging = FALSE;
3206     player->is_snapping = FALSE;
3207     player->is_collecting = FALSE;
3208     player->is_pushing = FALSE;
3209     player->is_switching = FALSE;
3210     player->is_dropping = FALSE;
3211     player->is_dropping_pressed = FALSE;
3212
3213     player->is_bored = FALSE;
3214     player->is_sleeping = FALSE;
3215
3216     player->was_waiting = TRUE;
3217     player->was_moving = FALSE;
3218     player->was_snapping = FALSE;
3219     player->was_dropping = FALSE;
3220
3221     player->frame_counter_bored = -1;
3222     player->frame_counter_sleeping = -1;
3223
3224     player->anim_delay_counter = 0;
3225     player->post_delay_counter = 0;
3226
3227     player->dir_waiting = initial_move_dir;
3228     player->action_waiting = ACTION_DEFAULT;
3229     player->last_action_waiting = ACTION_DEFAULT;
3230     player->special_action_bored = ACTION_DEFAULT;
3231     player->special_action_sleeping = ACTION_DEFAULT;
3232
3233     player->switch_x = -1;
3234     player->switch_y = -1;
3235
3236     player->drop_x = -1;
3237     player->drop_y = -1;
3238
3239     player->show_envelope = 0;
3240
3241     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3242
3243     player->push_delay       = -1;      /* initialized when pushing starts */
3244     player->push_delay_value = game.initial_push_delay_value;
3245
3246     player->drop_delay = 0;
3247     player->drop_pressed_delay = 0;
3248
3249     player->last_jx = -1;
3250     player->last_jy = -1;
3251     player->jx = -1;
3252     player->jy = -1;
3253
3254     player->shield_normal_time_left = 0;
3255     player->shield_deadly_time_left = 0;
3256
3257     player->inventory_infinite_element = EL_UNDEFINED;
3258     player->inventory_size = 0;
3259
3260     if (level.use_initial_inventory[i])
3261     {
3262       for (j = 0; j < level.initial_inventory_size[i]; j++)
3263       {
3264         int element = level.initial_inventory_content[i][j];
3265         int collect_count = element_info[element].collect_count_initial;
3266         int k;
3267
3268         if (!IS_CUSTOM_ELEMENT(element))
3269           collect_count = 1;
3270
3271         if (collect_count == 0)
3272           player->inventory_infinite_element = element;
3273         else
3274           for (k = 0; k < collect_count; k++)
3275             if (player->inventory_size < MAX_INVENTORY_SIZE)
3276               player->inventory_element[player->inventory_size++] = element;
3277       }
3278     }
3279
3280     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3281     SnapField(player, 0, 0);
3282
3283     player->LevelSolved = FALSE;
3284     player->GameOver = FALSE;
3285
3286     player->LevelSolved_GameWon = FALSE;
3287     player->LevelSolved_GameEnd = FALSE;
3288     player->LevelSolved_PanelOff = FALSE;
3289     player->LevelSolved_SaveTape = FALSE;
3290     player->LevelSolved_SaveScore = FALSE;
3291     player->LevelSolved_CountingTime = 0;
3292     player->LevelSolved_CountingScore = 0;
3293
3294     map_player_action[i] = i;
3295   }
3296
3297   network_player_action_received = FALSE;
3298
3299 #if defined(NETWORK_AVALIABLE)
3300   /* initial null action */
3301   if (network_playing)
3302     SendToServer_MovePlayer(MV_NONE);
3303 #endif
3304
3305   ZX = ZY = -1;
3306   ExitX = ExitY = -1;
3307
3308   FrameCounter = 0;
3309   TimeFrames = 0;
3310   TimePlayed = 0;
3311   TimeLeft = level.time;
3312   TapeTime = 0;
3313
3314   ScreenMovDir = MV_NONE;
3315   ScreenMovPos = 0;
3316   ScreenGfxPos = 0;
3317
3318   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3319
3320   AllPlayersGone = FALSE;
3321
3322   game.no_time_limit = (level.time == 0);
3323
3324   game.yamyam_content_nr = 0;
3325   game.robot_wheel_active = FALSE;
3326   game.magic_wall_active = FALSE;
3327   game.magic_wall_time_left = 0;
3328   game.light_time_left = 0;
3329   game.timegate_time_left = 0;
3330   game.switchgate_pos = 0;
3331   game.wind_direction = level.wind_direction_initial;
3332
3333   game.lenses_time_left = 0;
3334   game.magnify_time_left = 0;
3335
3336   game.ball_state = level.ball_state_initial;
3337   game.ball_content_nr = 0;
3338
3339   game.envelope_active = FALSE;
3340
3341   /* set focus to local player for network games, else to all players */
3342   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3343   game.centered_player_nr_next = game.centered_player_nr;
3344   game.set_centered_player = FALSE;
3345
3346   if (network_playing && tape.recording)
3347   {
3348     /* store client dependent player focus when recording network games */
3349     tape.centered_player_nr_next = game.centered_player_nr_next;
3350     tape.set_centered_player = TRUE;
3351   }
3352
3353   for (i = 0; i < NUM_BELTS; i++)
3354   {
3355     game.belt_dir[i] = MV_NONE;
3356     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3357   }
3358
3359   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3360     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3361
3362 #if DEBUG_INIT_PLAYER
3363   if (options.debug)
3364   {
3365     printf("Player status at level initialization:\n");
3366   }
3367 #endif
3368
3369   SCAN_PLAYFIELD(x, y)
3370   {
3371     Feld[x][y] = level.field[x][y];
3372     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3373     ChangeDelay[x][y] = 0;
3374     ChangePage[x][y] = -1;
3375     CustomValue[x][y] = 0;              /* initialized in InitField() */
3376     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3377     AmoebaNr[x][y] = 0;
3378     WasJustMoving[x][y] = 0;
3379     WasJustFalling[x][y] = 0;
3380     CheckCollision[x][y] = 0;
3381     CheckImpact[x][y] = 0;
3382     Stop[x][y] = FALSE;
3383     Pushed[x][y] = FALSE;
3384
3385     ChangeCount[x][y] = 0;
3386     ChangeEvent[x][y] = -1;
3387
3388     ExplodePhase[x][y] = 0;
3389     ExplodeDelay[x][y] = 0;
3390     ExplodeField[x][y] = EX_TYPE_NONE;
3391
3392     RunnerVisit[x][y] = 0;
3393     PlayerVisit[x][y] = 0;
3394
3395     GfxFrame[x][y] = 0;
3396     GfxRandom[x][y] = INIT_GFX_RANDOM();
3397     GfxElement[x][y] = EL_UNDEFINED;
3398     GfxAction[x][y] = ACTION_DEFAULT;
3399     GfxDir[x][y] = MV_NONE;
3400     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3401   }
3402
3403   SCAN_PLAYFIELD(x, y)
3404   {
3405     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3406       emulate_bd = FALSE;
3407     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3408       emulate_sb = FALSE;
3409     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3410       emulate_sp = FALSE;
3411
3412     InitField(x, y, TRUE);
3413
3414     ResetGfxAnimation(x, y);
3415   }
3416
3417   InitBeltMovement();
3418
3419   for (i = 0; i < MAX_PLAYERS; i++)
3420   {
3421     struct PlayerInfo *player = &stored_player[i];
3422
3423     /* set number of special actions for bored and sleeping animation */
3424     player->num_special_action_bored =
3425       get_num_special_action(player->artwork_element,
3426                              ACTION_BORING_1, ACTION_BORING_LAST);
3427     player->num_special_action_sleeping =
3428       get_num_special_action(player->artwork_element,
3429                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3430   }
3431
3432   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3433                     emulate_sb ? EMU_SOKOBAN :
3434                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3435
3436   /* initialize type of slippery elements */
3437   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3438   {
3439     if (!IS_CUSTOM_ELEMENT(i))
3440     {
3441       /* default: elements slip down either to the left or right randomly */
3442       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3443
3444       /* SP style elements prefer to slip down on the left side */
3445       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3446         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3447
3448       /* BD style elements prefer to slip down on the left side */
3449       if (game.emulation == EMU_BOULDERDASH)
3450         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3451     }
3452   }
3453
3454   /* initialize explosion and ignition delay */
3455   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3456   {
3457     if (!IS_CUSTOM_ELEMENT(i))
3458     {
3459       int num_phase = 8;
3460       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3461                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3462                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3463       int last_phase = (num_phase + 1) * delay;
3464       int half_phase = (num_phase / 2) * delay;
3465
3466       element_info[i].explosion_delay = last_phase - 1;
3467       element_info[i].ignition_delay = half_phase;
3468
3469       if (i == EL_BLACK_ORB)
3470         element_info[i].ignition_delay = 1;
3471     }
3472   }
3473
3474   /* correct non-moving belts to start moving left */
3475   for (i = 0; i < NUM_BELTS; i++)
3476     if (game.belt_dir[i] == MV_NONE)
3477       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3478
3479 #if USE_NEW_PLAYER_ASSIGNMENTS
3480   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3481   /* choose default local player */
3482   local_player = &stored_player[0];
3483
3484   for (i = 0; i < MAX_PLAYERS; i++)
3485     stored_player[i].connected = FALSE;
3486
3487   local_player->connected = TRUE;
3488   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3489
3490   if (tape.playing)
3491   {
3492     for (i = 0; i < MAX_PLAYERS; i++)
3493       stored_player[i].connected = tape.player_participates[i];
3494   }
3495   else if (game.team_mode && !options.network)
3496   {
3497     /* try to guess locally connected team mode players (needed for correct
3498        assignment of player figures from level to locally playing players) */
3499
3500     for (i = 0; i < MAX_PLAYERS; i++)
3501       if (setup.input[i].use_joystick ||
3502           setup.input[i].key.left != KSYM_UNDEFINED)
3503         stored_player[i].connected = TRUE;
3504   }
3505
3506 #if DEBUG_INIT_PLAYER
3507   if (options.debug)
3508   {
3509     printf("Player status after level initialization:\n");
3510
3511     for (i = 0; i < MAX_PLAYERS; i++)
3512     {
3513       struct PlayerInfo *player = &stored_player[i];
3514
3515       printf("- player %d: present == %d, connected == %d, active == %d",
3516              i + 1,
3517              player->present,
3518              player->connected,
3519              player->active);
3520
3521       if (local_player == player)
3522         printf(" (local player)");
3523
3524       printf("\n");
3525     }
3526   }
3527 #endif
3528
3529 #if DEBUG_INIT_PLAYER
3530   if (options.debug)
3531     printf("Reassigning players ...\n");
3532 #endif
3533
3534   /* check if any connected player was not found in playfield */
3535   for (i = 0; i < MAX_PLAYERS; i++)
3536   {
3537     struct PlayerInfo *player = &stored_player[i];
3538
3539     if (player->connected && !player->present)
3540     {
3541       struct PlayerInfo *field_player = NULL;
3542
3543 #if DEBUG_INIT_PLAYER
3544       if (options.debug)
3545         printf("- looking for field player for player %d ...\n", i + 1);
3546 #endif
3547
3548       /* assign first free player found that is present in the playfield */
3549
3550       /* first try: look for unmapped playfield player that is not connected */
3551       for (j = 0; j < MAX_PLAYERS; j++)
3552         if (field_player == NULL &&
3553             stored_player[j].present &&
3554             !stored_player[j].mapped &&
3555             !stored_player[j].connected)
3556           field_player = &stored_player[j];
3557
3558       /* second try: look for *any* unmapped playfield player */
3559       for (j = 0; j < MAX_PLAYERS; j++)
3560         if (field_player == NULL &&
3561             stored_player[j].present &&
3562             !stored_player[j].mapped)
3563           field_player = &stored_player[j];
3564
3565       if (field_player != NULL)
3566       {
3567         int jx = field_player->jx, jy = field_player->jy;
3568
3569 #if DEBUG_INIT_PLAYER
3570         if (options.debug)
3571           printf("- found player %d\n", field_player->index_nr + 1);
3572 #endif
3573
3574         player->present = FALSE;
3575         player->active = FALSE;
3576
3577         field_player->present = TRUE;
3578         field_player->active = TRUE;
3579
3580         /*
3581         player->initial_element = field_player->initial_element;
3582         player->artwork_element = field_player->artwork_element;
3583
3584         player->block_last_field       = field_player->block_last_field;
3585         player->block_delay_adjustment = field_player->block_delay_adjustment;
3586         */
3587
3588         StorePlayer[jx][jy] = field_player->element_nr;
3589
3590         field_player->jx = field_player->last_jx = jx;
3591         field_player->jy = field_player->last_jy = jy;
3592
3593         if (local_player == player)
3594           local_player = field_player;
3595
3596         map_player_action[field_player->index_nr] = i;
3597
3598         field_player->mapped = TRUE;
3599
3600 #if DEBUG_INIT_PLAYER
3601         if (options.debug)
3602           printf("- map_player_action[%d] == %d\n",
3603                  field_player->index_nr + 1, i + 1);
3604 #endif
3605       }
3606     }
3607
3608     if (player->connected && player->present)
3609       player->mapped = TRUE;
3610   }
3611
3612 #if DEBUG_INIT_PLAYER
3613   if (options.debug)
3614   {
3615     printf("Player status after player assignment (first stage):\n");
3616
3617     for (i = 0; i < MAX_PLAYERS; i++)
3618     {
3619       struct PlayerInfo *player = &stored_player[i];
3620
3621       printf("- player %d: present == %d, connected == %d, active == %d",
3622              i + 1,
3623              player->present,
3624              player->connected,
3625              player->active);
3626
3627       if (local_player == player)
3628         printf(" (local player)");
3629
3630       printf("\n");
3631     }
3632   }
3633 #endif
3634
3635 #else
3636
3637   /* check if any connected player was not found in playfield */
3638   for (i = 0; i < MAX_PLAYERS; i++)
3639   {
3640     struct PlayerInfo *player = &stored_player[i];
3641
3642     if (player->connected && !player->present)
3643     {
3644       for (j = 0; j < MAX_PLAYERS; j++)
3645       {
3646         struct PlayerInfo *field_player = &stored_player[j];
3647         int jx = field_player->jx, jy = field_player->jy;
3648
3649         /* assign first free player found that is present in the playfield */
3650         if (field_player->present && !field_player->connected)
3651         {
3652           player->present = TRUE;
3653           player->active = TRUE;
3654
3655           field_player->present = FALSE;
3656           field_player->active = FALSE;
3657
3658           player->initial_element = field_player->initial_element;
3659           player->artwork_element = field_player->artwork_element;
3660
3661           player->block_last_field       = field_player->block_last_field;
3662           player->block_delay_adjustment = field_player->block_delay_adjustment;
3663
3664           StorePlayer[jx][jy] = player->element_nr;
3665
3666           player->jx = player->last_jx = jx;
3667           player->jy = player->last_jy = jy;
3668
3669           break;
3670         }
3671       }
3672     }
3673   }
3674 #endif
3675
3676 #if 0
3677   printf("::: local_player->present == %d\n", local_player->present);
3678 #endif
3679
3680   if (tape.playing)
3681   {
3682     /* when playing a tape, eliminate all players who do not participate */
3683
3684 #if USE_NEW_PLAYER_ASSIGNMENTS
3685
3686     if (!game.team_mode)
3687     {
3688       for (i = 0; i < MAX_PLAYERS; i++)
3689       {
3690         if (stored_player[i].active &&
3691             !tape.player_participates[map_player_action[i]])
3692         {
3693           struct PlayerInfo *player = &stored_player[i];
3694           int jx = player->jx, jy = player->jy;
3695
3696 #if DEBUG_INIT_PLAYER
3697           if (options.debug)
3698             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3699 #endif
3700
3701           player->active = FALSE;
3702           StorePlayer[jx][jy] = 0;
3703           Feld[jx][jy] = EL_EMPTY;
3704         }
3705       }
3706     }
3707
3708 #else
3709
3710     for (i = 0; i < MAX_PLAYERS; i++)
3711     {
3712       if (stored_player[i].active &&
3713           !tape.player_participates[i])
3714       {
3715         struct PlayerInfo *player = &stored_player[i];
3716         int jx = player->jx, jy = player->jy;
3717
3718         player->active = FALSE;
3719         StorePlayer[jx][jy] = 0;
3720         Feld[jx][jy] = EL_EMPTY;
3721       }
3722     }
3723 #endif
3724   }
3725   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3726   {
3727     /* when in single player mode, eliminate all but the first active player */
3728
3729     for (i = 0; i < MAX_PLAYERS; i++)
3730     {
3731       if (stored_player[i].active)
3732       {
3733         for (j = i + 1; j < MAX_PLAYERS; j++)
3734         {
3735           if (stored_player[j].active)
3736           {
3737             struct PlayerInfo *player = &stored_player[j];
3738             int jx = player->jx, jy = player->jy;
3739
3740             player->active = FALSE;
3741             player->present = FALSE;
3742
3743             StorePlayer[jx][jy] = 0;
3744             Feld[jx][jy] = EL_EMPTY;
3745           }
3746         }
3747       }
3748     }
3749   }
3750
3751   /* when recording the game, store which players take part in the game */
3752   if (tape.recording)
3753   {
3754 #if USE_NEW_PLAYER_ASSIGNMENTS
3755     for (i = 0; i < MAX_PLAYERS; i++)
3756       if (stored_player[i].connected)
3757         tape.player_participates[i] = TRUE;
3758 #else
3759     for (i = 0; i < MAX_PLAYERS; i++)
3760       if (stored_player[i].active)
3761         tape.player_participates[i] = TRUE;
3762 #endif
3763   }
3764
3765 #if DEBUG_INIT_PLAYER
3766   if (options.debug)
3767   {
3768     printf("Player status after player assignment (final stage):\n");
3769
3770     for (i = 0; i < MAX_PLAYERS; i++)
3771     {
3772       struct PlayerInfo *player = &stored_player[i];
3773
3774       printf("- player %d: present == %d, connected == %d, active == %d",
3775              i + 1,
3776              player->present,
3777              player->connected,
3778              player->active);
3779
3780       if (local_player == player)
3781         printf(" (local player)");
3782
3783       printf("\n");
3784     }
3785   }
3786 #endif
3787
3788   if (BorderElement == EL_EMPTY)
3789   {
3790     SBX_Left = 0;
3791     SBX_Right = lev_fieldx - SCR_FIELDX;
3792     SBY_Upper = 0;
3793     SBY_Lower = lev_fieldy - SCR_FIELDY;
3794   }
3795   else
3796   {
3797     SBX_Left = -1;
3798     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3799     SBY_Upper = -1;
3800     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3801   }
3802
3803   if (full_lev_fieldx <= SCR_FIELDX)
3804     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3805   if (full_lev_fieldy <= SCR_FIELDY)
3806     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3807
3808   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3809     SBX_Left--;
3810   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3811     SBY_Upper--;
3812
3813   /* if local player not found, look for custom element that might create
3814      the player (make some assumptions about the right custom element) */
3815   if (!local_player->present)
3816   {
3817     int start_x = 0, start_y = 0;
3818     int found_rating = 0;
3819     int found_element = EL_UNDEFINED;
3820     int player_nr = local_player->index_nr;
3821
3822     SCAN_PLAYFIELD(x, y)
3823     {
3824       int element = Feld[x][y];
3825       int content;
3826       int xx, yy;
3827       boolean is_player;
3828
3829       if (level.use_start_element[player_nr] &&
3830           level.start_element[player_nr] == element &&
3831           found_rating < 4)
3832       {
3833         start_x = x;
3834         start_y = y;
3835
3836         found_rating = 4;
3837         found_element = element;
3838       }
3839
3840       if (!IS_CUSTOM_ELEMENT(element))
3841         continue;
3842
3843       if (CAN_CHANGE(element))
3844       {
3845         for (i = 0; i < element_info[element].num_change_pages; i++)
3846         {
3847           /* check for player created from custom element as single target */
3848           content = element_info[element].change_page[i].target_element;
3849           is_player = ELEM_IS_PLAYER(content);
3850
3851           if (is_player && (found_rating < 3 ||
3852                             (found_rating == 3 && element < found_element)))
3853           {
3854             start_x = x;
3855             start_y = y;
3856
3857             found_rating = 3;
3858             found_element = element;
3859           }
3860         }
3861       }
3862
3863       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3864       {
3865         /* check for player created from custom element as explosion content */
3866         content = element_info[element].content.e[xx][yy];
3867         is_player = ELEM_IS_PLAYER(content);
3868
3869         if (is_player && (found_rating < 2 ||
3870                           (found_rating == 2 && element < found_element)))
3871         {
3872           start_x = x + xx - 1;
3873           start_y = y + yy - 1;
3874
3875           found_rating = 2;
3876           found_element = element;
3877         }
3878
3879         if (!CAN_CHANGE(element))
3880           continue;
3881
3882         for (i = 0; i < element_info[element].num_change_pages; i++)
3883         {
3884           /* check for player created from custom element as extended target */
3885           content =
3886             element_info[element].change_page[i].target_content.e[xx][yy];
3887
3888           is_player = ELEM_IS_PLAYER(content);
3889
3890           if (is_player && (found_rating < 1 ||
3891                             (found_rating == 1 && element < found_element)))
3892           {
3893             start_x = x + xx - 1;
3894             start_y = y + yy - 1;
3895
3896             found_rating = 1;
3897             found_element = element;
3898           }
3899         }
3900       }
3901     }
3902
3903     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3904                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3905                 start_x - MIDPOSX);
3906
3907     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3908                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3909                 start_y - MIDPOSY);
3910   }
3911   else
3912   {
3913     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3914                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3915                 local_player->jx - MIDPOSX);
3916
3917     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3918                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3919                 local_player->jy - MIDPOSY);
3920   }
3921
3922   /* !!! FIX THIS (START) !!! */
3923   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3924   {
3925     InitGameEngine_EM();
3926   }
3927   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3928   {
3929     InitGameEngine_SP();
3930   }
3931   else
3932   {
3933     DrawLevel(REDRAW_FIELD);
3934     DrawAllPlayers();
3935
3936     /* after drawing the level, correct some elements */
3937     if (game.timegate_time_left == 0)
3938       CloseAllOpenTimegates();
3939   }
3940
3941   /* blit playfield from scroll buffer to normal back buffer for fading in */
3942   BlitScreenToBitmap(backbuffer);
3943   /* !!! FIX THIS (END) !!! */
3944
3945   DrawMaskedBorder(fade_mask);
3946
3947   FadeIn(fade_mask);
3948
3949 #if 1
3950   // full screen redraw is required at this point in the following cases:
3951   // - special editor door undrawn when game was started from level editor
3952   // - drawing area (playfield) was changed and has to be removed completely
3953   redraw_mask = REDRAW_ALL;
3954   BackToFront();
3955 #endif
3956
3957   if (!game.restart_level)
3958   {
3959     /* copy default game door content to main double buffer */
3960
3961     /* !!! CHECK AGAIN !!! */
3962     SetPanelBackground();
3963     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3964     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3965   }
3966
3967   SetPanelBackground();
3968   SetDrawBackgroundMask(REDRAW_DOOR_1);
3969
3970   UpdateAndDisplayGameControlValues();
3971
3972   if (!game.restart_level)
3973   {
3974     UnmapGameButtons();
3975     UnmapTapeButtons();
3976
3977     FreeGameButtons();
3978     CreateGameButtons();
3979
3980     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3981     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3982     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3983
3984     MapGameButtons();
3985     MapTapeButtons();
3986
3987     /* copy actual game door content to door double buffer for OpenDoor() */
3988     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3989
3990     OpenDoor(DOOR_OPEN_ALL);
3991
3992     PlaySound(SND_GAME_STARTING);
3993
3994     if (setup.sound_music)
3995       PlayLevelMusic();
3996
3997     KeyboardAutoRepeatOffUnlessAutoplay();
3998
3999 #if DEBUG_INIT_PLAYER
4000     if (options.debug)
4001     {
4002       printf("Player status (final):\n");
4003
4004       for (i = 0; i < MAX_PLAYERS; i++)
4005       {
4006         struct PlayerInfo *player = &stored_player[i];
4007
4008         printf("- player %d: present == %d, connected == %d, active == %d",
4009                i + 1,
4010                player->present,
4011                player->connected,
4012                player->active);
4013
4014         if (local_player == player)
4015           printf(" (local player)");
4016
4017         printf("\n");
4018       }
4019     }
4020 #endif
4021   }
4022
4023   UnmapAllGadgets();
4024
4025   MapGameButtons();
4026   MapTapeButtons();
4027
4028   if (!game.restart_level && !tape.playing)
4029   {
4030     LevelStats_incPlayed(level_nr);
4031
4032     SaveLevelSetup_SeriesInfo();
4033   }
4034
4035   game.restart_level = FALSE;
4036
4037   SaveEngineSnapshotToListInitial();
4038 }
4039
4040 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4041                         int actual_player_x, int actual_player_y)
4042 {
4043   /* this is used for non-R'n'D game engines to update certain engine values */
4044
4045   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4046   {
4047     actual_player_x = correctLevelPosX_EM(actual_player_x);
4048     actual_player_y = correctLevelPosY_EM(actual_player_y);
4049   }
4050
4051   /* needed to determine if sounds are played within the visible screen area */
4052   scroll_x = actual_scroll_x;
4053   scroll_y = actual_scroll_y;
4054
4055   /* needed to get player position for "follow finger" playing input method */
4056   local_player->jx = actual_player_x;
4057   local_player->jy = actual_player_y;
4058 }
4059
4060 void InitMovDir(int x, int y)
4061 {
4062   int i, element = Feld[x][y];
4063   static int xy[4][2] =
4064   {
4065     {  0, +1 },
4066     { +1,  0 },
4067     {  0, -1 },
4068     { -1,  0 }
4069   };
4070   static int direction[3][4] =
4071   {
4072     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4073     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4074     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4075   };
4076
4077   switch (element)
4078   {
4079     case EL_BUG_RIGHT:
4080     case EL_BUG_UP:
4081     case EL_BUG_LEFT:
4082     case EL_BUG_DOWN:
4083       Feld[x][y] = EL_BUG;
4084       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4085       break;
4086
4087     case EL_SPACESHIP_RIGHT:
4088     case EL_SPACESHIP_UP:
4089     case EL_SPACESHIP_LEFT:
4090     case EL_SPACESHIP_DOWN:
4091       Feld[x][y] = EL_SPACESHIP;
4092       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4093       break;
4094
4095     case EL_BD_BUTTERFLY_RIGHT:
4096     case EL_BD_BUTTERFLY_UP:
4097     case EL_BD_BUTTERFLY_LEFT:
4098     case EL_BD_BUTTERFLY_DOWN:
4099       Feld[x][y] = EL_BD_BUTTERFLY;
4100       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4101       break;
4102
4103     case EL_BD_FIREFLY_RIGHT:
4104     case EL_BD_FIREFLY_UP:
4105     case EL_BD_FIREFLY_LEFT:
4106     case EL_BD_FIREFLY_DOWN:
4107       Feld[x][y] = EL_BD_FIREFLY;
4108       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4109       break;
4110
4111     case EL_PACMAN_RIGHT:
4112     case EL_PACMAN_UP:
4113     case EL_PACMAN_LEFT:
4114     case EL_PACMAN_DOWN:
4115       Feld[x][y] = EL_PACMAN;
4116       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4117       break;
4118
4119     case EL_YAMYAM_LEFT:
4120     case EL_YAMYAM_RIGHT:
4121     case EL_YAMYAM_UP:
4122     case EL_YAMYAM_DOWN:
4123       Feld[x][y] = EL_YAMYAM;
4124       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4125       break;
4126
4127     case EL_SP_SNIKSNAK:
4128       MovDir[x][y] = MV_UP;
4129       break;
4130
4131     case EL_SP_ELECTRON:
4132       MovDir[x][y] = MV_LEFT;
4133       break;
4134
4135     case EL_MOLE_LEFT:
4136     case EL_MOLE_RIGHT:
4137     case EL_MOLE_UP:
4138     case EL_MOLE_DOWN:
4139       Feld[x][y] = EL_MOLE;
4140       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4141       break;
4142
4143     default:
4144       if (IS_CUSTOM_ELEMENT(element))
4145       {
4146         struct ElementInfo *ei = &element_info[element];
4147         int move_direction_initial = ei->move_direction_initial;
4148         int move_pattern = ei->move_pattern;
4149
4150         if (move_direction_initial == MV_START_PREVIOUS)
4151         {
4152           if (MovDir[x][y] != MV_NONE)
4153             return;
4154
4155           move_direction_initial = MV_START_AUTOMATIC;
4156         }
4157
4158         if (move_direction_initial == MV_START_RANDOM)
4159           MovDir[x][y] = 1 << RND(4);
4160         else if (move_direction_initial & MV_ANY_DIRECTION)
4161           MovDir[x][y] = move_direction_initial;
4162         else if (move_pattern == MV_ALL_DIRECTIONS ||
4163                  move_pattern == MV_TURNING_LEFT ||
4164                  move_pattern == MV_TURNING_RIGHT ||
4165                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4166                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4167                  move_pattern == MV_TURNING_RANDOM)
4168           MovDir[x][y] = 1 << RND(4);
4169         else if (move_pattern == MV_HORIZONTAL)
4170           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4171         else if (move_pattern == MV_VERTICAL)
4172           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4173         else if (move_pattern & MV_ANY_DIRECTION)
4174           MovDir[x][y] = element_info[element].move_pattern;
4175         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4176                  move_pattern == MV_ALONG_RIGHT_SIDE)
4177         {
4178           /* use random direction as default start direction */
4179           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4180             MovDir[x][y] = 1 << RND(4);
4181
4182           for (i = 0; i < NUM_DIRECTIONS; i++)
4183           {
4184             int x1 = x + xy[i][0];
4185             int y1 = y + xy[i][1];
4186
4187             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4188             {
4189               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4190                 MovDir[x][y] = direction[0][i];
4191               else
4192                 MovDir[x][y] = direction[1][i];
4193
4194               break;
4195             }
4196           }
4197         }                
4198       }
4199       else
4200       {
4201         MovDir[x][y] = 1 << RND(4);
4202
4203         if (element != EL_BUG &&
4204             element != EL_SPACESHIP &&
4205             element != EL_BD_BUTTERFLY &&
4206             element != EL_BD_FIREFLY)
4207           break;
4208
4209         for (i = 0; i < NUM_DIRECTIONS; i++)
4210         {
4211           int x1 = x + xy[i][0];
4212           int y1 = y + xy[i][1];
4213
4214           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4215           {
4216             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4217             {
4218               MovDir[x][y] = direction[0][i];
4219               break;
4220             }
4221             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4222                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4223             {
4224               MovDir[x][y] = direction[1][i];
4225               break;
4226             }
4227           }
4228         }
4229       }
4230       break;
4231   }
4232
4233   GfxDir[x][y] = MovDir[x][y];
4234 }
4235
4236 void InitAmoebaNr(int x, int y)
4237 {
4238   int i;
4239   int group_nr = AmoebeNachbarNr(x, y);
4240
4241   if (group_nr == 0)
4242   {
4243     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4244     {
4245       if (AmoebaCnt[i] == 0)
4246       {
4247         group_nr = i;
4248         break;
4249       }
4250     }
4251   }
4252
4253   AmoebaNr[x][y] = group_nr;
4254   AmoebaCnt[group_nr]++;
4255   AmoebaCnt2[group_nr]++;
4256 }
4257
4258 static void PlayerWins(struct PlayerInfo *player)
4259 {
4260   player->LevelSolved = TRUE;
4261   player->GameOver = TRUE;
4262
4263   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4264                          level.native_em_level->lev->score : player->score);
4265
4266   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4267                                       TimeLeft);
4268   player->LevelSolved_CountingScore = player->score_final;
4269 }
4270
4271 void GameWon()
4272 {
4273   static int time, time_final;
4274   static int score, score_final;
4275   static int game_over_delay_1 = 0;
4276   static int game_over_delay_2 = 0;
4277   int game_over_delay_value_1 = 50;
4278   int game_over_delay_value_2 = 50;
4279
4280   if (!local_player->LevelSolved_GameWon)
4281   {
4282     int i;
4283
4284     /* do not start end game actions before the player stops moving (to exit) */
4285     if (local_player->MovPos)
4286       return;
4287
4288     local_player->LevelSolved_GameWon = TRUE;
4289     local_player->LevelSolved_SaveTape = tape.recording;
4290     local_player->LevelSolved_SaveScore = !tape.playing;
4291
4292     if (!tape.playing)
4293     {
4294       LevelStats_incSolved(level_nr);
4295
4296       SaveLevelSetup_SeriesInfo();
4297     }
4298
4299     if (tape.auto_play)         /* tape might already be stopped here */
4300       tape.auto_play_level_solved = TRUE;
4301
4302     TapeStop();
4303
4304     game_over_delay_1 = game_over_delay_value_1;
4305     game_over_delay_2 = game_over_delay_value_2;
4306
4307     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4308     score = score_final = local_player->score_final;
4309
4310     if (TimeLeft > 0)
4311     {
4312       time_final = 0;
4313       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4314     }
4315     else if (game.no_time_limit && TimePlayed < 999)
4316     {
4317       time_final = 999;
4318       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4319     }
4320
4321     local_player->score_final = score_final;
4322
4323     if (level_editor_test_game)
4324     {
4325       time = time_final;
4326       score = score_final;
4327
4328       local_player->LevelSolved_CountingTime = time;
4329       local_player->LevelSolved_CountingScore = score;
4330
4331       game_panel_controls[GAME_PANEL_TIME].value = time;
4332       game_panel_controls[GAME_PANEL_SCORE].value = score;
4333
4334       DisplayGameControlValues();
4335     }
4336
4337     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4338     {
4339       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4340       {
4341         /* close exit door after last player */
4342         if ((AllPlayersGone &&
4343              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4344               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4345               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4346             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4347             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4348         {
4349           int element = Feld[ExitX][ExitY];
4350
4351           Feld[ExitX][ExitY] =
4352             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4353              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4354              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4355              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4356              EL_EM_STEEL_EXIT_CLOSING);
4357
4358           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4359         }
4360
4361         /* player disappears */
4362         DrawLevelField(ExitX, ExitY);
4363       }
4364
4365       for (i = 0; i < MAX_PLAYERS; i++)
4366       {
4367         struct PlayerInfo *player = &stored_player[i];
4368
4369         if (player->present)
4370         {
4371           RemovePlayer(player);
4372
4373           /* player disappears */
4374           DrawLevelField(player->jx, player->jy);
4375         }
4376       }
4377     }
4378
4379     PlaySound(SND_GAME_WINNING);
4380   }
4381
4382   if (game_over_delay_1 > 0)
4383   {
4384     game_over_delay_1--;
4385
4386     return;
4387   }
4388
4389   if (time != time_final)
4390   {
4391     int time_to_go = ABS(time_final - time);
4392     int time_count_dir = (time < time_final ? +1 : -1);
4393     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4394
4395     time  += time_count_steps * time_count_dir;
4396     score += time_count_steps * level.score[SC_TIME_BONUS];
4397
4398     local_player->LevelSolved_CountingTime = time;
4399     local_player->LevelSolved_CountingScore = score;
4400
4401     game_panel_controls[GAME_PANEL_TIME].value = time;
4402     game_panel_controls[GAME_PANEL_SCORE].value = score;
4403
4404     DisplayGameControlValues();
4405
4406     if (time == time_final)
4407       StopSound(SND_GAME_LEVELTIME_BONUS);
4408     else if (setup.sound_loops)
4409       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4410     else
4411       PlaySound(SND_GAME_LEVELTIME_BONUS);
4412
4413     return;
4414   }
4415
4416   local_player->LevelSolved_PanelOff = TRUE;
4417
4418   if (game_over_delay_2 > 0)
4419   {
4420     game_over_delay_2--;
4421
4422     return;
4423   }
4424
4425   GameEnd();
4426 }
4427
4428 void GameEnd()
4429 {
4430   int hi_pos;
4431   boolean raise_level = FALSE;
4432
4433   local_player->LevelSolved_GameEnd = TRUE;
4434
4435   if (!global.use_envelope_request)
4436     CloseDoor(DOOR_CLOSE_1);
4437
4438   if (local_player->LevelSolved_SaveTape)
4439   {
4440     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4441   }
4442
4443   CloseDoor(DOOR_CLOSE_ALL);
4444
4445   if (level_editor_test_game)
4446   {
4447     SetGameStatus(GAME_MODE_MAIN);
4448
4449     DrawMainMenu();
4450
4451     return;
4452   }
4453
4454   if (!local_player->LevelSolved_SaveScore)
4455   {
4456     SetGameStatus(GAME_MODE_MAIN);
4457
4458     DrawMainMenu();
4459
4460     return;
4461   }
4462
4463   if (level_nr == leveldir_current->handicap_level)
4464   {
4465     leveldir_current->handicap_level++;
4466
4467     SaveLevelSetup_SeriesInfo();
4468   }
4469
4470   if (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
4504   LoadScore(level_nr);
4505
4506   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4507       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4508     return -1;
4509
4510   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4511   {
4512     if (local_player->score_final > highscore[k].Score)
4513     {
4514       /* player has made it to the hall of fame */
4515
4516       if (k < MAX_SCORE_ENTRIES - 1)
4517       {
4518         int m = MAX_SCORE_ENTRIES - 1;
4519
4520 #ifdef ONE_PER_NAME
4521         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4522           if (strEqual(setup.player_name, highscore[l].Name))
4523             m = l;
4524         if (m == k)     /* player's new highscore overwrites his old one */
4525           goto put_into_list;
4526 #endif
4527
4528         for (l = m; l > k; l--)
4529         {
4530           strcpy(highscore[l].Name, highscore[l - 1].Name);
4531           highscore[l].Score = highscore[l - 1].Score;
4532         }
4533       }
4534
4535 #ifdef ONE_PER_NAME
4536       put_into_list:
4537 #endif
4538       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4539       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4540       highscore[k].Score = local_player->score_final; 
4541       position = k;
4542       break;
4543     }
4544
4545 #ifdef ONE_PER_NAME
4546     else if (!strncmp(setup.player_name, highscore[k].Name,
4547                       MAX_PLAYER_NAME_LEN))
4548       break;    /* player already there with a higher score */
4549 #endif
4550
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, boolean redraw)
4598 {
4599   int element = Feld[x][y];
4600   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4601   int last_gfx_frame = GfxFrame[x][y];
4602
4603   if (graphic_info[graphic].anim_global_sync)
4604     GfxFrame[x][y] = FrameCounter;
4605   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4606     GfxFrame[x][y] = CustomValue[x][y];
4607   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4608     GfxFrame[x][y] = element_info[element].collect_score;
4609   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4610     GfxFrame[x][y] = ChangeDelay[x][y];
4611
4612   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4613     DrawLevelGraphicAnimation(x, y, graphic);
4614 }
4615
4616 static void ResetGfxAnimation(int x, int y)
4617 {
4618   GfxAction[x][y] = ACTION_DEFAULT;
4619   GfxDir[x][y] = MovDir[x][y];
4620   GfxFrame[x][y] = 0;
4621
4622   ResetGfxFrame(x, y, FALSE);
4623 }
4624
4625 static void ResetRandomAnimationValue(int x, int y)
4626 {
4627   GfxRandom[x][y] = INIT_GFX_RANDOM();
4628 }
4629
4630 void InitMovingField(int x, int y, int direction)
4631 {
4632   int element = Feld[x][y];
4633   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4634   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4635   int newx = x + dx;
4636   int newy = y + dy;
4637   boolean is_moving_before, is_moving_after;
4638
4639   /* check if element was/is moving or being moved before/after mode change */
4640   is_moving_before = (WasJustMoving[x][y] != 0);
4641   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4642
4643   /* reset animation only for moving elements which change direction of moving
4644      or which just started or stopped moving
4645      (else CEs with property "can move" / "not moving" are reset each frame) */
4646   if (is_moving_before != is_moving_after ||
4647       direction != MovDir[x][y])
4648     ResetGfxAnimation(x, y);
4649
4650   MovDir[x][y] = direction;
4651   GfxDir[x][y] = direction;
4652
4653   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4654                      direction == MV_DOWN && CAN_FALL(element) ?
4655                      ACTION_FALLING : ACTION_MOVING);
4656
4657   /* this is needed for CEs with property "can move" / "not moving" */
4658
4659   if (is_moving_after)
4660   {
4661     if (Feld[newx][newy] == EL_EMPTY)
4662       Feld[newx][newy] = EL_BLOCKED;
4663
4664     MovDir[newx][newy] = MovDir[x][y];
4665
4666     CustomValue[newx][newy] = CustomValue[x][y];
4667
4668     GfxFrame[newx][newy] = GfxFrame[x][y];
4669     GfxRandom[newx][newy] = GfxRandom[x][y];
4670     GfxAction[newx][newy] = GfxAction[x][y];
4671     GfxDir[newx][newy] = GfxDir[x][y];
4672   }
4673 }
4674
4675 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4676 {
4677   int direction = MovDir[x][y];
4678   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4679   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4680
4681   *goes_to_x = newx;
4682   *goes_to_y = newy;
4683 }
4684
4685 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4686 {
4687   int oldx = x, oldy = y;
4688   int direction = MovDir[x][y];
4689
4690   if (direction == MV_LEFT)
4691     oldx++;
4692   else if (direction == MV_RIGHT)
4693     oldx--;
4694   else if (direction == MV_UP)
4695     oldy++;
4696   else if (direction == MV_DOWN)
4697     oldy--;
4698
4699   *comes_from_x = oldx;
4700   *comes_from_y = oldy;
4701 }
4702
4703 int MovingOrBlocked2Element(int x, int y)
4704 {
4705   int element = Feld[x][y];
4706
4707   if (element == EL_BLOCKED)
4708   {
4709     int oldx, oldy;
4710
4711     Blocked2Moving(x, y, &oldx, &oldy);
4712     return Feld[oldx][oldy];
4713   }
4714   else
4715     return element;
4716 }
4717
4718 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4719 {
4720   /* like MovingOrBlocked2Element(), but if element is moving
4721      and (x,y) is the field the moving element is just leaving,
4722      return EL_BLOCKED instead of the element value */
4723   int element = Feld[x][y];
4724
4725   if (IS_MOVING(x, y))
4726   {
4727     if (element == EL_BLOCKED)
4728     {
4729       int oldx, oldy;
4730
4731       Blocked2Moving(x, y, &oldx, &oldy);
4732       return Feld[oldx][oldy];
4733     }
4734     else
4735       return EL_BLOCKED;
4736   }
4737   else
4738     return element;
4739 }
4740
4741 static void RemoveField(int x, int y)
4742 {
4743   Feld[x][y] = EL_EMPTY;
4744
4745   MovPos[x][y] = 0;
4746   MovDir[x][y] = 0;
4747   MovDelay[x][y] = 0;
4748
4749   CustomValue[x][y] = 0;
4750
4751   AmoebaNr[x][y] = 0;
4752   ChangeDelay[x][y] = 0;
4753   ChangePage[x][y] = -1;
4754   Pushed[x][y] = FALSE;
4755
4756   GfxElement[x][y] = EL_UNDEFINED;
4757   GfxAction[x][y] = ACTION_DEFAULT;
4758   GfxDir[x][y] = MV_NONE;
4759 }
4760
4761 void RemoveMovingField(int x, int y)
4762 {
4763   int oldx = x, oldy = y, newx = x, newy = y;
4764   int element = Feld[x][y];
4765   int next_element = EL_UNDEFINED;
4766
4767   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4768     return;
4769
4770   if (IS_MOVING(x, y))
4771   {
4772     Moving2Blocked(x, y, &newx, &newy);
4773
4774     if (Feld[newx][newy] != EL_BLOCKED)
4775     {
4776       /* element is moving, but target field is not free (blocked), but
4777          already occupied by something different (example: acid pool);
4778          in this case, only remove the moving field, but not the target */
4779
4780       RemoveField(oldx, oldy);
4781
4782       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4783
4784       TEST_DrawLevelField(oldx, oldy);
4785
4786       return;
4787     }
4788   }
4789   else if (element == EL_BLOCKED)
4790   {
4791     Blocked2Moving(x, y, &oldx, &oldy);
4792     if (!IS_MOVING(oldx, oldy))
4793       return;
4794   }
4795
4796   if (element == EL_BLOCKED &&
4797       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4798        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4799        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4800        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4801        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4802        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4803     next_element = get_next_element(Feld[oldx][oldy]);
4804
4805   RemoveField(oldx, oldy);
4806   RemoveField(newx, newy);
4807
4808   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4809
4810   if (next_element != EL_UNDEFINED)
4811     Feld[oldx][oldy] = next_element;
4812
4813   TEST_DrawLevelField(oldx, oldy);
4814   TEST_DrawLevelField(newx, newy);
4815 }
4816
4817 void DrawDynamite(int x, int y)
4818 {
4819   int sx = SCREENX(x), sy = SCREENY(y);
4820   int graphic = el2img(Feld[x][y]);
4821   int frame;
4822
4823   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4824     return;
4825
4826   if (IS_WALKABLE_INSIDE(Back[x][y]))
4827     return;
4828
4829   if (Back[x][y])
4830     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4831   else if (Store[x][y])
4832     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4833
4834   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4835
4836   if (Back[x][y] || Store[x][y])
4837     DrawGraphicThruMask(sx, sy, graphic, frame);
4838   else
4839     DrawGraphic(sx, sy, graphic, frame);
4840 }
4841
4842 void CheckDynamite(int x, int y)
4843 {
4844   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4845   {
4846     MovDelay[x][y]--;
4847
4848     if (MovDelay[x][y] != 0)
4849     {
4850       DrawDynamite(x, y);
4851       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4852
4853       return;
4854     }
4855   }
4856
4857   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4858
4859   Bang(x, y);
4860 }
4861
4862 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4863 {
4864   boolean num_checked_players = 0;
4865   int i;
4866
4867   for (i = 0; i < MAX_PLAYERS; i++)
4868   {
4869     if (stored_player[i].active)
4870     {
4871       int sx = stored_player[i].jx;
4872       int sy = stored_player[i].jy;
4873
4874       if (num_checked_players == 0)
4875       {
4876         *sx1 = *sx2 = sx;
4877         *sy1 = *sy2 = sy;
4878       }
4879       else
4880       {
4881         *sx1 = MIN(*sx1, sx);
4882         *sy1 = MIN(*sy1, sy);
4883         *sx2 = MAX(*sx2, sx);
4884         *sy2 = MAX(*sy2, sy);
4885       }
4886
4887       num_checked_players++;
4888     }
4889   }
4890 }
4891
4892 static boolean checkIfAllPlayersFitToScreen_RND()
4893 {
4894   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4895
4896   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4897
4898   return (sx2 - sx1 < SCR_FIELDX &&
4899           sy2 - sy1 < SCR_FIELDY);
4900 }
4901
4902 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4903 {
4904   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4905
4906   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4907
4908   *sx = (sx1 + sx2) / 2;
4909   *sy = (sy1 + sy2) / 2;
4910 }
4911
4912 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4913                         boolean center_screen, boolean quick_relocation)
4914 {
4915   unsigned int frame_delay_value_old = GetVideoFrameDelay();
4916   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4917   boolean no_delay = (tape.warp_forward);
4918   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4919   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4920   int new_scroll_x, new_scroll_y;
4921
4922   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4923   {
4924     /* case 1: quick relocation inside visible screen (without scrolling) */
4925
4926     RedrawPlayfield();
4927
4928     return;
4929   }
4930
4931   if (!level.shifted_relocation || center_screen)
4932   {
4933     /* relocation _with_ centering of screen */
4934
4935     new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4936                     x > SBX_Right + MIDPOSX ? SBX_Right :
4937                     x - MIDPOSX);
4938
4939     new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4940                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4941                     y - MIDPOSY);
4942   }
4943   else
4944   {
4945     /* relocation _without_ centering of screen */
4946
4947     int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4948                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
4949                            old_x - MIDPOSX);
4950
4951     int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4952                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4953                            old_y - MIDPOSY);
4954
4955     int offset_x = x + (scroll_x - center_scroll_x);
4956     int offset_y = y + (scroll_y - center_scroll_y);
4957
4958     new_scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4959                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4960                     offset_x - MIDPOSX);
4961
4962     new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4963                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4964                     offset_y - MIDPOSY);
4965   }
4966
4967   if (quick_relocation)
4968   {
4969     /* case 2: quick relocation (redraw without visible scrolling) */
4970
4971     scroll_x = new_scroll_x;
4972     scroll_y = new_scroll_y;
4973
4974     RedrawPlayfield();
4975
4976     return;
4977   }
4978
4979   /* case 3: visible relocation (with scrolling to new position) */
4980
4981   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4982
4983   SetVideoFrameDelay(wait_delay_value);
4984
4985   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4986   {
4987     int dx = 0, dy = 0;
4988     int fx = FX, fy = FY;
4989
4990     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4991     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4992
4993     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4994       break;
4995
4996     scroll_x -= dx;
4997     scroll_y -= dy;
4998
4999     fx += dx * TILEX / 2;
5000     fy += dy * TILEY / 2;
5001
5002     ScrollLevel(dx, dy);
5003     DrawAllPlayers();
5004
5005     /* scroll in two steps of half tile size to make things smoother */
5006     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5007
5008     /* scroll second step to align at full tile size */
5009     BlitScreenToBitmap(window);
5010   }
5011
5012   DrawAllPlayers();
5013   BackToFront();
5014
5015   SetVideoFrameDelay(frame_delay_value_old);
5016 }
5017
5018 void RelocatePlayer(int jx, int jy, int el_player_raw)
5019 {
5020   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5021   int player_nr = GET_PLAYER_NR(el_player);
5022   struct PlayerInfo *player = &stored_player[player_nr];
5023   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5024   boolean no_delay = (tape.warp_forward);
5025   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5026   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5027   int old_jx = player->jx;
5028   int old_jy = player->jy;
5029   int old_element = Feld[old_jx][old_jy];
5030   int element = Feld[jx][jy];
5031   boolean player_relocated = (old_jx != jx || old_jy != jy);
5032
5033   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5034   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5035   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5036   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5037   int leave_side_horiz = move_dir_horiz;
5038   int leave_side_vert  = move_dir_vert;
5039   int enter_side = enter_side_horiz | enter_side_vert;
5040   int leave_side = leave_side_horiz | leave_side_vert;
5041
5042   if (player->GameOver)         /* do not reanimate dead player */
5043     return;
5044
5045   if (!player_relocated)        /* no need to relocate the player */
5046     return;
5047
5048   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5049   {
5050     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5051     DrawLevelField(jx, jy);
5052   }
5053
5054   if (player->present)
5055   {
5056     while (player->MovPos)
5057     {
5058       ScrollPlayer(player, SCROLL_GO_ON);
5059       ScrollScreen(NULL, SCROLL_GO_ON);
5060
5061       AdvanceFrameAndPlayerCounters(player->index_nr);
5062
5063       DrawPlayer(player);
5064
5065       BackToFront_WithFrameDelay(wait_delay_value);
5066     }
5067
5068     DrawPlayer(player);         /* needed here only to cleanup last field */
5069     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5070
5071     player->is_moving = FALSE;
5072   }
5073
5074   if (IS_CUSTOM_ELEMENT(old_element))
5075     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5076                                CE_LEFT_BY_PLAYER,
5077                                player->index_bit, leave_side);
5078
5079   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5080                                       CE_PLAYER_LEAVES_X,
5081                                       player->index_bit, leave_side);
5082
5083   Feld[jx][jy] = el_player;
5084   InitPlayerField(jx, jy, el_player, TRUE);
5085
5086   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5087      possible that the relocation target field did not contain a player element,
5088      but a walkable element, to which the new player was relocated -- in this
5089      case, restore that (already initialized!) element on the player field */
5090   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5091   {
5092     Feld[jx][jy] = element;     /* restore previously existing element */
5093   }
5094
5095   /* only visually relocate centered player */
5096   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5097                      FALSE, level.instant_relocation);
5098
5099   TestIfPlayerTouchesBadThing(jx, jy);
5100   TestIfPlayerTouchesCustomElement(jx, jy);
5101
5102   if (IS_CUSTOM_ELEMENT(element))
5103     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5104                                player->index_bit, enter_side);
5105
5106   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5107                                       player->index_bit, enter_side);
5108
5109   if (player->is_switching)
5110   {
5111     /* ensure that relocation while still switching an element does not cause
5112        a new element to be treated as also switched directly after relocation
5113        (this is important for teleporter switches that teleport the player to
5114        a place where another teleporter switch is in the same direction, which
5115        would then incorrectly be treated as immediately switched before the
5116        direction key that caused the switch was released) */
5117
5118     player->switch_x += jx - old_jx;
5119     player->switch_y += jy - old_jy;
5120   }
5121 }
5122
5123 void Explode(int ex, int ey, int phase, int mode)
5124 {
5125   int x, y;
5126   int last_phase;
5127   int border_element;
5128
5129   /* !!! eliminate this variable !!! */
5130   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5131
5132   if (game.explosions_delayed)
5133   {
5134     ExplodeField[ex][ey] = mode;
5135     return;
5136   }
5137
5138   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5139   {
5140     int center_element = Feld[ex][ey];
5141     int artwork_element, explosion_element;     /* set these values later */
5142
5143     /* remove things displayed in background while burning dynamite */
5144     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5145       Back[ex][ey] = 0;
5146
5147     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5148     {
5149       /* put moving element to center field (and let it explode there) */
5150       center_element = MovingOrBlocked2Element(ex, ey);
5151       RemoveMovingField(ex, ey);
5152       Feld[ex][ey] = center_element;
5153     }
5154
5155     /* now "center_element" is finally determined -- set related values now */
5156     artwork_element = center_element;           /* for custom player artwork */
5157     explosion_element = center_element;         /* for custom player artwork */
5158
5159     if (IS_PLAYER(ex, ey))
5160     {
5161       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5162
5163       artwork_element = stored_player[player_nr].artwork_element;
5164
5165       if (level.use_explosion_element[player_nr])
5166       {
5167         explosion_element = level.explosion_element[player_nr];
5168         artwork_element = explosion_element;
5169       }
5170     }
5171
5172     if (mode == EX_TYPE_NORMAL ||
5173         mode == EX_TYPE_CENTER ||
5174         mode == EX_TYPE_CROSS)
5175       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5176
5177     last_phase = element_info[explosion_element].explosion_delay + 1;
5178
5179     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5180     {
5181       int xx = x - ex + 1;
5182       int yy = y - ey + 1;
5183       int element;
5184
5185       if (!IN_LEV_FIELD(x, y) ||
5186           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5187           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5188         continue;
5189
5190       element = Feld[x][y];
5191
5192       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5193       {
5194         element = MovingOrBlocked2Element(x, y);
5195
5196         if (!IS_EXPLOSION_PROOF(element))
5197           RemoveMovingField(x, y);
5198       }
5199
5200       /* indestructible elements can only explode in center (but not flames) */
5201       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5202                                            mode == EX_TYPE_BORDER)) ||
5203           element == EL_FLAMES)
5204         continue;
5205
5206       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5207          behaviour, for example when touching a yamyam that explodes to rocks
5208          with active deadly shield, a rock is created under the player !!! */
5209       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5210 #if 0
5211       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5212           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5213            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5214 #else
5215       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5216 #endif
5217       {
5218         if (IS_ACTIVE_BOMB(element))
5219         {
5220           /* re-activate things under the bomb like gate or penguin */
5221           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5222           Back[x][y] = 0;
5223         }
5224
5225         continue;
5226       }
5227
5228       /* save walkable background elements while explosion on same tile */
5229       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5230           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5231         Back[x][y] = element;
5232
5233       /* ignite explodable elements reached by other explosion */
5234       if (element == EL_EXPLOSION)
5235         element = Store2[x][y];
5236
5237       if (AmoebaNr[x][y] &&
5238           (element == EL_AMOEBA_FULL ||
5239            element == EL_BD_AMOEBA ||
5240            element == EL_AMOEBA_GROWING))
5241       {
5242         AmoebaCnt[AmoebaNr[x][y]]--;
5243         AmoebaCnt2[AmoebaNr[x][y]]--;
5244       }
5245
5246       RemoveField(x, y);
5247
5248       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5249       {
5250         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5251
5252         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5253
5254         if (PLAYERINFO(ex, ey)->use_murphy)
5255           Store[x][y] = EL_EMPTY;
5256       }
5257
5258       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5259          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5260       else if (ELEM_IS_PLAYER(center_element))
5261         Store[x][y] = EL_EMPTY;
5262       else if (center_element == EL_YAMYAM)
5263         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5264       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5265         Store[x][y] = element_info[center_element].content.e[xx][yy];
5266 #if 1
5267       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5268          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5269          otherwise) -- FIX THIS !!! */
5270       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5271         Store[x][y] = element_info[element].content.e[1][1];
5272 #else
5273       else if (!CAN_EXPLODE(element))
5274         Store[x][y] = element_info[element].content.e[1][1];
5275 #endif
5276       else
5277         Store[x][y] = EL_EMPTY;
5278
5279       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5280           center_element == EL_AMOEBA_TO_DIAMOND)
5281         Store2[x][y] = element;
5282
5283       Feld[x][y] = EL_EXPLOSION;
5284       GfxElement[x][y] = artwork_element;
5285
5286       ExplodePhase[x][y] = 1;
5287       ExplodeDelay[x][y] = last_phase;
5288
5289       Stop[x][y] = TRUE;
5290     }
5291
5292     if (center_element == EL_YAMYAM)
5293       game.yamyam_content_nr =
5294         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5295
5296     return;
5297   }
5298
5299   if (Stop[ex][ey])
5300     return;
5301
5302   x = ex;
5303   y = ey;
5304
5305   if (phase == 1)
5306     GfxFrame[x][y] = 0;         /* restart explosion animation */
5307
5308   last_phase = ExplodeDelay[x][y];
5309
5310   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5311
5312   /* this can happen if the player leaves an explosion just in time */
5313   if (GfxElement[x][y] == EL_UNDEFINED)
5314     GfxElement[x][y] = EL_EMPTY;
5315
5316   border_element = Store2[x][y];
5317   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5318     border_element = StorePlayer[x][y];
5319
5320   if (phase == element_info[border_element].ignition_delay ||
5321       phase == last_phase)
5322   {
5323     boolean border_explosion = FALSE;
5324
5325     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5326         !PLAYER_EXPLOSION_PROTECTED(x, y))
5327     {
5328       KillPlayerUnlessExplosionProtected(x, y);
5329       border_explosion = TRUE;
5330     }
5331     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5332     {
5333       Feld[x][y] = Store2[x][y];
5334       Store2[x][y] = 0;
5335       Bang(x, y);
5336       border_explosion = TRUE;
5337     }
5338     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5339     {
5340       AmoebeUmwandeln(x, y);
5341       Store2[x][y] = 0;
5342       border_explosion = TRUE;
5343     }
5344
5345     /* if an element just explodes due to another explosion (chain-reaction),
5346        do not immediately end the new explosion when it was the last frame of
5347        the explosion (as it would be done in the following "if"-statement!) */
5348     if (border_explosion && phase == last_phase)
5349       return;
5350   }
5351
5352   if (phase == last_phase)
5353   {
5354     int element;
5355
5356     element = Feld[x][y] = Store[x][y];
5357     Store[x][y] = Store2[x][y] = 0;
5358     GfxElement[x][y] = EL_UNDEFINED;
5359
5360     /* player can escape from explosions and might therefore be still alive */
5361     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5362         element <= EL_PLAYER_IS_EXPLODING_4)
5363     {
5364       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5365       int explosion_element = EL_PLAYER_1 + player_nr;
5366       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5367       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5368
5369       if (level.use_explosion_element[player_nr])
5370         explosion_element = level.explosion_element[player_nr];
5371
5372       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5373                     element_info[explosion_element].content.e[xx][yy]);
5374     }
5375
5376     /* restore probably existing indestructible background element */
5377     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5378       element = Feld[x][y] = Back[x][y];
5379     Back[x][y] = 0;
5380
5381     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5382     GfxDir[x][y] = MV_NONE;
5383     ChangeDelay[x][y] = 0;
5384     ChangePage[x][y] = -1;
5385
5386     CustomValue[x][y] = 0;
5387
5388     InitField_WithBug2(x, y, FALSE);
5389
5390     TEST_DrawLevelField(x, y);
5391
5392     TestIfElementTouchesCustomElement(x, y);
5393
5394     if (GFX_CRUMBLED(element))
5395       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5396
5397     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5398       StorePlayer[x][y] = 0;
5399
5400     if (ELEM_IS_PLAYER(element))
5401       RelocatePlayer(x, y, element);
5402   }
5403   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5404   {
5405     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5406     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5407
5408     if (phase == delay)
5409       TEST_DrawLevelFieldCrumbled(x, y);
5410
5411     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5412     {
5413       DrawLevelElement(x, y, Back[x][y]);
5414       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5415     }
5416     else if (IS_WALKABLE_UNDER(Back[x][y]))
5417     {
5418       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5419       DrawLevelElementThruMask(x, y, Back[x][y]);
5420     }
5421     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5422       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5423   }
5424 }
5425
5426 void DynaExplode(int ex, int ey)
5427 {
5428   int i, j;
5429   int dynabomb_element = Feld[ex][ey];
5430   int dynabomb_size = 1;
5431   boolean dynabomb_xl = FALSE;
5432   struct PlayerInfo *player;
5433   static int xy[4][2] =
5434   {
5435     { 0, -1 },
5436     { -1, 0 },
5437     { +1, 0 },
5438     { 0, +1 }
5439   };
5440
5441   if (IS_ACTIVE_BOMB(dynabomb_element))
5442   {
5443     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5444     dynabomb_size = player->dynabomb_size;
5445     dynabomb_xl = player->dynabomb_xl;
5446     player->dynabombs_left++;
5447   }
5448
5449   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5450
5451   for (i = 0; i < NUM_DIRECTIONS; i++)
5452   {
5453     for (j = 1; j <= dynabomb_size; j++)
5454     {
5455       int x = ex + j * xy[i][0];
5456       int y = ey + j * xy[i][1];
5457       int element;
5458
5459       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5460         break;
5461
5462       element = Feld[x][y];
5463
5464       /* do not restart explosions of fields with active bombs */
5465       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5466         continue;
5467
5468       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5469
5470       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5471           !IS_DIGGABLE(element) && !dynabomb_xl)
5472         break;
5473     }
5474   }
5475 }
5476
5477 void Bang(int x, int y)
5478 {
5479   int element = MovingOrBlocked2Element(x, y);
5480   int explosion_type = EX_TYPE_NORMAL;
5481
5482   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5483   {
5484     struct PlayerInfo *player = PLAYERINFO(x, y);
5485
5486     element = Feld[x][y] = player->initial_element;
5487
5488     if (level.use_explosion_element[player->index_nr])
5489     {
5490       int explosion_element = level.explosion_element[player->index_nr];
5491
5492       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5493         explosion_type = EX_TYPE_CROSS;
5494       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5495         explosion_type = EX_TYPE_CENTER;
5496     }
5497   }
5498
5499   switch (element)
5500   {
5501     case EL_BUG:
5502     case EL_SPACESHIP:
5503     case EL_BD_BUTTERFLY:
5504     case EL_BD_FIREFLY:
5505     case EL_YAMYAM:
5506     case EL_DARK_YAMYAM:
5507     case EL_ROBOT:
5508     case EL_PACMAN:
5509     case EL_MOLE:
5510       RaiseScoreElement(element);
5511       break;
5512
5513     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5514     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5515     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5516     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5517     case EL_DYNABOMB_INCREASE_NUMBER:
5518     case EL_DYNABOMB_INCREASE_SIZE:
5519     case EL_DYNABOMB_INCREASE_POWER:
5520       explosion_type = EX_TYPE_DYNA;
5521       break;
5522
5523     case EL_DC_LANDMINE:
5524       explosion_type = EX_TYPE_CENTER;
5525       break;
5526
5527     case EL_PENGUIN:
5528     case EL_LAMP:
5529     case EL_LAMP_ACTIVE:
5530     case EL_AMOEBA_TO_DIAMOND:
5531       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5532         explosion_type = EX_TYPE_CENTER;
5533       break;
5534
5535     default:
5536       if (element_info[element].explosion_type == EXPLODES_CROSS)
5537         explosion_type = EX_TYPE_CROSS;
5538       else if (element_info[element].explosion_type == EXPLODES_1X1)
5539         explosion_type = EX_TYPE_CENTER;
5540       break;
5541   }
5542
5543   if (explosion_type == EX_TYPE_DYNA)
5544     DynaExplode(x, y);
5545   else
5546     Explode(x, y, EX_PHASE_START, explosion_type);
5547
5548   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5549 }
5550
5551 void SplashAcid(int x, int y)
5552 {
5553   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5554       (!IN_LEV_FIELD(x - 1, y - 2) ||
5555        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5556     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5557
5558   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5559       (!IN_LEV_FIELD(x + 1, y - 2) ||
5560        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5561     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5562
5563   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5564 }
5565
5566 static void InitBeltMovement()
5567 {
5568   static int belt_base_element[4] =
5569   {
5570     EL_CONVEYOR_BELT_1_LEFT,
5571     EL_CONVEYOR_BELT_2_LEFT,
5572     EL_CONVEYOR_BELT_3_LEFT,
5573     EL_CONVEYOR_BELT_4_LEFT
5574   };
5575   static int belt_base_active_element[4] =
5576   {
5577     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5578     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5579     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5580     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5581   };
5582
5583   int x, y, i, j;
5584
5585   /* set frame order for belt animation graphic according to belt direction */
5586   for (i = 0; i < NUM_BELTS; i++)
5587   {
5588     int belt_nr = i;
5589
5590     for (j = 0; j < NUM_BELT_PARTS; j++)
5591     {
5592       int element = belt_base_active_element[belt_nr] + j;
5593       int graphic_1 = el2img(element);
5594       int graphic_2 = el2panelimg(element);
5595
5596       if (game.belt_dir[i] == MV_LEFT)
5597       {
5598         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5599         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5600       }
5601       else
5602       {
5603         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5604         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5605       }
5606     }
5607   }
5608
5609   SCAN_PLAYFIELD(x, y)
5610   {
5611     int element = Feld[x][y];
5612
5613     for (i = 0; i < NUM_BELTS; i++)
5614     {
5615       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5616       {
5617         int e_belt_nr = getBeltNrFromBeltElement(element);
5618         int belt_nr = i;
5619
5620         if (e_belt_nr == belt_nr)
5621         {
5622           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5623
5624           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5625         }
5626       }
5627     }
5628   }
5629 }
5630
5631 static void ToggleBeltSwitch(int x, int y)
5632 {
5633   static int belt_base_element[4] =
5634   {
5635     EL_CONVEYOR_BELT_1_LEFT,
5636     EL_CONVEYOR_BELT_2_LEFT,
5637     EL_CONVEYOR_BELT_3_LEFT,
5638     EL_CONVEYOR_BELT_4_LEFT
5639   };
5640   static int belt_base_active_element[4] =
5641   {
5642     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5643     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5644     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5645     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5646   };
5647   static int belt_base_switch_element[4] =
5648   {
5649     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5650     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5651     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5652     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5653   };
5654   static int belt_move_dir[4] =
5655   {
5656     MV_LEFT,
5657     MV_NONE,
5658     MV_RIGHT,
5659     MV_NONE,
5660   };
5661
5662   int element = Feld[x][y];
5663   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5664   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5665   int belt_dir = belt_move_dir[belt_dir_nr];
5666   int xx, yy, i;
5667
5668   if (!IS_BELT_SWITCH(element))
5669     return;
5670
5671   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5672   game.belt_dir[belt_nr] = belt_dir;
5673
5674   if (belt_dir_nr == 3)
5675     belt_dir_nr = 1;
5676
5677   /* set frame order for belt animation graphic according to belt direction */
5678   for (i = 0; i < NUM_BELT_PARTS; i++)
5679   {
5680     int element = belt_base_active_element[belt_nr] + i;
5681     int graphic_1 = el2img(element);
5682     int graphic_2 = el2panelimg(element);
5683
5684     if (belt_dir == MV_LEFT)
5685     {
5686       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5687       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5688     }
5689     else
5690     {
5691       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5692       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5693     }
5694   }
5695
5696   SCAN_PLAYFIELD(xx, yy)
5697   {
5698     int element = Feld[xx][yy];
5699
5700     if (IS_BELT_SWITCH(element))
5701     {
5702       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5703
5704       if (e_belt_nr == belt_nr)
5705       {
5706         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5707         TEST_DrawLevelField(xx, yy);
5708       }
5709     }
5710     else if (IS_BELT(element) && belt_dir != MV_NONE)
5711     {
5712       int e_belt_nr = getBeltNrFromBeltElement(element);
5713
5714       if (e_belt_nr == belt_nr)
5715       {
5716         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5717
5718         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5719         TEST_DrawLevelField(xx, yy);
5720       }
5721     }
5722     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5723     {
5724       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5725
5726       if (e_belt_nr == belt_nr)
5727       {
5728         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5729
5730         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5731         TEST_DrawLevelField(xx, yy);
5732       }
5733     }
5734   }
5735 }
5736
5737 static void ToggleSwitchgateSwitch(int x, int y)
5738 {
5739   int xx, yy;
5740
5741   game.switchgate_pos = !game.switchgate_pos;
5742
5743   SCAN_PLAYFIELD(xx, yy)
5744   {
5745     int element = Feld[xx][yy];
5746
5747     if (element == EL_SWITCHGATE_SWITCH_UP)
5748     {
5749       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5750       TEST_DrawLevelField(xx, yy);
5751     }
5752     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5753     {
5754       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5755       TEST_DrawLevelField(xx, yy);
5756     }
5757     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5758     {
5759       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5760       TEST_DrawLevelField(xx, yy);
5761     }
5762     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5763     {
5764       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5765       TEST_DrawLevelField(xx, yy);
5766     }
5767     else if (element == EL_SWITCHGATE_OPEN ||
5768              element == EL_SWITCHGATE_OPENING)
5769     {
5770       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5771
5772       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5773     }
5774     else if (element == EL_SWITCHGATE_CLOSED ||
5775              element == EL_SWITCHGATE_CLOSING)
5776     {
5777       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5778
5779       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5780     }
5781   }
5782 }
5783
5784 static int getInvisibleActiveFromInvisibleElement(int element)
5785 {
5786   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5787           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5788           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5789           element);
5790 }
5791
5792 static int getInvisibleFromInvisibleActiveElement(int element)
5793 {
5794   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5795           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5796           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5797           element);
5798 }
5799
5800 static void RedrawAllLightSwitchesAndInvisibleElements()
5801 {
5802   int x, y;
5803
5804   SCAN_PLAYFIELD(x, y)
5805   {
5806     int element = Feld[x][y];
5807
5808     if (element == EL_LIGHT_SWITCH &&
5809         game.light_time_left > 0)
5810     {
5811       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5812       TEST_DrawLevelField(x, y);
5813     }
5814     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5815              game.light_time_left == 0)
5816     {
5817       Feld[x][y] = EL_LIGHT_SWITCH;
5818       TEST_DrawLevelField(x, y);
5819     }
5820     else if (element == EL_EMC_DRIPPER &&
5821              game.light_time_left > 0)
5822     {
5823       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5824       TEST_DrawLevelField(x, y);
5825     }
5826     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5827              game.light_time_left == 0)
5828     {
5829       Feld[x][y] = EL_EMC_DRIPPER;
5830       TEST_DrawLevelField(x, y);
5831     }
5832     else if (element == EL_INVISIBLE_STEELWALL ||
5833              element == EL_INVISIBLE_WALL ||
5834              element == EL_INVISIBLE_SAND)
5835     {
5836       if (game.light_time_left > 0)
5837         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5838
5839       TEST_DrawLevelField(x, y);
5840
5841       /* uncrumble neighbour fields, if needed */
5842       if (element == EL_INVISIBLE_SAND)
5843         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5844     }
5845     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5846              element == EL_INVISIBLE_WALL_ACTIVE ||
5847              element == EL_INVISIBLE_SAND_ACTIVE)
5848     {
5849       if (game.light_time_left == 0)
5850         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5851
5852       TEST_DrawLevelField(x, y);
5853
5854       /* re-crumble neighbour fields, if needed */
5855       if (element == EL_INVISIBLE_SAND)
5856         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5857     }
5858   }
5859 }
5860
5861 static void RedrawAllInvisibleElementsForLenses()
5862 {
5863   int x, y;
5864
5865   SCAN_PLAYFIELD(x, y)
5866   {
5867     int element = Feld[x][y];
5868
5869     if (element == EL_EMC_DRIPPER &&
5870         game.lenses_time_left > 0)
5871     {
5872       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5873       TEST_DrawLevelField(x, y);
5874     }
5875     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5876              game.lenses_time_left == 0)
5877     {
5878       Feld[x][y] = EL_EMC_DRIPPER;
5879       TEST_DrawLevelField(x, y);
5880     }
5881     else if (element == EL_INVISIBLE_STEELWALL ||
5882              element == EL_INVISIBLE_WALL ||
5883              element == EL_INVISIBLE_SAND)
5884     {
5885       if (game.lenses_time_left > 0)
5886         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5887
5888       TEST_DrawLevelField(x, y);
5889
5890       /* uncrumble neighbour fields, if needed */
5891       if (element == EL_INVISIBLE_SAND)
5892         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5893     }
5894     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5895              element == EL_INVISIBLE_WALL_ACTIVE ||
5896              element == EL_INVISIBLE_SAND_ACTIVE)
5897     {
5898       if (game.lenses_time_left == 0)
5899         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5900
5901       TEST_DrawLevelField(x, y);
5902
5903       /* re-crumble neighbour fields, if needed */
5904       if (element == EL_INVISIBLE_SAND)
5905         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5906     }
5907   }
5908 }
5909
5910 static void RedrawAllInvisibleElementsForMagnifier()
5911 {
5912   int x, y;
5913
5914   SCAN_PLAYFIELD(x, y)
5915   {
5916     int element = Feld[x][y];
5917
5918     if (element == EL_EMC_FAKE_GRASS &&
5919         game.magnify_time_left > 0)
5920     {
5921       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5922       TEST_DrawLevelField(x, y);
5923     }
5924     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5925              game.magnify_time_left == 0)
5926     {
5927       Feld[x][y] = EL_EMC_FAKE_GRASS;
5928       TEST_DrawLevelField(x, y);
5929     }
5930     else if (IS_GATE_GRAY(element) &&
5931              game.magnify_time_left > 0)
5932     {
5933       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5934                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5935                     IS_EM_GATE_GRAY(element) ?
5936                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5937                     IS_EMC_GATE_GRAY(element) ?
5938                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5939                     IS_DC_GATE_GRAY(element) ?
5940                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5941                     element);
5942       TEST_DrawLevelField(x, y);
5943     }
5944     else if (IS_GATE_GRAY_ACTIVE(element) &&
5945              game.magnify_time_left == 0)
5946     {
5947       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5948                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5949                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5950                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5951                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5952                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5953                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5954                     EL_DC_GATE_WHITE_GRAY :
5955                     element);
5956       TEST_DrawLevelField(x, y);
5957     }
5958   }
5959 }
5960
5961 static void ToggleLightSwitch(int x, int y)
5962 {
5963   int element = Feld[x][y];
5964
5965   game.light_time_left =
5966     (element == EL_LIGHT_SWITCH ?
5967      level.time_light * FRAMES_PER_SECOND : 0);
5968
5969   RedrawAllLightSwitchesAndInvisibleElements();
5970 }
5971
5972 static void ActivateTimegateSwitch(int x, int y)
5973 {
5974   int xx, yy;
5975
5976   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5977
5978   SCAN_PLAYFIELD(xx, yy)
5979   {
5980     int element = Feld[xx][yy];
5981
5982     if (element == EL_TIMEGATE_CLOSED ||
5983         element == EL_TIMEGATE_CLOSING)
5984     {
5985       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5986       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5987     }
5988
5989     /*
5990     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5991     {
5992       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5993       TEST_DrawLevelField(xx, yy);
5994     }
5995     */
5996
5997   }
5998
5999   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6000                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6001 }
6002
6003 void Impact(int x, int y)
6004 {
6005   boolean last_line = (y == lev_fieldy - 1);
6006   boolean object_hit = FALSE;
6007   boolean impact = (last_line || object_hit);
6008   int element = Feld[x][y];
6009   int smashed = EL_STEELWALL;
6010
6011   if (!last_line)       /* check if element below was hit */
6012   {
6013     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6014       return;
6015
6016     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6017                                          MovDir[x][y + 1] != MV_DOWN ||
6018                                          MovPos[x][y + 1] <= TILEY / 2));
6019
6020     /* do not smash moving elements that left the smashed field in time */
6021     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6022         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6023       object_hit = FALSE;
6024
6025 #if USE_QUICKSAND_IMPACT_BUGFIX
6026     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6027     {
6028       RemoveMovingField(x, y + 1);
6029       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6030       Feld[x][y + 2] = EL_ROCK;
6031       TEST_DrawLevelField(x, y + 2);
6032
6033       object_hit = TRUE;
6034     }
6035
6036     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6037     {
6038       RemoveMovingField(x, y + 1);
6039       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6040       Feld[x][y + 2] = EL_ROCK;
6041       TEST_DrawLevelField(x, y + 2);
6042
6043       object_hit = TRUE;
6044     }
6045 #endif
6046
6047     if (object_hit)
6048       smashed = MovingOrBlocked2Element(x, y + 1);
6049
6050     impact = (last_line || object_hit);
6051   }
6052
6053   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6054   {
6055     SplashAcid(x, y + 1);
6056     return;
6057   }
6058
6059   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6060   /* only reset graphic animation if graphic really changes after impact */
6061   if (impact &&
6062       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6063   {
6064     ResetGfxAnimation(x, y);
6065     TEST_DrawLevelField(x, y);
6066   }
6067
6068   if (impact && CAN_EXPLODE_IMPACT(element))
6069   {
6070     Bang(x, y);
6071     return;
6072   }
6073   else if (impact && element == EL_PEARL &&
6074            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6075   {
6076     ResetGfxAnimation(x, y);
6077
6078     Feld[x][y] = EL_PEARL_BREAKING;
6079     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6080     return;
6081   }
6082   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6083   {
6084     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6085
6086     return;
6087   }
6088
6089   if (impact && element == EL_AMOEBA_DROP)
6090   {
6091     if (object_hit && IS_PLAYER(x, y + 1))
6092       KillPlayerUnlessEnemyProtected(x, y + 1);
6093     else if (object_hit && smashed == EL_PENGUIN)
6094       Bang(x, y + 1);
6095     else
6096     {
6097       Feld[x][y] = EL_AMOEBA_GROWING;
6098       Store[x][y] = EL_AMOEBA_WET;
6099
6100       ResetRandomAnimationValue(x, y);
6101     }
6102     return;
6103   }
6104
6105   if (object_hit)               /* check which object was hit */
6106   {
6107     if ((CAN_PASS_MAGIC_WALL(element) && 
6108          (smashed == EL_MAGIC_WALL ||
6109           smashed == EL_BD_MAGIC_WALL)) ||
6110         (CAN_PASS_DC_MAGIC_WALL(element) &&
6111          smashed == EL_DC_MAGIC_WALL))
6112     {
6113       int xx, yy;
6114       int activated_magic_wall =
6115         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6116          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6117          EL_DC_MAGIC_WALL_ACTIVE);
6118
6119       /* activate magic wall / mill */
6120       SCAN_PLAYFIELD(xx, yy)
6121       {
6122         if (Feld[xx][yy] == smashed)
6123           Feld[xx][yy] = activated_magic_wall;
6124       }
6125
6126       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6127       game.magic_wall_active = TRUE;
6128
6129       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6130                             SND_MAGIC_WALL_ACTIVATING :
6131                             smashed == EL_BD_MAGIC_WALL ?
6132                             SND_BD_MAGIC_WALL_ACTIVATING :
6133                             SND_DC_MAGIC_WALL_ACTIVATING));
6134     }
6135
6136     if (IS_PLAYER(x, y + 1))
6137     {
6138       if (CAN_SMASH_PLAYER(element))
6139       {
6140         KillPlayerUnlessEnemyProtected(x, y + 1);
6141         return;
6142       }
6143     }
6144     else if (smashed == EL_PENGUIN)
6145     {
6146       if (CAN_SMASH_PLAYER(element))
6147       {
6148         Bang(x, y + 1);
6149         return;
6150       }
6151     }
6152     else if (element == EL_BD_DIAMOND)
6153     {
6154       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6155       {
6156         Bang(x, y + 1);
6157         return;
6158       }
6159     }
6160     else if (((element == EL_SP_INFOTRON ||
6161                element == EL_SP_ZONK) &&
6162               (smashed == EL_SP_SNIKSNAK ||
6163                smashed == EL_SP_ELECTRON ||
6164                smashed == EL_SP_DISK_ORANGE)) ||
6165              (element == EL_SP_INFOTRON &&
6166               smashed == EL_SP_DISK_YELLOW))
6167     {
6168       Bang(x, y + 1);
6169       return;
6170     }
6171     else if (CAN_SMASH_EVERYTHING(element))
6172     {
6173       if (IS_CLASSIC_ENEMY(smashed) ||
6174           CAN_EXPLODE_SMASHED(smashed))
6175       {
6176         Bang(x, y + 1);
6177         return;
6178       }
6179       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6180       {
6181         if (smashed == EL_LAMP ||
6182             smashed == EL_LAMP_ACTIVE)
6183         {
6184           Bang(x, y + 1);
6185           return;
6186         }
6187         else if (smashed == EL_NUT)
6188         {
6189           Feld[x][y + 1] = EL_NUT_BREAKING;
6190           PlayLevelSound(x, y, SND_NUT_BREAKING);
6191           RaiseScoreElement(EL_NUT);
6192           return;
6193         }
6194         else if (smashed == EL_PEARL)
6195         {
6196           ResetGfxAnimation(x, y);
6197
6198           Feld[x][y + 1] = EL_PEARL_BREAKING;
6199           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6200           return;
6201         }
6202         else if (smashed == EL_DIAMOND)
6203         {
6204           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6205           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6206           return;
6207         }
6208         else if (IS_BELT_SWITCH(smashed))
6209         {
6210           ToggleBeltSwitch(x, y + 1);
6211         }
6212         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6213                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6214                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6215                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6216         {
6217           ToggleSwitchgateSwitch(x, y + 1);
6218         }
6219         else if (smashed == EL_LIGHT_SWITCH ||
6220                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6221         {
6222           ToggleLightSwitch(x, y + 1);
6223         }
6224         else
6225         {
6226           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6227
6228           CheckElementChangeBySide(x, y + 1, smashed, element,
6229                                    CE_SWITCHED, CH_SIDE_TOP);
6230           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6231                                             CH_SIDE_TOP);
6232         }
6233       }
6234       else
6235       {
6236         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6237       }
6238     }
6239   }
6240
6241   /* play sound of magic wall / mill */
6242   if (!last_line &&
6243       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6244        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6245        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6246   {
6247     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6248       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6249     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6250       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6251     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6252       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6253
6254     return;
6255   }
6256
6257   /* play sound of object that hits the ground */
6258   if (last_line || object_hit)
6259     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6260 }
6261
6262 inline static void TurnRoundExt(int x, int y)
6263 {
6264   static struct
6265   {
6266     int dx, dy;
6267   } move_xy[] =
6268   {
6269     {  0,  0 },
6270     { -1,  0 },
6271     { +1,  0 },
6272     {  0,  0 },
6273     {  0, -1 },
6274     {  0,  0 }, { 0, 0 }, { 0, 0 },
6275     {  0, +1 }
6276   };
6277   static struct
6278   {
6279     int left, right, back;
6280   } turn[] =
6281   {
6282     { 0,        0,              0        },
6283     { MV_DOWN,  MV_UP,          MV_RIGHT },
6284     { MV_UP,    MV_DOWN,        MV_LEFT  },
6285     { 0,        0,              0        },
6286     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6287     { 0,        0,              0        },
6288     { 0,        0,              0        },
6289     { 0,        0,              0        },
6290     { MV_RIGHT, MV_LEFT,        MV_UP    }
6291   };
6292
6293   int element = Feld[x][y];
6294   int move_pattern = element_info[element].move_pattern;
6295
6296   int old_move_dir = MovDir[x][y];
6297   int left_dir  = turn[old_move_dir].left;
6298   int right_dir = turn[old_move_dir].right;
6299   int back_dir  = turn[old_move_dir].back;
6300
6301   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6302   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6303   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6304   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6305
6306   int left_x  = x + left_dx,  left_y  = y + left_dy;
6307   int right_x = x + right_dx, right_y = y + right_dy;
6308   int move_x  = x + move_dx,  move_y  = y + move_dy;
6309
6310   int xx, yy;
6311
6312   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6313   {
6314     TestIfBadThingTouchesOtherBadThing(x, y);
6315
6316     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6317       MovDir[x][y] = right_dir;
6318     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6319       MovDir[x][y] = left_dir;
6320
6321     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6322       MovDelay[x][y] = 9;
6323     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6324       MovDelay[x][y] = 1;
6325   }
6326   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6327   {
6328     TestIfBadThingTouchesOtherBadThing(x, y);
6329
6330     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6331       MovDir[x][y] = left_dir;
6332     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6333       MovDir[x][y] = right_dir;
6334
6335     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6336       MovDelay[x][y] = 9;
6337     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6338       MovDelay[x][y] = 1;
6339   }
6340   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6341   {
6342     TestIfBadThingTouchesOtherBadThing(x, y);
6343
6344     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6345       MovDir[x][y] = left_dir;
6346     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6347       MovDir[x][y] = right_dir;
6348
6349     if (MovDir[x][y] != old_move_dir)
6350       MovDelay[x][y] = 9;
6351   }
6352   else if (element == EL_YAMYAM)
6353   {
6354     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6355     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6356
6357     if (can_turn_left && can_turn_right)
6358       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6359     else if (can_turn_left)
6360       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6361     else if (can_turn_right)
6362       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6363     else
6364       MovDir[x][y] = back_dir;
6365
6366     MovDelay[x][y] = 16 + 16 * RND(3);
6367   }
6368   else if (element == EL_DARK_YAMYAM)
6369   {
6370     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6371                                                          left_x, left_y);
6372     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6373                                                          right_x, right_y);
6374
6375     if (can_turn_left && can_turn_right)
6376       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6377     else if (can_turn_left)
6378       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6379     else if (can_turn_right)
6380       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6381     else
6382       MovDir[x][y] = back_dir;
6383
6384     MovDelay[x][y] = 16 + 16 * RND(3);
6385   }
6386   else if (element == EL_PACMAN)
6387   {
6388     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6389     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6390
6391     if (can_turn_left && can_turn_right)
6392       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6393     else if (can_turn_left)
6394       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6395     else if (can_turn_right)
6396       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6397     else
6398       MovDir[x][y] = back_dir;
6399
6400     MovDelay[x][y] = 6 + RND(40);
6401   }
6402   else if (element == EL_PIG)
6403   {
6404     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6405     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6406     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6407     boolean should_turn_left, should_turn_right, should_move_on;
6408     int rnd_value = 24;
6409     int rnd = RND(rnd_value);
6410
6411     should_turn_left = (can_turn_left &&
6412                         (!can_move_on ||
6413                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6414                                                    y + back_dy + left_dy)));
6415     should_turn_right = (can_turn_right &&
6416                          (!can_move_on ||
6417                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6418                                                     y + back_dy + right_dy)));
6419     should_move_on = (can_move_on &&
6420                       (!can_turn_left ||
6421                        !can_turn_right ||
6422                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6423                                                  y + move_dy + left_dy) ||
6424                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6425                                                  y + move_dy + right_dy)));
6426
6427     if (should_turn_left || should_turn_right || should_move_on)
6428     {
6429       if (should_turn_left && should_turn_right && should_move_on)
6430         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6431                         rnd < 2 * rnd_value / 3 ? right_dir :
6432                         old_move_dir);
6433       else if (should_turn_left && should_turn_right)
6434         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6435       else if (should_turn_left && should_move_on)
6436         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6437       else if (should_turn_right && should_move_on)
6438         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6439       else if (should_turn_left)
6440         MovDir[x][y] = left_dir;
6441       else if (should_turn_right)
6442         MovDir[x][y] = right_dir;
6443       else if (should_move_on)
6444         MovDir[x][y] = old_move_dir;
6445     }
6446     else if (can_move_on && rnd > rnd_value / 8)
6447       MovDir[x][y] = old_move_dir;
6448     else if (can_turn_left && can_turn_right)
6449       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6450     else if (can_turn_left && rnd > rnd_value / 8)
6451       MovDir[x][y] = left_dir;
6452     else if (can_turn_right && rnd > rnd_value/8)
6453       MovDir[x][y] = right_dir;
6454     else
6455       MovDir[x][y] = back_dir;
6456
6457     xx = x + move_xy[MovDir[x][y]].dx;
6458     yy = y + move_xy[MovDir[x][y]].dy;
6459
6460     if (!IN_LEV_FIELD(xx, yy) ||
6461         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6462       MovDir[x][y] = old_move_dir;
6463
6464     MovDelay[x][y] = 0;
6465   }
6466   else if (element == EL_DRAGON)
6467   {
6468     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6469     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6470     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6471     int rnd_value = 24;
6472     int rnd = RND(rnd_value);
6473
6474     if (can_move_on && rnd > rnd_value / 8)
6475       MovDir[x][y] = old_move_dir;
6476     else if (can_turn_left && can_turn_right)
6477       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6478     else if (can_turn_left && rnd > rnd_value / 8)
6479       MovDir[x][y] = left_dir;
6480     else if (can_turn_right && rnd > rnd_value / 8)
6481       MovDir[x][y] = right_dir;
6482     else
6483       MovDir[x][y] = back_dir;
6484
6485     xx = x + move_xy[MovDir[x][y]].dx;
6486     yy = y + move_xy[MovDir[x][y]].dy;
6487
6488     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6489       MovDir[x][y] = old_move_dir;
6490
6491     MovDelay[x][y] = 0;
6492   }
6493   else if (element == EL_MOLE)
6494   {
6495     boolean can_move_on =
6496       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6497                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6498                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6499     if (!can_move_on)
6500     {
6501       boolean can_turn_left =
6502         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6503                               IS_AMOEBOID(Feld[left_x][left_y])));
6504
6505       boolean can_turn_right =
6506         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6507                               IS_AMOEBOID(Feld[right_x][right_y])));
6508
6509       if (can_turn_left && can_turn_right)
6510         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6511       else if (can_turn_left)
6512         MovDir[x][y] = left_dir;
6513       else
6514         MovDir[x][y] = right_dir;
6515     }
6516
6517     if (MovDir[x][y] != old_move_dir)
6518       MovDelay[x][y] = 9;
6519   }
6520   else if (element == EL_BALLOON)
6521   {
6522     MovDir[x][y] = game.wind_direction;
6523     MovDelay[x][y] = 0;
6524   }
6525   else if (element == EL_SPRING)
6526   {
6527     if (MovDir[x][y] & MV_HORIZONTAL)
6528     {
6529       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6530           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6531       {
6532         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6533         ResetGfxAnimation(move_x, move_y);
6534         TEST_DrawLevelField(move_x, move_y);
6535
6536         MovDir[x][y] = back_dir;
6537       }
6538       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6539                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6540         MovDir[x][y] = MV_NONE;
6541     }
6542
6543     MovDelay[x][y] = 0;
6544   }
6545   else if (element == EL_ROBOT ||
6546            element == EL_SATELLITE ||
6547            element == EL_PENGUIN ||
6548            element == EL_EMC_ANDROID)
6549   {
6550     int attr_x = -1, attr_y = -1;
6551
6552     if (AllPlayersGone)
6553     {
6554       attr_x = ExitX;
6555       attr_y = ExitY;
6556     }
6557     else
6558     {
6559       int i;
6560
6561       for (i = 0; i < MAX_PLAYERS; i++)
6562       {
6563         struct PlayerInfo *player = &stored_player[i];
6564         int jx = player->jx, jy = player->jy;
6565
6566         if (!player->active)
6567           continue;
6568
6569         if (attr_x == -1 ||
6570             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6571         {
6572           attr_x = jx;
6573           attr_y = jy;
6574         }
6575       }
6576     }
6577
6578     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6579         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6580          game.engine_version < VERSION_IDENT(3,1,0,0)))
6581     {
6582       attr_x = ZX;
6583       attr_y = ZY;
6584     }
6585
6586     if (element == EL_PENGUIN)
6587     {
6588       int i;
6589       static int xy[4][2] =
6590       {
6591         { 0, -1 },
6592         { -1, 0 },
6593         { +1, 0 },
6594         { 0, +1 }
6595       };
6596
6597       for (i = 0; i < NUM_DIRECTIONS; i++)
6598       {
6599         int ex = x + xy[i][0];
6600         int ey = y + xy[i][1];
6601
6602         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6603                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6604                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6605                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6606         {
6607           attr_x = ex;
6608           attr_y = ey;
6609           break;
6610         }
6611       }
6612     }
6613
6614     MovDir[x][y] = MV_NONE;
6615     if (attr_x < x)
6616       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6617     else if (attr_x > x)
6618       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6619     if (attr_y < y)
6620       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6621     else if (attr_y > y)
6622       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6623
6624     if (element == EL_ROBOT)
6625     {
6626       int newx, newy;
6627
6628       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6629         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6630       Moving2Blocked(x, y, &newx, &newy);
6631
6632       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6633         MovDelay[x][y] = 8 + 8 * !RND(3);
6634       else
6635         MovDelay[x][y] = 16;
6636     }
6637     else if (element == EL_PENGUIN)
6638     {
6639       int newx, newy;
6640
6641       MovDelay[x][y] = 1;
6642
6643       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6644       {
6645         boolean first_horiz = RND(2);
6646         int new_move_dir = MovDir[x][y];
6647
6648         MovDir[x][y] =
6649           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6650         Moving2Blocked(x, y, &newx, &newy);
6651
6652         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6653           return;
6654
6655         MovDir[x][y] =
6656           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6657         Moving2Blocked(x, y, &newx, &newy);
6658
6659         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6660           return;
6661
6662         MovDir[x][y] = old_move_dir;
6663         return;
6664       }
6665     }
6666     else if (element == EL_SATELLITE)
6667     {
6668       int newx, newy;
6669
6670       MovDelay[x][y] = 1;
6671
6672       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6673       {
6674         boolean first_horiz = RND(2);
6675         int new_move_dir = MovDir[x][y];
6676
6677         MovDir[x][y] =
6678           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6679         Moving2Blocked(x, y, &newx, &newy);
6680
6681         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6682           return;
6683
6684         MovDir[x][y] =
6685           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6686         Moving2Blocked(x, y, &newx, &newy);
6687
6688         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6689           return;
6690
6691         MovDir[x][y] = old_move_dir;
6692         return;
6693       }
6694     }
6695     else if (element == EL_EMC_ANDROID)
6696     {
6697       static int check_pos[16] =
6698       {
6699         -1,             /*  0 => (invalid)          */
6700         7,              /*  1 => MV_LEFT            */
6701         3,              /*  2 => MV_RIGHT           */
6702         -1,             /*  3 => (invalid)          */
6703         1,              /*  4 =>            MV_UP   */
6704         0,              /*  5 => MV_LEFT  | MV_UP   */
6705         2,              /*  6 => MV_RIGHT | MV_UP   */
6706         -1,             /*  7 => (invalid)          */
6707         5,              /*  8 =>            MV_DOWN */
6708         6,              /*  9 => MV_LEFT  | MV_DOWN */
6709         4,              /* 10 => MV_RIGHT | MV_DOWN */
6710         -1,             /* 11 => (invalid)          */
6711         -1,             /* 12 => (invalid)          */
6712         -1,             /* 13 => (invalid)          */
6713         -1,             /* 14 => (invalid)          */
6714         -1,             /* 15 => (invalid)          */
6715       };
6716       static struct
6717       {
6718         int dx, dy;
6719         int dir;
6720       } check_xy[8] =
6721       {
6722         { -1, -1,       MV_LEFT  | MV_UP   },
6723         {  0, -1,                  MV_UP   },
6724         { +1, -1,       MV_RIGHT | MV_UP   },
6725         { +1,  0,       MV_RIGHT           },
6726         { +1, +1,       MV_RIGHT | MV_DOWN },
6727         {  0, +1,                  MV_DOWN },
6728         { -1, +1,       MV_LEFT  | MV_DOWN },
6729         { -1,  0,       MV_LEFT            },
6730       };
6731       int start_pos, check_order;
6732       boolean can_clone = FALSE;
6733       int i;
6734
6735       /* check if there is any free field around current position */
6736       for (i = 0; i < 8; i++)
6737       {
6738         int newx = x + check_xy[i].dx;
6739         int newy = y + check_xy[i].dy;
6740
6741         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6742         {
6743           can_clone = TRUE;
6744
6745           break;
6746         }
6747       }
6748
6749       if (can_clone)            /* randomly find an element to clone */
6750       {
6751         can_clone = FALSE;
6752
6753         start_pos = check_pos[RND(8)];
6754         check_order = (RND(2) ? -1 : +1);
6755
6756         for (i = 0; i < 8; i++)
6757         {
6758           int pos_raw = start_pos + i * check_order;
6759           int pos = (pos_raw + 8) % 8;
6760           int newx = x + check_xy[pos].dx;
6761           int newy = y + check_xy[pos].dy;
6762
6763           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6764           {
6765             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6766             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6767
6768             Store[x][y] = Feld[newx][newy];
6769
6770             can_clone = TRUE;
6771
6772             break;
6773           }
6774         }
6775       }
6776
6777       if (can_clone)            /* randomly find a direction to move */
6778       {
6779         can_clone = FALSE;
6780
6781         start_pos = check_pos[RND(8)];
6782         check_order = (RND(2) ? -1 : +1);
6783
6784         for (i = 0; i < 8; i++)
6785         {
6786           int pos_raw = start_pos + i * check_order;
6787           int pos = (pos_raw + 8) % 8;
6788           int newx = x + check_xy[pos].dx;
6789           int newy = y + check_xy[pos].dy;
6790           int new_move_dir = check_xy[pos].dir;
6791
6792           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6793           {
6794             MovDir[x][y] = new_move_dir;
6795             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6796
6797             can_clone = TRUE;
6798
6799             break;
6800           }
6801         }
6802       }
6803
6804       if (can_clone)            /* cloning and moving successful */
6805         return;
6806
6807       /* cannot clone -- try to move towards player */
6808
6809       start_pos = check_pos[MovDir[x][y] & 0x0f];
6810       check_order = (RND(2) ? -1 : +1);
6811
6812       for (i = 0; i < 3; i++)
6813       {
6814         /* first check start_pos, then previous/next or (next/previous) pos */
6815         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6816         int pos = (pos_raw + 8) % 8;
6817         int newx = x + check_xy[pos].dx;
6818         int newy = y + check_xy[pos].dy;
6819         int new_move_dir = check_xy[pos].dir;
6820
6821         if (IS_PLAYER(newx, newy))
6822           break;
6823
6824         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6825         {
6826           MovDir[x][y] = new_move_dir;
6827           MovDelay[x][y] = level.android_move_time * 8 + 1;
6828
6829           break;
6830         }
6831       }
6832     }
6833   }
6834   else if (move_pattern == MV_TURNING_LEFT ||
6835            move_pattern == MV_TURNING_RIGHT ||
6836            move_pattern == MV_TURNING_LEFT_RIGHT ||
6837            move_pattern == MV_TURNING_RIGHT_LEFT ||
6838            move_pattern == MV_TURNING_RANDOM ||
6839            move_pattern == MV_ALL_DIRECTIONS)
6840   {
6841     boolean can_turn_left =
6842       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6843     boolean can_turn_right =
6844       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6845
6846     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6847       return;
6848
6849     if (move_pattern == MV_TURNING_LEFT)
6850       MovDir[x][y] = left_dir;
6851     else if (move_pattern == MV_TURNING_RIGHT)
6852       MovDir[x][y] = right_dir;
6853     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6854       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6855     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6856       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6857     else if (move_pattern == MV_TURNING_RANDOM)
6858       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6859                       can_turn_right && !can_turn_left ? right_dir :
6860                       RND(2) ? left_dir : right_dir);
6861     else if (can_turn_left && can_turn_right)
6862       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6863     else if (can_turn_left)
6864       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6865     else if (can_turn_right)
6866       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6867     else
6868       MovDir[x][y] = back_dir;
6869
6870     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6871   }
6872   else if (move_pattern == MV_HORIZONTAL ||
6873            move_pattern == MV_VERTICAL)
6874   {
6875     if (move_pattern & old_move_dir)
6876       MovDir[x][y] = back_dir;
6877     else if (move_pattern == MV_HORIZONTAL)
6878       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6879     else if (move_pattern == MV_VERTICAL)
6880       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6881
6882     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6883   }
6884   else if (move_pattern & MV_ANY_DIRECTION)
6885   {
6886     MovDir[x][y] = move_pattern;
6887     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6888   }
6889   else if (move_pattern & MV_WIND_DIRECTION)
6890   {
6891     MovDir[x][y] = game.wind_direction;
6892     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6893   }
6894   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6895   {
6896     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6897       MovDir[x][y] = left_dir;
6898     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6899       MovDir[x][y] = right_dir;
6900
6901     if (MovDir[x][y] != old_move_dir)
6902       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6903   }
6904   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6905   {
6906     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6907       MovDir[x][y] = right_dir;
6908     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6909       MovDir[x][y] = left_dir;
6910
6911     if (MovDir[x][y] != old_move_dir)
6912       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6913   }
6914   else if (move_pattern == MV_TOWARDS_PLAYER ||
6915            move_pattern == MV_AWAY_FROM_PLAYER)
6916   {
6917     int attr_x = -1, attr_y = -1;
6918     int newx, newy;
6919     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6920
6921     if (AllPlayersGone)
6922     {
6923       attr_x = ExitX;
6924       attr_y = ExitY;
6925     }
6926     else
6927     {
6928       int i;
6929
6930       for (i = 0; i < MAX_PLAYERS; i++)
6931       {
6932         struct PlayerInfo *player = &stored_player[i];
6933         int jx = player->jx, jy = player->jy;
6934
6935         if (!player->active)
6936           continue;
6937
6938         if (attr_x == -1 ||
6939             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6940         {
6941           attr_x = jx;
6942           attr_y = jy;
6943         }
6944       }
6945     }
6946
6947     MovDir[x][y] = MV_NONE;
6948     if (attr_x < x)
6949       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6950     else if (attr_x > x)
6951       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6952     if (attr_y < y)
6953       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6954     else if (attr_y > y)
6955       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6956
6957     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6958
6959     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6960     {
6961       boolean first_horiz = RND(2);
6962       int new_move_dir = MovDir[x][y];
6963
6964       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6965       {
6966         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6967         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6968
6969         return;
6970       }
6971
6972       MovDir[x][y] =
6973         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6974       Moving2Blocked(x, y, &newx, &newy);
6975
6976       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6977         return;
6978
6979       MovDir[x][y] =
6980         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6981       Moving2Blocked(x, y, &newx, &newy);
6982
6983       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6984         return;
6985
6986       MovDir[x][y] = old_move_dir;
6987     }
6988   }
6989   else if (move_pattern == MV_WHEN_PUSHED ||
6990            move_pattern == MV_WHEN_DROPPED)
6991   {
6992     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6993       MovDir[x][y] = MV_NONE;
6994
6995     MovDelay[x][y] = 0;
6996   }
6997   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6998   {
6999     static int test_xy[7][2] =
7000     {
7001       { 0, -1 },
7002       { -1, 0 },
7003       { +1, 0 },
7004       { 0, +1 },
7005       { 0, -1 },
7006       { -1, 0 },
7007       { +1, 0 },
7008     };
7009     static int test_dir[7] =
7010     {
7011       MV_UP,
7012       MV_LEFT,
7013       MV_RIGHT,
7014       MV_DOWN,
7015       MV_UP,
7016       MV_LEFT,
7017       MV_RIGHT,
7018     };
7019     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7020     int move_preference = -1000000;     /* start with very low preference */
7021     int new_move_dir = MV_NONE;
7022     int start_test = RND(4);
7023     int i;
7024
7025     for (i = 0; i < NUM_DIRECTIONS; i++)
7026     {
7027       int move_dir = test_dir[start_test + i];
7028       int move_dir_preference;
7029
7030       xx = x + test_xy[start_test + i][0];
7031       yy = y + test_xy[start_test + i][1];
7032
7033       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7034           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7035       {
7036         new_move_dir = move_dir;
7037
7038         break;
7039       }
7040
7041       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7042         continue;
7043
7044       move_dir_preference = -1 * RunnerVisit[xx][yy];
7045       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7046         move_dir_preference = PlayerVisit[xx][yy];
7047
7048       if (move_dir_preference > move_preference)
7049       {
7050         /* prefer field that has not been visited for the longest time */
7051         move_preference = move_dir_preference;
7052         new_move_dir = move_dir;
7053       }
7054       else if (move_dir_preference == move_preference &&
7055                move_dir == old_move_dir)
7056       {
7057         /* prefer last direction when all directions are preferred equally */
7058         move_preference = move_dir_preference;
7059         new_move_dir = move_dir;
7060       }
7061     }
7062
7063     MovDir[x][y] = new_move_dir;
7064     if (old_move_dir != new_move_dir)
7065       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7066   }
7067 }
7068
7069 static void TurnRound(int x, int y)
7070 {
7071   int direction = MovDir[x][y];
7072
7073   TurnRoundExt(x, y);
7074
7075   GfxDir[x][y] = MovDir[x][y];
7076
7077   if (direction != MovDir[x][y])
7078     GfxFrame[x][y] = 0;
7079
7080   if (MovDelay[x][y])
7081     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7082
7083   ResetGfxFrame(x, y, FALSE);
7084 }
7085
7086 static boolean JustBeingPushed(int x, int y)
7087 {
7088   int i;
7089
7090   for (i = 0; i < MAX_PLAYERS; i++)
7091   {
7092     struct PlayerInfo *player = &stored_player[i];
7093
7094     if (player->active && player->is_pushing && player->MovPos)
7095     {
7096       int next_jx = player->jx + (player->jx - player->last_jx);
7097       int next_jy = player->jy + (player->jy - player->last_jy);
7098
7099       if (x == next_jx && y == next_jy)
7100         return TRUE;
7101     }
7102   }
7103
7104   return FALSE;
7105 }
7106
7107 void StartMoving(int x, int y)
7108 {
7109   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7110   int element = Feld[x][y];
7111
7112   if (Stop[x][y])
7113     return;
7114
7115   if (MovDelay[x][y] == 0)
7116     GfxAction[x][y] = ACTION_DEFAULT;
7117
7118   if (CAN_FALL(element) && y < lev_fieldy - 1)
7119   {
7120     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7121         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7122       if (JustBeingPushed(x, y))
7123         return;
7124
7125     if (element == EL_QUICKSAND_FULL)
7126     {
7127       if (IS_FREE(x, y + 1))
7128       {
7129         InitMovingField(x, y, MV_DOWN);
7130         started_moving = TRUE;
7131
7132         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7133 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7134         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7135           Store[x][y] = EL_ROCK;
7136 #else
7137         Store[x][y] = EL_ROCK;
7138 #endif
7139
7140         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7141       }
7142       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7143       {
7144         if (!MovDelay[x][y])
7145         {
7146           MovDelay[x][y] = TILEY + 1;
7147
7148           ResetGfxAnimation(x, y);
7149           ResetGfxAnimation(x, y + 1);
7150         }
7151
7152         if (MovDelay[x][y])
7153         {
7154           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7155           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7156
7157           MovDelay[x][y]--;
7158           if (MovDelay[x][y])
7159             return;
7160         }
7161
7162         Feld[x][y] = EL_QUICKSAND_EMPTY;
7163         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7164         Store[x][y + 1] = Store[x][y];
7165         Store[x][y] = 0;
7166
7167         PlayLevelSoundAction(x, y, ACTION_FILLING);
7168       }
7169       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7170       {
7171         if (!MovDelay[x][y])
7172         {
7173           MovDelay[x][y] = TILEY + 1;
7174
7175           ResetGfxAnimation(x, y);
7176           ResetGfxAnimation(x, y + 1);
7177         }
7178
7179         if (MovDelay[x][y])
7180         {
7181           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7182           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7183
7184           MovDelay[x][y]--;
7185           if (MovDelay[x][y])
7186             return;
7187         }
7188
7189         Feld[x][y] = EL_QUICKSAND_EMPTY;
7190         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7191         Store[x][y + 1] = Store[x][y];
7192         Store[x][y] = 0;
7193
7194         PlayLevelSoundAction(x, y, ACTION_FILLING);
7195       }
7196     }
7197     else if (element == EL_QUICKSAND_FAST_FULL)
7198     {
7199       if (IS_FREE(x, y + 1))
7200       {
7201         InitMovingField(x, y, MV_DOWN);
7202         started_moving = TRUE;
7203
7204         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7205 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7206         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7207           Store[x][y] = EL_ROCK;
7208 #else
7209         Store[x][y] = EL_ROCK;
7210 #endif
7211
7212         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7213       }
7214       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7215       {
7216         if (!MovDelay[x][y])
7217         {
7218           MovDelay[x][y] = TILEY + 1;
7219
7220           ResetGfxAnimation(x, y);
7221           ResetGfxAnimation(x, y + 1);
7222         }
7223
7224         if (MovDelay[x][y])
7225         {
7226           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7227           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7228
7229           MovDelay[x][y]--;
7230           if (MovDelay[x][y])
7231             return;
7232         }
7233
7234         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7235         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7236         Store[x][y + 1] = Store[x][y];
7237         Store[x][y] = 0;
7238
7239         PlayLevelSoundAction(x, y, ACTION_FILLING);
7240       }
7241       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7242       {
7243         if (!MovDelay[x][y])
7244         {
7245           MovDelay[x][y] = TILEY + 1;
7246
7247           ResetGfxAnimation(x, y);
7248           ResetGfxAnimation(x, y + 1);
7249         }
7250
7251         if (MovDelay[x][y])
7252         {
7253           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7254           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7255
7256           MovDelay[x][y]--;
7257           if (MovDelay[x][y])
7258             return;
7259         }
7260
7261         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7262         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7263         Store[x][y + 1] = Store[x][y];
7264         Store[x][y] = 0;
7265
7266         PlayLevelSoundAction(x, y, ACTION_FILLING);
7267       }
7268     }
7269     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7270              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7271     {
7272       InitMovingField(x, y, MV_DOWN);
7273       started_moving = TRUE;
7274
7275       Feld[x][y] = EL_QUICKSAND_FILLING;
7276       Store[x][y] = element;
7277
7278       PlayLevelSoundAction(x, y, ACTION_FILLING);
7279     }
7280     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7281              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7282     {
7283       InitMovingField(x, y, MV_DOWN);
7284       started_moving = TRUE;
7285
7286       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7287       Store[x][y] = element;
7288
7289       PlayLevelSoundAction(x, y, ACTION_FILLING);
7290     }
7291     else if (element == EL_MAGIC_WALL_FULL)
7292     {
7293       if (IS_FREE(x, y + 1))
7294       {
7295         InitMovingField(x, y, MV_DOWN);
7296         started_moving = TRUE;
7297
7298         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7299         Store[x][y] = EL_CHANGED(Store[x][y]);
7300       }
7301       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7302       {
7303         if (!MovDelay[x][y])
7304           MovDelay[x][y] = TILEY / 4 + 1;
7305
7306         if (MovDelay[x][y])
7307         {
7308           MovDelay[x][y]--;
7309           if (MovDelay[x][y])
7310             return;
7311         }
7312
7313         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7314         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7315         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7316         Store[x][y] = 0;
7317       }
7318     }
7319     else if (element == EL_BD_MAGIC_WALL_FULL)
7320     {
7321       if (IS_FREE(x, y + 1))
7322       {
7323         InitMovingField(x, y, MV_DOWN);
7324         started_moving = TRUE;
7325
7326         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7327         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7328       }
7329       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7330       {
7331         if (!MovDelay[x][y])
7332           MovDelay[x][y] = TILEY / 4 + 1;
7333
7334         if (MovDelay[x][y])
7335         {
7336           MovDelay[x][y]--;
7337           if (MovDelay[x][y])
7338             return;
7339         }
7340
7341         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7342         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7343         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7344         Store[x][y] = 0;
7345       }
7346     }
7347     else if (element == EL_DC_MAGIC_WALL_FULL)
7348     {
7349       if (IS_FREE(x, y + 1))
7350       {
7351         InitMovingField(x, y, MV_DOWN);
7352         started_moving = TRUE;
7353
7354         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7355         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7356       }
7357       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7358       {
7359         if (!MovDelay[x][y])
7360           MovDelay[x][y] = TILEY / 4 + 1;
7361
7362         if (MovDelay[x][y])
7363         {
7364           MovDelay[x][y]--;
7365           if (MovDelay[x][y])
7366             return;
7367         }
7368
7369         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7370         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7371         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7372         Store[x][y] = 0;
7373       }
7374     }
7375     else if ((CAN_PASS_MAGIC_WALL(element) &&
7376               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7377                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7378              (CAN_PASS_DC_MAGIC_WALL(element) &&
7379               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7380
7381     {
7382       InitMovingField(x, y, MV_DOWN);
7383       started_moving = TRUE;
7384
7385       Feld[x][y] =
7386         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7387          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7388          EL_DC_MAGIC_WALL_FILLING);
7389       Store[x][y] = element;
7390     }
7391     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7392     {
7393       SplashAcid(x, y + 1);
7394
7395       InitMovingField(x, y, MV_DOWN);
7396       started_moving = TRUE;
7397
7398       Store[x][y] = EL_ACID;
7399     }
7400     else if (
7401              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7402               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7403              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7404               CAN_FALL(element) && WasJustFalling[x][y] &&
7405               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7406
7407              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7408               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7409               (Feld[x][y + 1] == EL_BLOCKED)))
7410     {
7411       /* this is needed for a special case not covered by calling "Impact()"
7412          from "ContinueMoving()": if an element moves to a tile directly below
7413          another element which was just falling on that tile (which was empty
7414          in the previous frame), the falling element above would just stop
7415          instead of smashing the element below (in previous version, the above
7416          element was just checked for "moving" instead of "falling", resulting
7417          in incorrect smashes caused by horizontal movement of the above
7418          element; also, the case of the player being the element to smash was
7419          simply not covered here... :-/ ) */
7420
7421       CheckCollision[x][y] = 0;
7422       CheckImpact[x][y] = 0;
7423
7424       Impact(x, y);
7425     }
7426     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7427     {
7428       if (MovDir[x][y] == MV_NONE)
7429       {
7430         InitMovingField(x, y, MV_DOWN);
7431         started_moving = TRUE;
7432       }
7433     }
7434     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7435     {
7436       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7437         MovDir[x][y] = MV_DOWN;
7438
7439       InitMovingField(x, y, MV_DOWN);
7440       started_moving = TRUE;
7441     }
7442     else if (element == EL_AMOEBA_DROP)
7443     {
7444       Feld[x][y] = EL_AMOEBA_GROWING;
7445       Store[x][y] = EL_AMOEBA_WET;
7446     }
7447     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7448               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7449              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7450              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7451     {
7452       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7453                                 (IS_FREE(x - 1, y + 1) ||
7454                                  Feld[x - 1][y + 1] == EL_ACID));
7455       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7456                                 (IS_FREE(x + 1, y + 1) ||
7457                                  Feld[x + 1][y + 1] == EL_ACID));
7458       boolean can_fall_any  = (can_fall_left || can_fall_right);
7459       boolean can_fall_both = (can_fall_left && can_fall_right);
7460       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7461
7462       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7463       {
7464         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7465           can_fall_right = FALSE;
7466         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7467           can_fall_left = FALSE;
7468         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7469           can_fall_right = FALSE;
7470         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7471           can_fall_left = FALSE;
7472
7473         can_fall_any  = (can_fall_left || can_fall_right);
7474         can_fall_both = FALSE;
7475       }
7476
7477       if (can_fall_both)
7478       {
7479         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7480           can_fall_right = FALSE;       /* slip down on left side */
7481         else
7482           can_fall_left = !(can_fall_right = RND(2));
7483
7484         can_fall_both = FALSE;
7485       }
7486
7487       if (can_fall_any)
7488       {
7489         /* if not determined otherwise, prefer left side for slipping down */
7490         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7491         started_moving = TRUE;
7492       }
7493     }
7494     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7495     {
7496       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7497       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7498       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7499       int belt_dir = game.belt_dir[belt_nr];
7500
7501       if ((belt_dir == MV_LEFT  && left_is_free) ||
7502           (belt_dir == MV_RIGHT && right_is_free))
7503       {
7504         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7505
7506         InitMovingField(x, y, belt_dir);
7507         started_moving = TRUE;
7508
7509         Pushed[x][y] = TRUE;
7510         Pushed[nextx][y] = TRUE;
7511
7512         GfxAction[x][y] = ACTION_DEFAULT;
7513       }
7514       else
7515       {
7516         MovDir[x][y] = 0;       /* if element was moving, stop it */
7517       }
7518     }
7519   }
7520
7521   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7522   if (CAN_MOVE(element) && !started_moving)
7523   {
7524     int move_pattern = element_info[element].move_pattern;
7525     int newx, newy;
7526
7527     Moving2Blocked(x, y, &newx, &newy);
7528
7529     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7530       return;
7531
7532     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7533         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7534     {
7535       WasJustMoving[x][y] = 0;
7536       CheckCollision[x][y] = 0;
7537
7538       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7539
7540       if (Feld[x][y] != element)        /* element has changed */
7541         return;
7542     }
7543
7544     if (!MovDelay[x][y])        /* start new movement phase */
7545     {
7546       /* all objects that can change their move direction after each step
7547          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7548
7549       if (element != EL_YAMYAM &&
7550           element != EL_DARK_YAMYAM &&
7551           element != EL_PACMAN &&
7552           !(move_pattern & MV_ANY_DIRECTION) &&
7553           move_pattern != MV_TURNING_LEFT &&
7554           move_pattern != MV_TURNING_RIGHT &&
7555           move_pattern != MV_TURNING_LEFT_RIGHT &&
7556           move_pattern != MV_TURNING_RIGHT_LEFT &&
7557           move_pattern != MV_TURNING_RANDOM)
7558       {
7559         TurnRound(x, y);
7560
7561         if (MovDelay[x][y] && (element == EL_BUG ||
7562                                element == EL_SPACESHIP ||
7563                                element == EL_SP_SNIKSNAK ||
7564                                element == EL_SP_ELECTRON ||
7565                                element == EL_MOLE))
7566           TEST_DrawLevelField(x, y);
7567       }
7568     }
7569
7570     if (MovDelay[x][y])         /* wait some time before next movement */
7571     {
7572       MovDelay[x][y]--;
7573
7574       if (element == EL_ROBOT ||
7575           element == EL_YAMYAM ||
7576           element == EL_DARK_YAMYAM)
7577       {
7578         DrawLevelElementAnimationIfNeeded(x, y, element);
7579         PlayLevelSoundAction(x, y, ACTION_WAITING);
7580       }
7581       else if (element == EL_SP_ELECTRON)
7582         DrawLevelElementAnimationIfNeeded(x, y, element);
7583       else if (element == EL_DRAGON)
7584       {
7585         int i;
7586         int dir = MovDir[x][y];
7587         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7588         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7589         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7590                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7591                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7592                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7593         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7594
7595         GfxAction[x][y] = ACTION_ATTACKING;
7596
7597         if (IS_PLAYER(x, y))
7598           DrawPlayerField(x, y);
7599         else
7600           TEST_DrawLevelField(x, y);
7601
7602         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7603
7604         for (i = 1; i <= 3; i++)
7605         {
7606           int xx = x + i * dx;
7607           int yy = y + i * dy;
7608           int sx = SCREENX(xx);
7609           int sy = SCREENY(yy);
7610           int flame_graphic = graphic + (i - 1);
7611
7612           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7613             break;
7614
7615           if (MovDelay[x][y])
7616           {
7617             int flamed = MovingOrBlocked2Element(xx, yy);
7618
7619             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7620               Bang(xx, yy);
7621             else
7622               RemoveMovingField(xx, yy);
7623
7624             ChangeDelay[xx][yy] = 0;
7625
7626             Feld[xx][yy] = EL_FLAMES;
7627
7628             if (IN_SCR_FIELD(sx, sy))
7629             {
7630               TEST_DrawLevelFieldCrumbled(xx, yy);
7631               DrawGraphic(sx, sy, flame_graphic, frame);
7632             }
7633           }
7634           else
7635           {
7636             if (Feld[xx][yy] == EL_FLAMES)
7637               Feld[xx][yy] = EL_EMPTY;
7638             TEST_DrawLevelField(xx, yy);
7639           }
7640         }
7641       }
7642
7643       if (MovDelay[x][y])       /* element still has to wait some time */
7644       {
7645         PlayLevelSoundAction(x, y, ACTION_WAITING);
7646
7647         return;
7648       }
7649     }
7650
7651     /* now make next step */
7652
7653     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7654
7655     if (DONT_COLLIDE_WITH(element) &&
7656         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7657         !PLAYER_ENEMY_PROTECTED(newx, newy))
7658     {
7659       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7660
7661       return;
7662     }
7663
7664     else if (CAN_MOVE_INTO_ACID(element) &&
7665              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7666              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7667              (MovDir[x][y] == MV_DOWN ||
7668               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7669     {
7670       SplashAcid(newx, newy);
7671       Store[x][y] = EL_ACID;
7672     }
7673     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7674     {
7675       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7676           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7677           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7678           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7679       {
7680         RemoveField(x, y);
7681         TEST_DrawLevelField(x, y);
7682
7683         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7684         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7685           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7686
7687         local_player->friends_still_needed--;
7688         if (!local_player->friends_still_needed &&
7689             !local_player->GameOver && AllPlayersGone)
7690           PlayerWins(local_player);
7691
7692         return;
7693       }
7694       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7695       {
7696         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7697           TEST_DrawLevelField(newx, newy);
7698         else
7699           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7700       }
7701       else if (!IS_FREE(newx, newy))
7702       {
7703         GfxAction[x][y] = ACTION_WAITING;
7704
7705         if (IS_PLAYER(x, y))
7706           DrawPlayerField(x, y);
7707         else
7708           TEST_DrawLevelField(x, y);
7709
7710         return;
7711       }
7712     }
7713     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7714     {
7715       if (IS_FOOD_PIG(Feld[newx][newy]))
7716       {
7717         if (IS_MOVING(newx, newy))
7718           RemoveMovingField(newx, newy);
7719         else
7720         {
7721           Feld[newx][newy] = EL_EMPTY;
7722           TEST_DrawLevelField(newx, newy);
7723         }
7724
7725         PlayLevelSound(x, y, SND_PIG_DIGGING);
7726       }
7727       else if (!IS_FREE(newx, newy))
7728       {
7729         if (IS_PLAYER(x, y))
7730           DrawPlayerField(x, y);
7731         else
7732           TEST_DrawLevelField(x, y);
7733
7734         return;
7735       }
7736     }
7737     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7738     {
7739       if (Store[x][y] != EL_EMPTY)
7740       {
7741         boolean can_clone = FALSE;
7742         int xx, yy;
7743
7744         /* check if element to clone is still there */
7745         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7746         {
7747           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7748           {
7749             can_clone = TRUE;
7750
7751             break;
7752           }
7753         }
7754
7755         /* cannot clone or target field not free anymore -- do not clone */
7756         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7757           Store[x][y] = EL_EMPTY;
7758       }
7759
7760       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7761       {
7762         if (IS_MV_DIAGONAL(MovDir[x][y]))
7763         {
7764           int diagonal_move_dir = MovDir[x][y];
7765           int stored = Store[x][y];
7766           int change_delay = 8;
7767           int graphic;
7768
7769           /* android is moving diagonally */
7770
7771           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7772
7773           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7774           GfxElement[x][y] = EL_EMC_ANDROID;
7775           GfxAction[x][y] = ACTION_SHRINKING;
7776           GfxDir[x][y] = diagonal_move_dir;
7777           ChangeDelay[x][y] = change_delay;
7778
7779           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7780                                    GfxDir[x][y]);
7781
7782           DrawLevelGraphicAnimation(x, y, graphic);
7783           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7784
7785           if (Feld[newx][newy] == EL_ACID)
7786           {
7787             SplashAcid(newx, newy);
7788
7789             return;
7790           }
7791
7792           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7793
7794           Store[newx][newy] = EL_EMC_ANDROID;
7795           GfxElement[newx][newy] = EL_EMC_ANDROID;
7796           GfxAction[newx][newy] = ACTION_GROWING;
7797           GfxDir[newx][newy] = diagonal_move_dir;
7798           ChangeDelay[newx][newy] = change_delay;
7799
7800           graphic = el_act_dir2img(GfxElement[newx][newy],
7801                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7802
7803           DrawLevelGraphicAnimation(newx, newy, graphic);
7804           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7805
7806           return;
7807         }
7808         else
7809         {
7810           Feld[newx][newy] = EL_EMPTY;
7811           TEST_DrawLevelField(newx, newy);
7812
7813           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7814         }
7815       }
7816       else if (!IS_FREE(newx, newy))
7817       {
7818         return;
7819       }
7820     }
7821     else if (IS_CUSTOM_ELEMENT(element) &&
7822              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7823     {
7824       if (!DigFieldByCE(newx, newy, element))
7825         return;
7826
7827       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7828       {
7829         RunnerVisit[x][y] = FrameCounter;
7830         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7831       }
7832     }
7833     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7834     {
7835       if (!IS_FREE(newx, newy))
7836       {
7837         if (IS_PLAYER(x, y))
7838           DrawPlayerField(x, y);
7839         else
7840           TEST_DrawLevelField(x, y);
7841
7842         return;
7843       }
7844       else
7845       {
7846         boolean wanna_flame = !RND(10);
7847         int dx = newx - x, dy = newy - y;
7848         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7849         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7850         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7851                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7852         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7853                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7854
7855         if ((wanna_flame ||
7856              IS_CLASSIC_ENEMY(element1) ||
7857              IS_CLASSIC_ENEMY(element2)) &&
7858             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7859             element1 != EL_FLAMES && element2 != EL_FLAMES)
7860         {
7861           ResetGfxAnimation(x, y);
7862           GfxAction[x][y] = ACTION_ATTACKING;
7863
7864           if (IS_PLAYER(x, y))
7865             DrawPlayerField(x, y);
7866           else
7867             TEST_DrawLevelField(x, y);
7868
7869           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7870
7871           MovDelay[x][y] = 50;
7872
7873           Feld[newx][newy] = EL_FLAMES;
7874           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7875             Feld[newx1][newy1] = EL_FLAMES;
7876           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7877             Feld[newx2][newy2] = EL_FLAMES;
7878
7879           return;
7880         }
7881       }
7882     }
7883     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7884              Feld[newx][newy] == EL_DIAMOND)
7885     {
7886       if (IS_MOVING(newx, newy))
7887         RemoveMovingField(newx, newy);
7888       else
7889       {
7890         Feld[newx][newy] = EL_EMPTY;
7891         TEST_DrawLevelField(newx, newy);
7892       }
7893
7894       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7895     }
7896     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7897              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7898     {
7899       if (AmoebaNr[newx][newy])
7900       {
7901         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7902         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7903             Feld[newx][newy] == EL_BD_AMOEBA)
7904           AmoebaCnt[AmoebaNr[newx][newy]]--;
7905       }
7906
7907       if (IS_MOVING(newx, newy))
7908       {
7909         RemoveMovingField(newx, newy);
7910       }
7911       else
7912       {
7913         Feld[newx][newy] = EL_EMPTY;
7914         TEST_DrawLevelField(newx, newy);
7915       }
7916
7917       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7918     }
7919     else if ((element == EL_PACMAN || element == EL_MOLE)
7920              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7921     {
7922       if (AmoebaNr[newx][newy])
7923       {
7924         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7925         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7926             Feld[newx][newy] == EL_BD_AMOEBA)
7927           AmoebaCnt[AmoebaNr[newx][newy]]--;
7928       }
7929
7930       if (element == EL_MOLE)
7931       {
7932         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7933         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7934
7935         ResetGfxAnimation(x, y);
7936         GfxAction[x][y] = ACTION_DIGGING;
7937         TEST_DrawLevelField(x, y);
7938
7939         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7940
7941         return;                         /* wait for shrinking amoeba */
7942       }
7943       else      /* element == EL_PACMAN */
7944       {
7945         Feld[newx][newy] = EL_EMPTY;
7946         TEST_DrawLevelField(newx, newy);
7947         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7948       }
7949     }
7950     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7951              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7952               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7953     {
7954       /* wait for shrinking amoeba to completely disappear */
7955       return;
7956     }
7957     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7958     {
7959       /* object was running against a wall */
7960
7961       TurnRound(x, y);
7962
7963       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7964         DrawLevelElementAnimation(x, y, element);
7965
7966       if (DONT_TOUCH(element))
7967         TestIfBadThingTouchesPlayer(x, y);
7968
7969       return;
7970     }
7971
7972     InitMovingField(x, y, MovDir[x][y]);
7973
7974     PlayLevelSoundAction(x, y, ACTION_MOVING);
7975   }
7976
7977   if (MovDir[x][y])
7978     ContinueMoving(x, y);
7979 }
7980
7981 void ContinueMoving(int x, int y)
7982 {
7983   int element = Feld[x][y];
7984   struct ElementInfo *ei = &element_info[element];
7985   int direction = MovDir[x][y];
7986   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7987   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7988   int newx = x + dx, newy = y + dy;
7989   int stored = Store[x][y];
7990   int stored_new = Store[newx][newy];
7991   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7992   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7993   boolean last_line = (newy == lev_fieldy - 1);
7994
7995   MovPos[x][y] += getElementMoveStepsize(x, y);
7996
7997   if (pushed_by_player) /* special case: moving object pushed by player */
7998     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7999
8000   if (ABS(MovPos[x][y]) < TILEX)
8001   {
8002     TEST_DrawLevelField(x, y);
8003
8004     return;     /* element is still moving */
8005   }
8006
8007   /* element reached destination field */
8008
8009   Feld[x][y] = EL_EMPTY;
8010   Feld[newx][newy] = element;
8011   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8012
8013   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8014   {
8015     element = Feld[newx][newy] = EL_ACID;
8016   }
8017   else if (element == EL_MOLE)
8018   {
8019     Feld[x][y] = EL_SAND;
8020
8021     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8022   }
8023   else if (element == EL_QUICKSAND_FILLING)
8024   {
8025     element = Feld[newx][newy] = get_next_element(element);
8026     Store[newx][newy] = Store[x][y];
8027   }
8028   else if (element == EL_QUICKSAND_EMPTYING)
8029   {
8030     Feld[x][y] = get_next_element(element);
8031     element = Feld[newx][newy] = Store[x][y];
8032   }
8033   else if (element == EL_QUICKSAND_FAST_FILLING)
8034   {
8035     element = Feld[newx][newy] = get_next_element(element);
8036     Store[newx][newy] = Store[x][y];
8037   }
8038   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8039   {
8040     Feld[x][y] = get_next_element(element);
8041     element = Feld[newx][newy] = Store[x][y];
8042   }
8043   else if (element == EL_MAGIC_WALL_FILLING)
8044   {
8045     element = Feld[newx][newy] = get_next_element(element);
8046     if (!game.magic_wall_active)
8047       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8048     Store[newx][newy] = Store[x][y];
8049   }
8050   else if (element == EL_MAGIC_WALL_EMPTYING)
8051   {
8052     Feld[x][y] = get_next_element(element);
8053     if (!game.magic_wall_active)
8054       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8055     element = Feld[newx][newy] = Store[x][y];
8056
8057     InitField(newx, newy, FALSE);
8058   }
8059   else if (element == EL_BD_MAGIC_WALL_FILLING)
8060   {
8061     element = Feld[newx][newy] = get_next_element(element);
8062     if (!game.magic_wall_active)
8063       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8064     Store[newx][newy] = Store[x][y];
8065   }
8066   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8067   {
8068     Feld[x][y] = get_next_element(element);
8069     if (!game.magic_wall_active)
8070       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8071     element = Feld[newx][newy] = Store[x][y];
8072
8073     InitField(newx, newy, FALSE);
8074   }
8075   else if (element == EL_DC_MAGIC_WALL_FILLING)
8076   {
8077     element = Feld[newx][newy] = get_next_element(element);
8078     if (!game.magic_wall_active)
8079       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8080     Store[newx][newy] = Store[x][y];
8081   }
8082   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8083   {
8084     Feld[x][y] = get_next_element(element);
8085     if (!game.magic_wall_active)
8086       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8087     element = Feld[newx][newy] = Store[x][y];
8088
8089     InitField(newx, newy, FALSE);
8090   }
8091   else if (element == EL_AMOEBA_DROPPING)
8092   {
8093     Feld[x][y] = get_next_element(element);
8094     element = Feld[newx][newy] = Store[x][y];
8095   }
8096   else if (element == EL_SOKOBAN_OBJECT)
8097   {
8098     if (Back[x][y])
8099       Feld[x][y] = Back[x][y];
8100
8101     if (Back[newx][newy])
8102       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8103
8104     Back[x][y] = Back[newx][newy] = 0;
8105   }
8106
8107   Store[x][y] = EL_EMPTY;
8108   MovPos[x][y] = 0;
8109   MovDir[x][y] = 0;
8110   MovDelay[x][y] = 0;
8111
8112   MovDelay[newx][newy] = 0;
8113
8114   if (CAN_CHANGE_OR_HAS_ACTION(element))
8115   {
8116     /* copy element change control values to new field */
8117     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8118     ChangePage[newx][newy]  = ChangePage[x][y];
8119     ChangeCount[newx][newy] = ChangeCount[x][y];
8120     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8121   }
8122
8123   CustomValue[newx][newy] = CustomValue[x][y];
8124
8125   ChangeDelay[x][y] = 0;
8126   ChangePage[x][y] = -1;
8127   ChangeCount[x][y] = 0;
8128   ChangeEvent[x][y] = -1;
8129
8130   CustomValue[x][y] = 0;
8131
8132   /* copy animation control values to new field */
8133   GfxFrame[newx][newy]  = GfxFrame[x][y];
8134   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8135   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8136   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8137
8138   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8139
8140   /* some elements can leave other elements behind after moving */
8141   if (ei->move_leave_element != EL_EMPTY &&
8142       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8143       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8144   {
8145     int move_leave_element = ei->move_leave_element;
8146
8147     /* this makes it possible to leave the removed element again */
8148     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8149       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8150
8151     Feld[x][y] = move_leave_element;
8152
8153     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8154       MovDir[x][y] = direction;
8155
8156     InitField(x, y, FALSE);
8157
8158     if (GFX_CRUMBLED(Feld[x][y]))
8159       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8160
8161     if (ELEM_IS_PLAYER(move_leave_element))
8162       RelocatePlayer(x, y, move_leave_element);
8163   }
8164
8165   /* do this after checking for left-behind element */
8166   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8167
8168   if (!CAN_MOVE(element) ||
8169       (CAN_FALL(element) && direction == MV_DOWN &&
8170        (element == EL_SPRING ||
8171         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8172         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8173     GfxDir[x][y] = MovDir[newx][newy] = 0;
8174
8175   TEST_DrawLevelField(x, y);
8176   TEST_DrawLevelField(newx, newy);
8177
8178   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8179
8180   /* prevent pushed element from moving on in pushed direction */
8181   if (pushed_by_player && CAN_MOVE(element) &&
8182       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8183       !(element_info[element].move_pattern & direction))
8184     TurnRound(newx, newy);
8185
8186   /* prevent elements on conveyor belt from moving on in last direction */
8187   if (pushed_by_conveyor && CAN_FALL(element) &&
8188       direction & MV_HORIZONTAL)
8189     MovDir[newx][newy] = 0;
8190
8191   if (!pushed_by_player)
8192   {
8193     int nextx = newx + dx, nexty = newy + dy;
8194     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8195
8196     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8197
8198     if (CAN_FALL(element) && direction == MV_DOWN)
8199       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8200
8201     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8202       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8203
8204     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8205       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8206   }
8207
8208   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8209   {
8210     TestIfBadThingTouchesPlayer(newx, newy);
8211     TestIfBadThingTouchesFriend(newx, newy);
8212
8213     if (!IS_CUSTOM_ELEMENT(element))
8214       TestIfBadThingTouchesOtherBadThing(newx, newy);
8215   }
8216   else if (element == EL_PENGUIN)
8217     TestIfFriendTouchesBadThing(newx, newy);
8218
8219   if (DONT_GET_HIT_BY(element))
8220   {
8221     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8222   }
8223
8224   /* give the player one last chance (one more frame) to move away */
8225   if (CAN_FALL(element) && direction == MV_DOWN &&
8226       (last_line || (!IS_FREE(x, newy + 1) &&
8227                      (!IS_PLAYER(x, newy + 1) ||
8228                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8229     Impact(x, newy);
8230
8231   if (pushed_by_player && !game.use_change_when_pushing_bug)
8232   {
8233     int push_side = MV_DIR_OPPOSITE(direction);
8234     struct PlayerInfo *player = PLAYERINFO(x, y);
8235
8236     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8237                                player->index_bit, push_side);
8238     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8239                                         player->index_bit, push_side);
8240   }
8241
8242   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8243     MovDelay[newx][newy] = 1;
8244
8245   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8246
8247   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8248   TestIfElementHitsCustomElement(newx, newy, direction);
8249   TestIfPlayerTouchesCustomElement(newx, newy);
8250   TestIfElementTouchesCustomElement(newx, newy);
8251
8252   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8253       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8254     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8255                              MV_DIR_OPPOSITE(direction));
8256 }
8257
8258 int AmoebeNachbarNr(int ax, int ay)
8259 {
8260   int i;
8261   int element = Feld[ax][ay];
8262   int group_nr = 0;
8263   static int xy[4][2] =
8264   {
8265     { 0, -1 },
8266     { -1, 0 },
8267     { +1, 0 },
8268     { 0, +1 }
8269   };
8270
8271   for (i = 0; i < NUM_DIRECTIONS; i++)
8272   {
8273     int x = ax + xy[i][0];
8274     int y = ay + xy[i][1];
8275
8276     if (!IN_LEV_FIELD(x, y))
8277       continue;
8278
8279     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8280       group_nr = AmoebaNr[x][y];
8281   }
8282
8283   return group_nr;
8284 }
8285
8286 void AmoebenVereinigen(int ax, int ay)
8287 {
8288   int i, x, y, xx, yy;
8289   int new_group_nr = AmoebaNr[ax][ay];
8290   static int xy[4][2] =
8291   {
8292     { 0, -1 },
8293     { -1, 0 },
8294     { +1, 0 },
8295     { 0, +1 }
8296   };
8297
8298   if (new_group_nr == 0)
8299     return;
8300
8301   for (i = 0; i < NUM_DIRECTIONS; i++)
8302   {
8303     x = ax + xy[i][0];
8304     y = ay + xy[i][1];
8305
8306     if (!IN_LEV_FIELD(x, y))
8307       continue;
8308
8309     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8310          Feld[x][y] == EL_BD_AMOEBA ||
8311          Feld[x][y] == EL_AMOEBA_DEAD) &&
8312         AmoebaNr[x][y] != new_group_nr)
8313     {
8314       int old_group_nr = AmoebaNr[x][y];
8315
8316       if (old_group_nr == 0)
8317         return;
8318
8319       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8320       AmoebaCnt[old_group_nr] = 0;
8321       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8322       AmoebaCnt2[old_group_nr] = 0;
8323
8324       SCAN_PLAYFIELD(xx, yy)
8325       {
8326         if (AmoebaNr[xx][yy] == old_group_nr)
8327           AmoebaNr[xx][yy] = new_group_nr;
8328       }
8329     }
8330   }
8331 }
8332
8333 void AmoebeUmwandeln(int ax, int ay)
8334 {
8335   int i, x, y;
8336
8337   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8338   {
8339     int group_nr = AmoebaNr[ax][ay];
8340
8341 #ifdef DEBUG
8342     if (group_nr == 0)
8343     {
8344       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8345       printf("AmoebeUmwandeln(): This should never happen!\n");
8346       return;
8347     }
8348 #endif
8349
8350     SCAN_PLAYFIELD(x, y)
8351     {
8352       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8353       {
8354         AmoebaNr[x][y] = 0;
8355         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8356       }
8357     }
8358
8359     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8360                             SND_AMOEBA_TURNING_TO_GEM :
8361                             SND_AMOEBA_TURNING_TO_ROCK));
8362     Bang(ax, ay);
8363   }
8364   else
8365   {
8366     static int xy[4][2] =
8367     {
8368       { 0, -1 },
8369       { -1, 0 },
8370       { +1, 0 },
8371       { 0, +1 }
8372     };
8373
8374     for (i = 0; i < NUM_DIRECTIONS; i++)
8375     {
8376       x = ax + xy[i][0];
8377       y = ay + xy[i][1];
8378
8379       if (!IN_LEV_FIELD(x, y))
8380         continue;
8381
8382       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8383       {
8384         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8385                               SND_AMOEBA_TURNING_TO_GEM :
8386                               SND_AMOEBA_TURNING_TO_ROCK));
8387         Bang(x, y);
8388       }
8389     }
8390   }
8391 }
8392
8393 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8394 {
8395   int x, y;
8396   int group_nr = AmoebaNr[ax][ay];
8397   boolean done = FALSE;
8398
8399 #ifdef DEBUG
8400   if (group_nr == 0)
8401   {
8402     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8403     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8404     return;
8405   }
8406 #endif
8407
8408   SCAN_PLAYFIELD(x, y)
8409   {
8410     if (AmoebaNr[x][y] == group_nr &&
8411         (Feld[x][y] == EL_AMOEBA_DEAD ||
8412          Feld[x][y] == EL_BD_AMOEBA ||
8413          Feld[x][y] == EL_AMOEBA_GROWING))
8414     {
8415       AmoebaNr[x][y] = 0;
8416       Feld[x][y] = new_element;
8417       InitField(x, y, FALSE);
8418       TEST_DrawLevelField(x, y);
8419       done = TRUE;
8420     }
8421   }
8422
8423   if (done)
8424     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8425                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8426                             SND_BD_AMOEBA_TURNING_TO_GEM));
8427 }
8428
8429 void AmoebeWaechst(int x, int y)
8430 {
8431   static unsigned int sound_delay = 0;
8432   static unsigned int sound_delay_value = 0;
8433
8434   if (!MovDelay[x][y])          /* start new growing cycle */
8435   {
8436     MovDelay[x][y] = 7;
8437
8438     if (DelayReached(&sound_delay, sound_delay_value))
8439     {
8440       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8441       sound_delay_value = 30;
8442     }
8443   }
8444
8445   if (MovDelay[x][y])           /* wait some time before growing bigger */
8446   {
8447     MovDelay[x][y]--;
8448     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8449     {
8450       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8451                                            6 - MovDelay[x][y]);
8452
8453       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8454     }
8455
8456     if (!MovDelay[x][y])
8457     {
8458       Feld[x][y] = Store[x][y];
8459       Store[x][y] = 0;
8460       TEST_DrawLevelField(x, y);
8461     }
8462   }
8463 }
8464
8465 void AmoebaDisappearing(int x, int y)
8466 {
8467   static unsigned int sound_delay = 0;
8468   static unsigned int sound_delay_value = 0;
8469
8470   if (!MovDelay[x][y])          /* start new shrinking cycle */
8471   {
8472     MovDelay[x][y] = 7;
8473
8474     if (DelayReached(&sound_delay, sound_delay_value))
8475       sound_delay_value = 30;
8476   }
8477
8478   if (MovDelay[x][y])           /* wait some time before shrinking */
8479   {
8480     MovDelay[x][y]--;
8481     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8482     {
8483       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8484                                            6 - MovDelay[x][y]);
8485
8486       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8487     }
8488
8489     if (!MovDelay[x][y])
8490     {
8491       Feld[x][y] = EL_EMPTY;
8492       TEST_DrawLevelField(x, y);
8493
8494       /* don't let mole enter this field in this cycle;
8495          (give priority to objects falling to this field from above) */
8496       Stop[x][y] = TRUE;
8497     }
8498   }
8499 }
8500
8501 void AmoebeAbleger(int ax, int ay)
8502 {
8503   int i;
8504   int element = Feld[ax][ay];
8505   int graphic = el2img(element);
8506   int newax = ax, neway = ay;
8507   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8508   static int xy[4][2] =
8509   {
8510     { 0, -1 },
8511     { -1, 0 },
8512     { +1, 0 },
8513     { 0, +1 }
8514   };
8515
8516   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8517   {
8518     Feld[ax][ay] = EL_AMOEBA_DEAD;
8519     TEST_DrawLevelField(ax, ay);
8520     return;
8521   }
8522
8523   if (IS_ANIMATED(graphic))
8524     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8525
8526   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8527     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8528
8529   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8530   {
8531     MovDelay[ax][ay]--;
8532     if (MovDelay[ax][ay])
8533       return;
8534   }
8535
8536   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8537   {
8538     int start = RND(4);
8539     int x = ax + xy[start][0];
8540     int y = ay + xy[start][1];
8541
8542     if (!IN_LEV_FIELD(x, y))
8543       return;
8544
8545     if (IS_FREE(x, y) ||
8546         CAN_GROW_INTO(Feld[x][y]) ||
8547         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8548         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8549     {
8550       newax = x;
8551       neway = y;
8552     }
8553
8554     if (newax == ax && neway == ay)
8555       return;
8556   }
8557   else                          /* normal or "filled" (BD style) amoeba */
8558   {
8559     int start = RND(4);
8560     boolean waiting_for_player = FALSE;
8561
8562     for (i = 0; i < NUM_DIRECTIONS; i++)
8563     {
8564       int j = (start + i) % 4;
8565       int x = ax + xy[j][0];
8566       int y = ay + xy[j][1];
8567
8568       if (!IN_LEV_FIELD(x, y))
8569         continue;
8570
8571       if (IS_FREE(x, y) ||
8572           CAN_GROW_INTO(Feld[x][y]) ||
8573           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8574           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8575       {
8576         newax = x;
8577         neway = y;
8578         break;
8579       }
8580       else if (IS_PLAYER(x, y))
8581         waiting_for_player = TRUE;
8582     }
8583
8584     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8585     {
8586       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8587       {
8588         Feld[ax][ay] = EL_AMOEBA_DEAD;
8589         TEST_DrawLevelField(ax, ay);
8590         AmoebaCnt[AmoebaNr[ax][ay]]--;
8591
8592         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8593         {
8594           if (element == EL_AMOEBA_FULL)
8595             AmoebeUmwandeln(ax, ay);
8596           else if (element == EL_BD_AMOEBA)
8597             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8598         }
8599       }
8600       return;
8601     }
8602     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8603     {
8604       /* amoeba gets larger by growing in some direction */
8605
8606       int new_group_nr = AmoebaNr[ax][ay];
8607
8608 #ifdef DEBUG
8609   if (new_group_nr == 0)
8610   {
8611     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8612     printf("AmoebeAbleger(): This should never happen!\n");
8613     return;
8614   }
8615 #endif
8616
8617       AmoebaNr[newax][neway] = new_group_nr;
8618       AmoebaCnt[new_group_nr]++;
8619       AmoebaCnt2[new_group_nr]++;
8620
8621       /* if amoeba touches other amoeba(s) after growing, unify them */
8622       AmoebenVereinigen(newax, neway);
8623
8624       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8625       {
8626         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8627         return;
8628       }
8629     }
8630   }
8631
8632   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8633       (neway == lev_fieldy - 1 && newax != ax))
8634   {
8635     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8636     Store[newax][neway] = element;
8637   }
8638   else if (neway == ay || element == EL_EMC_DRIPPER)
8639   {
8640     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8641
8642     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8643   }
8644   else
8645   {
8646     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8647     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8648     Store[ax][ay] = EL_AMOEBA_DROP;
8649     ContinueMoving(ax, ay);
8650     return;
8651   }
8652
8653   TEST_DrawLevelField(newax, neway);
8654 }
8655
8656 void Life(int ax, int ay)
8657 {
8658   int x1, y1, x2, y2;
8659   int life_time = 40;
8660   int element = Feld[ax][ay];
8661   int graphic = el2img(element);
8662   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8663                          level.biomaze);
8664   boolean changed = FALSE;
8665
8666   if (IS_ANIMATED(graphic))
8667     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8668
8669   if (Stop[ax][ay])
8670     return;
8671
8672   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8673     MovDelay[ax][ay] = life_time;
8674
8675   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8676   {
8677     MovDelay[ax][ay]--;
8678     if (MovDelay[ax][ay])
8679       return;
8680   }
8681
8682   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8683   {
8684     int xx = ax+x1, yy = ay+y1;
8685     int nachbarn = 0;
8686
8687     if (!IN_LEV_FIELD(xx, yy))
8688       continue;
8689
8690     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8691     {
8692       int x = xx+x2, y = yy+y2;
8693
8694       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8695         continue;
8696
8697       if (((Feld[x][y] == element ||
8698             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8699            !Stop[x][y]) ||
8700           (IS_FREE(x, y) && Stop[x][y]))
8701         nachbarn++;
8702     }
8703
8704     if (xx == ax && yy == ay)           /* field in the middle */
8705     {
8706       if (nachbarn < life_parameter[0] ||
8707           nachbarn > life_parameter[1])
8708       {
8709         Feld[xx][yy] = EL_EMPTY;
8710         if (!Stop[xx][yy])
8711           TEST_DrawLevelField(xx, yy);
8712         Stop[xx][yy] = TRUE;
8713         changed = TRUE;
8714       }
8715     }
8716     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8717     {                                   /* free border field */
8718       if (nachbarn >= life_parameter[2] &&
8719           nachbarn <= life_parameter[3])
8720       {
8721         Feld[xx][yy] = element;
8722         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8723         if (!Stop[xx][yy])
8724           TEST_DrawLevelField(xx, yy);
8725         Stop[xx][yy] = TRUE;
8726         changed = TRUE;
8727       }
8728     }
8729   }
8730
8731   if (changed)
8732     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8733                    SND_GAME_OF_LIFE_GROWING);
8734 }
8735
8736 static void InitRobotWheel(int x, int y)
8737 {
8738   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8739 }
8740
8741 static void RunRobotWheel(int x, int y)
8742 {
8743   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8744 }
8745
8746 static void StopRobotWheel(int x, int y)
8747 {
8748   if (ZX == x && ZY == y)
8749   {
8750     ZX = ZY = -1;
8751
8752     game.robot_wheel_active = FALSE;
8753   }
8754 }
8755
8756 static void InitTimegateWheel(int x, int y)
8757 {
8758   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8759 }
8760
8761 static void RunTimegateWheel(int x, int y)
8762 {
8763   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8764 }
8765
8766 static void InitMagicBallDelay(int x, int y)
8767 {
8768   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8769 }
8770
8771 static void ActivateMagicBall(int bx, int by)
8772 {
8773   int x, y;
8774
8775   if (level.ball_random)
8776   {
8777     int pos_border = RND(8);    /* select one of the eight border elements */
8778     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8779     int xx = pos_content % 3;
8780     int yy = pos_content / 3;
8781
8782     x = bx - 1 + xx;
8783     y = by - 1 + yy;
8784
8785     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8786       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8787   }
8788   else
8789   {
8790     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8791     {
8792       int xx = x - bx + 1;
8793       int yy = y - by + 1;
8794
8795       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8796         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8797     }
8798   }
8799
8800   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8801 }
8802
8803 void CheckExit(int x, int y)
8804 {
8805   if (local_player->gems_still_needed > 0 ||
8806       local_player->sokobanfields_still_needed > 0 ||
8807       local_player->lights_still_needed > 0)
8808   {
8809     int element = Feld[x][y];
8810     int graphic = el2img(element);
8811
8812     if (IS_ANIMATED(graphic))
8813       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8814
8815     return;
8816   }
8817
8818   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8819     return;
8820
8821   Feld[x][y] = EL_EXIT_OPENING;
8822
8823   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8824 }
8825
8826 void CheckExitEM(int x, int y)
8827 {
8828   if (local_player->gems_still_needed > 0 ||
8829       local_player->sokobanfields_still_needed > 0 ||
8830       local_player->lights_still_needed > 0)
8831   {
8832     int element = Feld[x][y];
8833     int graphic = el2img(element);
8834
8835     if (IS_ANIMATED(graphic))
8836       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8837
8838     return;
8839   }
8840
8841   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8842     return;
8843
8844   Feld[x][y] = EL_EM_EXIT_OPENING;
8845
8846   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8847 }
8848
8849 void CheckExitSteel(int x, int y)
8850 {
8851   if (local_player->gems_still_needed > 0 ||
8852       local_player->sokobanfields_still_needed > 0 ||
8853       local_player->lights_still_needed > 0)
8854   {
8855     int element = Feld[x][y];
8856     int graphic = el2img(element);
8857
8858     if (IS_ANIMATED(graphic))
8859       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8860
8861     return;
8862   }
8863
8864   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8865     return;
8866
8867   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8868
8869   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8870 }
8871
8872 void CheckExitSteelEM(int x, int y)
8873 {
8874   if (local_player->gems_still_needed > 0 ||
8875       local_player->sokobanfields_still_needed > 0 ||
8876       local_player->lights_still_needed > 0)
8877   {
8878     int element = Feld[x][y];
8879     int graphic = el2img(element);
8880
8881     if (IS_ANIMATED(graphic))
8882       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8883
8884     return;
8885   }
8886
8887   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8888     return;
8889
8890   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8891
8892   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8893 }
8894
8895 void CheckExitSP(int x, int y)
8896 {
8897   if (local_player->gems_still_needed > 0)
8898   {
8899     int element = Feld[x][y];
8900     int graphic = el2img(element);
8901
8902     if (IS_ANIMATED(graphic))
8903       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8904
8905     return;
8906   }
8907
8908   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8909     return;
8910
8911   Feld[x][y] = EL_SP_EXIT_OPENING;
8912
8913   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8914 }
8915
8916 static void CloseAllOpenTimegates()
8917 {
8918   int x, y;
8919
8920   SCAN_PLAYFIELD(x, y)
8921   {
8922     int element = Feld[x][y];
8923
8924     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8925     {
8926       Feld[x][y] = EL_TIMEGATE_CLOSING;
8927
8928       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8929     }
8930   }
8931 }
8932
8933 void DrawTwinkleOnField(int x, int y)
8934 {
8935   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8936     return;
8937
8938   if (Feld[x][y] == EL_BD_DIAMOND)
8939     return;
8940
8941   if (MovDelay[x][y] == 0)      /* next animation frame */
8942     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8943
8944   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8945   {
8946     MovDelay[x][y]--;
8947
8948     DrawLevelElementAnimation(x, y, Feld[x][y]);
8949
8950     if (MovDelay[x][y] != 0)
8951     {
8952       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8953                                            10 - MovDelay[x][y]);
8954
8955       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8956     }
8957   }
8958 }
8959
8960 void MauerWaechst(int x, int y)
8961 {
8962   int delay = 6;
8963
8964   if (!MovDelay[x][y])          /* next animation frame */
8965     MovDelay[x][y] = 3 * delay;
8966
8967   if (MovDelay[x][y])           /* wait some time before next frame */
8968   {
8969     MovDelay[x][y]--;
8970
8971     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8972     {
8973       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8974       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8975
8976       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8977     }
8978
8979     if (!MovDelay[x][y])
8980     {
8981       if (MovDir[x][y] == MV_LEFT)
8982       {
8983         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8984           TEST_DrawLevelField(x - 1, y);
8985       }
8986       else if (MovDir[x][y] == MV_RIGHT)
8987       {
8988         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8989           TEST_DrawLevelField(x + 1, y);
8990       }
8991       else if (MovDir[x][y] == MV_UP)
8992       {
8993         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8994           TEST_DrawLevelField(x, y - 1);
8995       }
8996       else
8997       {
8998         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8999           TEST_DrawLevelField(x, y + 1);
9000       }
9001
9002       Feld[x][y] = Store[x][y];
9003       Store[x][y] = 0;
9004       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9005       TEST_DrawLevelField(x, y);
9006     }
9007   }
9008 }
9009
9010 void MauerAbleger(int ax, int ay)
9011 {
9012   int element = Feld[ax][ay];
9013   int graphic = el2img(element);
9014   boolean oben_frei = FALSE, unten_frei = FALSE;
9015   boolean links_frei = FALSE, rechts_frei = FALSE;
9016   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9017   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9018   boolean new_wall = FALSE;
9019
9020   if (IS_ANIMATED(graphic))
9021     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9022
9023   if (!MovDelay[ax][ay])        /* start building new wall */
9024     MovDelay[ax][ay] = 6;
9025
9026   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9027   {
9028     MovDelay[ax][ay]--;
9029     if (MovDelay[ax][ay])
9030       return;
9031   }
9032
9033   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9034     oben_frei = TRUE;
9035   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9036     unten_frei = TRUE;
9037   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9038     links_frei = TRUE;
9039   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9040     rechts_frei = TRUE;
9041
9042   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9043       element == EL_EXPANDABLE_WALL_ANY)
9044   {
9045     if (oben_frei)
9046     {
9047       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9048       Store[ax][ay-1] = element;
9049       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9050       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9051         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9052                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9053       new_wall = TRUE;
9054     }
9055     if (unten_frei)
9056     {
9057       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9058       Store[ax][ay+1] = element;
9059       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9060       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9061         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9062                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9063       new_wall = TRUE;
9064     }
9065   }
9066
9067   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9068       element == EL_EXPANDABLE_WALL_ANY ||
9069       element == EL_EXPANDABLE_WALL ||
9070       element == EL_BD_EXPANDABLE_WALL)
9071   {
9072     if (links_frei)
9073     {
9074       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9075       Store[ax-1][ay] = element;
9076       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9077       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9078         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9079                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9080       new_wall = TRUE;
9081     }
9082
9083     if (rechts_frei)
9084     {
9085       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9086       Store[ax+1][ay] = element;
9087       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9088       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9089         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9090                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9091       new_wall = TRUE;
9092     }
9093   }
9094
9095   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9096     TEST_DrawLevelField(ax, ay);
9097
9098   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9099     oben_massiv = TRUE;
9100   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9101     unten_massiv = TRUE;
9102   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9103     links_massiv = TRUE;
9104   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9105     rechts_massiv = TRUE;
9106
9107   if (((oben_massiv && unten_massiv) ||
9108        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9109        element == EL_EXPANDABLE_WALL) &&
9110       ((links_massiv && rechts_massiv) ||
9111        element == EL_EXPANDABLE_WALL_VERTICAL))
9112     Feld[ax][ay] = EL_WALL;
9113
9114   if (new_wall)
9115     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9116 }
9117
9118 void MauerAblegerStahl(int ax, int ay)
9119 {
9120   int element = Feld[ax][ay];
9121   int graphic = el2img(element);
9122   boolean oben_frei = FALSE, unten_frei = FALSE;
9123   boolean links_frei = FALSE, rechts_frei = FALSE;
9124   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9125   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9126   boolean new_wall = FALSE;
9127
9128   if (IS_ANIMATED(graphic))
9129     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9130
9131   if (!MovDelay[ax][ay])        /* start building new wall */
9132     MovDelay[ax][ay] = 6;
9133
9134   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9135   {
9136     MovDelay[ax][ay]--;
9137     if (MovDelay[ax][ay])
9138       return;
9139   }
9140
9141   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9142     oben_frei = TRUE;
9143   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9144     unten_frei = TRUE;
9145   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9146     links_frei = TRUE;
9147   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9148     rechts_frei = TRUE;
9149
9150   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9151       element == EL_EXPANDABLE_STEELWALL_ANY)
9152   {
9153     if (oben_frei)
9154     {
9155       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9156       Store[ax][ay-1] = element;
9157       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9158       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9159         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9160                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9161       new_wall = TRUE;
9162     }
9163     if (unten_frei)
9164     {
9165       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9166       Store[ax][ay+1] = element;
9167       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9168       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9169         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9170                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9171       new_wall = TRUE;
9172     }
9173   }
9174
9175   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9176       element == EL_EXPANDABLE_STEELWALL_ANY)
9177   {
9178     if (links_frei)
9179     {
9180       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9181       Store[ax-1][ay] = element;
9182       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9183       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9184         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9185                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9186       new_wall = TRUE;
9187     }
9188
9189     if (rechts_frei)
9190     {
9191       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9192       Store[ax+1][ay] = element;
9193       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9194       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9195         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9196                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9197       new_wall = TRUE;
9198     }
9199   }
9200
9201   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9202     oben_massiv = TRUE;
9203   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9204     unten_massiv = TRUE;
9205   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9206     links_massiv = TRUE;
9207   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9208     rechts_massiv = TRUE;
9209
9210   if (((oben_massiv && unten_massiv) ||
9211        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9212       ((links_massiv && rechts_massiv) ||
9213        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9214     Feld[ax][ay] = EL_STEELWALL;
9215
9216   if (new_wall)
9217     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9218 }
9219
9220 void CheckForDragon(int x, int y)
9221 {
9222   int i, j;
9223   boolean dragon_found = FALSE;
9224   static int xy[4][2] =
9225   {
9226     { 0, -1 },
9227     { -1, 0 },
9228     { +1, 0 },
9229     { 0, +1 }
9230   };
9231
9232   for (i = 0; i < NUM_DIRECTIONS; i++)
9233   {
9234     for (j = 0; j < 4; j++)
9235     {
9236       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9237
9238       if (IN_LEV_FIELD(xx, yy) &&
9239           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9240       {
9241         if (Feld[xx][yy] == EL_DRAGON)
9242           dragon_found = TRUE;
9243       }
9244       else
9245         break;
9246     }
9247   }
9248
9249   if (!dragon_found)
9250   {
9251     for (i = 0; i < NUM_DIRECTIONS; i++)
9252     {
9253       for (j = 0; j < 3; j++)
9254       {
9255         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9256   
9257         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9258         {
9259           Feld[xx][yy] = EL_EMPTY;
9260           TEST_DrawLevelField(xx, yy);
9261         }
9262         else
9263           break;
9264       }
9265     }
9266   }
9267 }
9268
9269 static void InitBuggyBase(int x, int y)
9270 {
9271   int element = Feld[x][y];
9272   int activating_delay = FRAMES_PER_SECOND / 4;
9273
9274   ChangeDelay[x][y] =
9275     (element == EL_SP_BUGGY_BASE ?
9276      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9277      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9278      activating_delay :
9279      element == EL_SP_BUGGY_BASE_ACTIVE ?
9280      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9281 }
9282
9283 static void WarnBuggyBase(int x, int y)
9284 {
9285   int i;
9286   static int xy[4][2] =
9287   {
9288     { 0, -1 },
9289     { -1, 0 },
9290     { +1, 0 },
9291     { 0, +1 }
9292   };
9293
9294   for (i = 0; i < NUM_DIRECTIONS; i++)
9295   {
9296     int xx = x + xy[i][0];
9297     int yy = y + xy[i][1];
9298
9299     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9300     {
9301       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9302
9303       break;
9304     }
9305   }
9306 }
9307
9308 static void InitTrap(int x, int y)
9309 {
9310   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9311 }
9312
9313 static void ActivateTrap(int x, int y)
9314 {
9315   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9316 }
9317
9318 static void ChangeActiveTrap(int x, int y)
9319 {
9320   int graphic = IMG_TRAP_ACTIVE;
9321
9322   /* if new animation frame was drawn, correct crumbled sand border */
9323   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9324     TEST_DrawLevelFieldCrumbled(x, y);
9325 }
9326
9327 static int getSpecialActionElement(int element, int number, int base_element)
9328 {
9329   return (element != EL_EMPTY ? element :
9330           number != -1 ? base_element + number - 1 :
9331           EL_EMPTY);
9332 }
9333
9334 static int getModifiedActionNumber(int value_old, int operator, int operand,
9335                                    int value_min, int value_max)
9336 {
9337   int value_new = (operator == CA_MODE_SET      ? operand :
9338                    operator == CA_MODE_ADD      ? value_old + operand :
9339                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9340                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9341                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9342                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9343                    value_old);
9344
9345   return (value_new < value_min ? value_min :
9346           value_new > value_max ? value_max :
9347           value_new);
9348 }
9349
9350 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9351 {
9352   struct ElementInfo *ei = &element_info[element];
9353   struct ElementChangeInfo *change = &ei->change_page[page];
9354   int target_element = change->target_element;
9355   int action_type = change->action_type;
9356   int action_mode = change->action_mode;
9357   int action_arg = change->action_arg;
9358   int action_element = change->action_element;
9359   int i;
9360
9361   if (!change->has_action)
9362     return;
9363
9364   /* ---------- determine action paramater values -------------------------- */
9365
9366   int level_time_value =
9367     (level.time > 0 ? TimeLeft :
9368      TimePlayed);
9369
9370   int action_arg_element_raw =
9371     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9372      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9373      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9374      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9375      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9376      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9377      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9378      EL_EMPTY);
9379   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9380
9381   int action_arg_direction =
9382     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9383      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9384      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9385      change->actual_trigger_side :
9386      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9387      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9388      MV_NONE);
9389
9390   int action_arg_number_min =
9391     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9392      CA_ARG_MIN);
9393
9394   int action_arg_number_max =
9395     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9396      action_type == CA_SET_LEVEL_GEMS ? 999 :
9397      action_type == CA_SET_LEVEL_TIME ? 9999 :
9398      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9399      action_type == CA_SET_CE_VALUE ? 9999 :
9400      action_type == CA_SET_CE_SCORE ? 9999 :
9401      CA_ARG_MAX);
9402
9403   int action_arg_number_reset =
9404     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9405      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9406      action_type == CA_SET_LEVEL_TIME ? level.time :
9407      action_type == CA_SET_LEVEL_SCORE ? 0 :
9408      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9409      action_type == CA_SET_CE_SCORE ? 0 :
9410      0);
9411
9412   int action_arg_number =
9413     (action_arg <= CA_ARG_MAX ? action_arg :
9414      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9415      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9416      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9417      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9418      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9419      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9420      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9421      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9422      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9423      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9424      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9425      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9426      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9427      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9428      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9429      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9430      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9431      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9432      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9433      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9434      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9435      -1);
9436
9437   int action_arg_number_old =
9438     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9439      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9440      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9441      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9442      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9443      0);
9444
9445   int action_arg_number_new =
9446     getModifiedActionNumber(action_arg_number_old,
9447                             action_mode, action_arg_number,
9448                             action_arg_number_min, action_arg_number_max);
9449
9450   int trigger_player_bits =
9451     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9452      change->actual_trigger_player_bits : change->trigger_player);
9453
9454   int action_arg_player_bits =
9455     (action_arg >= CA_ARG_PLAYER_1 &&
9456      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9457      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9458      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9459      PLAYER_BITS_ANY);
9460
9461   /* ---------- execute action  -------------------------------------------- */
9462
9463   switch (action_type)
9464   {
9465     case CA_NO_ACTION:
9466     {
9467       return;
9468     }
9469
9470     /* ---------- level actions  ------------------------------------------- */
9471
9472     case CA_RESTART_LEVEL:
9473     {
9474       game.restart_level = TRUE;
9475
9476       break;
9477     }
9478
9479     case CA_SHOW_ENVELOPE:
9480     {
9481       int element = getSpecialActionElement(action_arg_element,
9482                                             action_arg_number, EL_ENVELOPE_1);
9483
9484       if (IS_ENVELOPE(element))
9485         local_player->show_envelope = element;
9486
9487       break;
9488     }
9489
9490     case CA_SET_LEVEL_TIME:
9491     {
9492       if (level.time > 0)       /* only modify limited time value */
9493       {
9494         TimeLeft = action_arg_number_new;
9495
9496         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9497
9498         DisplayGameControlValues();
9499
9500         if (!TimeLeft && setup.time_limit)
9501           for (i = 0; i < MAX_PLAYERS; i++)
9502             KillPlayer(&stored_player[i]);
9503       }
9504
9505       break;
9506     }
9507
9508     case CA_SET_LEVEL_SCORE:
9509     {
9510       local_player->score = action_arg_number_new;
9511
9512       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9513
9514       DisplayGameControlValues();
9515
9516       break;
9517     }
9518
9519     case CA_SET_LEVEL_GEMS:
9520     {
9521       local_player->gems_still_needed = action_arg_number_new;
9522
9523       game.snapshot.collected_item = TRUE;
9524
9525       game_panel_controls[GAME_PANEL_GEMS].value =
9526         local_player->gems_still_needed;
9527
9528       DisplayGameControlValues();
9529
9530       break;
9531     }
9532
9533     case CA_SET_LEVEL_WIND:
9534     {
9535       game.wind_direction = action_arg_direction;
9536
9537       break;
9538     }
9539
9540     case CA_SET_LEVEL_RANDOM_SEED:
9541     {
9542       /* ensure that setting a new random seed while playing is predictable */
9543       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9544
9545       break;
9546     }
9547
9548     /* ---------- player actions  ------------------------------------------ */
9549
9550     case CA_MOVE_PLAYER:
9551     {
9552       /* automatically move to the next field in specified direction */
9553       for (i = 0; i < MAX_PLAYERS; i++)
9554         if (trigger_player_bits & (1 << i))
9555           stored_player[i].programmed_action = action_arg_direction;
9556
9557       break;
9558     }
9559
9560     case CA_EXIT_PLAYER:
9561     {
9562       for (i = 0; i < MAX_PLAYERS; i++)
9563         if (action_arg_player_bits & (1 << i))
9564           PlayerWins(&stored_player[i]);
9565
9566       break;
9567     }
9568
9569     case CA_KILL_PLAYER:
9570     {
9571       for (i = 0; i < MAX_PLAYERS; i++)
9572         if (action_arg_player_bits & (1 << i))
9573           KillPlayer(&stored_player[i]);
9574
9575       break;
9576     }
9577
9578     case CA_SET_PLAYER_KEYS:
9579     {
9580       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9581       int element = getSpecialActionElement(action_arg_element,
9582                                             action_arg_number, EL_KEY_1);
9583
9584       if (IS_KEY(element))
9585       {
9586         for (i = 0; i < MAX_PLAYERS; i++)
9587         {
9588           if (trigger_player_bits & (1 << i))
9589           {
9590             stored_player[i].key[KEY_NR(element)] = key_state;
9591
9592             DrawGameDoorValues();
9593           }
9594         }
9595       }
9596
9597       break;
9598     }
9599
9600     case CA_SET_PLAYER_SPEED:
9601     {
9602       for (i = 0; i < MAX_PLAYERS; i++)
9603       {
9604         if (trigger_player_bits & (1 << i))
9605         {
9606           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9607
9608           if (action_arg == CA_ARG_SPEED_FASTER &&
9609               stored_player[i].cannot_move)
9610           {
9611             action_arg_number = STEPSIZE_VERY_SLOW;
9612           }
9613           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9614                    action_arg == CA_ARG_SPEED_FASTER)
9615           {
9616             action_arg_number = 2;
9617             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9618                            CA_MODE_MULTIPLY);
9619           }
9620           else if (action_arg == CA_ARG_NUMBER_RESET)
9621           {
9622             action_arg_number = level.initial_player_stepsize[i];
9623           }
9624
9625           move_stepsize =
9626             getModifiedActionNumber(move_stepsize,
9627                                     action_mode,
9628                                     action_arg_number,
9629                                     action_arg_number_min,
9630                                     action_arg_number_max);
9631
9632           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9633         }
9634       }
9635
9636       break;
9637     }
9638
9639     case CA_SET_PLAYER_SHIELD:
9640     {
9641       for (i = 0; i < MAX_PLAYERS; i++)
9642       {
9643         if (trigger_player_bits & (1 << i))
9644         {
9645           if (action_arg == CA_ARG_SHIELD_OFF)
9646           {
9647             stored_player[i].shield_normal_time_left = 0;
9648             stored_player[i].shield_deadly_time_left = 0;
9649           }
9650           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9651           {
9652             stored_player[i].shield_normal_time_left = 999999;
9653           }
9654           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9655           {
9656             stored_player[i].shield_normal_time_left = 999999;
9657             stored_player[i].shield_deadly_time_left = 999999;
9658           }
9659         }
9660       }
9661
9662       break;
9663     }
9664
9665     case CA_SET_PLAYER_GRAVITY:
9666     {
9667       for (i = 0; i < MAX_PLAYERS; i++)
9668       {
9669         if (trigger_player_bits & (1 << i))
9670         {
9671           stored_player[i].gravity =
9672             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9673              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9674              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9675              stored_player[i].gravity);
9676         }
9677       }
9678
9679       break;
9680     }
9681
9682     case CA_SET_PLAYER_ARTWORK:
9683     {
9684       for (i = 0; i < MAX_PLAYERS; i++)
9685       {
9686         if (trigger_player_bits & (1 << i))
9687         {
9688           int artwork_element = action_arg_element;
9689
9690           if (action_arg == CA_ARG_ELEMENT_RESET)
9691             artwork_element =
9692               (level.use_artwork_element[i] ? level.artwork_element[i] :
9693                stored_player[i].element_nr);
9694
9695           if (stored_player[i].artwork_element != artwork_element)
9696             stored_player[i].Frame = 0;
9697
9698           stored_player[i].artwork_element = artwork_element;
9699
9700           SetPlayerWaiting(&stored_player[i], FALSE);
9701
9702           /* set number of special actions for bored and sleeping animation */
9703           stored_player[i].num_special_action_bored =
9704             get_num_special_action(artwork_element,
9705                                    ACTION_BORING_1, ACTION_BORING_LAST);
9706           stored_player[i].num_special_action_sleeping =
9707             get_num_special_action(artwork_element,
9708                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9709         }
9710       }
9711
9712       break;
9713     }
9714
9715     case CA_SET_PLAYER_INVENTORY:
9716     {
9717       for (i = 0; i < MAX_PLAYERS; i++)
9718       {
9719         struct PlayerInfo *player = &stored_player[i];
9720         int j, k;
9721
9722         if (trigger_player_bits & (1 << i))
9723         {
9724           int inventory_element = action_arg_element;
9725
9726           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9727               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9728               action_arg == CA_ARG_ELEMENT_ACTION)
9729           {
9730             int element = inventory_element;
9731             int collect_count = element_info[element].collect_count_initial;
9732
9733             if (!IS_CUSTOM_ELEMENT(element))
9734               collect_count = 1;
9735
9736             if (collect_count == 0)
9737               player->inventory_infinite_element = element;
9738             else
9739               for (k = 0; k < collect_count; k++)
9740                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9741                   player->inventory_element[player->inventory_size++] =
9742                     element;
9743           }
9744           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9745                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9746                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9747           {
9748             if (player->inventory_infinite_element != EL_UNDEFINED &&
9749                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9750                                      action_arg_element_raw))
9751               player->inventory_infinite_element = EL_UNDEFINED;
9752
9753             for (k = 0, j = 0; j < player->inventory_size; j++)
9754             {
9755               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9756                                         action_arg_element_raw))
9757                 player->inventory_element[k++] = player->inventory_element[j];
9758             }
9759
9760             player->inventory_size = k;
9761           }
9762           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9763           {
9764             if (player->inventory_size > 0)
9765             {
9766               for (j = 0; j < player->inventory_size - 1; j++)
9767                 player->inventory_element[j] = player->inventory_element[j + 1];
9768
9769               player->inventory_size--;
9770             }
9771           }
9772           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9773           {
9774             if (player->inventory_size > 0)
9775               player->inventory_size--;
9776           }
9777           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9778           {
9779             player->inventory_infinite_element = EL_UNDEFINED;
9780             player->inventory_size = 0;
9781           }
9782           else if (action_arg == CA_ARG_INVENTORY_RESET)
9783           {
9784             player->inventory_infinite_element = EL_UNDEFINED;
9785             player->inventory_size = 0;
9786
9787             if (level.use_initial_inventory[i])
9788             {
9789               for (j = 0; j < level.initial_inventory_size[i]; j++)
9790               {
9791                 int element = level.initial_inventory_content[i][j];
9792                 int collect_count = element_info[element].collect_count_initial;
9793
9794                 if (!IS_CUSTOM_ELEMENT(element))
9795                   collect_count = 1;
9796
9797                 if (collect_count == 0)
9798                   player->inventory_infinite_element = element;
9799                 else
9800                   for (k = 0; k < collect_count; k++)
9801                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9802                       player->inventory_element[player->inventory_size++] =
9803                         element;
9804               }
9805             }
9806           }
9807         }
9808       }
9809
9810       break;
9811     }
9812
9813     /* ---------- CE actions  ---------------------------------------------- */
9814
9815     case CA_SET_CE_VALUE:
9816     {
9817       int last_ce_value = CustomValue[x][y];
9818
9819       CustomValue[x][y] = action_arg_number_new;
9820
9821       if (CustomValue[x][y] != last_ce_value)
9822       {
9823         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9824         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9825
9826         if (CustomValue[x][y] == 0)
9827         {
9828           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9829           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9830         }
9831       }
9832
9833       break;
9834     }
9835
9836     case CA_SET_CE_SCORE:
9837     {
9838       int last_ce_score = ei->collect_score;
9839
9840       ei->collect_score = action_arg_number_new;
9841
9842       if (ei->collect_score != last_ce_score)
9843       {
9844         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9845         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9846
9847         if (ei->collect_score == 0)
9848         {
9849           int xx, yy;
9850
9851           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9852           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9853
9854           /*
9855             This is a very special case that seems to be a mixture between
9856             CheckElementChange() and CheckTriggeredElementChange(): while
9857             the first one only affects single elements that are triggered
9858             directly, the second one affects multiple elements in the playfield
9859             that are triggered indirectly by another element. This is a third
9860             case: Changing the CE score always affects multiple identical CEs,
9861             so every affected CE must be checked, not only the single CE for
9862             which the CE score was changed in the first place (as every instance
9863             of that CE shares the same CE score, and therefore also can change)!
9864           */
9865           SCAN_PLAYFIELD(xx, yy)
9866           {
9867             if (Feld[xx][yy] == element)
9868               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9869                                  CE_SCORE_GETS_ZERO);
9870           }
9871         }
9872       }
9873
9874       break;
9875     }
9876
9877     case CA_SET_CE_ARTWORK:
9878     {
9879       int artwork_element = action_arg_element;
9880       boolean reset_frame = FALSE;
9881       int xx, yy;
9882
9883       if (action_arg == CA_ARG_ELEMENT_RESET)
9884         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9885                            element);
9886
9887       if (ei->gfx_element != artwork_element)
9888         reset_frame = TRUE;
9889
9890       ei->gfx_element = artwork_element;
9891
9892       SCAN_PLAYFIELD(xx, yy)
9893       {
9894         if (Feld[xx][yy] == element)
9895         {
9896           if (reset_frame)
9897           {
9898             ResetGfxAnimation(xx, yy);
9899             ResetRandomAnimationValue(xx, yy);
9900           }
9901
9902           TEST_DrawLevelField(xx, yy);
9903         }
9904       }
9905
9906       break;
9907     }
9908
9909     /* ---------- engine actions  ------------------------------------------ */
9910
9911     case CA_SET_ENGINE_SCAN_MODE:
9912     {
9913       InitPlayfieldScanMode(action_arg);
9914
9915       break;
9916     }
9917
9918     default:
9919       break;
9920   }
9921 }
9922
9923 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9924 {
9925   int old_element = Feld[x][y];
9926   int new_element = GetElementFromGroupElement(element);
9927   int previous_move_direction = MovDir[x][y];
9928   int last_ce_value = CustomValue[x][y];
9929   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9930   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9931   boolean add_player_onto_element = (new_element_is_player &&
9932                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9933                                      IS_WALKABLE(old_element));
9934
9935   if (!add_player_onto_element)
9936   {
9937     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9938       RemoveMovingField(x, y);
9939     else
9940       RemoveField(x, y);
9941
9942     Feld[x][y] = new_element;
9943
9944     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9945       MovDir[x][y] = previous_move_direction;
9946
9947     if (element_info[new_element].use_last_ce_value)
9948       CustomValue[x][y] = last_ce_value;
9949
9950     InitField_WithBug1(x, y, FALSE);
9951
9952     new_element = Feld[x][y];   /* element may have changed */
9953
9954     ResetGfxAnimation(x, y);
9955     ResetRandomAnimationValue(x, y);
9956
9957     TEST_DrawLevelField(x, y);
9958
9959     if (GFX_CRUMBLED(new_element))
9960       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9961   }
9962
9963   /* check if element under the player changes from accessible to unaccessible
9964      (needed for special case of dropping element which then changes) */
9965   /* (must be checked after creating new element for walkable group elements) */
9966   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9967       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9968   {
9969     Bang(x, y);
9970
9971     return;
9972   }
9973
9974   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9975   if (new_element_is_player)
9976     RelocatePlayer(x, y, new_element);
9977
9978   if (is_change)
9979     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9980
9981   TestIfBadThingTouchesPlayer(x, y);
9982   TestIfPlayerTouchesCustomElement(x, y);
9983   TestIfElementTouchesCustomElement(x, y);
9984 }
9985
9986 static void CreateField(int x, int y, int element)
9987 {
9988   CreateFieldExt(x, y, element, FALSE);
9989 }
9990
9991 static void CreateElementFromChange(int x, int y, int element)
9992 {
9993   element = GET_VALID_RUNTIME_ELEMENT(element);
9994
9995   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9996   {
9997     int old_element = Feld[x][y];
9998
9999     /* prevent changed element from moving in same engine frame
10000        unless both old and new element can either fall or move */
10001     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10002         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10003       Stop[x][y] = TRUE;
10004   }
10005
10006   CreateFieldExt(x, y, element, TRUE);
10007 }
10008
10009 static boolean ChangeElement(int x, int y, int element, int page)
10010 {
10011   struct ElementInfo *ei = &element_info[element];
10012   struct ElementChangeInfo *change = &ei->change_page[page];
10013   int ce_value = CustomValue[x][y];
10014   int ce_score = ei->collect_score;
10015   int target_element;
10016   int old_element = Feld[x][y];
10017
10018   /* always use default change event to prevent running into a loop */
10019   if (ChangeEvent[x][y] == -1)
10020     ChangeEvent[x][y] = CE_DELAY;
10021
10022   if (ChangeEvent[x][y] == CE_DELAY)
10023   {
10024     /* reset actual trigger element, trigger player and action element */
10025     change->actual_trigger_element = EL_EMPTY;
10026     change->actual_trigger_player = EL_EMPTY;
10027     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10028     change->actual_trigger_side = CH_SIDE_NONE;
10029     change->actual_trigger_ce_value = 0;
10030     change->actual_trigger_ce_score = 0;
10031   }
10032
10033   /* do not change elements more than a specified maximum number of changes */
10034   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10035     return FALSE;
10036
10037   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10038
10039   if (change->explode)
10040   {
10041     Bang(x, y);
10042
10043     return TRUE;
10044   }
10045
10046   if (change->use_target_content)
10047   {
10048     boolean complete_replace = TRUE;
10049     boolean can_replace[3][3];
10050     int xx, yy;
10051
10052     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10053     {
10054       boolean is_empty;
10055       boolean is_walkable;
10056       boolean is_diggable;
10057       boolean is_collectible;
10058       boolean is_removable;
10059       boolean is_destructible;
10060       int ex = x + xx - 1;
10061       int ey = y + yy - 1;
10062       int content_element = change->target_content.e[xx][yy];
10063       int e;
10064
10065       can_replace[xx][yy] = TRUE;
10066
10067       if (ex == x && ey == y)   /* do not check changing element itself */
10068         continue;
10069
10070       if (content_element == EL_EMPTY_SPACE)
10071       {
10072         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10073
10074         continue;
10075       }
10076
10077       if (!IN_LEV_FIELD(ex, ey))
10078       {
10079         can_replace[xx][yy] = FALSE;
10080         complete_replace = FALSE;
10081
10082         continue;
10083       }
10084
10085       e = Feld[ex][ey];
10086
10087       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10088         e = MovingOrBlocked2Element(ex, ey);
10089
10090       is_empty = (IS_FREE(ex, ey) ||
10091                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10092
10093       is_walkable     = (is_empty || IS_WALKABLE(e));
10094       is_diggable     = (is_empty || IS_DIGGABLE(e));
10095       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10096       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10097       is_removable    = (is_diggable || is_collectible);
10098
10099       can_replace[xx][yy] =
10100         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10101           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10102           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10103           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10104           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10105           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10106          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10107
10108       if (!can_replace[xx][yy])
10109         complete_replace = FALSE;
10110     }
10111
10112     if (!change->only_if_complete || complete_replace)
10113     {
10114       boolean something_has_changed = FALSE;
10115
10116       if (change->only_if_complete && change->use_random_replace &&
10117           RND(100) < change->random_percentage)
10118         return FALSE;
10119
10120       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10121       {
10122         int ex = x + xx - 1;
10123         int ey = y + yy - 1;
10124         int content_element;
10125
10126         if (can_replace[xx][yy] && (!change->use_random_replace ||
10127                                     RND(100) < change->random_percentage))
10128         {
10129           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10130             RemoveMovingField(ex, ey);
10131
10132           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10133
10134           content_element = change->target_content.e[xx][yy];
10135           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10136                                               ce_value, ce_score);
10137
10138           CreateElementFromChange(ex, ey, target_element);
10139
10140           something_has_changed = TRUE;
10141
10142           /* for symmetry reasons, freeze newly created border elements */
10143           if (ex != x || ey != y)
10144             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10145         }
10146       }
10147
10148       if (something_has_changed)
10149       {
10150         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10151         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10152       }
10153     }
10154   }
10155   else
10156   {
10157     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10158                                         ce_value, ce_score);
10159
10160     if (element == EL_DIAGONAL_GROWING ||
10161         element == EL_DIAGONAL_SHRINKING)
10162     {
10163       target_element = Store[x][y];
10164
10165       Store[x][y] = EL_EMPTY;
10166     }
10167
10168     CreateElementFromChange(x, y, target_element);
10169
10170     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10171     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10172   }
10173
10174   /* this uses direct change before indirect change */
10175   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10176
10177   return TRUE;
10178 }
10179
10180 static void HandleElementChange(int x, int y, int page)
10181 {
10182   int element = MovingOrBlocked2Element(x, y);
10183   struct ElementInfo *ei = &element_info[element];
10184   struct ElementChangeInfo *change = &ei->change_page[page];
10185   boolean handle_action_before_change = FALSE;
10186
10187 #ifdef DEBUG
10188   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10189       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10190   {
10191     printf("\n\n");
10192     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10193            x, y, element, element_info[element].token_name);
10194     printf("HandleElementChange(): This should never happen!\n");
10195     printf("\n\n");
10196   }
10197 #endif
10198
10199   /* this can happen with classic bombs on walkable, changing elements */
10200   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10201   {
10202     return;
10203   }
10204
10205   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10206   {
10207     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10208
10209     if (change->can_change)
10210     {
10211       /* !!! not clear why graphic animation should be reset at all here !!! */
10212       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10213       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10214
10215       /*
10216         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10217
10218         When using an animation frame delay of 1 (this only happens with
10219         "sp_zonk.moving.left/right" in the classic graphics), the default
10220         (non-moving) animation shows wrong animation frames (while the
10221         moving animation, like "sp_zonk.moving.left/right", is correct,
10222         so this graphical bug never shows up with the classic graphics).
10223         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10224         be drawn instead of the correct frames 0,1,2,3. This is caused by
10225         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10226         an element change: First when the change delay ("ChangeDelay[][]")
10227         counter has reached zero after decrementing, then a second time in
10228         the next frame (after "GfxFrame[][]" was already incremented) when
10229         "ChangeDelay[][]" is reset to the initial delay value again.
10230
10231         This causes frame 0 to be drawn twice, while the last frame won't
10232         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10233
10234         As some animations may already be cleverly designed around this bug
10235         (at least the "Snake Bite" snake tail animation does this), it cannot
10236         simply be fixed here without breaking such existing animations.
10237         Unfortunately, it cannot easily be detected if a graphics set was
10238         designed "before" or "after" the bug was fixed. As a workaround,
10239         a new graphics set option "game.graphics_engine_version" was added
10240         to be able to specify the game's major release version for which the
10241         graphics set was designed, which can then be used to decide if the
10242         bugfix should be used (version 4 and above) or not (version 3 or
10243         below, or if no version was specified at all, as with old sets).
10244
10245         (The wrong/fixed animation frames can be tested with the test level set
10246         "test_gfxframe" and level "000", which contains a specially prepared
10247         custom element at level position (x/y) == (11/9) which uses the zonk
10248         animation mentioned above. Using "game.graphics_engine_version: 4"
10249         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10250         This can also be seen from the debug output for this test element.)
10251       */
10252
10253       /* when a custom element is about to change (for example by change delay),
10254          do not reset graphic animation when the custom element is moving */
10255       if (game.graphics_engine_version < 4 &&
10256           !IS_MOVING(x, y))
10257       {
10258         ResetGfxAnimation(x, y);
10259         ResetRandomAnimationValue(x, y);
10260       }
10261
10262       if (change->pre_change_function)
10263         change->pre_change_function(x, y);
10264     }
10265   }
10266
10267   ChangeDelay[x][y]--;
10268
10269   if (ChangeDelay[x][y] != 0)           /* continue element change */
10270   {
10271     if (change->can_change)
10272     {
10273       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10274
10275       if (IS_ANIMATED(graphic))
10276         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10277
10278       if (change->change_function)
10279         change->change_function(x, y);
10280     }
10281   }
10282   else                                  /* finish element change */
10283   {
10284     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10285     {
10286       page = ChangePage[x][y];
10287       ChangePage[x][y] = -1;
10288
10289       change = &ei->change_page[page];
10290     }
10291
10292     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10293     {
10294       ChangeDelay[x][y] = 1;            /* try change after next move step */
10295       ChangePage[x][y] = page;          /* remember page to use for change */
10296
10297       return;
10298     }
10299
10300     /* special case: set new level random seed before changing element */
10301     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10302       handle_action_before_change = TRUE;
10303
10304     if (change->has_action && handle_action_before_change)
10305       ExecuteCustomElementAction(x, y, element, page);
10306
10307     if (change->can_change)
10308     {
10309       if (ChangeElement(x, y, element, page))
10310       {
10311         if (change->post_change_function)
10312           change->post_change_function(x, y);
10313       }
10314     }
10315
10316     if (change->has_action && !handle_action_before_change)
10317       ExecuteCustomElementAction(x, y, element, page);
10318   }
10319 }
10320
10321 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10322                                               int trigger_element,
10323                                               int trigger_event,
10324                                               int trigger_player,
10325                                               int trigger_side,
10326                                               int trigger_page)
10327 {
10328   boolean change_done_any = FALSE;
10329   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10330   int i;
10331
10332   if (!(trigger_events[trigger_element][trigger_event]))
10333     return FALSE;
10334
10335   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10336
10337   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10338   {
10339     int element = EL_CUSTOM_START + i;
10340     boolean change_done = FALSE;
10341     int p;
10342
10343     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10344         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10345       continue;
10346
10347     for (p = 0; p < element_info[element].num_change_pages; p++)
10348     {
10349       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10350
10351       if (change->can_change_or_has_action &&
10352           change->has_event[trigger_event] &&
10353           change->trigger_side & trigger_side &&
10354           change->trigger_player & trigger_player &&
10355           change->trigger_page & trigger_page_bits &&
10356           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10357       {
10358         change->actual_trigger_element = trigger_element;
10359         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10360         change->actual_trigger_player_bits = trigger_player;
10361         change->actual_trigger_side = trigger_side;
10362         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10363         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10364
10365         if ((change->can_change && !change_done) || change->has_action)
10366         {
10367           int x, y;
10368
10369           SCAN_PLAYFIELD(x, y)
10370           {
10371             if (Feld[x][y] == element)
10372             {
10373               if (change->can_change && !change_done)
10374               {
10375                 /* if element already changed in this frame, not only prevent
10376                    another element change (checked in ChangeElement()), but
10377                    also prevent additional element actions for this element */
10378
10379                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10380                     !level.use_action_after_change_bug)
10381                   continue;
10382
10383                 ChangeDelay[x][y] = 1;
10384                 ChangeEvent[x][y] = trigger_event;
10385
10386                 HandleElementChange(x, y, p);
10387               }
10388               else if (change->has_action)
10389               {
10390                 /* if element already changed in this frame, not only prevent
10391                    another element change (checked in ChangeElement()), but
10392                    also prevent additional element actions for this element */
10393
10394                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10395                     !level.use_action_after_change_bug)
10396                   continue;
10397
10398                 ExecuteCustomElementAction(x, y, element, p);
10399                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10400               }
10401             }
10402           }
10403
10404           if (change->can_change)
10405           {
10406             change_done = TRUE;
10407             change_done_any = TRUE;
10408           }
10409         }
10410       }
10411     }
10412   }
10413
10414   RECURSION_LOOP_DETECTION_END();
10415
10416   return change_done_any;
10417 }
10418
10419 static boolean CheckElementChangeExt(int x, int y,
10420                                      int element,
10421                                      int trigger_element,
10422                                      int trigger_event,
10423                                      int trigger_player,
10424                                      int trigger_side)
10425 {
10426   boolean change_done = FALSE;
10427   int p;
10428
10429   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10430       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10431     return FALSE;
10432
10433   if (Feld[x][y] == EL_BLOCKED)
10434   {
10435     Blocked2Moving(x, y, &x, &y);
10436     element = Feld[x][y];
10437   }
10438
10439   /* check if element has already changed or is about to change after moving */
10440   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10441        Feld[x][y] != element) ||
10442
10443       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10444        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10445         ChangePage[x][y] != -1)))
10446     return FALSE;
10447
10448   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10449
10450   for (p = 0; p < element_info[element].num_change_pages; p++)
10451   {
10452     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10453
10454     /* check trigger element for all events where the element that is checked
10455        for changing interacts with a directly adjacent element -- this is
10456        different to element changes that affect other elements to change on the
10457        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10458     boolean check_trigger_element =
10459       (trigger_event == CE_TOUCHING_X ||
10460        trigger_event == CE_HITTING_X ||
10461        trigger_event == CE_HIT_BY_X ||
10462        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10463
10464     if (change->can_change_or_has_action &&
10465         change->has_event[trigger_event] &&
10466         change->trigger_side & trigger_side &&
10467         change->trigger_player & trigger_player &&
10468         (!check_trigger_element ||
10469          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10470     {
10471       change->actual_trigger_element = trigger_element;
10472       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10473       change->actual_trigger_player_bits = trigger_player;
10474       change->actual_trigger_side = trigger_side;
10475       change->actual_trigger_ce_value = CustomValue[x][y];
10476       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10477
10478       /* special case: trigger element not at (x,y) position for some events */
10479       if (check_trigger_element)
10480       {
10481         static struct
10482         {
10483           int dx, dy;
10484         } move_xy[] =
10485           {
10486             {  0,  0 },
10487             { -1,  0 },
10488             { +1,  0 },
10489             {  0,  0 },
10490             {  0, -1 },
10491             {  0,  0 }, { 0, 0 }, { 0, 0 },
10492             {  0, +1 }
10493           };
10494
10495         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10496         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10497
10498         change->actual_trigger_ce_value = CustomValue[xx][yy];
10499         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10500       }
10501
10502       if (change->can_change && !change_done)
10503       {
10504         ChangeDelay[x][y] = 1;
10505         ChangeEvent[x][y] = trigger_event;
10506
10507         HandleElementChange(x, y, p);
10508
10509         change_done = TRUE;
10510       }
10511       else if (change->has_action)
10512       {
10513         ExecuteCustomElementAction(x, y, element, p);
10514         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10515       }
10516     }
10517   }
10518
10519   RECURSION_LOOP_DETECTION_END();
10520
10521   return change_done;
10522 }
10523
10524 static void PlayPlayerSound(struct PlayerInfo *player)
10525 {
10526   int jx = player->jx, jy = player->jy;
10527   int sound_element = player->artwork_element;
10528   int last_action = player->last_action_waiting;
10529   int action = player->action_waiting;
10530
10531   if (player->is_waiting)
10532   {
10533     if (action != last_action)
10534       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10535     else
10536       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10537   }
10538   else
10539   {
10540     if (action != last_action)
10541       StopSound(element_info[sound_element].sound[last_action]);
10542
10543     if (last_action == ACTION_SLEEPING)
10544       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10545   }
10546 }
10547
10548 static void PlayAllPlayersSound()
10549 {
10550   int i;
10551
10552   for (i = 0; i < MAX_PLAYERS; i++)
10553     if (stored_player[i].active)
10554       PlayPlayerSound(&stored_player[i]);
10555 }
10556
10557 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10558 {
10559   boolean last_waiting = player->is_waiting;
10560   int move_dir = player->MovDir;
10561
10562   player->dir_waiting = move_dir;
10563   player->last_action_waiting = player->action_waiting;
10564
10565   if (is_waiting)
10566   {
10567     if (!last_waiting)          /* not waiting -> waiting */
10568     {
10569       player->is_waiting = TRUE;
10570
10571       player->frame_counter_bored =
10572         FrameCounter +
10573         game.player_boring_delay_fixed +
10574         GetSimpleRandom(game.player_boring_delay_random);
10575       player->frame_counter_sleeping =
10576         FrameCounter +
10577         game.player_sleeping_delay_fixed +
10578         GetSimpleRandom(game.player_sleeping_delay_random);
10579
10580       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10581     }
10582
10583     if (game.player_sleeping_delay_fixed +
10584         game.player_sleeping_delay_random > 0 &&
10585         player->anim_delay_counter == 0 &&
10586         player->post_delay_counter == 0 &&
10587         FrameCounter >= player->frame_counter_sleeping)
10588       player->is_sleeping = TRUE;
10589     else if (game.player_boring_delay_fixed +
10590              game.player_boring_delay_random > 0 &&
10591              FrameCounter >= player->frame_counter_bored)
10592       player->is_bored = TRUE;
10593
10594     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10595                               player->is_bored ? ACTION_BORING :
10596                               ACTION_WAITING);
10597
10598     if (player->is_sleeping && player->use_murphy)
10599     {
10600       /* special case for sleeping Murphy when leaning against non-free tile */
10601
10602       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10603           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10604            !IS_MOVING(player->jx - 1, player->jy)))
10605         move_dir = MV_LEFT;
10606       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10607                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10608                 !IS_MOVING(player->jx + 1, player->jy)))
10609         move_dir = MV_RIGHT;
10610       else
10611         player->is_sleeping = FALSE;
10612
10613       player->dir_waiting = move_dir;
10614     }
10615
10616     if (player->is_sleeping)
10617     {
10618       if (player->num_special_action_sleeping > 0)
10619       {
10620         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10621         {
10622           int last_special_action = player->special_action_sleeping;
10623           int num_special_action = player->num_special_action_sleeping;
10624           int special_action =
10625             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10626              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10627              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10628              last_special_action + 1 : ACTION_SLEEPING);
10629           int special_graphic =
10630             el_act_dir2img(player->artwork_element, special_action, move_dir);
10631
10632           player->anim_delay_counter =
10633             graphic_info[special_graphic].anim_delay_fixed +
10634             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10635           player->post_delay_counter =
10636             graphic_info[special_graphic].post_delay_fixed +
10637             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10638
10639           player->special_action_sleeping = special_action;
10640         }
10641
10642         if (player->anim_delay_counter > 0)
10643         {
10644           player->action_waiting = player->special_action_sleeping;
10645           player->anim_delay_counter--;
10646         }
10647         else if (player->post_delay_counter > 0)
10648         {
10649           player->post_delay_counter--;
10650         }
10651       }
10652     }
10653     else if (player->is_bored)
10654     {
10655       if (player->num_special_action_bored > 0)
10656       {
10657         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10658         {
10659           int special_action =
10660             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10661           int special_graphic =
10662             el_act_dir2img(player->artwork_element, special_action, move_dir);
10663
10664           player->anim_delay_counter =
10665             graphic_info[special_graphic].anim_delay_fixed +
10666             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10667           player->post_delay_counter =
10668             graphic_info[special_graphic].post_delay_fixed +
10669             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10670
10671           player->special_action_bored = special_action;
10672         }
10673
10674         if (player->anim_delay_counter > 0)
10675         {
10676           player->action_waiting = player->special_action_bored;
10677           player->anim_delay_counter--;
10678         }
10679         else if (player->post_delay_counter > 0)
10680         {
10681           player->post_delay_counter--;
10682         }
10683       }
10684     }
10685   }
10686   else if (last_waiting)        /* waiting -> not waiting */
10687   {
10688     player->is_waiting = FALSE;
10689     player->is_bored = FALSE;
10690     player->is_sleeping = FALSE;
10691
10692     player->frame_counter_bored = -1;
10693     player->frame_counter_sleeping = -1;
10694
10695     player->anim_delay_counter = 0;
10696     player->post_delay_counter = 0;
10697
10698     player->dir_waiting = player->MovDir;
10699     player->action_waiting = ACTION_DEFAULT;
10700
10701     player->special_action_bored = ACTION_DEFAULT;
10702     player->special_action_sleeping = ACTION_DEFAULT;
10703   }
10704 }
10705
10706 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10707 {
10708   if ((!player->is_moving  && player->was_moving) ||
10709       (player->MovPos == 0 && player->was_moving) ||
10710       (player->is_snapping && !player->was_snapping) ||
10711       (player->is_dropping && !player->was_dropping))
10712   {
10713     if (!CheckSaveEngineSnapshotToList())
10714       return;
10715
10716     player->was_moving = FALSE;
10717     player->was_snapping = TRUE;
10718     player->was_dropping = TRUE;
10719   }
10720   else
10721   {
10722     if (player->is_moving)
10723       player->was_moving = TRUE;
10724
10725     if (!player->is_snapping)
10726       player->was_snapping = FALSE;
10727
10728     if (!player->is_dropping)
10729       player->was_dropping = FALSE;
10730   }
10731 }
10732
10733 static void CheckSingleStepMode(struct PlayerInfo *player)
10734 {
10735   if (tape.single_step && tape.recording && !tape.pausing)
10736   {
10737     /* as it is called "single step mode", just return to pause mode when the
10738        player stopped moving after one tile (or never starts moving at all) */
10739     if (!player->is_moving && !player->is_pushing)
10740     {
10741       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10742       SnapField(player, 0, 0);                  /* stop snapping */
10743     }
10744   }
10745
10746   CheckSaveEngineSnapshot(player);
10747 }
10748
10749 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10750 {
10751   int left      = player_action & JOY_LEFT;
10752   int right     = player_action & JOY_RIGHT;
10753   int up        = player_action & JOY_UP;
10754   int down      = player_action & JOY_DOWN;
10755   int button1   = player_action & JOY_BUTTON_1;
10756   int button2   = player_action & JOY_BUTTON_2;
10757   int dx        = (left ? -1 : right ? 1 : 0);
10758   int dy        = (up   ? -1 : down  ? 1 : 0);
10759
10760   if (!player->active || tape.pausing)
10761     return 0;
10762
10763   if (player_action)
10764   {
10765     if (button1)
10766       SnapField(player, dx, dy);
10767     else
10768     {
10769       if (button2)
10770         DropElement(player);
10771
10772       MovePlayer(player, dx, dy);
10773     }
10774
10775     CheckSingleStepMode(player);
10776
10777     SetPlayerWaiting(player, FALSE);
10778
10779     return player_action;
10780   }
10781   else
10782   {
10783     /* no actions for this player (no input at player's configured device) */
10784
10785     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10786     SnapField(player, 0, 0);
10787     CheckGravityMovementWhenNotMoving(player);
10788
10789     if (player->MovPos == 0)
10790       SetPlayerWaiting(player, TRUE);
10791
10792     if (player->MovPos == 0)    /* needed for tape.playing */
10793       player->is_moving = FALSE;
10794
10795     player->is_dropping = FALSE;
10796     player->is_dropping_pressed = FALSE;
10797     player->drop_pressed_delay = 0;
10798
10799     CheckSingleStepMode(player);
10800
10801     return 0;
10802   }
10803 }
10804
10805 static void CheckLevelTime()
10806 {
10807   int i;
10808
10809   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10810   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10811   {
10812     if (level.native_em_level->lev->home == 0)  /* all players at home */
10813     {
10814       PlayerWins(local_player);
10815
10816       AllPlayersGone = TRUE;
10817
10818       level.native_em_level->lev->home = -1;
10819     }
10820
10821     if (level.native_em_level->ply[0]->alive == 0 &&
10822         level.native_em_level->ply[1]->alive == 0 &&
10823         level.native_em_level->ply[2]->alive == 0 &&
10824         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10825       AllPlayersGone = TRUE;
10826   }
10827   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10828   {
10829     if (game_sp.LevelSolved &&
10830         !game_sp.GameOver)                              /* game won */
10831     {
10832       PlayerWins(local_player);
10833
10834       game_sp.GameOver = TRUE;
10835
10836       AllPlayersGone = TRUE;
10837     }
10838
10839     if (game_sp.GameOver)                               /* game lost */
10840       AllPlayersGone = TRUE;
10841   }
10842
10843   if (TimeFrames >= FRAMES_PER_SECOND)
10844   {
10845     TimeFrames = 0;
10846     TapeTime++;
10847
10848     for (i = 0; i < MAX_PLAYERS; i++)
10849     {
10850       struct PlayerInfo *player = &stored_player[i];
10851
10852       if (SHIELD_ON(player))
10853       {
10854         player->shield_normal_time_left--;
10855
10856         if (player->shield_deadly_time_left > 0)
10857           player->shield_deadly_time_left--;
10858       }
10859     }
10860
10861     if (!local_player->LevelSolved && !level.use_step_counter)
10862     {
10863       TimePlayed++;
10864
10865       if (TimeLeft > 0)
10866       {
10867         TimeLeft--;
10868
10869         if (TimeLeft <= 10 && setup.time_limit)
10870           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10871
10872         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10873            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10874
10875         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10876
10877         if (!TimeLeft && setup.time_limit)
10878         {
10879           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10880             level.native_em_level->lev->killed_out_of_time = TRUE;
10881           else
10882             for (i = 0; i < MAX_PLAYERS; i++)
10883               KillPlayer(&stored_player[i]);
10884         }
10885       }
10886       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10887       {
10888         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10889       }
10890
10891       level.native_em_level->lev->time =
10892         (game.no_time_limit ? TimePlayed : TimeLeft);
10893     }
10894
10895     if (tape.recording || tape.playing)
10896       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10897   }
10898
10899   if (tape.recording || tape.playing)
10900     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10901
10902   UpdateAndDisplayGameControlValues();
10903 }
10904
10905 void AdvanceFrameAndPlayerCounters(int player_nr)
10906 {
10907   int i;
10908
10909   /* advance frame counters (global frame counter and time frame counter) */
10910   FrameCounter++;
10911   TimeFrames++;
10912
10913   /* advance player counters (counters for move delay, move animation etc.) */
10914   for (i = 0; i < MAX_PLAYERS; i++)
10915   {
10916     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10917     int move_delay_value = stored_player[i].move_delay_value;
10918     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10919
10920     if (!advance_player_counters)       /* not all players may be affected */
10921       continue;
10922
10923     if (move_frames == 0)       /* less than one move per game frame */
10924     {
10925       int stepsize = TILEX / move_delay_value;
10926       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10927       int count = (stored_player[i].is_moving ?
10928                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10929
10930       if (count % delay == 0)
10931         move_frames = 1;
10932     }
10933
10934     stored_player[i].Frame += move_frames;
10935
10936     if (stored_player[i].MovPos != 0)
10937       stored_player[i].StepFrame += move_frames;
10938
10939     if (stored_player[i].move_delay > 0)
10940       stored_player[i].move_delay--;
10941
10942     /* due to bugs in previous versions, counter must count up, not down */
10943     if (stored_player[i].push_delay != -1)
10944       stored_player[i].push_delay++;
10945
10946     if (stored_player[i].drop_delay > 0)
10947       stored_player[i].drop_delay--;
10948
10949     if (stored_player[i].is_dropping_pressed)
10950       stored_player[i].drop_pressed_delay++;
10951   }
10952 }
10953
10954 void StartGameActions(boolean init_network_game, boolean record_tape,
10955                       int random_seed)
10956 {
10957   unsigned int new_random_seed = InitRND(random_seed);
10958
10959   if (record_tape)
10960     TapeStartRecording(new_random_seed);
10961
10962 #if defined(NETWORK_AVALIABLE)
10963   if (init_network_game)
10964   {
10965     SendToServer_StartPlaying();
10966
10967     return;
10968   }
10969 #endif
10970
10971   InitGame();
10972 }
10973
10974 void GameActionsExt()
10975 {
10976 #if 0
10977   static unsigned int game_frame_delay = 0;
10978 #endif
10979   unsigned int game_frame_delay_value;
10980   byte *recorded_player_action;
10981   byte summarized_player_action = 0;
10982   byte tape_action[MAX_PLAYERS];
10983   int i;
10984
10985   /* detect endless loops, caused by custom element programming */
10986   if (recursion_loop_detected && recursion_loop_depth == 0)
10987   {
10988     char *message = getStringCat3("Internal Error! Element ",
10989                                   EL_NAME(recursion_loop_element),
10990                                   " caused endless loop! Quit the game?");
10991
10992     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10993           EL_NAME(recursion_loop_element));
10994
10995     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10996
10997     recursion_loop_detected = FALSE;    /* if game should be continued */
10998
10999     free(message);
11000
11001     return;
11002   }
11003
11004   if (game.restart_level)
11005     StartGameActions(options.network, setup.autorecord, level.random_seed);
11006
11007   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11008   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11009   {
11010     if (level.native_em_level->lev->home == 0)  /* all players at home */
11011     {
11012       PlayerWins(local_player);
11013
11014       AllPlayersGone = TRUE;
11015
11016       level.native_em_level->lev->home = -1;
11017     }
11018
11019     if (level.native_em_level->ply[0]->alive == 0 &&
11020         level.native_em_level->ply[1]->alive == 0 &&
11021         level.native_em_level->ply[2]->alive == 0 &&
11022         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11023       AllPlayersGone = TRUE;
11024   }
11025   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11026   {
11027     if (game_sp.LevelSolved &&
11028         !game_sp.GameOver)                              /* game won */
11029     {
11030       PlayerWins(local_player);
11031
11032       game_sp.GameOver = TRUE;
11033
11034       AllPlayersGone = TRUE;
11035     }
11036
11037     if (game_sp.GameOver)                               /* game lost */
11038       AllPlayersGone = TRUE;
11039   }
11040
11041   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11042     GameWon();
11043
11044   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11045     TapeStop();
11046
11047   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11048     return;
11049
11050   game_frame_delay_value =
11051     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11052
11053   if (tape.playing && tape.warp_forward && !tape.pausing)
11054     game_frame_delay_value = 0;
11055
11056   SetVideoFrameDelay(game_frame_delay_value);
11057
11058 #if 0
11059 #if 0
11060   /* ---------- main game synchronization point ---------- */
11061
11062   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11063
11064   printf("::: skip == %d\n", skip);
11065
11066 #else
11067   /* ---------- main game synchronization point ---------- */
11068
11069   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11070 #endif
11071 #endif
11072
11073   if (network_playing && !network_player_action_received)
11074   {
11075     /* try to get network player actions in time */
11076
11077 #if defined(NETWORK_AVALIABLE)
11078     /* last chance to get network player actions without main loop delay */
11079     HandleNetworking();
11080 #endif
11081
11082     /* game was quit by network peer */
11083     if (game_status != GAME_MODE_PLAYING)
11084       return;
11085
11086     if (!network_player_action_received)
11087       return;           /* failed to get network player actions in time */
11088
11089     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11090   }
11091
11092   if (tape.pausing)
11093     return;
11094
11095   /* at this point we know that we really continue executing the game */
11096
11097   network_player_action_received = FALSE;
11098
11099   /* when playing tape, read previously recorded player input from tape data */
11100   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11101
11102   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11103   if (tape.pausing)
11104     return;
11105
11106   if (tape.set_centered_player)
11107   {
11108     game.centered_player_nr_next = tape.centered_player_nr_next;
11109     game.set_centered_player = TRUE;
11110   }
11111
11112   for (i = 0; i < MAX_PLAYERS; i++)
11113   {
11114     summarized_player_action |= stored_player[i].action;
11115
11116     if (!network_playing && (game.team_mode || tape.playing))
11117       stored_player[i].effective_action = stored_player[i].action;
11118   }
11119
11120 #if defined(NETWORK_AVALIABLE)
11121   if (network_playing)
11122     SendToServer_MovePlayer(summarized_player_action);
11123 #endif
11124
11125   // summarize all actions at local players mapped input device position
11126   // (this allows using different input devices in single player mode)
11127   if (!options.network && !game.team_mode)
11128     stored_player[map_player_action[local_player->index_nr]].effective_action =
11129       summarized_player_action;
11130
11131   if (tape.recording &&
11132       setup.team_mode &&
11133       setup.input_on_focus &&
11134       game.centered_player_nr != -1)
11135   {
11136     for (i = 0; i < MAX_PLAYERS; i++)
11137       stored_player[i].effective_action =
11138         (i == game.centered_player_nr ? summarized_player_action : 0);
11139   }
11140
11141   if (recorded_player_action != NULL)
11142     for (i = 0; i < MAX_PLAYERS; i++)
11143       stored_player[i].effective_action = recorded_player_action[i];
11144
11145   for (i = 0; i < MAX_PLAYERS; i++)
11146   {
11147     tape_action[i] = stored_player[i].effective_action;
11148
11149     /* (this may happen in the RND game engine if a player was not present on
11150        the playfield on level start, but appeared later from a custom element */
11151     if (setup.team_mode &&
11152         tape.recording &&
11153         tape_action[i] &&
11154         !tape.player_participates[i])
11155       tape.player_participates[i] = TRUE;
11156   }
11157
11158   /* only record actions from input devices, but not programmed actions */
11159   if (tape.recording)
11160     TapeRecordAction(tape_action);
11161
11162 #if USE_NEW_PLAYER_ASSIGNMENTS
11163   // !!! also map player actions in single player mode !!!
11164   // if (game.team_mode)
11165   if (1)
11166   {
11167     byte mapped_action[MAX_PLAYERS];
11168
11169 #if DEBUG_PLAYER_ACTIONS
11170     printf(":::");
11171     for (i = 0; i < MAX_PLAYERS; i++)
11172       printf(" %d, ", stored_player[i].effective_action);
11173 #endif
11174
11175     for (i = 0; i < MAX_PLAYERS; i++)
11176       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11177
11178     for (i = 0; i < MAX_PLAYERS; i++)
11179       stored_player[i].effective_action = mapped_action[i];
11180
11181 #if DEBUG_PLAYER_ACTIONS
11182     printf(" =>");
11183     for (i = 0; i < MAX_PLAYERS; i++)
11184       printf(" %d, ", stored_player[i].effective_action);
11185     printf("\n");
11186 #endif
11187   }
11188 #if DEBUG_PLAYER_ACTIONS
11189   else
11190   {
11191     printf(":::");
11192     for (i = 0; i < MAX_PLAYERS; i++)
11193       printf(" %d, ", stored_player[i].effective_action);
11194     printf("\n");
11195   }
11196 #endif
11197 #endif
11198
11199   for (i = 0; i < MAX_PLAYERS; i++)
11200   {
11201     // allow engine snapshot in case of changed movement attempt
11202     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11203         (stored_player[i].effective_action & KEY_MOTION))
11204       game.snapshot.changed_action = TRUE;
11205
11206     // allow engine snapshot in case of snapping/dropping attempt
11207     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11208         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11209       game.snapshot.changed_action = TRUE;
11210
11211     game.snapshot.last_action[i] = stored_player[i].effective_action;
11212   }
11213
11214   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11215   {
11216     GameActions_EM_Main();
11217   }
11218   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11219   {
11220     GameActions_SP_Main();
11221   }
11222   else
11223   {
11224     GameActions_RND_Main();
11225   }
11226
11227   BlitScreenToBitmap(backbuffer);
11228
11229   CheckLevelTime();
11230
11231   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11232
11233   if (options.debug)                    /* calculate frames per second */
11234   {
11235     static unsigned int fps_counter = 0;
11236     static int fps_frames = 0;
11237     unsigned int fps_delay_ms = Counter() - fps_counter;
11238
11239     fps_frames++;
11240
11241     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11242     {
11243       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11244
11245       fps_frames = 0;
11246       fps_counter = Counter();
11247     }
11248
11249     redraw_mask |= REDRAW_FPS;
11250   }
11251 }
11252
11253 static void GameActions_CheckSaveEngineSnapshot()
11254 {
11255   if (!game.snapshot.save_snapshot)
11256     return;
11257
11258   // clear flag for saving snapshot _before_ saving snapshot
11259   game.snapshot.save_snapshot = FALSE;
11260
11261   SaveEngineSnapshotToList();
11262 }
11263
11264 void GameActions()
11265 {
11266   GameActionsExt();
11267
11268   GameActions_CheckSaveEngineSnapshot();
11269 }
11270
11271 void GameActions_EM_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_EM(effective_action, warp_mode);
11281 }
11282
11283 void GameActions_SP_Main()
11284 {
11285   byte effective_action[MAX_PLAYERS];
11286   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11287   int i;
11288
11289   for (i = 0; i < MAX_PLAYERS; i++)
11290     effective_action[i] = stored_player[i].effective_action;
11291
11292   GameActions_SP(effective_action, warp_mode);
11293 }
11294
11295 void GameActions_RND_Main()
11296 {
11297   GameActions_RND();
11298 }
11299
11300 void GameActions_RND()
11301 {
11302   int magic_wall_x = 0, magic_wall_y = 0;
11303   int i, x, y, element, graphic;
11304
11305   InitPlayfieldScanModeVars();
11306
11307   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11308   {
11309     SCAN_PLAYFIELD(x, y)
11310     {
11311       ChangeCount[x][y] = 0;
11312       ChangeEvent[x][y] = -1;
11313     }
11314   }
11315
11316   if (game.set_centered_player)
11317   {
11318     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11319
11320     /* switching to "all players" only possible if all players fit to screen */
11321     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11322     {
11323       game.centered_player_nr_next = game.centered_player_nr;
11324       game.set_centered_player = FALSE;
11325     }
11326
11327     /* do not switch focus to non-existing (or non-active) player */
11328     if (game.centered_player_nr_next >= 0 &&
11329         !stored_player[game.centered_player_nr_next].active)
11330     {
11331       game.centered_player_nr_next = game.centered_player_nr;
11332       game.set_centered_player = FALSE;
11333     }
11334   }
11335
11336   if (game.set_centered_player &&
11337       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11338   {
11339     int sx, sy;
11340
11341     if (game.centered_player_nr_next == -1)
11342     {
11343       setScreenCenteredToAllPlayers(&sx, &sy);
11344     }
11345     else
11346     {
11347       sx = stored_player[game.centered_player_nr_next].jx;
11348       sy = stored_player[game.centered_player_nr_next].jy;
11349     }
11350
11351     game.centered_player_nr = game.centered_player_nr_next;
11352     game.set_centered_player = FALSE;
11353
11354     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11355     DrawGameDoorValues();
11356   }
11357
11358   for (i = 0; i < MAX_PLAYERS; i++)
11359   {
11360     int actual_player_action = stored_player[i].effective_action;
11361
11362 #if 1
11363     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11364        - rnd_equinox_tetrachloride 048
11365        - rnd_equinox_tetrachloride_ii 096
11366        - rnd_emanuel_schmieg 002
11367        - doctor_sloan_ww 001, 020
11368     */
11369     if (stored_player[i].MovPos == 0)
11370       CheckGravityMovement(&stored_player[i]);
11371 #endif
11372
11373     /* overwrite programmed action with tape action */
11374     if (stored_player[i].programmed_action)
11375       actual_player_action = stored_player[i].programmed_action;
11376
11377     PlayerActions(&stored_player[i], actual_player_action);
11378
11379     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11380   }
11381
11382   ScrollScreen(NULL, SCROLL_GO_ON);
11383
11384   /* for backwards compatibility, the following code emulates a fixed bug that
11385      occured when pushing elements (causing elements that just made their last
11386      pushing step to already (if possible) make their first falling step in the
11387      same game frame, which is bad); this code is also needed to use the famous
11388      "spring push bug" which is used in older levels and might be wanted to be
11389      used also in newer levels, but in this case the buggy pushing code is only
11390      affecting the "spring" element and no other elements */
11391
11392   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11393   {
11394     for (i = 0; i < MAX_PLAYERS; i++)
11395     {
11396       struct PlayerInfo *player = &stored_player[i];
11397       int x = player->jx;
11398       int y = player->jy;
11399
11400       if (player->active && player->is_pushing && player->is_moving &&
11401           IS_MOVING(x, y) &&
11402           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11403            Feld[x][y] == EL_SPRING))
11404       {
11405         ContinueMoving(x, y);
11406
11407         /* continue moving after pushing (this is actually a bug) */
11408         if (!IS_MOVING(x, y))
11409           Stop[x][y] = FALSE;
11410       }
11411     }
11412   }
11413
11414   SCAN_PLAYFIELD(x, y)
11415   {
11416     ChangeCount[x][y] = 0;
11417     ChangeEvent[x][y] = -1;
11418
11419     /* this must be handled before main playfield loop */
11420     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11421     {
11422       MovDelay[x][y]--;
11423       if (MovDelay[x][y] <= 0)
11424         RemoveField(x, y);
11425     }
11426
11427     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11428     {
11429       MovDelay[x][y]--;
11430       if (MovDelay[x][y] <= 0)
11431       {
11432         RemoveField(x, y);
11433         TEST_DrawLevelField(x, y);
11434
11435         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11436       }
11437     }
11438
11439 #if DEBUG
11440     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11441     {
11442       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11443       printf("GameActions(): This should never happen!\n");
11444
11445       ChangePage[x][y] = -1;
11446     }
11447 #endif
11448
11449     Stop[x][y] = FALSE;
11450     if (WasJustMoving[x][y] > 0)
11451       WasJustMoving[x][y]--;
11452     if (WasJustFalling[x][y] > 0)
11453       WasJustFalling[x][y]--;
11454     if (CheckCollision[x][y] > 0)
11455       CheckCollision[x][y]--;
11456     if (CheckImpact[x][y] > 0)
11457       CheckImpact[x][y]--;
11458
11459     GfxFrame[x][y]++;
11460
11461     /* reset finished pushing action (not done in ContinueMoving() to allow
11462        continuous pushing animation for elements with zero push delay) */
11463     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11464     {
11465       ResetGfxAnimation(x, y);
11466       TEST_DrawLevelField(x, y);
11467     }
11468
11469 #if DEBUG
11470     if (IS_BLOCKED(x, y))
11471     {
11472       int oldx, oldy;
11473
11474       Blocked2Moving(x, y, &oldx, &oldy);
11475       if (!IS_MOVING(oldx, oldy))
11476       {
11477         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11478         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11479         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11480         printf("GameActions(): This should never happen!\n");
11481       }
11482     }
11483 #endif
11484   }
11485
11486   SCAN_PLAYFIELD(x, y)
11487   {
11488     element = Feld[x][y];
11489     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11490
11491     ResetGfxFrame(x, y, TRUE);
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               element == EL_EXIT_OPEN ||
11538               element == EL_EM_EXIT_OPEN ||
11539               element == EL_SP_EXIT_OPEN ||
11540               element == EL_STEEL_EXIT_OPEN ||
11541               element == EL_EM_STEEL_EXIT_OPEN ||
11542               element == EL_SP_TERMINAL ||
11543               element == EL_SP_TERMINAL_ACTIVE ||
11544               element == EL_EXTRA_TIME ||
11545               element == EL_SHIELD_NORMAL ||
11546               element == EL_SHIELD_DEADLY) &&
11547              IS_ANIMATED(graphic))
11548       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11549     else if (IS_MOVING(x, y))
11550       ContinueMoving(x, y);
11551     else if (IS_ACTIVE_BOMB(element))
11552       CheckDynamite(x, y);
11553     else if (element == EL_AMOEBA_GROWING)
11554       AmoebeWaechst(x, y);
11555     else if (element == EL_AMOEBA_SHRINKING)
11556       AmoebaDisappearing(x, y);
11557
11558 #if !USE_NEW_AMOEBA_CODE
11559     else if (IS_AMOEBALIVE(element))
11560       AmoebeAbleger(x, y);
11561 #endif
11562
11563     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11564       Life(x, y);
11565     else if (element == EL_EXIT_CLOSED)
11566       CheckExit(x, y);
11567     else if (element == EL_EM_EXIT_CLOSED)
11568       CheckExitEM(x, y);
11569     else if (element == EL_STEEL_EXIT_CLOSED)
11570       CheckExitSteel(x, y);
11571     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11572       CheckExitSteelEM(x, y);
11573     else if (element == EL_SP_EXIT_CLOSED)
11574       CheckExitSP(x, y);
11575     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11576              element == EL_EXPANDABLE_STEELWALL_GROWING)
11577       MauerWaechst(x, y);
11578     else if (element == EL_EXPANDABLE_WALL ||
11579              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11580              element == EL_EXPANDABLE_WALL_VERTICAL ||
11581              element == EL_EXPANDABLE_WALL_ANY ||
11582              element == EL_BD_EXPANDABLE_WALL)
11583       MauerAbleger(x, y);
11584     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11585              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11586              element == EL_EXPANDABLE_STEELWALL_ANY)
11587       MauerAblegerStahl(x, y);
11588     else if (element == EL_FLAMES)
11589       CheckForDragon(x, y);
11590     else if (element == EL_EXPLOSION)
11591       ; /* drawing of correct explosion animation is handled separately */
11592     else if (element == EL_ELEMENT_SNAPPING ||
11593              element == EL_DIAGONAL_SHRINKING ||
11594              element == EL_DIAGONAL_GROWING)
11595     {
11596       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11597
11598       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11599     }
11600     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11601       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11602
11603     if (IS_BELT_ACTIVE(element))
11604       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11605
11606     if (game.magic_wall_active)
11607     {
11608       int jx = local_player->jx, jy = local_player->jy;
11609
11610       /* play the element sound at the position nearest to the player */
11611       if ((element == EL_MAGIC_WALL_FULL ||
11612            element == EL_MAGIC_WALL_ACTIVE ||
11613            element == EL_MAGIC_WALL_EMPTYING ||
11614            element == EL_BD_MAGIC_WALL_FULL ||
11615            element == EL_BD_MAGIC_WALL_ACTIVE ||
11616            element == EL_BD_MAGIC_WALL_EMPTYING ||
11617            element == EL_DC_MAGIC_WALL_FULL ||
11618            element == EL_DC_MAGIC_WALL_ACTIVE ||
11619            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11620           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11621       {
11622         magic_wall_x = x;
11623         magic_wall_y = y;
11624       }
11625     }
11626   }
11627
11628 #if USE_NEW_AMOEBA_CODE
11629   /* new experimental amoeba growth stuff */
11630   if (!(FrameCounter % 8))
11631   {
11632     static unsigned int random = 1684108901;
11633
11634     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11635     {
11636       x = RND(lev_fieldx);
11637       y = RND(lev_fieldy);
11638       element = Feld[x][y];
11639
11640       if (!IS_PLAYER(x,y) &&
11641           (element == EL_EMPTY ||
11642            CAN_GROW_INTO(element) ||
11643            element == EL_QUICKSAND_EMPTY ||
11644            element == EL_QUICKSAND_FAST_EMPTY ||
11645            element == EL_ACID_SPLASH_LEFT ||
11646            element == EL_ACID_SPLASH_RIGHT))
11647       {
11648         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11649             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11650             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11651             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11652           Feld[x][y] = EL_AMOEBA_DROP;
11653       }
11654
11655       random = random * 129 + 1;
11656     }
11657   }
11658 #endif
11659
11660   game.explosions_delayed = FALSE;
11661
11662   SCAN_PLAYFIELD(x, y)
11663   {
11664     element = Feld[x][y];
11665
11666     if (ExplodeField[x][y])
11667       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11668     else if (element == EL_EXPLOSION)
11669       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11670
11671     ExplodeField[x][y] = EX_TYPE_NONE;
11672   }
11673
11674   game.explosions_delayed = TRUE;
11675
11676   if (game.magic_wall_active)
11677   {
11678     if (!(game.magic_wall_time_left % 4))
11679     {
11680       int element = Feld[magic_wall_x][magic_wall_y];
11681
11682       if (element == EL_BD_MAGIC_WALL_FULL ||
11683           element == EL_BD_MAGIC_WALL_ACTIVE ||
11684           element == EL_BD_MAGIC_WALL_EMPTYING)
11685         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11686       else if (element == EL_DC_MAGIC_WALL_FULL ||
11687                element == EL_DC_MAGIC_WALL_ACTIVE ||
11688                element == EL_DC_MAGIC_WALL_EMPTYING)
11689         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11690       else
11691         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11692     }
11693
11694     if (game.magic_wall_time_left > 0)
11695     {
11696       game.magic_wall_time_left--;
11697
11698       if (!game.magic_wall_time_left)
11699       {
11700         SCAN_PLAYFIELD(x, y)
11701         {
11702           element = Feld[x][y];
11703
11704           if (element == EL_MAGIC_WALL_ACTIVE ||
11705               element == EL_MAGIC_WALL_FULL)
11706           {
11707             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11708             TEST_DrawLevelField(x, y);
11709           }
11710           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11711                    element == EL_BD_MAGIC_WALL_FULL)
11712           {
11713             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11714             TEST_DrawLevelField(x, y);
11715           }
11716           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11717                    element == EL_DC_MAGIC_WALL_FULL)
11718           {
11719             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11720             TEST_DrawLevelField(x, y);
11721           }
11722         }
11723
11724         game.magic_wall_active = FALSE;
11725       }
11726     }
11727   }
11728
11729   if (game.light_time_left > 0)
11730   {
11731     game.light_time_left--;
11732
11733     if (game.light_time_left == 0)
11734       RedrawAllLightSwitchesAndInvisibleElements();
11735   }
11736
11737   if (game.timegate_time_left > 0)
11738   {
11739     game.timegate_time_left--;
11740
11741     if (game.timegate_time_left == 0)
11742       CloseAllOpenTimegates();
11743   }
11744
11745   if (game.lenses_time_left > 0)
11746   {
11747     game.lenses_time_left--;
11748
11749     if (game.lenses_time_left == 0)
11750       RedrawAllInvisibleElementsForLenses();
11751   }
11752
11753   if (game.magnify_time_left > 0)
11754   {
11755     game.magnify_time_left--;
11756
11757     if (game.magnify_time_left == 0)
11758       RedrawAllInvisibleElementsForMagnifier();
11759   }
11760
11761   for (i = 0; i < MAX_PLAYERS; i++)
11762   {
11763     struct PlayerInfo *player = &stored_player[i];
11764
11765     if (SHIELD_ON(player))
11766     {
11767       if (player->shield_deadly_time_left)
11768         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11769       else if (player->shield_normal_time_left)
11770         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11771     }
11772   }
11773
11774 #if USE_DELAYED_GFX_REDRAW
11775   SCAN_PLAYFIELD(x, y)
11776   {
11777     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11778     {
11779       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11780          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11781
11782       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11783         DrawLevelField(x, y);
11784
11785       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11786         DrawLevelFieldCrumbled(x, y);
11787
11788       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11789         DrawLevelFieldCrumbledNeighbours(x, y);
11790
11791       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11792         DrawTwinkleOnField(x, y);
11793     }
11794
11795     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11796   }
11797 #endif
11798
11799   DrawAllPlayers();
11800   PlayAllPlayersSound();
11801
11802   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11803   {
11804     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11805
11806     local_player->show_envelope = 0;
11807   }
11808
11809   /* use random number generator in every frame to make it less predictable */
11810   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11811     RND(1);
11812 }
11813
11814 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11815 {
11816   int min_x = x, min_y = y, max_x = x, max_y = y;
11817   int i;
11818
11819   for (i = 0; i < MAX_PLAYERS; i++)
11820   {
11821     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11822
11823     if (!stored_player[i].active || &stored_player[i] == player)
11824       continue;
11825
11826     min_x = MIN(min_x, jx);
11827     min_y = MIN(min_y, jy);
11828     max_x = MAX(max_x, jx);
11829     max_y = MAX(max_y, jy);
11830   }
11831
11832   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11833 }
11834
11835 static boolean AllPlayersInVisibleScreen()
11836 {
11837   int i;
11838
11839   for (i = 0; i < MAX_PLAYERS; i++)
11840   {
11841     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11842
11843     if (!stored_player[i].active)
11844       continue;
11845
11846     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11847       return FALSE;
11848   }
11849
11850   return TRUE;
11851 }
11852
11853 void ScrollLevel(int dx, int dy)
11854 {
11855   int scroll_offset = 2 * TILEX_VAR;
11856   int x, y;
11857
11858   BlitBitmap(drawto_field, drawto_field,
11859              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11860              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11861              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11862              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11863              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11864              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11865
11866   if (dx != 0)
11867   {
11868     x = (dx == 1 ? BX1 : BX2);
11869     for (y = BY1; y <= BY2; y++)
11870       DrawScreenField(x, y);
11871   }
11872
11873   if (dy != 0)
11874   {
11875     y = (dy == 1 ? BY1 : BY2);
11876     for (x = BX1; x <= BX2; x++)
11877       DrawScreenField(x, y);
11878   }
11879
11880   redraw_mask |= REDRAW_FIELD;
11881 }
11882
11883 static boolean canFallDown(struct PlayerInfo *player)
11884 {
11885   int jx = player->jx, jy = player->jy;
11886
11887   return (IN_LEV_FIELD(jx, jy + 1) &&
11888           (IS_FREE(jx, jy + 1) ||
11889            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11890           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11891           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11892 }
11893
11894 static boolean canPassField(int x, int y, int move_dir)
11895 {
11896   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11897   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11898   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11899   int nextx = x + dx;
11900   int nexty = y + dy;
11901   int element = Feld[x][y];
11902
11903   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11904           !CAN_MOVE(element) &&
11905           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11906           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11907           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11908 }
11909
11910 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11911 {
11912   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11913   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11914   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11915   int newx = x + dx;
11916   int newy = y + dy;
11917
11918   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11919           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11920           (IS_DIGGABLE(Feld[newx][newy]) ||
11921            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11922            canPassField(newx, newy, move_dir)));
11923 }
11924
11925 static void CheckGravityMovement(struct PlayerInfo *player)
11926 {
11927   if (player->gravity && !player->programmed_action)
11928   {
11929     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11930     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11931     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11932     int jx = player->jx, jy = player->jy;
11933     boolean player_is_moving_to_valid_field =
11934       (!player_is_snapping &&
11935        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11936         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11937     boolean player_can_fall_down = canFallDown(player);
11938
11939     if (player_can_fall_down &&
11940         !player_is_moving_to_valid_field)
11941       player->programmed_action = MV_DOWN;
11942   }
11943 }
11944
11945 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11946 {
11947   return CheckGravityMovement(player);
11948
11949   if (player->gravity && !player->programmed_action)
11950   {
11951     int jx = player->jx, jy = player->jy;
11952     boolean field_under_player_is_free =
11953       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11954     boolean player_is_standing_on_valid_field =
11955       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11956        (IS_WALKABLE(Feld[jx][jy]) &&
11957         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11958
11959     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11960       player->programmed_action = MV_DOWN;
11961   }
11962 }
11963
11964 /*
11965   MovePlayerOneStep()
11966   -----------------------------------------------------------------------------
11967   dx, dy:               direction (non-diagonal) to try to move the player to
11968   real_dx, real_dy:     direction as read from input device (can be diagonal)
11969 */
11970
11971 boolean MovePlayerOneStep(struct PlayerInfo *player,
11972                           int dx, int dy, int real_dx, int real_dy)
11973 {
11974   int jx = player->jx, jy = player->jy;
11975   int new_jx = jx + dx, new_jy = jy + dy;
11976   int can_move;
11977   boolean player_can_move = !player->cannot_move;
11978
11979   if (!player->active || (!dx && !dy))
11980     return MP_NO_ACTION;
11981
11982   player->MovDir = (dx < 0 ? MV_LEFT :
11983                     dx > 0 ? MV_RIGHT :
11984                     dy < 0 ? MV_UP :
11985                     dy > 0 ? MV_DOWN :  MV_NONE);
11986
11987   if (!IN_LEV_FIELD(new_jx, new_jy))
11988     return MP_NO_ACTION;
11989
11990   if (!player_can_move)
11991   {
11992     if (player->MovPos == 0)
11993     {
11994       player->is_moving = FALSE;
11995       player->is_digging = FALSE;
11996       player->is_collecting = FALSE;
11997       player->is_snapping = FALSE;
11998       player->is_pushing = FALSE;
11999     }
12000   }
12001
12002   if (!options.network && game.centered_player_nr == -1 &&
12003       !AllPlayersInSight(player, new_jx, new_jy))
12004     return MP_NO_ACTION;
12005
12006   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12007   if (can_move != MP_MOVING)
12008     return can_move;
12009
12010   /* check if DigField() has caused relocation of the player */
12011   if (player->jx != jx || player->jy != jy)
12012     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12013
12014   StorePlayer[jx][jy] = 0;
12015   player->last_jx = jx;
12016   player->last_jy = jy;
12017   player->jx = new_jx;
12018   player->jy = new_jy;
12019   StorePlayer[new_jx][new_jy] = player->element_nr;
12020
12021   if (player->move_delay_value_next != -1)
12022   {
12023     player->move_delay_value = player->move_delay_value_next;
12024     player->move_delay_value_next = -1;
12025   }
12026
12027   player->MovPos =
12028     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12029
12030   player->step_counter++;
12031
12032   PlayerVisit[jx][jy] = FrameCounter;
12033
12034   player->is_moving = TRUE;
12035
12036 #if 1
12037   /* should better be called in MovePlayer(), but this breaks some tapes */
12038   ScrollPlayer(player, SCROLL_INIT);
12039 #endif
12040
12041   return MP_MOVING;
12042 }
12043
12044 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12045 {
12046   int jx = player->jx, jy = player->jy;
12047   int old_jx = jx, old_jy = jy;
12048   int moved = MP_NO_ACTION;
12049
12050   if (!player->active)
12051     return FALSE;
12052
12053   if (!dx && !dy)
12054   {
12055     if (player->MovPos == 0)
12056     {
12057       player->is_moving = FALSE;
12058       player->is_digging = FALSE;
12059       player->is_collecting = FALSE;
12060       player->is_snapping = FALSE;
12061       player->is_pushing = FALSE;
12062     }
12063
12064     return FALSE;
12065   }
12066
12067   if (player->move_delay > 0)
12068     return FALSE;
12069
12070   player->move_delay = -1;              /* set to "uninitialized" value */
12071
12072   /* store if player is automatically moved to next field */
12073   player->is_auto_moving = (player->programmed_action != MV_NONE);
12074
12075   /* remove the last programmed player action */
12076   player->programmed_action = 0;
12077
12078   if (player->MovPos)
12079   {
12080     /* should only happen if pre-1.2 tape recordings are played */
12081     /* this is only for backward compatibility */
12082
12083     int original_move_delay_value = player->move_delay_value;
12084
12085 #if DEBUG
12086     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12087            tape.counter);
12088 #endif
12089
12090     /* scroll remaining steps with finest movement resolution */
12091     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12092
12093     while (player->MovPos)
12094     {
12095       ScrollPlayer(player, SCROLL_GO_ON);
12096       ScrollScreen(NULL, SCROLL_GO_ON);
12097
12098       AdvanceFrameAndPlayerCounters(player->index_nr);
12099
12100       DrawAllPlayers();
12101       BackToFront_WithFrameDelay(0);
12102     }
12103
12104     player->move_delay_value = original_move_delay_value;
12105   }
12106
12107   player->is_active = FALSE;
12108
12109   if (player->last_move_dir & MV_HORIZONTAL)
12110   {
12111     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12112       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12113   }
12114   else
12115   {
12116     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12117       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12118   }
12119
12120   if (!moved && !player->is_active)
12121   {
12122     player->is_moving = FALSE;
12123     player->is_digging = FALSE;
12124     player->is_collecting = FALSE;
12125     player->is_snapping = FALSE;
12126     player->is_pushing = FALSE;
12127   }
12128
12129   jx = player->jx;
12130   jy = player->jy;
12131
12132   if (moved & MP_MOVING && !ScreenMovPos &&
12133       (player->index_nr == game.centered_player_nr ||
12134        game.centered_player_nr == -1))
12135   {
12136     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12137     int offset = game.scroll_delay_value;
12138
12139     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12140     {
12141       /* actual player has left the screen -- scroll in that direction */
12142       if (jx != old_jx)         /* player has moved horizontally */
12143         scroll_x += (jx - old_jx);
12144       else                      /* player has moved vertically */
12145         scroll_y += (jy - old_jy);
12146     }
12147     else
12148     {
12149       if (jx != old_jx)         /* player has moved horizontally */
12150       {
12151         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12152             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12153           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12154
12155         /* don't scroll over playfield boundaries */
12156         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12157           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12158
12159         /* don't scroll more than one field at a time */
12160         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12161
12162         /* don't scroll against the player's moving direction */
12163         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12164             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12165           scroll_x = old_scroll_x;
12166       }
12167       else                      /* player has moved vertically */
12168       {
12169         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12170             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12171           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12172
12173         /* don't scroll over playfield boundaries */
12174         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12175           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12176
12177         /* don't scroll more than one field at a time */
12178         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12179
12180         /* don't scroll against the player's moving direction */
12181         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12182             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12183           scroll_y = old_scroll_y;
12184       }
12185     }
12186
12187     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12188     {
12189       if (!options.network && game.centered_player_nr == -1 &&
12190           !AllPlayersInVisibleScreen())
12191       {
12192         scroll_x = old_scroll_x;
12193         scroll_y = old_scroll_y;
12194       }
12195       else
12196       {
12197         ScrollScreen(player, SCROLL_INIT);
12198         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12199       }
12200     }
12201   }
12202
12203   player->StepFrame = 0;
12204
12205   if (moved & MP_MOVING)
12206   {
12207     if (old_jx != jx && old_jy == jy)
12208       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12209     else if (old_jx == jx && old_jy != jy)
12210       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12211
12212     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12213
12214     player->last_move_dir = player->MovDir;
12215     player->is_moving = TRUE;
12216     player->is_snapping = FALSE;
12217     player->is_switching = FALSE;
12218     player->is_dropping = FALSE;
12219     player->is_dropping_pressed = FALSE;
12220     player->drop_pressed_delay = 0;
12221
12222 #if 0
12223     /* should better be called here than above, but this breaks some tapes */
12224     ScrollPlayer(player, SCROLL_INIT);
12225 #endif
12226   }
12227   else
12228   {
12229     CheckGravityMovementWhenNotMoving(player);
12230
12231     player->is_moving = FALSE;
12232
12233     /* at this point, the player is allowed to move, but cannot move right now
12234        (e.g. because of something blocking the way) -- ensure that the player
12235        is also allowed to move in the next frame (in old versions before 3.1.1,
12236        the player was forced to wait again for eight frames before next try) */
12237
12238     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12239       player->move_delay = 0;   /* allow direct movement in the next frame */
12240   }
12241
12242   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12243     player->move_delay = player->move_delay_value;
12244
12245   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12246   {
12247     TestIfPlayerTouchesBadThing(jx, jy);
12248     TestIfPlayerTouchesCustomElement(jx, jy);
12249   }
12250
12251   if (!player->active)
12252     RemovePlayer(player);
12253
12254   return moved;
12255 }
12256
12257 void ScrollPlayer(struct PlayerInfo *player, int mode)
12258 {
12259   int jx = player->jx, jy = player->jy;
12260   int last_jx = player->last_jx, last_jy = player->last_jy;
12261   int move_stepsize = TILEX / player->move_delay_value;
12262
12263   if (!player->active)
12264     return;
12265
12266   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12267     return;
12268
12269   if (mode == SCROLL_INIT)
12270   {
12271     player->actual_frame_counter = FrameCounter;
12272     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12273
12274     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12275         Feld[last_jx][last_jy] == EL_EMPTY)
12276     {
12277       int last_field_block_delay = 0;   /* start with no blocking at all */
12278       int block_delay_adjustment = player->block_delay_adjustment;
12279
12280       /* if player blocks last field, add delay for exactly one move */
12281       if (player->block_last_field)
12282       {
12283         last_field_block_delay += player->move_delay_value;
12284
12285         /* when blocking enabled, prevent moving up despite gravity */
12286         if (player->gravity && player->MovDir == MV_UP)
12287           block_delay_adjustment = -1;
12288       }
12289
12290       /* add block delay adjustment (also possible when not blocking) */
12291       last_field_block_delay += block_delay_adjustment;
12292
12293       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12294       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12295     }
12296
12297     if (player->MovPos != 0)    /* player has not yet reached destination */
12298       return;
12299   }
12300   else if (!FrameReached(&player->actual_frame_counter, 1))
12301     return;
12302
12303   if (player->MovPos != 0)
12304   {
12305     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12306     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12307
12308     /* before DrawPlayer() to draw correct player graphic for this case */
12309     if (player->MovPos == 0)
12310       CheckGravityMovement(player);
12311   }
12312
12313   if (player->MovPos == 0)      /* player reached destination field */
12314   {
12315     if (player->move_delay_reset_counter > 0)
12316     {
12317       player->move_delay_reset_counter--;
12318
12319       if (player->move_delay_reset_counter == 0)
12320       {
12321         /* continue with normal speed after quickly moving through gate */
12322         HALVE_PLAYER_SPEED(player);
12323
12324         /* be able to make the next move without delay */
12325         player->move_delay = 0;
12326       }
12327     }
12328
12329     player->last_jx = jx;
12330     player->last_jy = jy;
12331
12332     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12333         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12334         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12335         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12336         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12337         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12338         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12339         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12340     {
12341       DrawPlayer(player);       /* needed here only to cleanup last field */
12342       RemovePlayer(player);
12343
12344       if (local_player->friends_still_needed == 0 ||
12345           IS_SP_ELEMENT(Feld[jx][jy]))
12346         PlayerWins(player);
12347     }
12348
12349     /* this breaks one level: "machine", level 000 */
12350     {
12351       int move_direction = player->MovDir;
12352       int enter_side = MV_DIR_OPPOSITE(move_direction);
12353       int leave_side = move_direction;
12354       int old_jx = last_jx;
12355       int old_jy = last_jy;
12356       int old_element = Feld[old_jx][old_jy];
12357       int new_element = Feld[jx][jy];
12358
12359       if (IS_CUSTOM_ELEMENT(old_element))
12360         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12361                                    CE_LEFT_BY_PLAYER,
12362                                    player->index_bit, leave_side);
12363
12364       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12365                                           CE_PLAYER_LEAVES_X,
12366                                           player->index_bit, leave_side);
12367
12368       if (IS_CUSTOM_ELEMENT(new_element))
12369         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12370                                    player->index_bit, enter_side);
12371
12372       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12373                                           CE_PLAYER_ENTERS_X,
12374                                           player->index_bit, enter_side);
12375
12376       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12377                                         CE_MOVE_OF_X, move_direction);
12378     }
12379
12380     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12381     {
12382       TestIfPlayerTouchesBadThing(jx, jy);
12383       TestIfPlayerTouchesCustomElement(jx, jy);
12384
12385       /* needed because pushed element has not yet reached its destination,
12386          so it would trigger a change event at its previous field location */
12387       if (!player->is_pushing)
12388         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12389
12390       if (!player->active)
12391         RemovePlayer(player);
12392     }
12393
12394     if (!local_player->LevelSolved && level.use_step_counter)
12395     {
12396       int i;
12397
12398       TimePlayed++;
12399
12400       if (TimeLeft > 0)
12401       {
12402         TimeLeft--;
12403
12404         if (TimeLeft <= 10 && setup.time_limit)
12405           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12406
12407         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12408
12409         DisplayGameControlValues();
12410
12411         if (!TimeLeft && setup.time_limit)
12412           for (i = 0; i < MAX_PLAYERS; i++)
12413             KillPlayer(&stored_player[i]);
12414       }
12415       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12416       {
12417         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12418
12419         DisplayGameControlValues();
12420       }
12421     }
12422
12423     if (tape.single_step && tape.recording && !tape.pausing &&
12424         !player->programmed_action)
12425       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12426
12427     if (!player->programmed_action)
12428       CheckSaveEngineSnapshot(player);
12429   }
12430 }
12431
12432 void ScrollScreen(struct PlayerInfo *player, int mode)
12433 {
12434   static unsigned int screen_frame_counter = 0;
12435
12436   if (mode == SCROLL_INIT)
12437   {
12438     /* set scrolling step size according to actual player's moving speed */
12439     ScrollStepSize = TILEX / player->move_delay_value;
12440
12441     screen_frame_counter = FrameCounter;
12442     ScreenMovDir = player->MovDir;
12443     ScreenMovPos = player->MovPos;
12444     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12445     return;
12446   }
12447   else if (!FrameReached(&screen_frame_counter, 1))
12448     return;
12449
12450   if (ScreenMovPos)
12451   {
12452     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12453     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12454     redraw_mask |= REDRAW_FIELD;
12455   }
12456   else
12457     ScreenMovDir = MV_NONE;
12458 }
12459
12460 void TestIfPlayerTouchesCustomElement(int x, int y)
12461 {
12462   static int xy[4][2] =
12463   {
12464     { 0, -1 },
12465     { -1, 0 },
12466     { +1, 0 },
12467     { 0, +1 }
12468   };
12469   static int trigger_sides[4][2] =
12470   {
12471     /* center side       border side */
12472     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12473     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12474     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12475     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12476   };
12477   static int touch_dir[4] =
12478   {
12479     MV_LEFT | MV_RIGHT,
12480     MV_UP   | MV_DOWN,
12481     MV_UP   | MV_DOWN,
12482     MV_LEFT | MV_RIGHT
12483   };
12484   int center_element = Feld[x][y];      /* should always be non-moving! */
12485   int i;
12486
12487   for (i = 0; i < NUM_DIRECTIONS; i++)
12488   {
12489     int xx = x + xy[i][0];
12490     int yy = y + xy[i][1];
12491     int center_side = trigger_sides[i][0];
12492     int border_side = trigger_sides[i][1];
12493     int border_element;
12494
12495     if (!IN_LEV_FIELD(xx, yy))
12496       continue;
12497
12498     if (IS_PLAYER(x, y))                /* player found at center element */
12499     {
12500       struct PlayerInfo *player = PLAYERINFO(x, y);
12501
12502       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12503         border_element = Feld[xx][yy];          /* may be moving! */
12504       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12505         border_element = Feld[xx][yy];
12506       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12507         border_element = MovingOrBlocked2Element(xx, yy);
12508       else
12509         continue;               /* center and border element do not touch */
12510
12511       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12512                                  player->index_bit, border_side);
12513       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12514                                           CE_PLAYER_TOUCHES_X,
12515                                           player->index_bit, border_side);
12516
12517       {
12518         /* use player element that is initially defined in the level playfield,
12519            not the player element that corresponds to the runtime player number
12520            (example: a level that contains EL_PLAYER_3 as the only player would
12521            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12522         int player_element = PLAYERINFO(x, y)->initial_element;
12523
12524         CheckElementChangeBySide(xx, yy, border_element, player_element,
12525                                  CE_TOUCHING_X, border_side);
12526       }
12527     }
12528     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12529     {
12530       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12531
12532       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12533       {
12534         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12535           continue;             /* center and border element do not touch */
12536       }
12537
12538       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12539                                  player->index_bit, center_side);
12540       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12541                                           CE_PLAYER_TOUCHES_X,
12542                                           player->index_bit, center_side);
12543
12544       {
12545         /* use player element that is initially defined in the level playfield,
12546            not the player element that corresponds to the runtime player number
12547            (example: a level that contains EL_PLAYER_3 as the only player would
12548            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12549         int player_element = PLAYERINFO(xx, yy)->initial_element;
12550
12551         CheckElementChangeBySide(x, y, center_element, player_element,
12552                                  CE_TOUCHING_X, center_side);
12553       }
12554
12555       break;
12556     }
12557   }
12558 }
12559
12560 void TestIfElementTouchesCustomElement(int x, int y)
12561 {
12562   static int xy[4][2] =
12563   {
12564     { 0, -1 },
12565     { -1, 0 },
12566     { +1, 0 },
12567     { 0, +1 }
12568   };
12569   static int trigger_sides[4][2] =
12570   {
12571     /* center side      border side */
12572     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12573     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12574     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12575     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12576   };
12577   static int touch_dir[4] =
12578   {
12579     MV_LEFT | MV_RIGHT,
12580     MV_UP   | MV_DOWN,
12581     MV_UP   | MV_DOWN,
12582     MV_LEFT | MV_RIGHT
12583   };
12584   boolean change_center_element = FALSE;
12585   int center_element = Feld[x][y];      /* should always be non-moving! */
12586   int border_element_old[NUM_DIRECTIONS];
12587   int i;
12588
12589   for (i = 0; i < NUM_DIRECTIONS; i++)
12590   {
12591     int xx = x + xy[i][0];
12592     int yy = y + xy[i][1];
12593     int border_element;
12594
12595     border_element_old[i] = -1;
12596
12597     if (!IN_LEV_FIELD(xx, yy))
12598       continue;
12599
12600     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12601       border_element = Feld[xx][yy];    /* may be moving! */
12602     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12603       border_element = Feld[xx][yy];
12604     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12605       border_element = MovingOrBlocked2Element(xx, yy);
12606     else
12607       continue;                 /* center and border element do not touch */
12608
12609     border_element_old[i] = border_element;
12610   }
12611
12612   for (i = 0; i < NUM_DIRECTIONS; i++)
12613   {
12614     int xx = x + xy[i][0];
12615     int yy = y + xy[i][1];
12616     int center_side = trigger_sides[i][0];
12617     int border_element = border_element_old[i];
12618
12619     if (border_element == -1)
12620       continue;
12621
12622     /* check for change of border element */
12623     CheckElementChangeBySide(xx, yy, border_element, center_element,
12624                              CE_TOUCHING_X, center_side);
12625
12626     /* (center element cannot be player, so we dont have to check this here) */
12627   }
12628
12629   for (i = 0; i < NUM_DIRECTIONS; i++)
12630   {
12631     int xx = x + xy[i][0];
12632     int yy = y + xy[i][1];
12633     int border_side = trigger_sides[i][1];
12634     int border_element = border_element_old[i];
12635
12636     if (border_element == -1)
12637       continue;
12638
12639     /* check for change of center element (but change it only once) */
12640     if (!change_center_element)
12641       change_center_element =
12642         CheckElementChangeBySide(x, y, center_element, border_element,
12643                                  CE_TOUCHING_X, border_side);
12644
12645     if (IS_PLAYER(xx, yy))
12646     {
12647       /* use player element that is initially defined in the level playfield,
12648          not the player element that corresponds to the runtime player number
12649          (example: a level that contains EL_PLAYER_3 as the only player would
12650          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12651       int player_element = PLAYERINFO(xx, yy)->initial_element;
12652
12653       CheckElementChangeBySide(x, y, center_element, player_element,
12654                                CE_TOUCHING_X, border_side);
12655     }
12656   }
12657 }
12658
12659 void TestIfElementHitsCustomElement(int x, int y, int direction)
12660 {
12661   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12662   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12663   int hitx = x + dx, hity = y + dy;
12664   int hitting_element = Feld[x][y];
12665   int touched_element;
12666
12667   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12668     return;
12669
12670   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12671                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12672
12673   if (IN_LEV_FIELD(hitx, hity))
12674   {
12675     int opposite_direction = MV_DIR_OPPOSITE(direction);
12676     int hitting_side = direction;
12677     int touched_side = opposite_direction;
12678     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12679                           MovDir[hitx][hity] != direction ||
12680                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12681
12682     object_hit = TRUE;
12683
12684     if (object_hit)
12685     {
12686       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12687                                CE_HITTING_X, touched_side);
12688
12689       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12690                                CE_HIT_BY_X, hitting_side);
12691
12692       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12693                                CE_HIT_BY_SOMETHING, opposite_direction);
12694
12695       if (IS_PLAYER(hitx, hity))
12696       {
12697         /* use player element that is initially defined in the level playfield,
12698            not the player element that corresponds to the runtime player number
12699            (example: a level that contains EL_PLAYER_3 as the only player would
12700            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12701         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12702
12703         CheckElementChangeBySide(x, y, hitting_element, player_element,
12704                                  CE_HITTING_X, touched_side);
12705       }
12706     }
12707   }
12708
12709   /* "hitting something" is also true when hitting the playfield border */
12710   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12711                            CE_HITTING_SOMETHING, direction);
12712 }
12713
12714 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12715 {
12716   int i, kill_x = -1, kill_y = -1;
12717
12718   int bad_element = -1;
12719   static int test_xy[4][2] =
12720   {
12721     { 0, -1 },
12722     { -1, 0 },
12723     { +1, 0 },
12724     { 0, +1 }
12725   };
12726   static int test_dir[4] =
12727   {
12728     MV_UP,
12729     MV_LEFT,
12730     MV_RIGHT,
12731     MV_DOWN
12732   };
12733
12734   for (i = 0; i < NUM_DIRECTIONS; i++)
12735   {
12736     int test_x, test_y, test_move_dir, test_element;
12737
12738     test_x = good_x + test_xy[i][0];
12739     test_y = good_y + test_xy[i][1];
12740
12741     if (!IN_LEV_FIELD(test_x, test_y))
12742       continue;
12743
12744     test_move_dir =
12745       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12746
12747     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12748
12749     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12750        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12751     */
12752     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12753         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12754     {
12755       kill_x = test_x;
12756       kill_y = test_y;
12757       bad_element = test_element;
12758
12759       break;
12760     }
12761   }
12762
12763   if (kill_x != -1 || kill_y != -1)
12764   {
12765     if (IS_PLAYER(good_x, good_y))
12766     {
12767       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12768
12769       if (player->shield_deadly_time_left > 0 &&
12770           !IS_INDESTRUCTIBLE(bad_element))
12771         Bang(kill_x, kill_y);
12772       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12773         KillPlayer(player);
12774     }
12775     else
12776       Bang(good_x, good_y);
12777   }
12778 }
12779
12780 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12781 {
12782   int i, kill_x = -1, kill_y = -1;
12783   int bad_element = Feld[bad_x][bad_y];
12784   static int test_xy[4][2] =
12785   {
12786     { 0, -1 },
12787     { -1, 0 },
12788     { +1, 0 },
12789     { 0, +1 }
12790   };
12791   static int touch_dir[4] =
12792   {
12793     MV_LEFT | MV_RIGHT,
12794     MV_UP   | MV_DOWN,
12795     MV_UP   | MV_DOWN,
12796     MV_LEFT | MV_RIGHT
12797   };
12798   static int test_dir[4] =
12799   {
12800     MV_UP,
12801     MV_LEFT,
12802     MV_RIGHT,
12803     MV_DOWN
12804   };
12805
12806   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12807     return;
12808
12809   for (i = 0; i < NUM_DIRECTIONS; i++)
12810   {
12811     int test_x, test_y, test_move_dir, test_element;
12812
12813     test_x = bad_x + test_xy[i][0];
12814     test_y = bad_y + test_xy[i][1];
12815
12816     if (!IN_LEV_FIELD(test_x, test_y))
12817       continue;
12818
12819     test_move_dir =
12820       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12821
12822     test_element = Feld[test_x][test_y];
12823
12824     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12825        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12826     */
12827     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12828         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12829     {
12830       /* good thing is player or penguin that does not move away */
12831       if (IS_PLAYER(test_x, test_y))
12832       {
12833         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12834
12835         if (bad_element == EL_ROBOT && player->is_moving)
12836           continue;     /* robot does not kill player if he is moving */
12837
12838         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12839         {
12840           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12841             continue;           /* center and border element do not touch */
12842         }
12843
12844         kill_x = test_x;
12845         kill_y = test_y;
12846
12847         break;
12848       }
12849       else if (test_element == EL_PENGUIN)
12850       {
12851         kill_x = test_x;
12852         kill_y = test_y;
12853
12854         break;
12855       }
12856     }
12857   }
12858
12859   if (kill_x != -1 || kill_y != -1)
12860   {
12861     if (IS_PLAYER(kill_x, kill_y))
12862     {
12863       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12864
12865       if (player->shield_deadly_time_left > 0 &&
12866           !IS_INDESTRUCTIBLE(bad_element))
12867         Bang(bad_x, bad_y);
12868       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12869         KillPlayer(player);
12870     }
12871     else
12872       Bang(kill_x, kill_y);
12873   }
12874 }
12875
12876 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12877 {
12878   int bad_element = Feld[bad_x][bad_y];
12879   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12880   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12881   int test_x = bad_x + dx, test_y = bad_y + dy;
12882   int test_move_dir, test_element;
12883   int kill_x = -1, kill_y = -1;
12884
12885   if (!IN_LEV_FIELD(test_x, test_y))
12886     return;
12887
12888   test_move_dir =
12889     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12890
12891   test_element = Feld[test_x][test_y];
12892
12893   if (test_move_dir != bad_move_dir)
12894   {
12895     /* good thing can be player or penguin that does not move away */
12896     if (IS_PLAYER(test_x, test_y))
12897     {
12898       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12899
12900       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12901          player as being hit when he is moving towards the bad thing, because
12902          the "get hit by" condition would be lost after the player stops) */
12903       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12904         return;         /* player moves away from bad thing */
12905
12906       kill_x = test_x;
12907       kill_y = test_y;
12908     }
12909     else if (test_element == EL_PENGUIN)
12910     {
12911       kill_x = test_x;
12912       kill_y = test_y;
12913     }
12914   }
12915
12916   if (kill_x != -1 || kill_y != -1)
12917   {
12918     if (IS_PLAYER(kill_x, kill_y))
12919     {
12920       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12921
12922       if (player->shield_deadly_time_left > 0 &&
12923           !IS_INDESTRUCTIBLE(bad_element))
12924         Bang(bad_x, bad_y);
12925       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12926         KillPlayer(player);
12927     }
12928     else
12929       Bang(kill_x, kill_y);
12930   }
12931 }
12932
12933 void TestIfPlayerTouchesBadThing(int x, int y)
12934 {
12935   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12936 }
12937
12938 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12939 {
12940   TestIfGoodThingHitsBadThing(x, y, move_dir);
12941 }
12942
12943 void TestIfBadThingTouchesPlayer(int x, int y)
12944 {
12945   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12946 }
12947
12948 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12949 {
12950   TestIfBadThingHitsGoodThing(x, y, move_dir);
12951 }
12952
12953 void TestIfFriendTouchesBadThing(int x, int y)
12954 {
12955   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12956 }
12957
12958 void TestIfBadThingTouchesFriend(int x, int y)
12959 {
12960   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12961 }
12962
12963 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12964 {
12965   int i, kill_x = bad_x, kill_y = bad_y;
12966   static int xy[4][2] =
12967   {
12968     { 0, -1 },
12969     { -1, 0 },
12970     { +1, 0 },
12971     { 0, +1 }
12972   };
12973
12974   for (i = 0; i < NUM_DIRECTIONS; i++)
12975   {
12976     int x, y, element;
12977
12978     x = bad_x + xy[i][0];
12979     y = bad_y + xy[i][1];
12980     if (!IN_LEV_FIELD(x, y))
12981       continue;
12982
12983     element = Feld[x][y];
12984     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12985         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12986     {
12987       kill_x = x;
12988       kill_y = y;
12989       break;
12990     }
12991   }
12992
12993   if (kill_x != bad_x || kill_y != bad_y)
12994     Bang(bad_x, bad_y);
12995 }
12996
12997 void KillPlayer(struct PlayerInfo *player)
12998 {
12999   int jx = player->jx, jy = player->jy;
13000
13001   if (!player->active)
13002     return;
13003
13004 #if 0
13005   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13006          player->killed, player->active, player->reanimated);
13007 #endif
13008
13009   /* the following code was introduced to prevent an infinite loop when calling
13010      -> Bang()
13011      -> CheckTriggeredElementChangeExt()
13012      -> ExecuteCustomElementAction()
13013      -> KillPlayer()
13014      -> (infinitely repeating the above sequence of function calls)
13015      which occurs when killing the player while having a CE with the setting
13016      "kill player X when explosion of <player X>"; the solution using a new
13017      field "player->killed" was chosen for backwards compatibility, although
13018      clever use of the fields "player->active" etc. would probably also work */
13019 #if 1
13020   if (player->killed)
13021     return;
13022 #endif
13023
13024   player->killed = TRUE;
13025
13026   /* remove accessible field at the player's position */
13027   Feld[jx][jy] = EL_EMPTY;
13028
13029   /* deactivate shield (else Bang()/Explode() would not work right) */
13030   player->shield_normal_time_left = 0;
13031   player->shield_deadly_time_left = 0;
13032
13033 #if 0
13034   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13035          player->killed, player->active, player->reanimated);
13036 #endif
13037
13038   Bang(jx, jy);
13039
13040 #if 0
13041   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13042          player->killed, player->active, player->reanimated);
13043 #endif
13044
13045   if (player->reanimated)       /* killed player may have been reanimated */
13046     player->killed = player->reanimated = FALSE;
13047   else
13048     BuryPlayer(player);
13049 }
13050
13051 static void KillPlayerUnlessEnemyProtected(int x, int y)
13052 {
13053   if (!PLAYER_ENEMY_PROTECTED(x, y))
13054     KillPlayer(PLAYERINFO(x, y));
13055 }
13056
13057 static void KillPlayerUnlessExplosionProtected(int x, int y)
13058 {
13059   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13060     KillPlayer(PLAYERINFO(x, y));
13061 }
13062
13063 void BuryPlayer(struct PlayerInfo *player)
13064 {
13065   int jx = player->jx, jy = player->jy;
13066
13067   if (!player->active)
13068     return;
13069
13070   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13071   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13072
13073   player->GameOver = TRUE;
13074   RemovePlayer(player);
13075 }
13076
13077 void RemovePlayer(struct PlayerInfo *player)
13078 {
13079   int jx = player->jx, jy = player->jy;
13080   int i, found = FALSE;
13081
13082   player->present = FALSE;
13083   player->active = FALSE;
13084
13085   if (!ExplodeField[jx][jy])
13086     StorePlayer[jx][jy] = 0;
13087
13088   if (player->is_moving)
13089     TEST_DrawLevelField(player->last_jx, player->last_jy);
13090
13091   for (i = 0; i < MAX_PLAYERS; i++)
13092     if (stored_player[i].active)
13093       found = TRUE;
13094
13095   if (!found)
13096     AllPlayersGone = TRUE;
13097
13098   ExitX = ZX = jx;
13099   ExitY = ZY = jy;
13100 }
13101
13102 static void setFieldForSnapping(int x, int y, int element, int direction)
13103 {
13104   struct ElementInfo *ei = &element_info[element];
13105   int direction_bit = MV_DIR_TO_BIT(direction);
13106   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13107   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13108                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13109
13110   Feld[x][y] = EL_ELEMENT_SNAPPING;
13111   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13112
13113   ResetGfxAnimation(x, y);
13114
13115   GfxElement[x][y] = element;
13116   GfxAction[x][y] = action;
13117   GfxDir[x][y] = direction;
13118   GfxFrame[x][y] = -1;
13119 }
13120
13121 /*
13122   =============================================================================
13123   checkDiagonalPushing()
13124   -----------------------------------------------------------------------------
13125   check if diagonal input device direction results in pushing of object
13126   (by checking if the alternative direction is walkable, diggable, ...)
13127   =============================================================================
13128 */
13129
13130 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13131                                     int x, int y, int real_dx, int real_dy)
13132 {
13133   int jx, jy, dx, dy, xx, yy;
13134
13135   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13136     return TRUE;
13137
13138   /* diagonal direction: check alternative direction */
13139   jx = player->jx;
13140   jy = player->jy;
13141   dx = x - jx;
13142   dy = y - jy;
13143   xx = jx + (dx == 0 ? real_dx : 0);
13144   yy = jy + (dy == 0 ? real_dy : 0);
13145
13146   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13147 }
13148
13149 /*
13150   =============================================================================
13151   DigField()
13152   -----------------------------------------------------------------------------
13153   x, y:                 field next to player (non-diagonal) to try to dig to
13154   real_dx, real_dy:     direction as read from input device (can be diagonal)
13155   =============================================================================
13156 */
13157
13158 static int DigField(struct PlayerInfo *player,
13159                     int oldx, int oldy, int x, int y,
13160                     int real_dx, int real_dy, int mode)
13161 {
13162   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13163   boolean player_was_pushing = player->is_pushing;
13164   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13165   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13166   int jx = oldx, jy = oldy;
13167   int dx = x - jx, dy = y - jy;
13168   int nextx = x + dx, nexty = y + dy;
13169   int move_direction = (dx == -1 ? MV_LEFT  :
13170                         dx == +1 ? MV_RIGHT :
13171                         dy == -1 ? MV_UP    :
13172                         dy == +1 ? MV_DOWN  : MV_NONE);
13173   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13174   int dig_side = MV_DIR_OPPOSITE(move_direction);
13175   int old_element = Feld[jx][jy];
13176   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13177   int collect_count;
13178
13179   if (is_player)                /* function can also be called by EL_PENGUIN */
13180   {
13181     if (player->MovPos == 0)
13182     {
13183       player->is_digging = FALSE;
13184       player->is_collecting = FALSE;
13185     }
13186
13187     if (player->MovPos == 0)    /* last pushing move finished */
13188       player->is_pushing = FALSE;
13189
13190     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13191     {
13192       player->is_switching = FALSE;
13193       player->push_delay = -1;
13194
13195       return MP_NO_ACTION;
13196     }
13197   }
13198
13199   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13200     old_element = Back[jx][jy];
13201
13202   /* in case of element dropped at player position, check background */
13203   else if (Back[jx][jy] != EL_EMPTY &&
13204            game.engine_version >= VERSION_IDENT(2,2,0,0))
13205     old_element = Back[jx][jy];
13206
13207   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13208     return MP_NO_ACTION;        /* field has no opening in this direction */
13209
13210   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13211     return MP_NO_ACTION;        /* field has no opening in this direction */
13212
13213   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13214   {
13215     SplashAcid(x, y);
13216
13217     Feld[jx][jy] = player->artwork_element;
13218     InitMovingField(jx, jy, MV_DOWN);
13219     Store[jx][jy] = EL_ACID;
13220     ContinueMoving(jx, jy);
13221     BuryPlayer(player);
13222
13223     return MP_DONT_RUN_INTO;
13224   }
13225
13226   if (player_can_move && DONT_RUN_INTO(element))
13227   {
13228     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13229
13230     return MP_DONT_RUN_INTO;
13231   }
13232
13233   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13234     return MP_NO_ACTION;
13235
13236   collect_count = element_info[element].collect_count_initial;
13237
13238   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13239     return MP_NO_ACTION;
13240
13241   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13242     player_can_move = player_can_move_or_snap;
13243
13244   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13245       game.engine_version >= VERSION_IDENT(2,2,0,0))
13246   {
13247     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13248                                player->index_bit, dig_side);
13249     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13250                                         player->index_bit, dig_side);
13251
13252     if (element == EL_DC_LANDMINE)
13253       Bang(x, y);
13254
13255     if (Feld[x][y] != element)          /* field changed by snapping */
13256       return MP_ACTION;
13257
13258     return MP_NO_ACTION;
13259   }
13260
13261   if (player->gravity && is_player && !player->is_auto_moving &&
13262       canFallDown(player) && move_direction != MV_DOWN &&
13263       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13264     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13265
13266   if (player_can_move &&
13267       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13268   {
13269     int sound_element = SND_ELEMENT(element);
13270     int sound_action = ACTION_WALKING;
13271
13272     if (IS_RND_GATE(element))
13273     {
13274       if (!player->key[RND_GATE_NR(element)])
13275         return MP_NO_ACTION;
13276     }
13277     else if (IS_RND_GATE_GRAY(element))
13278     {
13279       if (!player->key[RND_GATE_GRAY_NR(element)])
13280         return MP_NO_ACTION;
13281     }
13282     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13283     {
13284       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13285         return MP_NO_ACTION;
13286     }
13287     else if (element == EL_EXIT_OPEN ||
13288              element == EL_EM_EXIT_OPEN ||
13289              element == EL_EM_EXIT_OPENING ||
13290              element == EL_STEEL_EXIT_OPEN ||
13291              element == EL_EM_STEEL_EXIT_OPEN ||
13292              element == EL_EM_STEEL_EXIT_OPENING ||
13293              element == EL_SP_EXIT_OPEN ||
13294              element == EL_SP_EXIT_OPENING)
13295     {
13296       sound_action = ACTION_PASSING;    /* player is passing exit */
13297     }
13298     else if (element == EL_EMPTY)
13299     {
13300       sound_action = ACTION_MOVING;             /* nothing to walk on */
13301     }
13302
13303     /* play sound from background or player, whatever is available */
13304     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13305       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13306     else
13307       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13308   }
13309   else if (player_can_move &&
13310            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13311   {
13312     if (!ACCESS_FROM(element, opposite_direction))
13313       return MP_NO_ACTION;      /* field not accessible from this direction */
13314
13315     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13316       return MP_NO_ACTION;
13317
13318     if (IS_EM_GATE(element))
13319     {
13320       if (!player->key[EM_GATE_NR(element)])
13321         return MP_NO_ACTION;
13322     }
13323     else if (IS_EM_GATE_GRAY(element))
13324     {
13325       if (!player->key[EM_GATE_GRAY_NR(element)])
13326         return MP_NO_ACTION;
13327     }
13328     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13329     {
13330       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13331         return MP_NO_ACTION;
13332     }
13333     else if (IS_EMC_GATE(element))
13334     {
13335       if (!player->key[EMC_GATE_NR(element)])
13336         return MP_NO_ACTION;
13337     }
13338     else if (IS_EMC_GATE_GRAY(element))
13339     {
13340       if (!player->key[EMC_GATE_GRAY_NR(element)])
13341         return MP_NO_ACTION;
13342     }
13343     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13344     {
13345       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13346         return MP_NO_ACTION;
13347     }
13348     else if (element == EL_DC_GATE_WHITE ||
13349              element == EL_DC_GATE_WHITE_GRAY ||
13350              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13351     {
13352       if (player->num_white_keys == 0)
13353         return MP_NO_ACTION;
13354
13355       player->num_white_keys--;
13356     }
13357     else if (IS_SP_PORT(element))
13358     {
13359       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13360           element == EL_SP_GRAVITY_PORT_RIGHT ||
13361           element == EL_SP_GRAVITY_PORT_UP ||
13362           element == EL_SP_GRAVITY_PORT_DOWN)
13363         player->gravity = !player->gravity;
13364       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13365                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13366                element == EL_SP_GRAVITY_ON_PORT_UP ||
13367                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13368         player->gravity = TRUE;
13369       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13370                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13371                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13372                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13373         player->gravity = FALSE;
13374     }
13375
13376     /* automatically move to the next field with double speed */
13377     player->programmed_action = move_direction;
13378
13379     if (player->move_delay_reset_counter == 0)
13380     {
13381       player->move_delay_reset_counter = 2;     /* two double speed steps */
13382
13383       DOUBLE_PLAYER_SPEED(player);
13384     }
13385
13386     PlayLevelSoundAction(x, y, ACTION_PASSING);
13387   }
13388   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13389   {
13390     RemoveField(x, y);
13391
13392     if (mode != DF_SNAP)
13393     {
13394       GfxElement[x][y] = GFX_ELEMENT(element);
13395       player->is_digging = TRUE;
13396     }
13397
13398     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13399
13400     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13401                                         player->index_bit, dig_side);
13402
13403     if (mode == DF_SNAP)
13404     {
13405       if (level.block_snap_field)
13406         setFieldForSnapping(x, y, element, move_direction);
13407       else
13408         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13409
13410       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13411                                           player->index_bit, dig_side);
13412     }
13413   }
13414   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13415   {
13416     RemoveField(x, y);
13417
13418     if (is_player && mode != DF_SNAP)
13419     {
13420       GfxElement[x][y] = element;
13421       player->is_collecting = TRUE;
13422     }
13423
13424     if (element == EL_SPEED_PILL)
13425     {
13426       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13427     }
13428     else if (element == EL_EXTRA_TIME && level.time > 0)
13429     {
13430       TimeLeft += level.extra_time;
13431
13432       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13433
13434       DisplayGameControlValues();
13435     }
13436     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13437     {
13438       player->shield_normal_time_left += level.shield_normal_time;
13439       if (element == EL_SHIELD_DEADLY)
13440         player->shield_deadly_time_left += level.shield_deadly_time;
13441     }
13442     else if (element == EL_DYNAMITE ||
13443              element == EL_EM_DYNAMITE ||
13444              element == EL_SP_DISK_RED)
13445     {
13446       if (player->inventory_size < MAX_INVENTORY_SIZE)
13447         player->inventory_element[player->inventory_size++] = element;
13448
13449       DrawGameDoorValues();
13450     }
13451     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13452     {
13453       player->dynabomb_count++;
13454       player->dynabombs_left++;
13455     }
13456     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13457     {
13458       player->dynabomb_size++;
13459     }
13460     else if (element == EL_DYNABOMB_INCREASE_POWER)
13461     {
13462       player->dynabomb_xl = TRUE;
13463     }
13464     else if (IS_KEY(element))
13465     {
13466       player->key[KEY_NR(element)] = TRUE;
13467
13468       DrawGameDoorValues();
13469     }
13470     else if (element == EL_DC_KEY_WHITE)
13471     {
13472       player->num_white_keys++;
13473
13474       /* display white keys? */
13475       /* DrawGameDoorValues(); */
13476     }
13477     else if (IS_ENVELOPE(element))
13478     {
13479       player->show_envelope = element;
13480     }
13481     else if (element == EL_EMC_LENSES)
13482     {
13483       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13484
13485       RedrawAllInvisibleElementsForLenses();
13486     }
13487     else if (element == EL_EMC_MAGNIFIER)
13488     {
13489       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13490
13491       RedrawAllInvisibleElementsForMagnifier();
13492     }
13493     else if (IS_DROPPABLE(element) ||
13494              IS_THROWABLE(element))     /* can be collected and dropped */
13495     {
13496       int i;
13497
13498       if (collect_count == 0)
13499         player->inventory_infinite_element = element;
13500       else
13501         for (i = 0; i < collect_count; i++)
13502           if (player->inventory_size < MAX_INVENTORY_SIZE)
13503             player->inventory_element[player->inventory_size++] = element;
13504
13505       DrawGameDoorValues();
13506     }
13507     else if (collect_count > 0)
13508     {
13509       local_player->gems_still_needed -= collect_count;
13510       if (local_player->gems_still_needed < 0)
13511         local_player->gems_still_needed = 0;
13512
13513       game.snapshot.collected_item = TRUE;
13514
13515       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13516
13517       DisplayGameControlValues();
13518     }
13519
13520     RaiseScoreElement(element);
13521     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13522
13523     if (is_player)
13524       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13525                                           player->index_bit, dig_side);
13526
13527     if (mode == DF_SNAP)
13528     {
13529       if (level.block_snap_field)
13530         setFieldForSnapping(x, y, element, move_direction);
13531       else
13532         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13533
13534       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13535                                           player->index_bit, dig_side);
13536     }
13537   }
13538   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13539   {
13540     if (mode == DF_SNAP && element != EL_BD_ROCK)
13541       return MP_NO_ACTION;
13542
13543     if (CAN_FALL(element) && dy)
13544       return MP_NO_ACTION;
13545
13546     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13547         !(element == EL_SPRING && level.use_spring_bug))
13548       return MP_NO_ACTION;
13549
13550     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13551         ((move_direction & MV_VERTICAL &&
13552           ((element_info[element].move_pattern & MV_LEFT &&
13553             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13554            (element_info[element].move_pattern & MV_RIGHT &&
13555             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13556          (move_direction & MV_HORIZONTAL &&
13557           ((element_info[element].move_pattern & MV_UP &&
13558             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13559            (element_info[element].move_pattern & MV_DOWN &&
13560             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13561       return MP_NO_ACTION;
13562
13563     /* do not push elements already moving away faster than player */
13564     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13565         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13566       return MP_NO_ACTION;
13567
13568     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13569     {
13570       if (player->push_delay_value == -1 || !player_was_pushing)
13571         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13572     }
13573     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13574     {
13575       if (player->push_delay_value == -1)
13576         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13577     }
13578     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13579     {
13580       if (!player->is_pushing)
13581         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13582     }
13583
13584     player->is_pushing = TRUE;
13585     player->is_active = TRUE;
13586
13587     if (!(IN_LEV_FIELD(nextx, nexty) &&
13588           (IS_FREE(nextx, nexty) ||
13589            (IS_SB_ELEMENT(element) &&
13590             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13591            (IS_CUSTOM_ELEMENT(element) &&
13592             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13593       return MP_NO_ACTION;
13594
13595     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13596       return MP_NO_ACTION;
13597
13598     if (player->push_delay == -1)       /* new pushing; restart delay */
13599       player->push_delay = 0;
13600
13601     if (player->push_delay < player->push_delay_value &&
13602         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13603         element != EL_SPRING && element != EL_BALLOON)
13604     {
13605       /* make sure that there is no move delay before next try to push */
13606       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13607         player->move_delay = 0;
13608
13609       return MP_NO_ACTION;
13610     }
13611
13612     if (IS_CUSTOM_ELEMENT(element) &&
13613         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13614     {
13615       if (!DigFieldByCE(nextx, nexty, element))
13616         return MP_NO_ACTION;
13617     }
13618
13619     if (IS_SB_ELEMENT(element))
13620     {
13621       if (element == EL_SOKOBAN_FIELD_FULL)
13622       {
13623         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13624         local_player->sokobanfields_still_needed++;
13625       }
13626
13627       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13628       {
13629         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13630         local_player->sokobanfields_still_needed--;
13631       }
13632
13633       Feld[x][y] = EL_SOKOBAN_OBJECT;
13634
13635       if (Back[x][y] == Back[nextx][nexty])
13636         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13637       else if (Back[x][y] != 0)
13638         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13639                                     ACTION_EMPTYING);
13640       else
13641         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13642                                     ACTION_FILLING);
13643
13644       if (local_player->sokobanfields_still_needed == 0 &&
13645           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13646       {
13647         PlayerWins(player);
13648
13649         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13650       }
13651     }
13652     else
13653       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13654
13655     InitMovingField(x, y, move_direction);
13656     GfxAction[x][y] = ACTION_PUSHING;
13657
13658     if (mode == DF_SNAP)
13659       ContinueMoving(x, y);
13660     else
13661       MovPos[x][y] = (dx != 0 ? dx : dy);
13662
13663     Pushed[x][y] = TRUE;
13664     Pushed[nextx][nexty] = TRUE;
13665
13666     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13667       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13668     else
13669       player->push_delay_value = -1;    /* get new value later */
13670
13671     /* check for element change _after_ element has been pushed */
13672     if (game.use_change_when_pushing_bug)
13673     {
13674       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13675                                  player->index_bit, dig_side);
13676       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13677                                           player->index_bit, dig_side);
13678     }
13679   }
13680   else if (IS_SWITCHABLE(element))
13681   {
13682     if (PLAYER_SWITCHING(player, x, y))
13683     {
13684       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13685                                           player->index_bit, dig_side);
13686
13687       return MP_ACTION;
13688     }
13689
13690     player->is_switching = TRUE;
13691     player->switch_x = x;
13692     player->switch_y = y;
13693
13694     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13695
13696     if (element == EL_ROBOT_WHEEL)
13697     {
13698       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13699       ZX = x;
13700       ZY = y;
13701
13702       game.robot_wheel_active = TRUE;
13703
13704       TEST_DrawLevelField(x, y);
13705     }
13706     else if (element == EL_SP_TERMINAL)
13707     {
13708       int xx, yy;
13709
13710       SCAN_PLAYFIELD(xx, yy)
13711       {
13712         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13713         {
13714           Bang(xx, yy);
13715         }
13716         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13717         {
13718           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13719
13720           ResetGfxAnimation(xx, yy);
13721           TEST_DrawLevelField(xx, yy);
13722         }
13723       }
13724     }
13725     else if (IS_BELT_SWITCH(element))
13726     {
13727       ToggleBeltSwitch(x, y);
13728     }
13729     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13730              element == EL_SWITCHGATE_SWITCH_DOWN ||
13731              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13732              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13733     {
13734       ToggleSwitchgateSwitch(x, y);
13735     }
13736     else if (element == EL_LIGHT_SWITCH ||
13737              element == EL_LIGHT_SWITCH_ACTIVE)
13738     {
13739       ToggleLightSwitch(x, y);
13740     }
13741     else if (element == EL_TIMEGATE_SWITCH ||
13742              element == EL_DC_TIMEGATE_SWITCH)
13743     {
13744       ActivateTimegateSwitch(x, y);
13745     }
13746     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13747              element == EL_BALLOON_SWITCH_RIGHT ||
13748              element == EL_BALLOON_SWITCH_UP    ||
13749              element == EL_BALLOON_SWITCH_DOWN  ||
13750              element == EL_BALLOON_SWITCH_NONE  ||
13751              element == EL_BALLOON_SWITCH_ANY)
13752     {
13753       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13754                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13755                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13756                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13757                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13758                              move_direction);
13759     }
13760     else if (element == EL_LAMP)
13761     {
13762       Feld[x][y] = EL_LAMP_ACTIVE;
13763       local_player->lights_still_needed--;
13764
13765       ResetGfxAnimation(x, y);
13766       TEST_DrawLevelField(x, y);
13767     }
13768     else if (element == EL_TIME_ORB_FULL)
13769     {
13770       Feld[x][y] = EL_TIME_ORB_EMPTY;
13771
13772       if (level.time > 0 || level.use_time_orb_bug)
13773       {
13774         TimeLeft += level.time_orb_time;
13775         game.no_time_limit = FALSE;
13776
13777         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13778
13779         DisplayGameControlValues();
13780       }
13781
13782       ResetGfxAnimation(x, y);
13783       TEST_DrawLevelField(x, y);
13784     }
13785     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13786              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13787     {
13788       int xx, yy;
13789
13790       game.ball_state = !game.ball_state;
13791
13792       SCAN_PLAYFIELD(xx, yy)
13793       {
13794         int e = Feld[xx][yy];
13795
13796         if (game.ball_state)
13797         {
13798           if (e == EL_EMC_MAGIC_BALL)
13799             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13800           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13801             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13802         }
13803         else
13804         {
13805           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13806             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13807           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13808             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13809         }
13810       }
13811     }
13812
13813     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13814                                         player->index_bit, dig_side);
13815
13816     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13817                                         player->index_bit, dig_side);
13818
13819     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13820                                         player->index_bit, dig_side);
13821
13822     return MP_ACTION;
13823   }
13824   else
13825   {
13826     if (!PLAYER_SWITCHING(player, x, y))
13827     {
13828       player->is_switching = TRUE;
13829       player->switch_x = x;
13830       player->switch_y = y;
13831
13832       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13833                                  player->index_bit, dig_side);
13834       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13835                                           player->index_bit, dig_side);
13836
13837       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13838                                  player->index_bit, dig_side);
13839       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13840                                           player->index_bit, dig_side);
13841     }
13842
13843     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13844                                player->index_bit, dig_side);
13845     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13846                                         player->index_bit, dig_side);
13847
13848     return MP_NO_ACTION;
13849   }
13850
13851   player->push_delay = -1;
13852
13853   if (is_player)                /* function can also be called by EL_PENGUIN */
13854   {
13855     if (Feld[x][y] != element)          /* really digged/collected something */
13856     {
13857       player->is_collecting = !player->is_digging;
13858       player->is_active = TRUE;
13859     }
13860   }
13861
13862   return MP_MOVING;
13863 }
13864
13865 static boolean DigFieldByCE(int x, int y, int digging_element)
13866 {
13867   int element = Feld[x][y];
13868
13869   if (!IS_FREE(x, y))
13870   {
13871     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13872                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13873                   ACTION_BREAKING);
13874
13875     /* no element can dig solid indestructible elements */
13876     if (IS_INDESTRUCTIBLE(element) &&
13877         !IS_DIGGABLE(element) &&
13878         !IS_COLLECTIBLE(element))
13879       return FALSE;
13880
13881     if (AmoebaNr[x][y] &&
13882         (element == EL_AMOEBA_FULL ||
13883          element == EL_BD_AMOEBA ||
13884          element == EL_AMOEBA_GROWING))
13885     {
13886       AmoebaCnt[AmoebaNr[x][y]]--;
13887       AmoebaCnt2[AmoebaNr[x][y]]--;
13888     }
13889
13890     if (IS_MOVING(x, y))
13891       RemoveMovingField(x, y);
13892     else
13893     {
13894       RemoveField(x, y);
13895       TEST_DrawLevelField(x, y);
13896     }
13897
13898     /* if digged element was about to explode, prevent the explosion */
13899     ExplodeField[x][y] = EX_TYPE_NONE;
13900
13901     PlayLevelSoundAction(x, y, action);
13902   }
13903
13904   Store[x][y] = EL_EMPTY;
13905
13906   /* this makes it possible to leave the removed element again */
13907   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13908     Store[x][y] = element;
13909
13910   return TRUE;
13911 }
13912
13913 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13914 {
13915   int jx = player->jx, jy = player->jy;
13916   int x = jx + dx, y = jy + dy;
13917   int snap_direction = (dx == -1 ? MV_LEFT  :
13918                         dx == +1 ? MV_RIGHT :
13919                         dy == -1 ? MV_UP    :
13920                         dy == +1 ? MV_DOWN  : MV_NONE);
13921   boolean can_continue_snapping = (level.continuous_snapping &&
13922                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13923
13924   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13925     return FALSE;
13926
13927   if (!player->active || !IN_LEV_FIELD(x, y))
13928     return FALSE;
13929
13930   if (dx && dy)
13931     return FALSE;
13932
13933   if (!dx && !dy)
13934   {
13935     if (player->MovPos == 0)
13936       player->is_pushing = FALSE;
13937
13938     player->is_snapping = FALSE;
13939
13940     if (player->MovPos == 0)
13941     {
13942       player->is_moving = FALSE;
13943       player->is_digging = FALSE;
13944       player->is_collecting = FALSE;
13945     }
13946
13947     return FALSE;
13948   }
13949
13950   /* prevent snapping with already pressed snap key when not allowed */
13951   if (player->is_snapping && !can_continue_snapping)
13952     return FALSE;
13953
13954   player->MovDir = snap_direction;
13955
13956   if (player->MovPos == 0)
13957   {
13958     player->is_moving = FALSE;
13959     player->is_digging = FALSE;
13960     player->is_collecting = FALSE;
13961   }
13962
13963   player->is_dropping = FALSE;
13964   player->is_dropping_pressed = FALSE;
13965   player->drop_pressed_delay = 0;
13966
13967   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13968     return FALSE;
13969
13970   player->is_snapping = TRUE;
13971   player->is_active = TRUE;
13972
13973   if (player->MovPos == 0)
13974   {
13975     player->is_moving = FALSE;
13976     player->is_digging = FALSE;
13977     player->is_collecting = FALSE;
13978   }
13979
13980   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13981     TEST_DrawLevelField(player->last_jx, player->last_jy);
13982
13983   TEST_DrawLevelField(x, y);
13984
13985   return TRUE;
13986 }
13987
13988 static boolean DropElement(struct PlayerInfo *player)
13989 {
13990   int old_element, new_element;
13991   int dropx = player->jx, dropy = player->jy;
13992   int drop_direction = player->MovDir;
13993   int drop_side = drop_direction;
13994   int drop_element = get_next_dropped_element(player);
13995
13996   player->is_dropping_pressed = TRUE;
13997
13998   /* do not drop an element on top of another element; when holding drop key
13999      pressed without moving, dropped element must move away before the next
14000      element can be dropped (this is especially important if the next element
14001      is dynamite, which can be placed on background for historical reasons) */
14002   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14003     return MP_ACTION;
14004
14005   if (IS_THROWABLE(drop_element))
14006   {
14007     dropx += GET_DX_FROM_DIR(drop_direction);
14008     dropy += GET_DY_FROM_DIR(drop_direction);
14009
14010     if (!IN_LEV_FIELD(dropx, dropy))
14011       return FALSE;
14012   }
14013
14014   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14015   new_element = drop_element;           /* default: no change when dropping */
14016
14017   /* check if player is active, not moving and ready to drop */
14018   if (!player->active || player->MovPos || player->drop_delay > 0)
14019     return FALSE;
14020
14021   /* check if player has anything that can be dropped */
14022   if (new_element == EL_UNDEFINED)
14023     return FALSE;
14024
14025   /* check if drop key was pressed long enough for EM style dynamite */
14026   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14027     return FALSE;
14028
14029   /* check if anything can be dropped at the current position */
14030   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14031     return FALSE;
14032
14033   /* collected custom elements can only be dropped on empty fields */
14034   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14035     return FALSE;
14036
14037   if (old_element != EL_EMPTY)
14038     Back[dropx][dropy] = old_element;   /* store old element on this field */
14039
14040   ResetGfxAnimation(dropx, dropy);
14041   ResetRandomAnimationValue(dropx, dropy);
14042
14043   if (player->inventory_size > 0 ||
14044       player->inventory_infinite_element != EL_UNDEFINED)
14045   {
14046     if (player->inventory_size > 0)
14047     {
14048       player->inventory_size--;
14049
14050       DrawGameDoorValues();
14051
14052       if (new_element == EL_DYNAMITE)
14053         new_element = EL_DYNAMITE_ACTIVE;
14054       else if (new_element == EL_EM_DYNAMITE)
14055         new_element = EL_EM_DYNAMITE_ACTIVE;
14056       else if (new_element == EL_SP_DISK_RED)
14057         new_element = EL_SP_DISK_RED_ACTIVE;
14058     }
14059
14060     Feld[dropx][dropy] = new_element;
14061
14062     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14063       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14064                           el2img(Feld[dropx][dropy]), 0);
14065
14066     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14067
14068     /* needed if previous element just changed to "empty" in the last frame */
14069     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14070
14071     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14072                                player->index_bit, drop_side);
14073     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14074                                         CE_PLAYER_DROPS_X,
14075                                         player->index_bit, drop_side);
14076
14077     TestIfElementTouchesCustomElement(dropx, dropy);
14078   }
14079   else          /* player is dropping a dyna bomb */
14080   {
14081     player->dynabombs_left--;
14082
14083     Feld[dropx][dropy] = new_element;
14084
14085     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14086       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14087                           el2img(Feld[dropx][dropy]), 0);
14088
14089     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14090   }
14091
14092   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14093     InitField_WithBug1(dropx, dropy, FALSE);
14094
14095   new_element = Feld[dropx][dropy];     /* element might have changed */
14096
14097   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14098       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14099   {
14100     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14101       MovDir[dropx][dropy] = drop_direction;
14102
14103     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14104
14105     /* do not cause impact style collision by dropping elements that can fall */
14106     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14107   }
14108
14109   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14110   player->is_dropping = TRUE;
14111
14112   player->drop_pressed_delay = 0;
14113   player->is_dropping_pressed = FALSE;
14114
14115   player->drop_x = dropx;
14116   player->drop_y = dropy;
14117
14118   return TRUE;
14119 }
14120
14121 /* ------------------------------------------------------------------------- */
14122 /* game sound playing functions                                              */
14123 /* ------------------------------------------------------------------------- */
14124
14125 static int *loop_sound_frame = NULL;
14126 static int *loop_sound_volume = NULL;
14127
14128 void InitPlayLevelSound()
14129 {
14130   int num_sounds = getSoundListSize();
14131
14132   checked_free(loop_sound_frame);
14133   checked_free(loop_sound_volume);
14134
14135   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14136   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14137 }
14138
14139 static void PlayLevelSound(int x, int y, int nr)
14140 {
14141   int sx = SCREENX(x), sy = SCREENY(y);
14142   int volume, stereo_position;
14143   int max_distance = 8;
14144   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14145
14146   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14147       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14148     return;
14149
14150   if (!IN_LEV_FIELD(x, y) ||
14151       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14152       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14153     return;
14154
14155   volume = SOUND_MAX_VOLUME;
14156
14157   if (!IN_SCR_FIELD(sx, sy))
14158   {
14159     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14160     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14161
14162     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14163   }
14164
14165   stereo_position = (SOUND_MAX_LEFT +
14166                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14167                      (SCR_FIELDX + 2 * max_distance));
14168
14169   if (IS_LOOP_SOUND(nr))
14170   {
14171     /* This assures that quieter loop sounds do not overwrite louder ones,
14172        while restarting sound volume comparison with each new game frame. */
14173
14174     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14175       return;
14176
14177     loop_sound_volume[nr] = volume;
14178     loop_sound_frame[nr] = FrameCounter;
14179   }
14180
14181   PlaySoundExt(nr, volume, stereo_position, type);
14182 }
14183
14184 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14185 {
14186   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14187                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14188                  y < LEVELY(BY1) ? LEVELY(BY1) :
14189                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14190                  sound_action);
14191 }
14192
14193 static void PlayLevelSoundAction(int x, int y, int action)
14194 {
14195   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14196 }
14197
14198 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14199 {
14200   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14201
14202   if (sound_effect != SND_UNDEFINED)
14203     PlayLevelSound(x, y, sound_effect);
14204 }
14205
14206 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14207                                               int action)
14208 {
14209   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14210
14211   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14212     PlayLevelSound(x, y, sound_effect);
14213 }
14214
14215 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14216 {
14217   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14218
14219   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14220     PlayLevelSound(x, y, sound_effect);
14221 }
14222
14223 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14224 {
14225   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14226
14227   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14228     StopSound(sound_effect);
14229 }
14230
14231 static void PlayLevelMusic()
14232 {
14233   if (levelset.music[level_nr] != MUS_UNDEFINED)
14234     PlayMusic(levelset.music[level_nr]);        /* from config file */
14235   else
14236     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14237 }
14238
14239 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14240 {
14241   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14242   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14243   int x = xx - 1 - offset;
14244   int y = yy - 1 - offset;
14245
14246   switch (sample)
14247   {
14248     case SAMPLE_blank:
14249       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14250       break;
14251
14252     case SAMPLE_roll:
14253       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14254       break;
14255
14256     case SAMPLE_stone:
14257       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14258       break;
14259
14260     case SAMPLE_nut:
14261       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14262       break;
14263
14264     case SAMPLE_crack:
14265       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14266       break;
14267
14268     case SAMPLE_bug:
14269       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14270       break;
14271
14272     case SAMPLE_tank:
14273       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14274       break;
14275
14276     case SAMPLE_android_clone:
14277       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14278       break;
14279
14280     case SAMPLE_android_move:
14281       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14282       break;
14283
14284     case SAMPLE_spring:
14285       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14286       break;
14287
14288     case SAMPLE_slurp:
14289       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14290       break;
14291
14292     case SAMPLE_eater:
14293       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14294       break;
14295
14296     case SAMPLE_eater_eat:
14297       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14298       break;
14299
14300     case SAMPLE_alien:
14301       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14302       break;
14303
14304     case SAMPLE_collect:
14305       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14306       break;
14307
14308     case SAMPLE_diamond:
14309       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14310       break;
14311
14312     case SAMPLE_squash:
14313       /* !!! CHECK THIS !!! */
14314 #if 1
14315       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14316 #else
14317       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14318 #endif
14319       break;
14320
14321     case SAMPLE_wonderfall:
14322       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14323       break;
14324
14325     case SAMPLE_drip:
14326       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14327       break;
14328
14329     case SAMPLE_push:
14330       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14331       break;
14332
14333     case SAMPLE_dirt:
14334       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14335       break;
14336
14337     case SAMPLE_acid:
14338       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14339       break;
14340
14341     case SAMPLE_ball:
14342       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14343       break;
14344
14345     case SAMPLE_grow:
14346       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14347       break;
14348
14349     case SAMPLE_wonder:
14350       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14351       break;
14352
14353     case SAMPLE_door:
14354       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14355       break;
14356
14357     case SAMPLE_exit_open:
14358       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14359       break;
14360
14361     case SAMPLE_exit_leave:
14362       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14363       break;
14364
14365     case SAMPLE_dynamite:
14366       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14367       break;
14368
14369     case SAMPLE_tick:
14370       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14371       break;
14372
14373     case SAMPLE_press:
14374       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14375       break;
14376
14377     case SAMPLE_wheel:
14378       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14379       break;
14380
14381     case SAMPLE_boom:
14382       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14383       break;
14384
14385     case SAMPLE_die:
14386       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14387       break;
14388
14389     case SAMPLE_time:
14390       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14391       break;
14392
14393     default:
14394       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14395       break;
14396   }
14397 }
14398
14399 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14400 {
14401   int element = map_element_SP_to_RND(element_sp);
14402   int action = map_action_SP_to_RND(action_sp);
14403   int offset = (setup.sp_show_border_elements ? 0 : 1);
14404   int x = xx - offset;
14405   int y = yy - offset;
14406
14407   PlayLevelSoundElementAction(x, y, element, action);
14408 }
14409
14410 void RaiseScore(int value)
14411 {
14412   local_player->score += value;
14413
14414   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14415
14416   DisplayGameControlValues();
14417 }
14418
14419 void RaiseScoreElement(int element)
14420 {
14421   switch (element)
14422   {
14423     case EL_EMERALD:
14424     case EL_BD_DIAMOND:
14425     case EL_EMERALD_YELLOW:
14426     case EL_EMERALD_RED:
14427     case EL_EMERALD_PURPLE:
14428     case EL_SP_INFOTRON:
14429       RaiseScore(level.score[SC_EMERALD]);
14430       break;
14431     case EL_DIAMOND:
14432       RaiseScore(level.score[SC_DIAMOND]);
14433       break;
14434     case EL_CRYSTAL:
14435       RaiseScore(level.score[SC_CRYSTAL]);
14436       break;
14437     case EL_PEARL:
14438       RaiseScore(level.score[SC_PEARL]);
14439       break;
14440     case EL_BUG:
14441     case EL_BD_BUTTERFLY:
14442     case EL_SP_ELECTRON:
14443       RaiseScore(level.score[SC_BUG]);
14444       break;
14445     case EL_SPACESHIP:
14446     case EL_BD_FIREFLY:
14447     case EL_SP_SNIKSNAK:
14448       RaiseScore(level.score[SC_SPACESHIP]);
14449       break;
14450     case EL_YAMYAM:
14451     case EL_DARK_YAMYAM:
14452       RaiseScore(level.score[SC_YAMYAM]);
14453       break;
14454     case EL_ROBOT:
14455       RaiseScore(level.score[SC_ROBOT]);
14456       break;
14457     case EL_PACMAN:
14458       RaiseScore(level.score[SC_PACMAN]);
14459       break;
14460     case EL_NUT:
14461       RaiseScore(level.score[SC_NUT]);
14462       break;
14463     case EL_DYNAMITE:
14464     case EL_EM_DYNAMITE:
14465     case EL_SP_DISK_RED:
14466     case EL_DYNABOMB_INCREASE_NUMBER:
14467     case EL_DYNABOMB_INCREASE_SIZE:
14468     case EL_DYNABOMB_INCREASE_POWER:
14469       RaiseScore(level.score[SC_DYNAMITE]);
14470       break;
14471     case EL_SHIELD_NORMAL:
14472     case EL_SHIELD_DEADLY:
14473       RaiseScore(level.score[SC_SHIELD]);
14474       break;
14475     case EL_EXTRA_TIME:
14476       RaiseScore(level.extra_time_score);
14477       break;
14478     case EL_KEY_1:
14479     case EL_KEY_2:
14480     case EL_KEY_3:
14481     case EL_KEY_4:
14482     case EL_EM_KEY_1:
14483     case EL_EM_KEY_2:
14484     case EL_EM_KEY_3:
14485     case EL_EM_KEY_4:
14486     case EL_EMC_KEY_5:
14487     case EL_EMC_KEY_6:
14488     case EL_EMC_KEY_7:
14489     case EL_EMC_KEY_8:
14490     case EL_DC_KEY_WHITE:
14491       RaiseScore(level.score[SC_KEY]);
14492       break;
14493     default:
14494       RaiseScore(element_info[element].collect_score);
14495       break;
14496   }
14497 }
14498
14499 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14500 {
14501   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14502   {
14503     /* closing door required in case of envelope style request dialogs */
14504     if (!skip_request)
14505       CloseDoor(DOOR_CLOSE_1);
14506
14507 #if defined(NETWORK_AVALIABLE)
14508     if (options.network)
14509       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14510     else
14511 #endif
14512     {
14513       if (quick_quit)
14514         FadeSkipNextFadeIn();
14515
14516       SetGameStatus(GAME_MODE_MAIN);
14517
14518       DrawMainMenu();
14519     }
14520   }
14521   else          /* continue playing the game */
14522   {
14523     if (tape.playing && tape.deactivate_display)
14524       TapeDeactivateDisplayOff(TRUE);
14525
14526     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14527
14528     if (tape.playing && tape.deactivate_display)
14529       TapeDeactivateDisplayOn();
14530   }
14531 }
14532
14533 void RequestQuitGame(boolean ask_if_really_quit)
14534 {
14535   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14536   boolean skip_request = AllPlayersGone || quick_quit;
14537
14538   RequestQuitGameExt(skip_request, quick_quit,
14539                      "Do you really want to quit the game?");
14540 }
14541
14542
14543 /* ------------------------------------------------------------------------- */
14544 /* random generator functions                                                */
14545 /* ------------------------------------------------------------------------- */
14546
14547 unsigned int InitEngineRandom_RND(int seed)
14548 {
14549   game.num_random_calls = 0;
14550
14551   return InitEngineRandom(seed);
14552 }
14553
14554 unsigned int RND(int max)
14555 {
14556   if (max > 0)
14557   {
14558     game.num_random_calls++;
14559
14560     return GetEngineRandom(max);
14561   }
14562
14563   return 0;
14564 }
14565
14566
14567 /* ------------------------------------------------------------------------- */
14568 /* game engine snapshot handling functions                                   */
14569 /* ------------------------------------------------------------------------- */
14570
14571 struct EngineSnapshotInfo
14572 {
14573   /* runtime values for custom element collect score */
14574   int collect_score[NUM_CUSTOM_ELEMENTS];
14575
14576   /* runtime values for group element choice position */
14577   int choice_pos[NUM_GROUP_ELEMENTS];
14578
14579   /* runtime values for belt position animations */
14580   int belt_graphic[4][NUM_BELT_PARTS];
14581   int belt_anim_mode[4][NUM_BELT_PARTS];
14582 };
14583
14584 static struct EngineSnapshotInfo engine_snapshot_rnd;
14585 static char *snapshot_level_identifier = NULL;
14586 static int snapshot_level_nr = -1;
14587
14588 static void SaveEngineSnapshotValues_RND()
14589 {
14590   static int belt_base_active_element[4] =
14591   {
14592     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14593     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14594     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14595     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14596   };
14597   int i, j;
14598
14599   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14600   {
14601     int element = EL_CUSTOM_START + i;
14602
14603     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14604   }
14605
14606   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14607   {
14608     int element = EL_GROUP_START + i;
14609
14610     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14611   }
14612
14613   for (i = 0; i < 4; i++)
14614   {
14615     for (j = 0; j < NUM_BELT_PARTS; j++)
14616     {
14617       int element = belt_base_active_element[i] + j;
14618       int graphic = el2img(element);
14619       int anim_mode = graphic_info[graphic].anim_mode;
14620
14621       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14622       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14623     }
14624   }
14625 }
14626
14627 static void LoadEngineSnapshotValues_RND()
14628 {
14629   unsigned int num_random_calls = game.num_random_calls;
14630   int i, j;
14631
14632   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14633   {
14634     int element = EL_CUSTOM_START + i;
14635
14636     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14637   }
14638
14639   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14640   {
14641     int element = EL_GROUP_START + i;
14642
14643     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14644   }
14645
14646   for (i = 0; i < 4; i++)
14647   {
14648     for (j = 0; j < NUM_BELT_PARTS; j++)
14649     {
14650       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14651       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14652
14653       graphic_info[graphic].anim_mode = anim_mode;
14654     }
14655   }
14656
14657   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14658   {
14659     InitRND(tape.random_seed);
14660     for (i = 0; i < num_random_calls; i++)
14661       RND(1);
14662   }
14663
14664   if (game.num_random_calls != num_random_calls)
14665   {
14666     Error(ERR_INFO, "number of random calls out of sync");
14667     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14668     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14669     Error(ERR_EXIT, "this should not happen -- please debug");
14670   }
14671 }
14672
14673 void FreeEngineSnapshotSingle()
14674 {
14675   FreeSnapshotSingle();
14676
14677   setString(&snapshot_level_identifier, NULL);
14678   snapshot_level_nr = -1;
14679 }
14680
14681 void FreeEngineSnapshotList()
14682 {
14683   FreeSnapshotList();
14684 }
14685
14686 ListNode *SaveEngineSnapshotBuffers()
14687 {
14688   ListNode *buffers = NULL;
14689
14690   /* copy some special values to a structure better suited for the snapshot */
14691
14692   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14693     SaveEngineSnapshotValues_RND();
14694   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14695     SaveEngineSnapshotValues_EM();
14696   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14697     SaveEngineSnapshotValues_SP(&buffers);
14698
14699   /* save values stored in special snapshot structure */
14700
14701   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14702     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14703   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14704     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14705   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14706     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14707
14708   /* save further RND engine values */
14709
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14713
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14718
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14724
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14728
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14730
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14732
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14735
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14754
14755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14757
14758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14761
14762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14764
14765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14770
14771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14773
14774 #if 0
14775   ListNode *node = engine_snapshot_list_rnd;
14776   int num_bytes = 0;
14777
14778   while (node != NULL)
14779   {
14780     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14781
14782     node = node->next;
14783   }
14784
14785   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14786 #endif
14787
14788   return buffers;
14789 }
14790
14791 void SaveEngineSnapshotSingle()
14792 {
14793   ListNode *buffers = SaveEngineSnapshotBuffers();
14794
14795   /* finally save all snapshot buffers to single snapshot */
14796   SaveSnapshotSingle(buffers);
14797
14798   /* save level identification information */
14799   setString(&snapshot_level_identifier, leveldir_current->identifier);
14800   snapshot_level_nr = level_nr;
14801 }
14802
14803 boolean CheckSaveEngineSnapshotToList()
14804 {
14805   boolean save_snapshot =
14806     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14807      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14808       game.snapshot.changed_action) ||
14809      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14810       game.snapshot.collected_item));
14811
14812   game.snapshot.changed_action = FALSE;
14813   game.snapshot.collected_item = FALSE;
14814   game.snapshot.save_snapshot = save_snapshot;
14815
14816   return save_snapshot;
14817 }
14818
14819 void SaveEngineSnapshotToList()
14820 {
14821   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14822       tape.quick_resume)
14823     return;
14824
14825   ListNode *buffers = SaveEngineSnapshotBuffers();
14826
14827   /* finally save all snapshot buffers to snapshot list */
14828   SaveSnapshotToList(buffers);
14829 }
14830
14831 void SaveEngineSnapshotToListInitial()
14832 {
14833   FreeEngineSnapshotList();
14834
14835   SaveEngineSnapshotToList();
14836 }
14837
14838 void LoadEngineSnapshotValues()
14839 {
14840   /* restore special values from snapshot structure */
14841
14842   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14843     LoadEngineSnapshotValues_RND();
14844   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14845     LoadEngineSnapshotValues_EM();
14846   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14847     LoadEngineSnapshotValues_SP();
14848 }
14849
14850 void LoadEngineSnapshotSingle()
14851 {
14852   LoadSnapshotSingle();
14853
14854   LoadEngineSnapshotValues();
14855 }
14856
14857 void LoadEngineSnapshot_Undo(int steps)
14858 {
14859   LoadSnapshotFromList_Older(steps);
14860
14861   LoadEngineSnapshotValues();
14862 }
14863
14864 void LoadEngineSnapshot_Redo(int steps)
14865 {
14866   LoadSnapshotFromList_Newer(steps);
14867
14868   LoadEngineSnapshotValues();
14869 }
14870
14871 boolean CheckEngineSnapshotSingle()
14872 {
14873   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14874           snapshot_level_nr == level_nr);
14875 }
14876
14877 boolean CheckEngineSnapshotList()
14878 {
14879   return CheckSnapshotList();
14880 }
14881
14882
14883 /* ---------- new game button stuff ---------------------------------------- */
14884
14885 static struct
14886 {
14887   int graphic;
14888   struct XY *pos;
14889   int gadget_id;
14890   char *infotext;
14891 } gamebutton_info[NUM_GAME_BUTTONS] =
14892 {
14893   {
14894     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14895     GAME_CTRL_ID_STOP,                  "stop game"
14896   },
14897   {
14898     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14899     GAME_CTRL_ID_PAUSE,                 "pause game"
14900   },
14901   {
14902     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14903     GAME_CTRL_ID_PLAY,                  "play game"
14904   },
14905   {
14906     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14907     GAME_CTRL_ID_UNDO,                  "undo step"
14908   },
14909   {
14910     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14911     GAME_CTRL_ID_REDO,                  "redo step"
14912   },
14913   {
14914     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14915     GAME_CTRL_ID_SAVE,                  "save game"
14916   },
14917   {
14918     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14919     GAME_CTRL_ID_PAUSE2,                "pause game"
14920   },
14921   {
14922     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14923     GAME_CTRL_ID_LOAD,                  "load game"
14924   },
14925   {
14926     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14927     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14928   },
14929   {
14930     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14931     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14932   },
14933   {
14934     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14935     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14936   }
14937 };
14938
14939 void CreateGameButtons()
14940 {
14941   int i;
14942
14943   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14944   {
14945     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14946     struct XY *pos = gamebutton_info[i].pos;
14947     struct GadgetInfo *gi;
14948     int button_type;
14949     boolean checked;
14950     unsigned int event_mask;
14951     int base_x = (tape.show_game_buttons ? VX : DX);
14952     int base_y = (tape.show_game_buttons ? VY : DY);
14953     int gd_x   = gfx->src_x;
14954     int gd_y   = gfx->src_y;
14955     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14956     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14957     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14958     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14959     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14960     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14961     int id = i;
14962
14963     if (gfx->bitmap == NULL)
14964     {
14965       game_gadget[id] = NULL;
14966
14967       continue;
14968     }
14969
14970     if (id == GAME_CTRL_ID_STOP ||
14971         id == GAME_CTRL_ID_PLAY ||
14972         id == GAME_CTRL_ID_SAVE ||
14973         id == GAME_CTRL_ID_LOAD)
14974     {
14975       button_type = GD_TYPE_NORMAL_BUTTON;
14976       checked = FALSE;
14977       event_mask = GD_EVENT_RELEASED;
14978     }
14979     else if (id == GAME_CTRL_ID_UNDO ||
14980              id == GAME_CTRL_ID_REDO)
14981     {
14982       button_type = GD_TYPE_NORMAL_BUTTON;
14983       checked = FALSE;
14984       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14985     }
14986     else
14987     {
14988       button_type = GD_TYPE_CHECK_BUTTON;
14989       checked =
14990         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14991          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14992          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14993       event_mask = GD_EVENT_PRESSED;
14994     }
14995
14996     gi = CreateGadget(GDI_CUSTOM_ID, id,
14997                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14998                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14999                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15000                       GDI_WIDTH, gfx->width,
15001                       GDI_HEIGHT, gfx->height,
15002                       GDI_TYPE, button_type,
15003                       GDI_STATE, GD_BUTTON_UNPRESSED,
15004                       GDI_CHECKED, checked,
15005                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15006                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15007                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15008                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15009                       GDI_DIRECT_DRAW, FALSE,
15010                       GDI_EVENT_MASK, event_mask,
15011                       GDI_CALLBACK_ACTION, HandleGameButtons,
15012                       GDI_END);
15013
15014     if (gi == NULL)
15015       Error(ERR_EXIT, "cannot create gadget");
15016
15017     game_gadget[id] = gi;
15018   }
15019 }
15020
15021 void FreeGameButtons()
15022 {
15023   int i;
15024
15025   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15026     FreeGadget(game_gadget[i]);
15027 }
15028
15029 static void UnmapGameButtonsAtSamePosition(int id)
15030 {
15031   int i;
15032
15033   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15034     if (i != id &&
15035         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15036         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15037       UnmapGadget(game_gadget[i]);
15038 }
15039
15040 static void UnmapGameButtonsAtSamePosition_All()
15041 {
15042   if (setup.show_snapshot_buttons)
15043   {
15044     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15045     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15046     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15047   }
15048   else
15049   {
15050     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15051     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15052     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15053   }
15054 }
15055
15056 static void MapGameButtonsAtSamePosition(int id)
15057 {
15058   int i;
15059
15060   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15061     if (i != id &&
15062         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15063         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15064       MapGadget(game_gadget[i]);
15065
15066   UnmapGameButtonsAtSamePosition_All();
15067 }
15068
15069 void MapUndoRedoButtons()
15070 {
15071   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15072   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15073
15074   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15075   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15076
15077   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15078 }
15079
15080 void UnmapUndoRedoButtons()
15081 {
15082   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15083   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15084
15085   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15086   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15087
15088   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15089 }
15090
15091 void MapGameButtons()
15092 {
15093   int i;
15094
15095   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15096     if (i != GAME_CTRL_ID_UNDO &&
15097         i != GAME_CTRL_ID_REDO)
15098       MapGadget(game_gadget[i]);
15099
15100   UnmapGameButtonsAtSamePosition_All();
15101
15102   RedrawGameButtons();
15103 }
15104
15105 void UnmapGameButtons()
15106 {
15107   int i;
15108
15109   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15110     UnmapGadget(game_gadget[i]);
15111 }
15112
15113 void RedrawGameButtons()
15114 {
15115   int i;
15116
15117   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15118     RedrawGadget(game_gadget[i]);
15119
15120   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15121   redraw_mask &= ~REDRAW_ALL;
15122 }
15123
15124 void GameUndoRedoExt()
15125 {
15126   ClearPlayerAction();
15127
15128   tape.pausing = TRUE;
15129
15130   RedrawPlayfield();
15131   UpdateAndDisplayGameControlValues();
15132
15133   DrawCompleteVideoDisplay();
15134   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15135   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15136   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15137
15138   BackToFront();
15139 }
15140
15141 void GameUndo(int steps)
15142 {
15143   if (!CheckEngineSnapshotList())
15144     return;
15145
15146   LoadEngineSnapshot_Undo(steps);
15147
15148   GameUndoRedoExt();
15149 }
15150
15151 void GameRedo(int steps)
15152 {
15153   if (!CheckEngineSnapshotList())
15154     return;
15155
15156   LoadEngineSnapshot_Redo(steps);
15157
15158   GameUndoRedoExt();
15159 }
15160
15161 static void HandleGameButtonsExt(int id, int button)
15162 {
15163   static boolean game_undo_executed = FALSE;
15164   int steps = BUTTON_STEPSIZE(button);
15165   boolean handle_game_buttons =
15166     (game_status == GAME_MODE_PLAYING ||
15167      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15168
15169   if (!handle_game_buttons)
15170     return;
15171
15172   switch (id)
15173   {
15174     case GAME_CTRL_ID_STOP:
15175       if (game_status == GAME_MODE_MAIN)
15176         break;
15177
15178       if (tape.playing)
15179         TapeStop();
15180       else
15181         RequestQuitGame(TRUE);
15182
15183       break;
15184
15185     case GAME_CTRL_ID_PAUSE:
15186     case GAME_CTRL_ID_PAUSE2:
15187       if (options.network && game_status == GAME_MODE_PLAYING)
15188       {
15189 #if defined(NETWORK_AVALIABLE)
15190         if (tape.pausing)
15191           SendToServer_ContinuePlaying();
15192         else
15193           SendToServer_PausePlaying();
15194 #endif
15195       }
15196       else
15197         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15198
15199       game_undo_executed = FALSE;
15200
15201       break;
15202
15203     case GAME_CTRL_ID_PLAY:
15204       if (game_status == GAME_MODE_MAIN)
15205       {
15206         StartGameActions(options.network, setup.autorecord, level.random_seed);
15207       }
15208       else if (tape.pausing)
15209       {
15210 #if defined(NETWORK_AVALIABLE)
15211         if (options.network)
15212           SendToServer_ContinuePlaying();
15213         else
15214 #endif
15215           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15216       }
15217       break;
15218
15219     case GAME_CTRL_ID_UNDO:
15220       // Important: When using "save snapshot when collecting an item" mode,
15221       // load last (current) snapshot for first "undo" after pressing "pause"
15222       // (else the last-but-one snapshot would be loaded, because the snapshot
15223       // pointer already points to the last snapshot when pressing "pause",
15224       // which is fine for "every step/move" mode, but not for "every collect")
15225       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15226           !game_undo_executed)
15227         steps--;
15228
15229       game_undo_executed = TRUE;
15230
15231       GameUndo(steps);
15232       break;
15233
15234     case GAME_CTRL_ID_REDO:
15235       GameRedo(steps);
15236       break;
15237
15238     case GAME_CTRL_ID_SAVE:
15239       TapeQuickSave();
15240       break;
15241
15242     case GAME_CTRL_ID_LOAD:
15243       TapeQuickLoad();
15244       break;
15245
15246     case SOUND_CTRL_ID_MUSIC:
15247       if (setup.sound_music)
15248       { 
15249         setup.sound_music = FALSE;
15250
15251         FadeMusic();
15252       }
15253       else if (audio.music_available)
15254       { 
15255         setup.sound = setup.sound_music = TRUE;
15256
15257         SetAudioMode(setup.sound);
15258
15259         PlayLevelMusic();
15260       }
15261       break;
15262
15263     case SOUND_CTRL_ID_LOOPS:
15264       if (setup.sound_loops)
15265         setup.sound_loops = FALSE;
15266       else if (audio.loops_available)
15267       {
15268         setup.sound = setup.sound_loops = TRUE;
15269
15270         SetAudioMode(setup.sound);
15271       }
15272       break;
15273
15274     case SOUND_CTRL_ID_SIMPLE:
15275       if (setup.sound_simple)
15276         setup.sound_simple = FALSE;
15277       else if (audio.sound_available)
15278       {
15279         setup.sound = setup.sound_simple = TRUE;
15280
15281         SetAudioMode(setup.sound);
15282       }
15283       break;
15284
15285     default:
15286       break;
15287   }
15288 }
15289
15290 static void HandleGameButtons(struct GadgetInfo *gi)
15291 {
15292   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15293 }
15294
15295 void HandleSoundButtonKeys(Key key)
15296 {
15297
15298   if (key == setup.shortcut.sound_simple)
15299     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15300   else if (key == setup.shortcut.sound_loops)
15301     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15302   else if (key == setup.shortcut.sound_music)
15303     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15304 }