13f264e8614f3c3a9cde745e95c2de62096dae69
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_FRAME                        35
126 #define GAME_PANEL_SHIELD_NORMAL                36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
128 #define GAME_PANEL_SHIELD_DEADLY                38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
130 #define GAME_PANEL_EXIT                         40
131 #define GAME_PANEL_EMC_MAGIC_BALL               41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
133 #define GAME_PANEL_LIGHT_SWITCH                 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
135 #define GAME_PANEL_TIMEGATE_SWITCH              45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
137 #define GAME_PANEL_SWITCHGATE_SWITCH            47
138 #define GAME_PANEL_EMC_LENSES                   48
139 #define GAME_PANEL_EMC_LENSES_TIME              49
140 #define GAME_PANEL_EMC_MAGNIFIER                50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
142 #define GAME_PANEL_BALLOON_SWITCH               52
143 #define GAME_PANEL_DYNABOMB_NUMBER              53
144 #define GAME_PANEL_DYNABOMB_SIZE                54
145 #define GAME_PANEL_DYNABOMB_POWER               55
146 #define GAME_PANEL_PENGUINS                     56
147 #define GAME_PANEL_SOKOBAN_OBJECTS              57
148 #define GAME_PANEL_SOKOBAN_FIELDS               58
149 #define GAME_PANEL_ROBOT_WHEEL                  59
150 #define GAME_PANEL_CONVEYOR_BELT_1              60
151 #define GAME_PANEL_CONVEYOR_BELT_2              61
152 #define GAME_PANEL_CONVEYOR_BELT_3              62
153 #define GAME_PANEL_CONVEYOR_BELT_4              63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
158 #define GAME_PANEL_MAGIC_WALL                   68
159 #define GAME_PANEL_MAGIC_WALL_TIME              69
160 #define GAME_PANEL_GRAVITY_STATE                70
161 #define GAME_PANEL_GRAPHIC_1                    71
162 #define GAME_PANEL_GRAPHIC_2                    72
163 #define GAME_PANEL_GRAPHIC_3                    73
164 #define GAME_PANEL_GRAPHIC_4                    74
165 #define GAME_PANEL_GRAPHIC_5                    75
166 #define GAME_PANEL_GRAPHIC_6                    76
167 #define GAME_PANEL_GRAPHIC_7                    77
168 #define GAME_PANEL_GRAPHIC_8                    78
169 #define GAME_PANEL_ELEMENT_1                    79
170 #define GAME_PANEL_ELEMENT_2                    80
171 #define GAME_PANEL_ELEMENT_3                    81
172 #define GAME_PANEL_ELEMENT_4                    82
173 #define GAME_PANEL_ELEMENT_5                    83
174 #define GAME_PANEL_ELEMENT_6                    84
175 #define GAME_PANEL_ELEMENT_7                    85
176 #define GAME_PANEL_ELEMENT_8                    86
177 #define GAME_PANEL_ELEMENT_COUNT_1              87
178 #define GAME_PANEL_ELEMENT_COUNT_2              88
179 #define GAME_PANEL_ELEMENT_COUNT_3              89
180 #define GAME_PANEL_ELEMENT_COUNT_4              90
181 #define GAME_PANEL_ELEMENT_COUNT_5              91
182 #define GAME_PANEL_ELEMENT_COUNT_6              92
183 #define GAME_PANEL_ELEMENT_COUNT_7              93
184 #define GAME_PANEL_ELEMENT_COUNT_8              94
185 #define GAME_PANEL_CE_SCORE_1                   95
186 #define GAME_PANEL_CE_SCORE_2                   96
187 #define GAME_PANEL_CE_SCORE_3                   97
188 #define GAME_PANEL_CE_SCORE_4                   98
189 #define GAME_PANEL_CE_SCORE_5                   99
190 #define GAME_PANEL_CE_SCORE_6                   100
191 #define GAME_PANEL_CE_SCORE_7                   101
192 #define GAME_PANEL_CE_SCORE_8                   102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
201 #define GAME_PANEL_PLAYER_NAME                  111
202 #define GAME_PANEL_LEVEL_NAME                   112
203 #define GAME_PANEL_LEVEL_AUTHOR                 113
204
205 #define NUM_GAME_PANEL_CONTROLS                 114
206
207 struct GamePanelOrderInfo
208 {
209   int nr;
210   int sort_priority;
211 };
212
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
214
215 struct GamePanelControlInfo
216 {
217   int nr;
218
219   struct TextPosInfo *pos;
220   int type;
221
222   int value, last_value;
223   int frame, last_frame;
224   int gfx_frame;
225   int gfx_random;
226 };
227
228 static struct GamePanelControlInfo game_panel_controls[] =
229 {
230   {
231     GAME_PANEL_LEVEL_NUMBER,
232     &game.panel.level_number,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_PANEL_GEMS,
237     &game.panel.gems,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_INVENTORY_COUNT,
242     &game.panel.inventory_count,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_FIRST_1,
247     &game.panel.inventory_first[0],
248     TYPE_ELEMENT,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_2,
252     &game.panel.inventory_first[1],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_3,
257     &game.panel.inventory_first[2],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_4,
262     &game.panel.inventory_first[3],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_5,
267     &game.panel.inventory_first[4],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_6,
272     &game.panel.inventory_first[5],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_7,
277     &game.panel.inventory_first[6],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_8,
282     &game.panel.inventory_first[7],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_LAST_1,
287     &game.panel.inventory_last[0],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_2,
292     &game.panel.inventory_last[1],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_3,
297     &game.panel.inventory_last[2],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_4,
302     &game.panel.inventory_last[3],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_5,
307     &game.panel.inventory_last[4],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_6,
312     &game.panel.inventory_last[5],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_7,
317     &game.panel.inventory_last[6],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_8,
322     &game.panel.inventory_last[7],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_KEY_1,
327     &game.panel.key[0],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_2,
332     &game.panel.key[1],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_3,
337     &game.panel.key[2],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_4,
342     &game.panel.key[3],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_5,
347     &game.panel.key[4],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_6,
352     &game.panel.key[5],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_7,
357     &game.panel.key[6],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_8,
362     &game.panel.key[7],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_WHITE,
367     &game.panel.key_white,
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE_COUNT,
372     &game.panel.key_white_count,
373     TYPE_INTEGER,
374   },
375   {
376     GAME_PANEL_SCORE,
377     &game.panel.score,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_HIGHSCORE,
382     &game.panel.highscore,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_TIME,
387     &game.panel.time,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME_HH,
392     &game.panel.time_hh,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_MM,
397     &game.panel.time_mm,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_SS,
402     &game.panel.time_ss,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_FRAME,
407     &game.panel.frame,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_SHIELD_NORMAL,
412     &game.panel.shield_normal,
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_PANEL_SHIELD_NORMAL_TIME,
417     &game.panel.shield_normal_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_PANEL_SHIELD_DEADLY,
422     &game.panel.shield_deadly,
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_PANEL_SHIELD_DEADLY_TIME,
427     &game.panel.shield_deadly_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_PANEL_EXIT,
432     &game.panel.exit,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_PANEL_EMC_MAGIC_BALL,
437     &game.panel.emc_magic_ball,
438     TYPE_ELEMENT,
439   },
440   {
441     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442     &game.panel.emc_magic_ball_switch,
443     TYPE_ELEMENT,
444   },
445   {
446     GAME_PANEL_LIGHT_SWITCH,
447     &game.panel.light_switch,
448     TYPE_ELEMENT,
449   },
450   {
451     GAME_PANEL_LIGHT_SWITCH_TIME,
452     &game.panel.light_switch_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_PANEL_TIMEGATE_SWITCH,
457     &game.panel.timegate_switch,
458     TYPE_ELEMENT,
459   },
460   {
461     GAME_PANEL_TIMEGATE_SWITCH_TIME,
462     &game.panel.timegate_switch_time,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_PANEL_SWITCHGATE_SWITCH,
467     &game.panel.switchgate_switch,
468     TYPE_ELEMENT,
469   },
470   {
471     GAME_PANEL_EMC_LENSES,
472     &game.panel.emc_lenses,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_PANEL_EMC_LENSES_TIME,
477     &game.panel.emc_lenses_time,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_PANEL_EMC_MAGNIFIER,
482     &game.panel.emc_magnifier,
483     TYPE_ELEMENT,
484   },
485   {
486     GAME_PANEL_EMC_MAGNIFIER_TIME,
487     &game.panel.emc_magnifier_time,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_PANEL_BALLOON_SWITCH,
492     &game.panel.balloon_switch,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_PANEL_DYNABOMB_NUMBER,
497     &game.panel.dynabomb_number,
498     TYPE_INTEGER,
499   },
500   {
501     GAME_PANEL_DYNABOMB_SIZE,
502     &game.panel.dynabomb_size,
503     TYPE_INTEGER,
504   },
505   {
506     GAME_PANEL_DYNABOMB_POWER,
507     &game.panel.dynabomb_power,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_PANEL_PENGUINS,
512     &game.panel.penguins,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_PANEL_SOKOBAN_OBJECTS,
517     &game.panel.sokoban_objects,
518     TYPE_INTEGER,
519   },
520   {
521     GAME_PANEL_SOKOBAN_FIELDS,
522     &game.panel.sokoban_fields,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_PANEL_ROBOT_WHEEL,
527     &game.panel.robot_wheel,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_PANEL_CONVEYOR_BELT_1,
532     &game.panel.conveyor_belt[0],
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_PANEL_CONVEYOR_BELT_2,
537     &game.panel.conveyor_belt[1],
538     TYPE_ELEMENT,
539   },
540   {
541     GAME_PANEL_CONVEYOR_BELT_3,
542     &game.panel.conveyor_belt[2],
543     TYPE_ELEMENT,
544   },
545   {
546     GAME_PANEL_CONVEYOR_BELT_4,
547     &game.panel.conveyor_belt[3],
548     TYPE_ELEMENT,
549   },
550   {
551     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552     &game.panel.conveyor_belt_switch[0],
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557     &game.panel.conveyor_belt_switch[1],
558     TYPE_ELEMENT,
559   },
560   {
561     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562     &game.panel.conveyor_belt_switch[2],
563     TYPE_ELEMENT,
564   },
565   {
566     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567     &game.panel.conveyor_belt_switch[3],
568     TYPE_ELEMENT,
569   },
570   {
571     GAME_PANEL_MAGIC_WALL,
572     &game.panel.magic_wall,
573     TYPE_ELEMENT,
574   },
575   {
576     GAME_PANEL_MAGIC_WALL_TIME,
577     &game.panel.magic_wall_time,
578     TYPE_INTEGER,
579   },
580   {
581     GAME_PANEL_GRAVITY_STATE,
582     &game.panel.gravity_state,
583     TYPE_STRING,
584   },
585   {
586     GAME_PANEL_GRAPHIC_1,
587     &game.panel.graphic[0],
588     TYPE_ELEMENT,
589   },
590   {
591     GAME_PANEL_GRAPHIC_2,
592     &game.panel.graphic[1],
593     TYPE_ELEMENT,
594   },
595   {
596     GAME_PANEL_GRAPHIC_3,
597     &game.panel.graphic[2],
598     TYPE_ELEMENT,
599   },
600   {
601     GAME_PANEL_GRAPHIC_4,
602     &game.panel.graphic[3],
603     TYPE_ELEMENT,
604   },
605   {
606     GAME_PANEL_GRAPHIC_5,
607     &game.panel.graphic[4],
608     TYPE_ELEMENT,
609   },
610   {
611     GAME_PANEL_GRAPHIC_6,
612     &game.panel.graphic[5],
613     TYPE_ELEMENT,
614   },
615   {
616     GAME_PANEL_GRAPHIC_7,
617     &game.panel.graphic[6],
618     TYPE_ELEMENT,
619   },
620   {
621     GAME_PANEL_GRAPHIC_8,
622     &game.panel.graphic[7],
623     TYPE_ELEMENT,
624   },
625   {
626     GAME_PANEL_ELEMENT_1,
627     &game.panel.element[0],
628     TYPE_ELEMENT,
629   },
630   {
631     GAME_PANEL_ELEMENT_2,
632     &game.panel.element[1],
633     TYPE_ELEMENT,
634   },
635   {
636     GAME_PANEL_ELEMENT_3,
637     &game.panel.element[2],
638     TYPE_ELEMENT,
639   },
640   {
641     GAME_PANEL_ELEMENT_4,
642     &game.panel.element[3],
643     TYPE_ELEMENT,
644   },
645   {
646     GAME_PANEL_ELEMENT_5,
647     &game.panel.element[4],
648     TYPE_ELEMENT,
649   },
650   {
651     GAME_PANEL_ELEMENT_6,
652     &game.panel.element[5],
653     TYPE_ELEMENT,
654   },
655   {
656     GAME_PANEL_ELEMENT_7,
657     &game.panel.element[6],
658     TYPE_ELEMENT,
659   },
660   {
661     GAME_PANEL_ELEMENT_8,
662     &game.panel.element[7],
663     TYPE_ELEMENT,
664   },
665   {
666     GAME_PANEL_ELEMENT_COUNT_1,
667     &game.panel.element_count[0],
668     TYPE_INTEGER,
669   },
670   {
671     GAME_PANEL_ELEMENT_COUNT_2,
672     &game.panel.element_count[1],
673     TYPE_INTEGER,
674   },
675   {
676     GAME_PANEL_ELEMENT_COUNT_3,
677     &game.panel.element_count[2],
678     TYPE_INTEGER,
679   },
680   {
681     GAME_PANEL_ELEMENT_COUNT_4,
682     &game.panel.element_count[3],
683     TYPE_INTEGER,
684   },
685   {
686     GAME_PANEL_ELEMENT_COUNT_5,
687     &game.panel.element_count[4],
688     TYPE_INTEGER,
689   },
690   {
691     GAME_PANEL_ELEMENT_COUNT_6,
692     &game.panel.element_count[5],
693     TYPE_INTEGER,
694   },
695   {
696     GAME_PANEL_ELEMENT_COUNT_7,
697     &game.panel.element_count[6],
698     TYPE_INTEGER,
699   },
700   {
701     GAME_PANEL_ELEMENT_COUNT_8,
702     &game.panel.element_count[7],
703     TYPE_INTEGER,
704   },
705   {
706     GAME_PANEL_CE_SCORE_1,
707     &game.panel.ce_score[0],
708     TYPE_INTEGER,
709   },
710   {
711     GAME_PANEL_CE_SCORE_2,
712     &game.panel.ce_score[1],
713     TYPE_INTEGER,
714   },
715   {
716     GAME_PANEL_CE_SCORE_3,
717     &game.panel.ce_score[2],
718     TYPE_INTEGER,
719   },
720   {
721     GAME_PANEL_CE_SCORE_4,
722     &game.panel.ce_score[3],
723     TYPE_INTEGER,
724   },
725   {
726     GAME_PANEL_CE_SCORE_5,
727     &game.panel.ce_score[4],
728     TYPE_INTEGER,
729   },
730   {
731     GAME_PANEL_CE_SCORE_6,
732     &game.panel.ce_score[5],
733     TYPE_INTEGER,
734   },
735   {
736     GAME_PANEL_CE_SCORE_7,
737     &game.panel.ce_score[6],
738     TYPE_INTEGER,
739   },
740   {
741     GAME_PANEL_CE_SCORE_8,
742     &game.panel.ce_score[7],
743     TYPE_INTEGER,
744   },
745   {
746     GAME_PANEL_CE_SCORE_1_ELEMENT,
747     &game.panel.ce_score_element[0],
748     TYPE_ELEMENT,
749   },
750   {
751     GAME_PANEL_CE_SCORE_2_ELEMENT,
752     &game.panel.ce_score_element[1],
753     TYPE_ELEMENT,
754   },
755   {
756     GAME_PANEL_CE_SCORE_3_ELEMENT,
757     &game.panel.ce_score_element[2],
758     TYPE_ELEMENT,
759   },
760   {
761     GAME_PANEL_CE_SCORE_4_ELEMENT,
762     &game.panel.ce_score_element[3],
763     TYPE_ELEMENT,
764   },
765   {
766     GAME_PANEL_CE_SCORE_5_ELEMENT,
767     &game.panel.ce_score_element[4],
768     TYPE_ELEMENT,
769   },
770   {
771     GAME_PANEL_CE_SCORE_6_ELEMENT,
772     &game.panel.ce_score_element[5],
773     TYPE_ELEMENT,
774   },
775   {
776     GAME_PANEL_CE_SCORE_7_ELEMENT,
777     &game.panel.ce_score_element[6],
778     TYPE_ELEMENT,
779   },
780   {
781     GAME_PANEL_CE_SCORE_8_ELEMENT,
782     &game.panel.ce_score_element[7],
783     TYPE_ELEMENT,
784   },
785   {
786     GAME_PANEL_PLAYER_NAME,
787     &game.panel.player_name,
788     TYPE_STRING,
789   },
790   {
791     GAME_PANEL_LEVEL_NAME,
792     &game.panel.level_name,
793     TYPE_STRING,
794   },
795   {
796     GAME_PANEL_LEVEL_AUTHOR,
797     &game.panel.level_author,
798     TYPE_STRING,
799   },
800
801   {
802     -1,
803     NULL,
804     -1,
805   }
806 };
807
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING      3
810 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION   2
812 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
813
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF  -1
816 #define INITIAL_MOVE_DELAY_ON   0
817
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED    32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED   4
822 #define MOVE_DELAY_MAX_SPEED    1
823
824 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
826
827 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
829
830 /* values for scroll positions */
831 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
832                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
833                                  (x) - MIDPOSX)
834 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
835                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
836                                  (y) - MIDPOSY)
837
838 /* values for other actions */
839 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
840 #define MOVE_STEPSIZE_MIN       (1)
841 #define MOVE_STEPSIZE_MAX       (TILEX)
842
843 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
844 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
845
846 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
847
848 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
849                                  RND(element_info[e].push_delay_random))
850 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
851                                  RND(element_info[e].drop_delay_random))
852 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
853                                  RND(element_info[e].move_delay_random))
854 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
855                                     (element_info[e].move_delay_random))
856 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
857                                  RND(element_info[e].ce_value_random_initial))
858 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
859 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
860                                  RND((c)->delay_random * (c)->delay_frames))
861 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
862                                  RND((c)->delay_random))
863
864
865 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
866          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
867
868 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
869         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
870          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
871          (be) + (e) - EL_SELF)
872
873 #define GET_PLAYER_FROM_BITS(p)                                         \
874         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
875
876 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
877         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
878          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
879          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
880          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
881          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
882          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
883          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
884          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
885          (e))
886
887 #define CAN_GROW_INTO(e)                                                \
888         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
889
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
891                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
892                                         (condition)))
893
894 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
895                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
896                                         (CAN_MOVE_INTO_ACID(e) &&       \
897                                          Feld[x][y] == EL_ACID) ||      \
898                                         (condition)))
899
900 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
901                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
902                                         (CAN_MOVE_INTO_ACID(e) &&       \
903                                          Feld[x][y] == EL_ACID) ||      \
904                                         (condition)))
905
906 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
907                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
908                                         (condition) ||                  \
909                                         (CAN_MOVE_INTO_ACID(e) &&       \
910                                          Feld[x][y] == EL_ACID) ||      \
911                                         (DONT_COLLIDE_WITH(e) &&        \
912                                          IS_PLAYER(x, y) &&             \
913                                          !PLAYER_ENEMY_PROTECTED(x, y))))
914
915 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
916         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
917
918 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
919         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
920
921 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
922         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
923
924 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
925         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
926                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
927
928 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
929         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
930
931 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
932         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
933
934 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
935         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
936
937 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
938         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
939
940 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
942
943 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
945                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
946                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
947                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
948                                                  IS_FOOD_PENGUIN(Feld[x][y])))
949 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
950         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
951
952 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
953         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
954
955 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
956         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957
958 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
959         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
960                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
961
962 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
963
964 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
965                 (!IS_PLAYER(x, y) &&                                    \
966                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
967
968 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
969         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
970
971 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
972 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
973
974 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
975 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
976 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
977 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
978
979 /* game button identifiers */
980 #define GAME_CTRL_ID_STOP               0
981 #define GAME_CTRL_ID_PAUSE              1
982 #define GAME_CTRL_ID_PLAY               2
983 #define GAME_CTRL_ID_UNDO               3
984 #define GAME_CTRL_ID_REDO               4
985 #define GAME_CTRL_ID_SAVE               5
986 #define GAME_CTRL_ID_PAUSE2             6
987 #define GAME_CTRL_ID_LOAD               7
988 #define SOUND_CTRL_ID_MUSIC             8
989 #define SOUND_CTRL_ID_LOOPS             9
990 #define SOUND_CTRL_ID_SIMPLE            10
991
992 #define NUM_GAME_BUTTONS                11
993
994
995 /* forward declaration for internal use */
996
997 static void CreateField(int, int, int);
998
999 static void ResetGfxAnimation(int, int);
1000
1001 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1002 static void AdvanceFrameAndPlayerCounters(int);
1003
1004 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1005 static boolean MovePlayer(struct PlayerInfo *, int, int);
1006 static void ScrollPlayer(struct PlayerInfo *, int);
1007 static void ScrollScreen(struct PlayerInfo *, int);
1008
1009 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1010 static boolean DigFieldByCE(int, int, int);
1011 static boolean SnapField(struct PlayerInfo *, int, int);
1012 static boolean DropElement(struct PlayerInfo *);
1013
1014 static void InitBeltMovement(void);
1015 static void CloseAllOpenTimegates(void);
1016 static void CheckGravityMovement(struct PlayerInfo *);
1017 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1018 static void KillPlayerUnlessEnemyProtected(int, int);
1019 static void KillPlayerUnlessExplosionProtected(int, int);
1020
1021 static void TestIfPlayerTouchesCustomElement(int, int);
1022 static void TestIfElementTouchesCustomElement(int, int);
1023 static void TestIfElementHitsCustomElement(int, int, int);
1024
1025 static void HandleElementChange(int, int, int);
1026 static void ExecuteCustomElementAction(int, int, int, int);
1027 static boolean ChangeElement(int, int, int, int);
1028
1029 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1030 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1031         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1032 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1033         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1034 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1035         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1036 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1037         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1038
1039 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1040 #define CheckElementChange(x, y, e, te, ev)                             \
1041         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1042 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1043         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1044 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1045         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1046
1047 static void PlayLevelSound(int, int, int);
1048 static void PlayLevelSoundNearest(int, int, int);
1049 static void PlayLevelSoundAction(int, int, int);
1050 static void PlayLevelSoundElementAction(int, int, int, int);
1051 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1052 static void PlayLevelSoundActionIfLoop(int, int, int);
1053 static void StopLevelSoundActionIfLoop(int, int, int);
1054 static void PlayLevelMusic();
1055 static void FadeLevelSoundsAndMusic();
1056
1057 static void HandleGameButtons(struct GadgetInfo *);
1058
1059 int AmoebeNachbarNr(int, int);
1060 void AmoebeUmwandeln(int, int);
1061 void ContinueMoving(int, int);
1062 void Bang(int, int);
1063 void InitMovDir(int, int);
1064 void InitAmoebaNr(int, int);
1065 int NewHiScore(void);
1066
1067 void TestIfGoodThingHitsBadThing(int, int, int);
1068 void TestIfBadThingHitsGoodThing(int, int, int);
1069 void TestIfPlayerTouchesBadThing(int, int);
1070 void TestIfPlayerRunsIntoBadThing(int, int, int);
1071 void TestIfBadThingTouchesPlayer(int, int);
1072 void TestIfBadThingRunsIntoPlayer(int, int, int);
1073 void TestIfFriendTouchesBadThing(int, int);
1074 void TestIfBadThingTouchesFriend(int, int);
1075 void TestIfBadThingTouchesOtherBadThing(int, int);
1076 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1077
1078 void KillPlayer(struct PlayerInfo *);
1079 void BuryPlayer(struct PlayerInfo *);
1080 void RemovePlayer(struct PlayerInfo *);
1081
1082 static int getInvisibleActiveFromInvisibleElement(int);
1083 static int getInvisibleFromInvisibleActiveElement(int);
1084
1085 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1086
1087 /* for detection of endless loops, caused by custom element programming */
1088 /* (using maximal playfield width x 10 is just a rough approximation) */
1089 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1090
1091 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1092 {                                                                       \
1093   if (recursion_loop_detected)                                          \
1094     return (rc);                                                        \
1095                                                                         \
1096   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1097   {                                                                     \
1098     recursion_loop_detected = TRUE;                                     \
1099     recursion_loop_element = (e);                                       \
1100   }                                                                     \
1101                                                                         \
1102   recursion_loop_depth++;                                               \
1103 }
1104
1105 #define RECURSION_LOOP_DETECTION_END()                                  \
1106 {                                                                       \
1107   recursion_loop_depth--;                                               \
1108 }
1109
1110 static int recursion_loop_depth;
1111 static boolean recursion_loop_detected;
1112 static boolean recursion_loop_element;
1113
1114 static int map_player_action[MAX_PLAYERS];
1115
1116
1117 /* ------------------------------------------------------------------------- */
1118 /* definition of elements that automatically change to other elements after  */
1119 /* a specified time, eventually calling a function when changing             */
1120 /* ------------------------------------------------------------------------- */
1121
1122 /* forward declaration for changer functions */
1123 static void InitBuggyBase(int, int);
1124 static void WarnBuggyBase(int, int);
1125
1126 static void InitTrap(int, int);
1127 static void ActivateTrap(int, int);
1128 static void ChangeActiveTrap(int, int);
1129
1130 static void InitRobotWheel(int, int);
1131 static void RunRobotWheel(int, int);
1132 static void StopRobotWheel(int, int);
1133
1134 static void InitTimegateWheel(int, int);
1135 static void RunTimegateWheel(int, int);
1136
1137 static void InitMagicBallDelay(int, int);
1138 static void ActivateMagicBall(int, int);
1139
1140 struct ChangingElementInfo
1141 {
1142   int element;
1143   int target_element;
1144   int change_delay;
1145   void (*pre_change_function)(int x, int y);
1146   void (*change_function)(int x, int y);
1147   void (*post_change_function)(int x, int y);
1148 };
1149
1150 static struct ChangingElementInfo change_delay_list[] =
1151 {
1152   {
1153     EL_NUT_BREAKING,
1154     EL_EMERALD,
1155     6,
1156     NULL,
1157     NULL,
1158     NULL
1159   },
1160   {
1161     EL_PEARL_BREAKING,
1162     EL_EMPTY,
1163     8,
1164     NULL,
1165     NULL,
1166     NULL
1167   },
1168   {
1169     EL_EXIT_OPENING,
1170     EL_EXIT_OPEN,
1171     29,
1172     NULL,
1173     NULL,
1174     NULL
1175   },
1176   {
1177     EL_EXIT_CLOSING,
1178     EL_EXIT_CLOSED,
1179     29,
1180     NULL,
1181     NULL,
1182     NULL
1183   },
1184   {
1185     EL_STEEL_EXIT_OPENING,
1186     EL_STEEL_EXIT_OPEN,
1187     29,
1188     NULL,
1189     NULL,
1190     NULL
1191   },
1192   {
1193     EL_STEEL_EXIT_CLOSING,
1194     EL_STEEL_EXIT_CLOSED,
1195     29,
1196     NULL,
1197     NULL,
1198     NULL
1199   },
1200   {
1201     EL_EM_EXIT_OPENING,
1202     EL_EM_EXIT_OPEN,
1203     29,
1204     NULL,
1205     NULL,
1206     NULL
1207   },
1208   {
1209     EL_EM_EXIT_CLOSING,
1210     EL_EMPTY,
1211     29,
1212     NULL,
1213     NULL,
1214     NULL
1215   },
1216   {
1217     EL_EM_STEEL_EXIT_OPENING,
1218     EL_EM_STEEL_EXIT_OPEN,
1219     29,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_EM_STEEL_EXIT_CLOSING,
1226     EL_STEELWALL,
1227     29,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_SP_EXIT_OPENING,
1234     EL_SP_EXIT_OPEN,
1235     29,
1236     NULL,
1237     NULL,
1238     NULL
1239   },
1240   {
1241     EL_SP_EXIT_CLOSING,
1242     EL_SP_EXIT_CLOSED,
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_SWITCHGATE_OPENING,
1250     EL_SWITCHGATE_OPEN,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_SWITCHGATE_CLOSING,
1258     EL_SWITCHGATE_CLOSED,
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_TIMEGATE_OPENING,
1266     EL_TIMEGATE_OPEN,
1267     29,
1268     NULL,
1269     NULL,
1270     NULL
1271   },
1272   {
1273     EL_TIMEGATE_CLOSING,
1274     EL_TIMEGATE_CLOSED,
1275     29,
1276     NULL,
1277     NULL,
1278     NULL
1279   },
1280
1281   {
1282     EL_ACID_SPLASH_LEFT,
1283     EL_EMPTY,
1284     8,
1285     NULL,
1286     NULL,
1287     NULL
1288   },
1289   {
1290     EL_ACID_SPLASH_RIGHT,
1291     EL_EMPTY,
1292     8,
1293     NULL,
1294     NULL,
1295     NULL
1296   },
1297   {
1298     EL_SP_BUGGY_BASE,
1299     EL_SP_BUGGY_BASE_ACTIVATING,
1300     0,
1301     InitBuggyBase,
1302     NULL,
1303     NULL
1304   },
1305   {
1306     EL_SP_BUGGY_BASE_ACTIVATING,
1307     EL_SP_BUGGY_BASE_ACTIVE,
1308     0,
1309     InitBuggyBase,
1310     NULL,
1311     NULL
1312   },
1313   {
1314     EL_SP_BUGGY_BASE_ACTIVE,
1315     EL_SP_BUGGY_BASE,
1316     0,
1317     InitBuggyBase,
1318     WarnBuggyBase,
1319     NULL
1320   },
1321   {
1322     EL_TRAP,
1323     EL_TRAP_ACTIVE,
1324     0,
1325     InitTrap,
1326     NULL,
1327     ActivateTrap
1328   },
1329   {
1330     EL_TRAP_ACTIVE,
1331     EL_TRAP,
1332     31,
1333     NULL,
1334     ChangeActiveTrap,
1335     NULL
1336   },
1337   {
1338     EL_ROBOT_WHEEL_ACTIVE,
1339     EL_ROBOT_WHEEL,
1340     0,
1341     InitRobotWheel,
1342     RunRobotWheel,
1343     StopRobotWheel
1344   },
1345   {
1346     EL_TIMEGATE_SWITCH_ACTIVE,
1347     EL_TIMEGATE_SWITCH,
1348     0,
1349     InitTimegateWheel,
1350     RunTimegateWheel,
1351     NULL
1352   },
1353   {
1354     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1355     EL_DC_TIMEGATE_SWITCH,
1356     0,
1357     InitTimegateWheel,
1358     RunTimegateWheel,
1359     NULL
1360   },
1361   {
1362     EL_EMC_MAGIC_BALL_ACTIVE,
1363     EL_EMC_MAGIC_BALL_ACTIVE,
1364     0,
1365     InitMagicBallDelay,
1366     NULL,
1367     ActivateMagicBall
1368   },
1369   {
1370     EL_EMC_SPRING_BUMPER_ACTIVE,
1371     EL_EMC_SPRING_BUMPER,
1372     8,
1373     NULL,
1374     NULL,
1375     NULL
1376   },
1377   {
1378     EL_DIAGONAL_SHRINKING,
1379     EL_UNDEFINED,
1380     0,
1381     NULL,
1382     NULL,
1383     NULL
1384   },
1385   {
1386     EL_DIAGONAL_GROWING,
1387     EL_UNDEFINED,
1388     0,
1389     NULL,
1390     NULL,
1391     NULL,
1392   },
1393
1394   {
1395     EL_UNDEFINED,
1396     EL_UNDEFINED,
1397     -1,
1398     NULL,
1399     NULL,
1400     NULL
1401   }
1402 };
1403
1404 struct
1405 {
1406   int element;
1407   int push_delay_fixed, push_delay_random;
1408 }
1409 push_delay_list[] =
1410 {
1411   { EL_SPRING,                  0, 0 },
1412   { EL_BALLOON,                 0, 0 },
1413
1414   { EL_SOKOBAN_OBJECT,          2, 0 },
1415   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1416   { EL_SATELLITE,               2, 0 },
1417   { EL_SP_DISK_YELLOW,          2, 0 },
1418
1419   { EL_UNDEFINED,               0, 0 },
1420 };
1421
1422 struct
1423 {
1424   int element;
1425   int move_stepsize;
1426 }
1427 move_stepsize_list[] =
1428 {
1429   { EL_AMOEBA_DROP,             2 },
1430   { EL_AMOEBA_DROPPING,         2 },
1431   { EL_QUICKSAND_FILLING,       1 },
1432   { EL_QUICKSAND_EMPTYING,      1 },
1433   { EL_QUICKSAND_FAST_FILLING,  2 },
1434   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1435   { EL_MAGIC_WALL_FILLING,      2 },
1436   { EL_MAGIC_WALL_EMPTYING,     2 },
1437   { EL_BD_MAGIC_WALL_FILLING,   2 },
1438   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1439   { EL_DC_MAGIC_WALL_FILLING,   2 },
1440   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1441
1442   { EL_UNDEFINED,               0 },
1443 };
1444
1445 struct
1446 {
1447   int element;
1448   int count;
1449 }
1450 collect_count_list[] =
1451 {
1452   { EL_EMERALD,                 1 },
1453   { EL_BD_DIAMOND,              1 },
1454   { EL_EMERALD_YELLOW,          1 },
1455   { EL_EMERALD_RED,             1 },
1456   { EL_EMERALD_PURPLE,          1 },
1457   { EL_DIAMOND,                 3 },
1458   { EL_SP_INFOTRON,             1 },
1459   { EL_PEARL,                   5 },
1460   { EL_CRYSTAL,                 8 },
1461
1462   { EL_UNDEFINED,               0 },
1463 };
1464
1465 struct
1466 {
1467   int element;
1468   int direction;
1469 }
1470 access_direction_list[] =
1471 {
1472   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1473   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1474   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1475   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1476   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1477   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1478   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1479   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1480   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1481   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1482   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1483
1484   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1485   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1486   { EL_SP_PORT_UP,                                                   MV_DOWN },
1487   { EL_SP_PORT_DOWN,                                         MV_UP           },
1488   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1489   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1490   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1491   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1492   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1493   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1494   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1495   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1496   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1497   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1498   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1499   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1500   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1501   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1502   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1503
1504   { EL_UNDEFINED,                       MV_NONE                              }
1505 };
1506
1507 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1508
1509 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1510 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1511 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1512                                  IS_JUST_CHANGING(x, y))
1513
1514 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1515
1516 /* static variables for playfield scan mode (scanning forward or backward) */
1517 static int playfield_scan_start_x = 0;
1518 static int playfield_scan_start_y = 0;
1519 static int playfield_scan_delta_x = 1;
1520 static int playfield_scan_delta_y = 1;
1521
1522 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1523                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1524                                      (y) += playfield_scan_delta_y)     \
1525                                 for ((x) = playfield_scan_start_x;      \
1526                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1527                                      (x) += playfield_scan_delta_x)
1528
1529 #ifdef DEBUG
1530 void DEBUG_SetMaximumDynamite()
1531 {
1532   int i;
1533
1534   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1535     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1536       local_player->inventory_element[local_player->inventory_size++] =
1537         EL_DYNAMITE;
1538 }
1539 #endif
1540
1541 static void InitPlayfieldScanModeVars()
1542 {
1543   if (game.use_reverse_scan_direction)
1544   {
1545     playfield_scan_start_x = lev_fieldx - 1;
1546     playfield_scan_start_y = lev_fieldy - 1;
1547
1548     playfield_scan_delta_x = -1;
1549     playfield_scan_delta_y = -1;
1550   }
1551   else
1552   {
1553     playfield_scan_start_x = 0;
1554     playfield_scan_start_y = 0;
1555
1556     playfield_scan_delta_x = 1;
1557     playfield_scan_delta_y = 1;
1558   }
1559 }
1560
1561 static void InitPlayfieldScanMode(int mode)
1562 {
1563   game.use_reverse_scan_direction =
1564     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1565
1566   InitPlayfieldScanModeVars();
1567 }
1568
1569 static int get_move_delay_from_stepsize(int move_stepsize)
1570 {
1571   move_stepsize =
1572     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1573
1574   /* make sure that stepsize value is always a power of 2 */
1575   move_stepsize = (1 << log_2(move_stepsize));
1576
1577   return TILEX / move_stepsize;
1578 }
1579
1580 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1581                                boolean init_game)
1582 {
1583   int player_nr = player->index_nr;
1584   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1585   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1586
1587   /* do no immediately change move delay -- the player might just be moving */
1588   player->move_delay_value_next = move_delay;
1589
1590   /* information if player can move must be set separately */
1591   player->cannot_move = cannot_move;
1592
1593   if (init_game)
1594   {
1595     player->move_delay       = game.initial_move_delay[player_nr];
1596     player->move_delay_value = game.initial_move_delay_value[player_nr];
1597
1598     player->move_delay_value_next = -1;
1599
1600     player->move_delay_reset_counter = 0;
1601   }
1602 }
1603
1604 void GetPlayerConfig()
1605 {
1606   GameFrameDelay = setup.game_frame_delay;
1607
1608   if (!audio.sound_available)
1609     setup.sound_simple = FALSE;
1610
1611   if (!audio.loops_available)
1612     setup.sound_loops = FALSE;
1613
1614   if (!audio.music_available)
1615     setup.sound_music = FALSE;
1616
1617   if (!video.fullscreen_available)
1618     setup.fullscreen = FALSE;
1619
1620   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1621
1622   SetAudioMode(setup.sound);
1623 }
1624
1625 int GetElementFromGroupElement(int element)
1626 {
1627   if (IS_GROUP_ELEMENT(element))
1628   {
1629     struct ElementGroupInfo *group = element_info[element].group;
1630     int last_anim_random_frame = gfx.anim_random_frame;
1631     int element_pos;
1632
1633     if (group->choice_mode == ANIM_RANDOM)
1634       gfx.anim_random_frame = RND(group->num_elements_resolved);
1635
1636     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1637                                     group->choice_mode, 0,
1638                                     group->choice_pos);
1639
1640     if (group->choice_mode == ANIM_RANDOM)
1641       gfx.anim_random_frame = last_anim_random_frame;
1642
1643     group->choice_pos++;
1644
1645     element = group->element_resolved[element_pos];
1646   }
1647
1648   return element;
1649 }
1650
1651 static void InitPlayerField(int x, int y, int element, boolean init_game)
1652 {
1653   if (element == EL_SP_MURPHY)
1654   {
1655     if (init_game)
1656     {
1657       if (stored_player[0].present)
1658       {
1659         Feld[x][y] = EL_SP_MURPHY_CLONE;
1660
1661         return;
1662       }
1663       else
1664       {
1665         stored_player[0].initial_element = element;
1666         stored_player[0].use_murphy = TRUE;
1667
1668         if (!level.use_artwork_element[0])
1669           stored_player[0].artwork_element = EL_SP_MURPHY;
1670       }
1671
1672       Feld[x][y] = EL_PLAYER_1;
1673     }
1674   }
1675
1676   if (init_game)
1677   {
1678     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1679     int jx = player->jx, jy = player->jy;
1680
1681     player->present = TRUE;
1682
1683     player->block_last_field = (element == EL_SP_MURPHY ?
1684                                 level.sp_block_last_field :
1685                                 level.block_last_field);
1686
1687     /* ---------- initialize player's last field block delay --------------- */
1688
1689     /* always start with reliable default value (no adjustment needed) */
1690     player->block_delay_adjustment = 0;
1691
1692     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1693     if (player->block_last_field && element == EL_SP_MURPHY)
1694       player->block_delay_adjustment = 1;
1695
1696     /* special case 2: in game engines before 3.1.1, blocking was different */
1697     if (game.use_block_last_field_bug)
1698       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1699
1700     if (!options.network || player->connected)
1701     {
1702       player->active = TRUE;
1703
1704       /* remove potentially duplicate players */
1705       if (StorePlayer[jx][jy] == Feld[x][y])
1706         StorePlayer[jx][jy] = 0;
1707
1708       StorePlayer[x][y] = Feld[x][y];
1709
1710 #if DEBUG_INIT_PLAYER
1711       if (options.debug)
1712       {
1713         printf("- player element %d activated", player->element_nr);
1714         printf(" (local player is %d and currently %s)\n",
1715                local_player->element_nr,
1716                local_player->active ? "active" : "not active");
1717       }
1718     }
1719 #endif
1720
1721     Feld[x][y] = EL_EMPTY;
1722
1723     player->jx = player->last_jx = x;
1724     player->jy = player->last_jy = y;
1725   }
1726
1727   if (!init_game)
1728   {
1729     int player_nr = GET_PLAYER_NR(element);
1730     struct PlayerInfo *player = &stored_player[player_nr];
1731
1732     if (player->active && player->killed)
1733       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1734   }
1735 }
1736
1737 static void InitField(int x, int y, boolean init_game)
1738 {
1739   int element = Feld[x][y];
1740
1741   switch (element)
1742   {
1743     case EL_SP_MURPHY:
1744     case EL_PLAYER_1:
1745     case EL_PLAYER_2:
1746     case EL_PLAYER_3:
1747     case EL_PLAYER_4:
1748       InitPlayerField(x, y, element, init_game);
1749       break;
1750
1751     case EL_SOKOBAN_FIELD_PLAYER:
1752       element = Feld[x][y] = EL_PLAYER_1;
1753       InitField(x, y, init_game);
1754
1755       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1756       InitField(x, y, init_game);
1757       break;
1758
1759     case EL_SOKOBAN_FIELD_EMPTY:
1760       local_player->sokobanfields_still_needed++;
1761       break;
1762
1763     case EL_STONEBLOCK:
1764       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1765         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1766       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1767         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1768       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1769         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1770       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1771         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1772       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1773         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1774       break;
1775
1776     case EL_BUG:
1777     case EL_BUG_RIGHT:
1778     case EL_BUG_UP:
1779     case EL_BUG_LEFT:
1780     case EL_BUG_DOWN:
1781     case EL_SPACESHIP:
1782     case EL_SPACESHIP_RIGHT:
1783     case EL_SPACESHIP_UP:
1784     case EL_SPACESHIP_LEFT:
1785     case EL_SPACESHIP_DOWN:
1786     case EL_BD_BUTTERFLY:
1787     case EL_BD_BUTTERFLY_RIGHT:
1788     case EL_BD_BUTTERFLY_UP:
1789     case EL_BD_BUTTERFLY_LEFT:
1790     case EL_BD_BUTTERFLY_DOWN:
1791     case EL_BD_FIREFLY:
1792     case EL_BD_FIREFLY_RIGHT:
1793     case EL_BD_FIREFLY_UP:
1794     case EL_BD_FIREFLY_LEFT:
1795     case EL_BD_FIREFLY_DOWN:
1796     case EL_PACMAN_RIGHT:
1797     case EL_PACMAN_UP:
1798     case EL_PACMAN_LEFT:
1799     case EL_PACMAN_DOWN:
1800     case EL_YAMYAM:
1801     case EL_YAMYAM_LEFT:
1802     case EL_YAMYAM_RIGHT:
1803     case EL_YAMYAM_UP:
1804     case EL_YAMYAM_DOWN:
1805     case EL_DARK_YAMYAM:
1806     case EL_ROBOT:
1807     case EL_PACMAN:
1808     case EL_SP_SNIKSNAK:
1809     case EL_SP_ELECTRON:
1810     case EL_MOLE:
1811     case EL_MOLE_LEFT:
1812     case EL_MOLE_RIGHT:
1813     case EL_MOLE_UP:
1814     case EL_MOLE_DOWN:
1815       InitMovDir(x, y);
1816       break;
1817
1818     case EL_AMOEBA_FULL:
1819     case EL_BD_AMOEBA:
1820       InitAmoebaNr(x, y);
1821       break;
1822
1823     case EL_AMOEBA_DROP:
1824       if (y == lev_fieldy - 1)
1825       {
1826         Feld[x][y] = EL_AMOEBA_GROWING;
1827         Store[x][y] = EL_AMOEBA_WET;
1828       }
1829       break;
1830
1831     case EL_DYNAMITE_ACTIVE:
1832     case EL_SP_DISK_RED_ACTIVE:
1833     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1834     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1835     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1836     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1837       MovDelay[x][y] = 96;
1838       break;
1839
1840     case EL_EM_DYNAMITE_ACTIVE:
1841       MovDelay[x][y] = 32;
1842       break;
1843
1844     case EL_LAMP:
1845       local_player->lights_still_needed++;
1846       break;
1847
1848     case EL_PENGUIN:
1849       local_player->friends_still_needed++;
1850       break;
1851
1852     case EL_PIG:
1853     case EL_DRAGON:
1854       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1855       break;
1856
1857     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1860     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1861     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1862     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1863     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1864     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1865     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1866     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1867     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1868     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1869       if (init_game)
1870       {
1871         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1872         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1873         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1874
1875         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1876         {
1877           game.belt_dir[belt_nr] = belt_dir;
1878           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1879         }
1880         else    /* more than one switch -- set it like the first switch */
1881         {
1882           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1883         }
1884       }
1885       break;
1886
1887     case EL_LIGHT_SWITCH_ACTIVE:
1888       if (init_game)
1889         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1890       break;
1891
1892     case EL_INVISIBLE_STEELWALL:
1893     case EL_INVISIBLE_WALL:
1894     case EL_INVISIBLE_SAND:
1895       if (game.light_time_left > 0 ||
1896           game.lenses_time_left > 0)
1897         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1898       break;
1899
1900     case EL_EMC_MAGIC_BALL:
1901       if (game.ball_state)
1902         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1903       break;
1904
1905     case EL_EMC_MAGIC_BALL_SWITCH:
1906       if (game.ball_state)
1907         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1908       break;
1909
1910     case EL_TRIGGER_PLAYER:
1911     case EL_TRIGGER_ELEMENT:
1912     case EL_TRIGGER_CE_VALUE:
1913     case EL_TRIGGER_CE_SCORE:
1914     case EL_SELF:
1915     case EL_ANY_ELEMENT:
1916     case EL_CURRENT_CE_VALUE:
1917     case EL_CURRENT_CE_SCORE:
1918     case EL_PREV_CE_1:
1919     case EL_PREV_CE_2:
1920     case EL_PREV_CE_3:
1921     case EL_PREV_CE_4:
1922     case EL_PREV_CE_5:
1923     case EL_PREV_CE_6:
1924     case EL_PREV_CE_7:
1925     case EL_PREV_CE_8:
1926     case EL_NEXT_CE_1:
1927     case EL_NEXT_CE_2:
1928     case EL_NEXT_CE_3:
1929     case EL_NEXT_CE_4:
1930     case EL_NEXT_CE_5:
1931     case EL_NEXT_CE_6:
1932     case EL_NEXT_CE_7:
1933     case EL_NEXT_CE_8:
1934       /* reference elements should not be used on the playfield */
1935       Feld[x][y] = EL_EMPTY;
1936       break;
1937
1938     default:
1939       if (IS_CUSTOM_ELEMENT(element))
1940       {
1941         if (CAN_MOVE(element))
1942           InitMovDir(x, y);
1943
1944         if (!element_info[element].use_last_ce_value || init_game)
1945           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1946       }
1947       else if (IS_GROUP_ELEMENT(element))
1948       {
1949         Feld[x][y] = GetElementFromGroupElement(element);
1950
1951         InitField(x, y, init_game);
1952       }
1953
1954       break;
1955   }
1956
1957   if (!init_game)
1958     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1959 }
1960
1961 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1962 {
1963   InitField(x, y, init_game);
1964
1965   /* not needed to call InitMovDir() -- already done by InitField()! */
1966   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1967       CAN_MOVE(Feld[x][y]))
1968     InitMovDir(x, y);
1969 }
1970
1971 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1972 {
1973   int old_element = Feld[x][y];
1974
1975   InitField(x, y, init_game);
1976
1977   /* not needed to call InitMovDir() -- already done by InitField()! */
1978   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1979       CAN_MOVE(old_element) &&
1980       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1981     InitMovDir(x, y);
1982
1983   /* this case is in fact a combination of not less than three bugs:
1984      first, it calls InitMovDir() for elements that can move, although this is
1985      already done by InitField(); then, it checks the element that was at this
1986      field _before_ the call to InitField() (which can change it); lastly, it
1987      was not called for "mole with direction" elements, which were treated as
1988      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1989   */
1990 }
1991
1992 static int get_key_element_from_nr(int key_nr)
1993 {
1994   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1995                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1996                           EL_EM_KEY_1 : EL_KEY_1);
1997
1998   return key_base_element + key_nr;
1999 }
2000
2001 static int get_next_dropped_element(struct PlayerInfo *player)
2002 {
2003   return (player->inventory_size > 0 ?
2004           player->inventory_element[player->inventory_size - 1] :
2005           player->inventory_infinite_element != EL_UNDEFINED ?
2006           player->inventory_infinite_element :
2007           player->dynabombs_left > 0 ?
2008           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2009           EL_UNDEFINED);
2010 }
2011
2012 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2013 {
2014   /* pos >= 0: get element from bottom of the stack;
2015      pos <  0: get element from top of the stack */
2016
2017   if (pos < 0)
2018   {
2019     int min_inventory_size = -pos;
2020     int inventory_pos = player->inventory_size - min_inventory_size;
2021     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2022
2023     return (player->inventory_size >= min_inventory_size ?
2024             player->inventory_element[inventory_pos] :
2025             player->inventory_infinite_element != EL_UNDEFINED ?
2026             player->inventory_infinite_element :
2027             player->dynabombs_left >= min_dynabombs_left ?
2028             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2029             EL_UNDEFINED);
2030   }
2031   else
2032   {
2033     int min_dynabombs_left = pos + 1;
2034     int min_inventory_size = pos + 1 - player->dynabombs_left;
2035     int inventory_pos = pos - player->dynabombs_left;
2036
2037     return (player->inventory_infinite_element != EL_UNDEFINED ?
2038             player->inventory_infinite_element :
2039             player->dynabombs_left >= min_dynabombs_left ?
2040             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2041             player->inventory_size >= min_inventory_size ?
2042             player->inventory_element[inventory_pos] :
2043             EL_UNDEFINED);
2044   }
2045 }
2046
2047 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2048 {
2049   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2050   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2051   int compare_result;
2052
2053   if (gpo1->sort_priority != gpo2->sort_priority)
2054     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2055   else
2056     compare_result = gpo1->nr - gpo2->nr;
2057
2058   return compare_result;
2059 }
2060
2061 int getPlayerInventorySize(int player_nr)
2062 {
2063   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2064     return level.native_em_level->ply[player_nr]->dynamite;
2065   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2066     return level.native_sp_level->game_sp->red_disk_count;
2067   else
2068     return stored_player[player_nr].inventory_size;
2069 }
2070
2071 void InitGameControlValues()
2072 {
2073   int i;
2074
2075   for (i = 0; game_panel_controls[i].nr != -1; i++)
2076   {
2077     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2078     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2079     struct TextPosInfo *pos = gpc->pos;
2080     int nr = gpc->nr;
2081     int type = gpc->type;
2082
2083     if (nr != i)
2084     {
2085       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2086       Error(ERR_EXIT, "this should not happen -- please debug");
2087     }
2088
2089     /* force update of game controls after initialization */
2090     gpc->value = gpc->last_value = -1;
2091     gpc->frame = gpc->last_frame = -1;
2092     gpc->gfx_frame = -1;
2093
2094     /* determine panel value width for later calculation of alignment */
2095     if (type == TYPE_INTEGER || type == TYPE_STRING)
2096     {
2097       pos->width = pos->size * getFontWidth(pos->font);
2098       pos->height = getFontHeight(pos->font);
2099     }
2100     else if (type == TYPE_ELEMENT)
2101     {
2102       pos->width = pos->size;
2103       pos->height = pos->size;
2104     }
2105
2106     /* fill structure for game panel draw order */
2107     gpo->nr = gpc->nr;
2108     gpo->sort_priority = pos->sort_priority;
2109   }
2110
2111   /* sort game panel controls according to sort_priority and control number */
2112   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2113         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2114 }
2115
2116 void UpdatePlayfieldElementCount()
2117 {
2118   boolean use_element_count = FALSE;
2119   int i, j, x, y;
2120
2121   /* first check if it is needed at all to calculate playfield element count */
2122   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2123     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2124       use_element_count = TRUE;
2125
2126   if (!use_element_count)
2127     return;
2128
2129   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2130     element_info[i].element_count = 0;
2131
2132   SCAN_PLAYFIELD(x, y)
2133   {
2134     element_info[Feld[x][y]].element_count++;
2135   }
2136
2137   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2138     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2139       if (IS_IN_GROUP(j, i))
2140         element_info[EL_GROUP_START + i].element_count +=
2141           element_info[j].element_count;
2142 }
2143
2144 void UpdateGameControlValues()
2145 {
2146   int i, k;
2147   int time = (local_player->LevelSolved ?
2148               local_player->LevelSolved_CountingTime :
2149               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2150               level.native_em_level->lev->time :
2151               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2152               level.native_sp_level->game_sp->time_played :
2153               game.no_time_limit ? TimePlayed : TimeLeft);
2154   int score = (local_player->LevelSolved ?
2155                local_player->LevelSolved_CountingScore :
2156                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157                level.native_em_level->lev->score :
2158                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2159                level.native_sp_level->game_sp->score :
2160                local_player->score);
2161   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2162               level.native_em_level->lev->required :
2163               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2164               level.native_sp_level->game_sp->infotrons_still_needed :
2165               local_player->gems_still_needed);
2166   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2167                      level.native_em_level->lev->required > 0 :
2168                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2169                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2170                      local_player->gems_still_needed > 0 ||
2171                      local_player->sokobanfields_still_needed > 0 ||
2172                      local_player->lights_still_needed > 0);
2173
2174   UpdatePlayfieldElementCount();
2175
2176   /* update game panel control values */
2177
2178   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2179   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2180
2181   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2182   for (i = 0; i < MAX_NUM_KEYS; i++)
2183     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2184   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2185   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2186
2187   if (game.centered_player_nr == -1)
2188   {
2189     for (i = 0; i < MAX_PLAYERS; i++)
2190     {
2191       /* only one player in Supaplex game engine */
2192       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2193         break;
2194
2195       for (k = 0; k < MAX_NUM_KEYS; k++)
2196       {
2197         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2198         {
2199           if (level.native_em_level->ply[i]->keys & (1 << k))
2200             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2201               get_key_element_from_nr(k);
2202         }
2203         else if (stored_player[i].key[k])
2204           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2205             get_key_element_from_nr(k);
2206       }
2207
2208       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2209         getPlayerInventorySize(i);
2210
2211       if (stored_player[i].num_white_keys > 0)
2212         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2213           EL_DC_KEY_WHITE;
2214
2215       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2216         stored_player[i].num_white_keys;
2217     }
2218   }
2219   else
2220   {
2221     int player_nr = game.centered_player_nr;
2222
2223     for (k = 0; k < MAX_NUM_KEYS; k++)
2224     {
2225       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226       {
2227         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2228           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2229             get_key_element_from_nr(k);
2230       }
2231       else if (stored_player[player_nr].key[k])
2232         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2233           get_key_element_from_nr(k);
2234     }
2235
2236     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2237       getPlayerInventorySize(player_nr);
2238
2239     if (stored_player[player_nr].num_white_keys > 0)
2240       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2241
2242     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2243       stored_player[player_nr].num_white_keys;
2244   }
2245
2246   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2247   {
2248     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2249       get_inventory_element_from_pos(local_player, i);
2250     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2251       get_inventory_element_from_pos(local_player, -i - 1);
2252   }
2253
2254   game_panel_controls[GAME_PANEL_SCORE].value = score;
2255   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2256
2257   game_panel_controls[GAME_PANEL_TIME].value = time;
2258
2259   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2260   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2261   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2262
2263   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2264
2265   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2266     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2269     local_player->shield_normal_time_left;
2270   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2271     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2272      EL_EMPTY);
2273   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2274     local_player->shield_deadly_time_left;
2275
2276   game_panel_controls[GAME_PANEL_EXIT].value =
2277     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2278
2279   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2280     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2281   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2282     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2283      EL_EMC_MAGIC_BALL_SWITCH);
2284
2285   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2286     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2287   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2288     game.light_time_left;
2289
2290   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2291     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2292   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2293     game.timegate_time_left;
2294
2295   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2296     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2297
2298   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2299     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2301     game.lenses_time_left;
2302
2303   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2304     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2305   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2306     game.magnify_time_left;
2307
2308   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2309     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2310      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2311      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2312      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2313      EL_BALLOON_SWITCH_NONE);
2314
2315   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2316     local_player->dynabomb_count;
2317   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2318     local_player->dynabomb_size;
2319   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2320     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2321
2322   game_panel_controls[GAME_PANEL_PENGUINS].value =
2323     local_player->friends_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2326     local_player->sokobanfields_still_needed;
2327   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2328     local_player->sokobanfields_still_needed;
2329
2330   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2331     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2332
2333   for (i = 0; i < NUM_BELTS; i++)
2334   {
2335     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2336       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2337        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2338     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2339       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2340   }
2341
2342   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2343     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2344   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2345     game.magic_wall_time_left;
2346
2347   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2348     local_player->gravity;
2349
2350   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2351     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2355       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2356        game.panel.element[i].id : EL_UNDEFINED);
2357
2358   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2359     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2360       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2361        element_info[game.panel.element_count[i].id].element_count : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2366        element_info[game.panel.ce_score[i].id].collect_score : 0);
2367
2368   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2369     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2370       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2371        element_info[game.panel.ce_score_element[i].id].collect_score :
2372        EL_UNDEFINED);
2373
2374   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2375   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2376   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2377
2378   /* update game panel control frames */
2379
2380   for (i = 0; game_panel_controls[i].nr != -1; i++)
2381   {
2382     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2383
2384     if (gpc->type == TYPE_ELEMENT)
2385     {
2386       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2387       {
2388         int last_anim_random_frame = gfx.anim_random_frame;
2389         int element = gpc->value;
2390         int graphic = el2panelimg(element);
2391
2392         if (gpc->value != gpc->last_value)
2393         {
2394           gpc->gfx_frame = 0;
2395           gpc->gfx_random = INIT_GFX_RANDOM();
2396         }
2397         else
2398         {
2399           gpc->gfx_frame++;
2400
2401           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2402               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2403             gpc->gfx_random = INIT_GFX_RANDOM();
2404         }
2405
2406         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407           gfx.anim_random_frame = gpc->gfx_random;
2408
2409         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2410           gpc->gfx_frame = element_info[element].collect_score;
2411
2412         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2413                                               gpc->gfx_frame);
2414
2415         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2416           gfx.anim_random_frame = last_anim_random_frame;
2417       }
2418     }
2419   }
2420 }
2421
2422 void DisplayGameControlValues()
2423 {
2424   boolean redraw_panel = FALSE;
2425   int i;
2426
2427   for (i = 0; game_panel_controls[i].nr != -1; i++)
2428   {
2429     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2430
2431     if (PANEL_DEACTIVATED(gpc->pos))
2432       continue;
2433
2434     if (gpc->value == gpc->last_value &&
2435         gpc->frame == gpc->last_frame)
2436       continue;
2437
2438     redraw_panel = TRUE;
2439   }
2440
2441   if (!redraw_panel)
2442     return;
2443
2444   /* copy default game door content to main double buffer */
2445
2446   /* !!! CHECK AGAIN !!! */
2447   SetPanelBackground();
2448   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2449   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2450
2451   /* redraw game control buttons */
2452   RedrawGameButtons();
2453
2454   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2455
2456   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2457   {
2458     int nr = game_panel_order[i].nr;
2459     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2460     struct TextPosInfo *pos = gpc->pos;
2461     int type = gpc->type;
2462     int value = gpc->value;
2463     int frame = gpc->frame;
2464     int size = pos->size;
2465     int font = pos->font;
2466     boolean draw_masked = pos->draw_masked;
2467     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2468
2469     if (PANEL_DEACTIVATED(pos))
2470       continue;
2471
2472     gpc->last_value = value;
2473     gpc->last_frame = frame;
2474
2475     if (type == TYPE_INTEGER)
2476     {
2477       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2478           nr == GAME_PANEL_TIME)
2479       {
2480         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2481
2482         if (use_dynamic_size)           /* use dynamic number of digits */
2483         {
2484           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2485           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2486           int size2 = size1 + 1;
2487           int font1 = pos->font;
2488           int font2 = pos->font_alt;
2489
2490           size = (value < value_change ? size1 : size2);
2491           font = (value < value_change ? font1 : font2);
2492         }
2493       }
2494
2495       /* correct text size if "digits" is zero or less */
2496       if (size <= 0)
2497         size = strlen(int2str(value, size));
2498
2499       /* dynamically correct text alignment */
2500       pos->width = size * getFontWidth(font);
2501
2502       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2503                   int2str(value, size), font, mask_mode);
2504     }
2505     else if (type == TYPE_ELEMENT)
2506     {
2507       int element, graphic;
2508       Bitmap *src_bitmap;
2509       int src_x, src_y;
2510       int width, height;
2511       int dst_x = PANEL_XPOS(pos);
2512       int dst_y = PANEL_YPOS(pos);
2513
2514       if (value != EL_UNDEFINED && value != EL_EMPTY)
2515       {
2516         element = value;
2517         graphic = el2panelimg(value);
2518
2519         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2520
2521         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2522           size = TILESIZE;
2523
2524         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2525                               &src_x, &src_y);
2526
2527         width  = graphic_info[graphic].width  * size / TILESIZE;
2528         height = graphic_info[graphic].height * size / TILESIZE;
2529
2530         if (draw_masked)
2531           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2532                            dst_x, dst_y);
2533         else
2534           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2535                      dst_x, dst_y);
2536       }
2537     }
2538     else if (type == TYPE_STRING)
2539     {
2540       boolean active = (value != 0);
2541       char *state_normal = "off";
2542       char *state_active = "on";
2543       char *state = (active ? state_active : state_normal);
2544       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2545                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2546                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2547                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2548
2549       if (nr == GAME_PANEL_GRAVITY_STATE)
2550       {
2551         int font1 = pos->font;          /* (used for normal state) */
2552         int font2 = pos->font_alt;      /* (used for active state) */
2553
2554         font = (active ? font2 : font1);
2555       }
2556
2557       if (s != NULL)
2558       {
2559         char *s_cut;
2560
2561         if (size <= 0)
2562         {
2563           /* don't truncate output if "chars" is zero or less */
2564           size = strlen(s);
2565
2566           /* dynamically correct text alignment */
2567           pos->width = size * getFontWidth(font);
2568         }
2569
2570         s_cut = getStringCopyN(s, size);
2571
2572         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2573                     s_cut, font, mask_mode);
2574
2575         free(s_cut);
2576       }
2577     }
2578
2579     redraw_mask |= REDRAW_DOOR_1;
2580   }
2581
2582   SetGameStatus(GAME_MODE_PLAYING);
2583 }
2584
2585 void UpdateAndDisplayGameControlValues()
2586 {
2587   if (tape.deactivate_display)
2588     return;
2589
2590   UpdateGameControlValues();
2591   DisplayGameControlValues();
2592 }
2593
2594 void UpdateGameDoorValues()
2595 {
2596   UpdateGameControlValues();
2597 }
2598
2599 void DrawGameDoorValues()
2600 {
2601   DisplayGameControlValues();
2602 }
2603
2604
2605 /*
2606   =============================================================================
2607   InitGameEngine()
2608   -----------------------------------------------------------------------------
2609   initialize game engine due to level / tape version number
2610   =============================================================================
2611 */
2612
2613 static void InitGameEngine()
2614 {
2615   int i, j, k, l, x, y;
2616
2617   /* set game engine from tape file when re-playing, else from level file */
2618   game.engine_version = (tape.playing ? tape.engine_version :
2619                          level.game_version);
2620
2621   /* set single or multi-player game mode (needed for re-playing tapes) */
2622   game.team_mode = setup.team_mode;
2623
2624   if (tape.playing)
2625   {
2626     int num_players = 0;
2627
2628     for (i = 0; i < MAX_PLAYERS; i++)
2629       if (tape.player_participates[i])
2630         num_players++;
2631
2632     /* multi-player tapes contain input data for more than one player */
2633     game.team_mode = (num_players > 1);
2634   }
2635
2636   /* ---------------------------------------------------------------------- */
2637   /* set flags for bugs and changes according to active game engine version */
2638   /* ---------------------------------------------------------------------- */
2639
2640   /*
2641     Summary of bugfix/change:
2642     Fixed handling for custom elements that change when pushed by the player.
2643
2644     Fixed/changed in version:
2645     3.1.0
2646
2647     Description:
2648     Before 3.1.0, custom elements that "change when pushing" changed directly
2649     after the player started pushing them (until then handled in "DigField()").
2650     Since 3.1.0, these custom elements are not changed until the "pushing"
2651     move of the element is finished (now handled in "ContinueMoving()").
2652
2653     Affected levels/tapes:
2654     The first condition is generally needed for all levels/tapes before version
2655     3.1.0, which might use the old behaviour before it was changed; known tapes
2656     that are affected are some tapes from the level set "Walpurgis Gardens" by
2657     Jamie Cullen.
2658     The second condition is an exception from the above case and is needed for
2659     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2660     above (including some development versions of 3.1.0), but before it was
2661     known that this change would break tapes like the above and was fixed in
2662     3.1.1, so that the changed behaviour was active although the engine version
2663     while recording maybe was before 3.1.0. There is at least one tape that is
2664     affected by this exception, which is the tape for the one-level set "Bug
2665     Machine" by Juergen Bonhagen.
2666   */
2667
2668   game.use_change_when_pushing_bug =
2669     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2670      !(tape.playing &&
2671        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2672        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2673
2674   /*
2675     Summary of bugfix/change:
2676     Fixed handling for blocking the field the player leaves when moving.
2677
2678     Fixed/changed in version:
2679     3.1.1
2680
2681     Description:
2682     Before 3.1.1, when "block last field when moving" was enabled, the field
2683     the player is leaving when moving was blocked for the time of the move,
2684     and was directly unblocked afterwards. This resulted in the last field
2685     being blocked for exactly one less than the number of frames of one player
2686     move. Additionally, even when blocking was disabled, the last field was
2687     blocked for exactly one frame.
2688     Since 3.1.1, due to changes in player movement handling, the last field
2689     is not blocked at all when blocking is disabled. When blocking is enabled,
2690     the last field is blocked for exactly the number of frames of one player
2691     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2692     last field is blocked for exactly one more than the number of frames of
2693     one player move.
2694
2695     Affected levels/tapes:
2696     (!!! yet to be determined -- probably many !!!)
2697   */
2698
2699   game.use_block_last_field_bug =
2700     (game.engine_version < VERSION_IDENT(3,1,1,0));
2701
2702   game_em.use_single_button =
2703     (game.engine_version > VERSION_IDENT(4,0,0,2));
2704
2705   game_em.use_snap_key_bug =
2706     (game.engine_version < VERSION_IDENT(4,0,1,0));
2707
2708   /* ---------------------------------------------------------------------- */
2709
2710   /* set maximal allowed number of custom element changes per game frame */
2711   game.max_num_changes_per_frame = 1;
2712
2713   /* default scan direction: scan playfield from top/left to bottom/right */
2714   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2715
2716   /* dynamically adjust element properties according to game engine version */
2717   InitElementPropertiesEngine(game.engine_version);
2718
2719 #if 0
2720   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2721   printf("          tape version == %06d [%s] [file: %06d]\n",
2722          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2723          tape.file_version);
2724   printf("       => game.engine_version == %06d\n", game.engine_version);
2725 #endif
2726
2727   /* ---------- initialize player's initial move delay --------------------- */
2728
2729   /* dynamically adjust player properties according to level information */
2730   for (i = 0; i < MAX_PLAYERS; i++)
2731     game.initial_move_delay_value[i] =
2732       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2733
2734   /* dynamically adjust player properties according to game engine version */
2735   for (i = 0; i < MAX_PLAYERS; i++)
2736     game.initial_move_delay[i] =
2737       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2738        game.initial_move_delay_value[i] : 0);
2739
2740   /* ---------- initialize player's initial push delay --------------------- */
2741
2742   /* dynamically adjust player properties according to game engine version */
2743   game.initial_push_delay_value =
2744     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2745
2746   /* ---------- initialize changing elements ------------------------------- */
2747
2748   /* initialize changing elements information */
2749   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2750   {
2751     struct ElementInfo *ei = &element_info[i];
2752
2753     /* this pointer might have been changed in the level editor */
2754     ei->change = &ei->change_page[0];
2755
2756     if (!IS_CUSTOM_ELEMENT(i))
2757     {
2758       ei->change->target_element = EL_EMPTY_SPACE;
2759       ei->change->delay_fixed = 0;
2760       ei->change->delay_random = 0;
2761       ei->change->delay_frames = 1;
2762     }
2763
2764     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2765     {
2766       ei->has_change_event[j] = FALSE;
2767
2768       ei->event_page_nr[j] = 0;
2769       ei->event_page[j] = &ei->change_page[0];
2770     }
2771   }
2772
2773   /* add changing elements from pre-defined list */
2774   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2775   {
2776     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2777     struct ElementInfo *ei = &element_info[ch_delay->element];
2778
2779     ei->change->target_element       = ch_delay->target_element;
2780     ei->change->delay_fixed          = ch_delay->change_delay;
2781
2782     ei->change->pre_change_function  = ch_delay->pre_change_function;
2783     ei->change->change_function      = ch_delay->change_function;
2784     ei->change->post_change_function = ch_delay->post_change_function;
2785
2786     ei->change->can_change = TRUE;
2787     ei->change->can_change_or_has_action = TRUE;
2788
2789     ei->has_change_event[CE_DELAY] = TRUE;
2790
2791     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2792     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2793   }
2794
2795   /* ---------- initialize internal run-time variables --------------------- */
2796
2797   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2798   {
2799     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2800
2801     for (j = 0; j < ei->num_change_pages; j++)
2802     {
2803       ei->change_page[j].can_change_or_has_action =
2804         (ei->change_page[j].can_change |
2805          ei->change_page[j].has_action);
2806     }
2807   }
2808
2809   /* add change events from custom element configuration */
2810   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2811   {
2812     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2813
2814     for (j = 0; j < ei->num_change_pages; j++)
2815     {
2816       if (!ei->change_page[j].can_change_or_has_action)
2817         continue;
2818
2819       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2820       {
2821         /* only add event page for the first page found with this event */
2822         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2823         {
2824           ei->has_change_event[k] = TRUE;
2825
2826           ei->event_page_nr[k] = j;
2827           ei->event_page[k] = &ei->change_page[j];
2828         }
2829       }
2830     }
2831   }
2832
2833   /* ---------- initialize reference elements in change conditions --------- */
2834
2835   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2836   {
2837     int element = EL_CUSTOM_START + i;
2838     struct ElementInfo *ei = &element_info[element];
2839
2840     for (j = 0; j < ei->num_change_pages; j++)
2841     {
2842       int trigger_element = ei->change_page[j].initial_trigger_element;
2843
2844       if (trigger_element >= EL_PREV_CE_8 &&
2845           trigger_element <= EL_NEXT_CE_8)
2846         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2847
2848       ei->change_page[j].trigger_element = trigger_element;
2849     }
2850   }
2851
2852   /* ---------- initialize run-time trigger player and element ------------- */
2853
2854   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2855   {
2856     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2857
2858     for (j = 0; j < ei->num_change_pages; j++)
2859     {
2860       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2861       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2862       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2863       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2864       ei->change_page[j].actual_trigger_ce_value = 0;
2865       ei->change_page[j].actual_trigger_ce_score = 0;
2866     }
2867   }
2868
2869   /* ---------- initialize trigger events ---------------------------------- */
2870
2871   /* initialize trigger events information */
2872   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2873     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2874       trigger_events[i][j] = FALSE;
2875
2876   /* add trigger events from element change event properties */
2877   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2878   {
2879     struct ElementInfo *ei = &element_info[i];
2880
2881     for (j = 0; j < ei->num_change_pages; j++)
2882     {
2883       if (!ei->change_page[j].can_change_or_has_action)
2884         continue;
2885
2886       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2887       {
2888         int trigger_element = ei->change_page[j].trigger_element;
2889
2890         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2891         {
2892           if (ei->change_page[j].has_event[k])
2893           {
2894             if (IS_GROUP_ELEMENT(trigger_element))
2895             {
2896               struct ElementGroupInfo *group =
2897                 element_info[trigger_element].group;
2898
2899               for (l = 0; l < group->num_elements_resolved; l++)
2900                 trigger_events[group->element_resolved[l]][k] = TRUE;
2901             }
2902             else if (trigger_element == EL_ANY_ELEMENT)
2903               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2904                 trigger_events[l][k] = TRUE;
2905             else
2906               trigger_events[trigger_element][k] = TRUE;
2907           }
2908         }
2909       }
2910     }
2911   }
2912
2913   /* ---------- initialize push delay -------------------------------------- */
2914
2915   /* initialize push delay values to default */
2916   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2917   {
2918     if (!IS_CUSTOM_ELEMENT(i))
2919     {
2920       /* set default push delay values (corrected since version 3.0.7-1) */
2921       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2922       {
2923         element_info[i].push_delay_fixed = 2;
2924         element_info[i].push_delay_random = 8;
2925       }
2926       else
2927       {
2928         element_info[i].push_delay_fixed = 8;
2929         element_info[i].push_delay_random = 8;
2930       }
2931     }
2932   }
2933
2934   /* set push delay value for certain elements from pre-defined list */
2935   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2936   {
2937     int e = push_delay_list[i].element;
2938
2939     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2940     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2941   }
2942
2943   /* set push delay value for Supaplex elements for newer engine versions */
2944   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2945   {
2946     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2947     {
2948       if (IS_SP_ELEMENT(i))
2949       {
2950         /* set SP push delay to just enough to push under a falling zonk */
2951         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2952
2953         element_info[i].push_delay_fixed  = delay;
2954         element_info[i].push_delay_random = 0;
2955       }
2956     }
2957   }
2958
2959   /* ---------- initialize move stepsize ----------------------------------- */
2960
2961   /* initialize move stepsize values to default */
2962   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2963     if (!IS_CUSTOM_ELEMENT(i))
2964       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2965
2966   /* set move stepsize value for certain elements from pre-defined list */
2967   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2968   {
2969     int e = move_stepsize_list[i].element;
2970
2971     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2972   }
2973
2974   /* ---------- initialize collect score ----------------------------------- */
2975
2976   /* initialize collect score values for custom elements from initial value */
2977   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2978     if (IS_CUSTOM_ELEMENT(i))
2979       element_info[i].collect_score = element_info[i].collect_score_initial;
2980
2981   /* ---------- initialize collect count ----------------------------------- */
2982
2983   /* initialize collect count values for non-custom elements */
2984   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2985     if (!IS_CUSTOM_ELEMENT(i))
2986       element_info[i].collect_count_initial = 0;
2987
2988   /* add collect count values for all elements from pre-defined list */
2989   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2990     element_info[collect_count_list[i].element].collect_count_initial =
2991       collect_count_list[i].count;
2992
2993   /* ---------- initialize access direction -------------------------------- */
2994
2995   /* initialize access direction values to default (access from every side) */
2996   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2997     if (!IS_CUSTOM_ELEMENT(i))
2998       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2999
3000   /* set access direction value for certain elements from pre-defined list */
3001   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3002     element_info[access_direction_list[i].element].access_direction =
3003       access_direction_list[i].direction;
3004
3005   /* ---------- initialize explosion content ------------------------------- */
3006   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3007   {
3008     if (IS_CUSTOM_ELEMENT(i))
3009       continue;
3010
3011     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3012     {
3013       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3014
3015       element_info[i].content.e[x][y] =
3016         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3017          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3018          i == EL_PLAYER_3 ? EL_EMERALD :
3019          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3020          i == EL_MOLE ? EL_EMERALD_RED :
3021          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3022          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3023          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3024          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3025          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3026          i == EL_WALL_EMERALD ? EL_EMERALD :
3027          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3028          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3029          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3030          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3031          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3032          i == EL_WALL_PEARL ? EL_PEARL :
3033          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3034          EL_EMPTY);
3035     }
3036   }
3037
3038   /* ---------- initialize recursion detection ------------------------------ */
3039   recursion_loop_depth = 0;
3040   recursion_loop_detected = FALSE;
3041   recursion_loop_element = EL_UNDEFINED;
3042
3043   /* ---------- initialize graphics engine ---------------------------------- */
3044   game.scroll_delay_value =
3045     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3046      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3047   game.scroll_delay_value =
3048     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3049
3050   /* ---------- initialize game engine snapshots ---------------------------- */
3051   for (i = 0; i < MAX_PLAYERS; i++)
3052     game.snapshot.last_action[i] = 0;
3053   game.snapshot.changed_action = FALSE;
3054   game.snapshot.collected_item = FALSE;
3055   game.snapshot.mode =
3056     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3057      SNAPSHOT_MODE_EVERY_STEP :
3058      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3059      SNAPSHOT_MODE_EVERY_MOVE :
3060      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3061      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3062   game.snapshot.save_snapshot = FALSE;
3063
3064   /* ---------- initialize level time for Supaplex engine ------------------- */
3065   /* Supaplex levels with time limit currently unsupported -- should be added */
3066   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3067     level.time = 0;
3068 }
3069
3070 int get_num_special_action(int element, int action_first, int action_last)
3071 {
3072   int num_special_action = 0;
3073   int i, j;
3074
3075   for (i = action_first; i <= action_last; i++)
3076   {
3077     boolean found = FALSE;
3078
3079     for (j = 0; j < NUM_DIRECTIONS; j++)
3080       if (el_act_dir2img(element, i, j) !=
3081           el_act_dir2img(element, ACTION_DEFAULT, j))
3082         found = TRUE;
3083
3084     if (found)
3085       num_special_action++;
3086     else
3087       break;
3088   }
3089
3090   return num_special_action;
3091 }
3092
3093
3094 /*
3095   =============================================================================
3096   InitGame()
3097   -----------------------------------------------------------------------------
3098   initialize and start new game
3099   =============================================================================
3100 */
3101
3102 void InitGame()
3103 {
3104   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3105   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3106   int fade_mask = REDRAW_FIELD;
3107
3108   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3109   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3110   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3111   int initial_move_dir = MV_DOWN;
3112   int i, j, x, y;
3113
3114   // required here to update video display before fading (FIX THIS)
3115   DrawMaskedBorder(REDRAW_DOOR_2);
3116
3117   if (!game.restart_level)
3118     CloseDoor(DOOR_CLOSE_1);
3119
3120   SetGameStatus(GAME_MODE_PLAYING);
3121
3122   if (level_editor_test_game)
3123     FadeSkipNextFadeIn();
3124   else
3125     FadeSetEnterScreen();
3126
3127   if (CheckIfGlobalBorderHasChanged())
3128     fade_mask = REDRAW_ALL;
3129
3130   FadeLevelSoundsAndMusic();
3131
3132   ExpireSoundLoops(TRUE);
3133
3134   FadeOut(fade_mask);
3135
3136   /* needed if different viewport properties defined for playing */
3137   ChangeViewportPropertiesIfNeeded();
3138
3139   ClearField();
3140
3141   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3142
3143   DrawCompleteVideoDisplay();
3144
3145   InitGameEngine();
3146   InitGameControlValues();
3147
3148   /* don't play tapes over network */
3149   network_playing = (options.network && !tape.playing);
3150
3151   for (i = 0; i < MAX_PLAYERS; i++)
3152   {
3153     struct PlayerInfo *player = &stored_player[i];
3154
3155     player->index_nr = i;
3156     player->index_bit = (1 << i);
3157     player->element_nr = EL_PLAYER_1 + i;
3158
3159     player->present = FALSE;
3160     player->active = FALSE;
3161     player->mapped = FALSE;
3162
3163     player->killed = FALSE;
3164     player->reanimated = FALSE;
3165
3166     player->action = 0;
3167     player->effective_action = 0;
3168     player->programmed_action = 0;
3169
3170     player->score = 0;
3171     player->score_final = 0;
3172
3173     player->gems_still_needed = level.gems_needed;
3174     player->sokobanfields_still_needed = 0;
3175     player->lights_still_needed = 0;
3176     player->friends_still_needed = 0;
3177
3178     for (j = 0; j < MAX_NUM_KEYS; j++)
3179       player->key[j] = FALSE;
3180
3181     player->num_white_keys = 0;
3182
3183     player->dynabomb_count = 0;
3184     player->dynabomb_size = 1;
3185     player->dynabombs_left = 0;
3186     player->dynabomb_xl = FALSE;
3187
3188     player->MovDir = initial_move_dir;
3189     player->MovPos = 0;
3190     player->GfxPos = 0;
3191     player->GfxDir = initial_move_dir;
3192     player->GfxAction = ACTION_DEFAULT;
3193     player->Frame = 0;
3194     player->StepFrame = 0;
3195
3196     player->initial_element = player->element_nr;
3197     player->artwork_element =
3198       (level.use_artwork_element[i] ? level.artwork_element[i] :
3199        player->element_nr);
3200     player->use_murphy = FALSE;
3201
3202     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3203     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3204
3205     player->gravity = level.initial_player_gravity[i];
3206
3207     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3208
3209     player->actual_frame_counter = 0;
3210
3211     player->step_counter = 0;
3212
3213     player->last_move_dir = initial_move_dir;
3214
3215     player->is_active = FALSE;
3216
3217     player->is_waiting = FALSE;
3218     player->is_moving = FALSE;
3219     player->is_auto_moving = FALSE;
3220     player->is_digging = FALSE;
3221     player->is_snapping = FALSE;
3222     player->is_collecting = FALSE;
3223     player->is_pushing = FALSE;
3224     player->is_switching = FALSE;
3225     player->is_dropping = FALSE;
3226     player->is_dropping_pressed = FALSE;
3227
3228     player->is_bored = FALSE;
3229     player->is_sleeping = FALSE;
3230
3231     player->was_waiting = TRUE;
3232     player->was_moving = FALSE;
3233     player->was_snapping = FALSE;
3234     player->was_dropping = FALSE;
3235
3236     player->force_dropping = FALSE;
3237
3238     player->frame_counter_bored = -1;
3239     player->frame_counter_sleeping = -1;
3240
3241     player->anim_delay_counter = 0;
3242     player->post_delay_counter = 0;
3243
3244     player->dir_waiting = initial_move_dir;
3245     player->action_waiting = ACTION_DEFAULT;
3246     player->last_action_waiting = ACTION_DEFAULT;
3247     player->special_action_bored = ACTION_DEFAULT;
3248     player->special_action_sleeping = ACTION_DEFAULT;
3249
3250     player->switch_x = -1;
3251     player->switch_y = -1;
3252
3253     player->drop_x = -1;
3254     player->drop_y = -1;
3255
3256     player->show_envelope = 0;
3257
3258     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3259
3260     player->push_delay       = -1;      /* initialized when pushing starts */
3261     player->push_delay_value = game.initial_push_delay_value;
3262
3263     player->drop_delay = 0;
3264     player->drop_pressed_delay = 0;
3265
3266     player->last_jx = -1;
3267     player->last_jy = -1;
3268     player->jx = -1;
3269     player->jy = -1;
3270
3271     player->shield_normal_time_left = 0;
3272     player->shield_deadly_time_left = 0;
3273
3274     player->inventory_infinite_element = EL_UNDEFINED;
3275     player->inventory_size = 0;
3276
3277     if (level.use_initial_inventory[i])
3278     {
3279       for (j = 0; j < level.initial_inventory_size[i]; j++)
3280       {
3281         int element = level.initial_inventory_content[i][j];
3282         int collect_count = element_info[element].collect_count_initial;
3283         int k;
3284
3285         if (!IS_CUSTOM_ELEMENT(element))
3286           collect_count = 1;
3287
3288         if (collect_count == 0)
3289           player->inventory_infinite_element = element;
3290         else
3291           for (k = 0; k < collect_count; k++)
3292             if (player->inventory_size < MAX_INVENTORY_SIZE)
3293               player->inventory_element[player->inventory_size++] = element;
3294       }
3295     }
3296
3297     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3298     SnapField(player, 0, 0);
3299
3300     player->LevelSolved = FALSE;
3301     player->GameOver = FALSE;
3302
3303     player->LevelSolved_GameWon = FALSE;
3304     player->LevelSolved_GameEnd = FALSE;
3305     player->LevelSolved_PanelOff = FALSE;
3306     player->LevelSolved_SaveTape = FALSE;
3307     player->LevelSolved_SaveScore = FALSE;
3308     player->LevelSolved_CountingTime = 0;
3309     player->LevelSolved_CountingScore = 0;
3310
3311     map_player_action[i] = i;
3312   }
3313
3314   network_player_action_received = FALSE;
3315
3316 #if defined(NETWORK_AVALIABLE)
3317   /* initial null action */
3318   if (network_playing)
3319     SendToServer_MovePlayer(MV_NONE);
3320 #endif
3321
3322   ZX = ZY = -1;
3323   ExitX = ExitY = -1;
3324
3325   FrameCounter = 0;
3326   TimeFrames = 0;
3327   TimePlayed = 0;
3328   TimeLeft = level.time;
3329   TapeTime = 0;
3330
3331   ScreenMovDir = MV_NONE;
3332   ScreenMovPos = 0;
3333   ScreenGfxPos = 0;
3334
3335   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3336
3337   AllPlayersGone = FALSE;
3338
3339   game.no_time_limit = (level.time == 0);
3340
3341   game.yamyam_content_nr = 0;
3342   game.robot_wheel_active = FALSE;
3343   game.magic_wall_active = FALSE;
3344   game.magic_wall_time_left = 0;
3345   game.light_time_left = 0;
3346   game.timegate_time_left = 0;
3347   game.switchgate_pos = 0;
3348   game.wind_direction = level.wind_direction_initial;
3349
3350   game.lenses_time_left = 0;
3351   game.magnify_time_left = 0;
3352
3353   game.ball_state = level.ball_state_initial;
3354   game.ball_content_nr = 0;
3355
3356   game.envelope_active = FALSE;
3357
3358   /* set focus to local player for network games, else to all players */
3359   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3360   game.centered_player_nr_next = game.centered_player_nr;
3361   game.set_centered_player = FALSE;
3362
3363   if (network_playing && tape.recording)
3364   {
3365     /* store client dependent player focus when recording network games */
3366     tape.centered_player_nr_next = game.centered_player_nr_next;
3367     tape.set_centered_player = TRUE;
3368   }
3369
3370   for (i = 0; i < NUM_BELTS; i++)
3371   {
3372     game.belt_dir[i] = MV_NONE;
3373     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3374   }
3375
3376   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3377     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3378
3379 #if DEBUG_INIT_PLAYER
3380   if (options.debug)
3381   {
3382     printf("Player status at level initialization:\n");
3383   }
3384 #endif
3385
3386   SCAN_PLAYFIELD(x, y)
3387   {
3388     Feld[x][y] = level.field[x][y];
3389     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3390     ChangeDelay[x][y] = 0;
3391     ChangePage[x][y] = -1;
3392     CustomValue[x][y] = 0;              /* initialized in InitField() */
3393     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3394     AmoebaNr[x][y] = 0;
3395     WasJustMoving[x][y] = 0;
3396     WasJustFalling[x][y] = 0;
3397     CheckCollision[x][y] = 0;
3398     CheckImpact[x][y] = 0;
3399     Stop[x][y] = FALSE;
3400     Pushed[x][y] = FALSE;
3401
3402     ChangeCount[x][y] = 0;
3403     ChangeEvent[x][y] = -1;
3404
3405     ExplodePhase[x][y] = 0;
3406     ExplodeDelay[x][y] = 0;
3407     ExplodeField[x][y] = EX_TYPE_NONE;
3408
3409     RunnerVisit[x][y] = 0;
3410     PlayerVisit[x][y] = 0;
3411
3412     GfxFrame[x][y] = 0;
3413     GfxRandom[x][y] = INIT_GFX_RANDOM();
3414     GfxElement[x][y] = EL_UNDEFINED;
3415     GfxAction[x][y] = ACTION_DEFAULT;
3416     GfxDir[x][y] = MV_NONE;
3417     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3418   }
3419
3420   SCAN_PLAYFIELD(x, y)
3421   {
3422     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3423       emulate_bd = FALSE;
3424     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3425       emulate_sb = FALSE;
3426     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3427       emulate_sp = FALSE;
3428
3429     InitField(x, y, TRUE);
3430
3431     ResetGfxAnimation(x, y);
3432   }
3433
3434   InitBeltMovement();
3435
3436   for (i = 0; i < MAX_PLAYERS; i++)
3437   {
3438     struct PlayerInfo *player = &stored_player[i];
3439
3440     /* set number of special actions for bored and sleeping animation */
3441     player->num_special_action_bored =
3442       get_num_special_action(player->artwork_element,
3443                              ACTION_BORING_1, ACTION_BORING_LAST);
3444     player->num_special_action_sleeping =
3445       get_num_special_action(player->artwork_element,
3446                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3447   }
3448
3449   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3450                     emulate_sb ? EMU_SOKOBAN :
3451                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3452
3453   /* initialize type of slippery elements */
3454   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3455   {
3456     if (!IS_CUSTOM_ELEMENT(i))
3457     {
3458       /* default: elements slip down either to the left or right randomly */
3459       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3460
3461       /* SP style elements prefer to slip down on the left side */
3462       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3463         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3464
3465       /* BD style elements prefer to slip down on the left side */
3466       if (game.emulation == EMU_BOULDERDASH)
3467         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3468     }
3469   }
3470
3471   /* initialize explosion and ignition delay */
3472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3473   {
3474     if (!IS_CUSTOM_ELEMENT(i))
3475     {
3476       int num_phase = 8;
3477       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3478                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3479                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3480       int last_phase = (num_phase + 1) * delay;
3481       int half_phase = (num_phase / 2) * delay;
3482
3483       element_info[i].explosion_delay = last_phase - 1;
3484       element_info[i].ignition_delay = half_phase;
3485
3486       if (i == EL_BLACK_ORB)
3487         element_info[i].ignition_delay = 1;
3488     }
3489   }
3490
3491   /* correct non-moving belts to start moving left */
3492   for (i = 0; i < NUM_BELTS; i++)
3493     if (game.belt_dir[i] == MV_NONE)
3494       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3495
3496 #if USE_NEW_PLAYER_ASSIGNMENTS
3497   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3498   /* choose default local player */
3499   local_player = &stored_player[0];
3500
3501   for (i = 0; i < MAX_PLAYERS; i++)
3502     stored_player[i].connected = FALSE;
3503
3504   local_player->connected = TRUE;
3505   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3506
3507   if (tape.playing)
3508   {
3509     for (i = 0; i < MAX_PLAYERS; i++)
3510       stored_player[i].connected = tape.player_participates[i];
3511   }
3512   else if (game.team_mode && !options.network)
3513   {
3514     /* try to guess locally connected team mode players (needed for correct
3515        assignment of player figures from level to locally playing players) */
3516
3517     for (i = 0; i < MAX_PLAYERS; i++)
3518       if (setup.input[i].use_joystick ||
3519           setup.input[i].key.left != KSYM_UNDEFINED)
3520         stored_player[i].connected = TRUE;
3521   }
3522
3523 #if DEBUG_INIT_PLAYER
3524   if (options.debug)
3525   {
3526     printf("Player status after level initialization:\n");
3527
3528     for (i = 0; i < MAX_PLAYERS; i++)
3529     {
3530       struct PlayerInfo *player = &stored_player[i];
3531
3532       printf("- player %d: present == %d, connected == %d, active == %d",
3533              i + 1,
3534              player->present,
3535              player->connected,
3536              player->active);
3537
3538       if (local_player == player)
3539         printf(" (local player)");
3540
3541       printf("\n");
3542     }
3543   }
3544 #endif
3545
3546 #if DEBUG_INIT_PLAYER
3547   if (options.debug)
3548     printf("Reassigning players ...\n");
3549 #endif
3550
3551   /* check if any connected player was not found in playfield */
3552   for (i = 0; i < MAX_PLAYERS; i++)
3553   {
3554     struct PlayerInfo *player = &stored_player[i];
3555
3556     if (player->connected && !player->present)
3557     {
3558       struct PlayerInfo *field_player = NULL;
3559
3560 #if DEBUG_INIT_PLAYER
3561       if (options.debug)
3562         printf("- looking for field player for player %d ...\n", i + 1);
3563 #endif
3564
3565       /* assign first free player found that is present in the playfield */
3566
3567       /* first try: look for unmapped playfield player that is not connected */
3568       for (j = 0; j < MAX_PLAYERS; j++)
3569         if (field_player == NULL &&
3570             stored_player[j].present &&
3571             !stored_player[j].mapped &&
3572             !stored_player[j].connected)
3573           field_player = &stored_player[j];
3574
3575       /* second try: look for *any* unmapped playfield player */
3576       for (j = 0; j < MAX_PLAYERS; j++)
3577         if (field_player == NULL &&
3578             stored_player[j].present &&
3579             !stored_player[j].mapped)
3580           field_player = &stored_player[j];
3581
3582       if (field_player != NULL)
3583       {
3584         int jx = field_player->jx, jy = field_player->jy;
3585
3586 #if DEBUG_INIT_PLAYER
3587         if (options.debug)
3588           printf("- found player %d\n", field_player->index_nr + 1);
3589 #endif
3590
3591         player->present = FALSE;
3592         player->active = FALSE;
3593
3594         field_player->present = TRUE;
3595         field_player->active = TRUE;
3596
3597         /*
3598         player->initial_element = field_player->initial_element;
3599         player->artwork_element = field_player->artwork_element;
3600
3601         player->block_last_field       = field_player->block_last_field;
3602         player->block_delay_adjustment = field_player->block_delay_adjustment;
3603         */
3604
3605         StorePlayer[jx][jy] = field_player->element_nr;
3606
3607         field_player->jx = field_player->last_jx = jx;
3608         field_player->jy = field_player->last_jy = jy;
3609
3610         if (local_player == player)
3611           local_player = field_player;
3612
3613         map_player_action[field_player->index_nr] = i;
3614
3615         field_player->mapped = TRUE;
3616
3617 #if DEBUG_INIT_PLAYER
3618         if (options.debug)
3619           printf("- map_player_action[%d] == %d\n",
3620                  field_player->index_nr + 1, i + 1);
3621 #endif
3622       }
3623     }
3624
3625     if (player->connected && player->present)
3626       player->mapped = TRUE;
3627   }
3628
3629 #if DEBUG_INIT_PLAYER
3630   if (options.debug)
3631   {
3632     printf("Player status after player assignment (first stage):\n");
3633
3634     for (i = 0; i < MAX_PLAYERS; i++)
3635     {
3636       struct PlayerInfo *player = &stored_player[i];
3637
3638       printf("- player %d: present == %d, connected == %d, active == %d",
3639              i + 1,
3640              player->present,
3641              player->connected,
3642              player->active);
3643
3644       if (local_player == player)
3645         printf(" (local player)");
3646
3647       printf("\n");
3648     }
3649   }
3650 #endif
3651
3652 #else
3653
3654   /* check if any connected player was not found in playfield */
3655   for (i = 0; i < MAX_PLAYERS; i++)
3656   {
3657     struct PlayerInfo *player = &stored_player[i];
3658
3659     if (player->connected && !player->present)
3660     {
3661       for (j = 0; j < MAX_PLAYERS; j++)
3662       {
3663         struct PlayerInfo *field_player = &stored_player[j];
3664         int jx = field_player->jx, jy = field_player->jy;
3665
3666         /* assign first free player found that is present in the playfield */
3667         if (field_player->present && !field_player->connected)
3668         {
3669           player->present = TRUE;
3670           player->active = TRUE;
3671
3672           field_player->present = FALSE;
3673           field_player->active = FALSE;
3674
3675           player->initial_element = field_player->initial_element;
3676           player->artwork_element = field_player->artwork_element;
3677
3678           player->block_last_field       = field_player->block_last_field;
3679           player->block_delay_adjustment = field_player->block_delay_adjustment;
3680
3681           StorePlayer[jx][jy] = player->element_nr;
3682
3683           player->jx = player->last_jx = jx;
3684           player->jy = player->last_jy = jy;
3685
3686           break;
3687         }
3688       }
3689     }
3690   }
3691 #endif
3692
3693 #if 0
3694   printf("::: local_player->present == %d\n", local_player->present);
3695 #endif
3696
3697   if (tape.playing)
3698   {
3699     /* when playing a tape, eliminate all players who do not participate */
3700
3701 #if USE_NEW_PLAYER_ASSIGNMENTS
3702
3703     if (!game.team_mode)
3704     {
3705       for (i = 0; i < MAX_PLAYERS; i++)
3706       {
3707         if (stored_player[i].active &&
3708             !tape.player_participates[map_player_action[i]])
3709         {
3710           struct PlayerInfo *player = &stored_player[i];
3711           int jx = player->jx, jy = player->jy;
3712
3713 #if DEBUG_INIT_PLAYER
3714           if (options.debug)
3715             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3716 #endif
3717
3718           player->active = FALSE;
3719           StorePlayer[jx][jy] = 0;
3720           Feld[jx][jy] = EL_EMPTY;
3721         }
3722       }
3723     }
3724
3725 #else
3726
3727     for (i = 0; i < MAX_PLAYERS; i++)
3728     {
3729       if (stored_player[i].active &&
3730           !tape.player_participates[i])
3731       {
3732         struct PlayerInfo *player = &stored_player[i];
3733         int jx = player->jx, jy = player->jy;
3734
3735         player->active = FALSE;
3736         StorePlayer[jx][jy] = 0;
3737         Feld[jx][jy] = EL_EMPTY;
3738       }
3739     }
3740 #endif
3741   }
3742   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3743   {
3744     /* when in single player mode, eliminate all but the first active player */
3745
3746     for (i = 0; i < MAX_PLAYERS; i++)
3747     {
3748       if (stored_player[i].active)
3749       {
3750         for (j = i + 1; j < MAX_PLAYERS; j++)
3751         {
3752           if (stored_player[j].active)
3753           {
3754             struct PlayerInfo *player = &stored_player[j];
3755             int jx = player->jx, jy = player->jy;
3756
3757             player->active = FALSE;
3758             player->present = FALSE;
3759
3760             StorePlayer[jx][jy] = 0;
3761             Feld[jx][jy] = EL_EMPTY;
3762           }
3763         }
3764       }
3765     }
3766   }
3767
3768   /* when recording the game, store which players take part in the game */
3769   if (tape.recording)
3770   {
3771 #if USE_NEW_PLAYER_ASSIGNMENTS
3772     for (i = 0; i < MAX_PLAYERS; i++)
3773       if (stored_player[i].connected)
3774         tape.player_participates[i] = TRUE;
3775 #else
3776     for (i = 0; i < MAX_PLAYERS; i++)
3777       if (stored_player[i].active)
3778         tape.player_participates[i] = TRUE;
3779 #endif
3780   }
3781
3782 #if DEBUG_INIT_PLAYER
3783   if (options.debug)
3784   {
3785     printf("Player status after player assignment (final stage):\n");
3786
3787     for (i = 0; i < MAX_PLAYERS; i++)
3788     {
3789       struct PlayerInfo *player = &stored_player[i];
3790
3791       printf("- player %d: present == %d, connected == %d, active == %d",
3792              i + 1,
3793              player->present,
3794              player->connected,
3795              player->active);
3796
3797       if (local_player == player)
3798         printf(" (local player)");
3799
3800       printf("\n");
3801     }
3802   }
3803 #endif
3804
3805   if (BorderElement == EL_EMPTY)
3806   {
3807     SBX_Left = 0;
3808     SBX_Right = lev_fieldx - SCR_FIELDX;
3809     SBY_Upper = 0;
3810     SBY_Lower = lev_fieldy - SCR_FIELDY;
3811   }
3812   else
3813   {
3814     SBX_Left = -1;
3815     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3816     SBY_Upper = -1;
3817     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3818   }
3819
3820   if (full_lev_fieldx <= SCR_FIELDX)
3821     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3822   if (full_lev_fieldy <= SCR_FIELDY)
3823     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3824
3825   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3826     SBX_Left--;
3827   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3828     SBY_Upper--;
3829
3830   /* if local player not found, look for custom element that might create
3831      the player (make some assumptions about the right custom element) */
3832   if (!local_player->present)
3833   {
3834     int start_x = 0, start_y = 0;
3835     int found_rating = 0;
3836     int found_element = EL_UNDEFINED;
3837     int player_nr = local_player->index_nr;
3838
3839     SCAN_PLAYFIELD(x, y)
3840     {
3841       int element = Feld[x][y];
3842       int content;
3843       int xx, yy;
3844       boolean is_player;
3845
3846       if (level.use_start_element[player_nr] &&
3847           level.start_element[player_nr] == element &&
3848           found_rating < 4)
3849       {
3850         start_x = x;
3851         start_y = y;
3852
3853         found_rating = 4;
3854         found_element = element;
3855       }
3856
3857       if (!IS_CUSTOM_ELEMENT(element))
3858         continue;
3859
3860       if (CAN_CHANGE(element))
3861       {
3862         for (i = 0; i < element_info[element].num_change_pages; i++)
3863         {
3864           /* check for player created from custom element as single target */
3865           content = element_info[element].change_page[i].target_element;
3866           is_player = ELEM_IS_PLAYER(content);
3867
3868           if (is_player && (found_rating < 3 ||
3869                             (found_rating == 3 && element < found_element)))
3870           {
3871             start_x = x;
3872             start_y = y;
3873
3874             found_rating = 3;
3875             found_element = element;
3876           }
3877         }
3878       }
3879
3880       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3881       {
3882         /* check for player created from custom element as explosion content */
3883         content = element_info[element].content.e[xx][yy];
3884         is_player = ELEM_IS_PLAYER(content);
3885
3886         if (is_player && (found_rating < 2 ||
3887                           (found_rating == 2 && element < found_element)))
3888         {
3889           start_x = x + xx - 1;
3890           start_y = y + yy - 1;
3891
3892           found_rating = 2;
3893           found_element = element;
3894         }
3895
3896         if (!CAN_CHANGE(element))
3897           continue;
3898
3899         for (i = 0; i < element_info[element].num_change_pages; i++)
3900         {
3901           /* check for player created from custom element as extended target */
3902           content =
3903             element_info[element].change_page[i].target_content.e[xx][yy];
3904
3905           is_player = ELEM_IS_PLAYER(content);
3906
3907           if (is_player && (found_rating < 1 ||
3908                             (found_rating == 1 && element < found_element)))
3909           {
3910             start_x = x + xx - 1;
3911             start_y = y + yy - 1;
3912
3913             found_rating = 1;
3914             found_element = element;
3915           }
3916         }
3917       }
3918     }
3919
3920     scroll_x = SCROLL_POSITION_X(start_x);
3921     scroll_y = SCROLL_POSITION_Y(start_y);
3922   }
3923   else
3924   {
3925     scroll_x = SCROLL_POSITION_X(local_player->jx);
3926     scroll_y = SCROLL_POSITION_Y(local_player->jy);
3927   }
3928
3929   /* !!! FIX THIS (START) !!! */
3930   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3931   {
3932     InitGameEngine_EM();
3933   }
3934   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3935   {
3936     InitGameEngine_SP();
3937   }
3938   else
3939   {
3940     DrawLevel(REDRAW_FIELD);
3941     DrawAllPlayers();
3942
3943     /* after drawing the level, correct some elements */
3944     if (game.timegate_time_left == 0)
3945       CloseAllOpenTimegates();
3946   }
3947
3948   /* blit playfield from scroll buffer to normal back buffer for fading in */
3949   BlitScreenToBitmap(backbuffer);
3950   /* !!! FIX THIS (END) !!! */
3951
3952   DrawMaskedBorder(fade_mask);
3953
3954   FadeIn(fade_mask);
3955
3956 #if 1
3957   // full screen redraw is required at this point in the following cases:
3958   // - special editor door undrawn when game was started from level editor
3959   // - drawing area (playfield) was changed and has to be removed completely
3960   redraw_mask = REDRAW_ALL;
3961   BackToFront();
3962 #endif
3963
3964   if (!game.restart_level)
3965   {
3966     /* copy default game door content to main double buffer */
3967
3968     /* !!! CHECK AGAIN !!! */
3969     SetPanelBackground();
3970     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3971     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3972   }
3973
3974   SetPanelBackground();
3975   SetDrawBackgroundMask(REDRAW_DOOR_1);
3976
3977   UpdateAndDisplayGameControlValues();
3978
3979   if (!game.restart_level)
3980   {
3981     UnmapGameButtons();
3982     UnmapTapeButtons();
3983
3984     FreeGameButtons();
3985     CreateGameButtons();
3986
3987     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3988     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3989     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3990
3991     MapGameButtons();
3992     MapTapeButtons();
3993
3994     /* copy actual game door content to door double buffer for OpenDoor() */
3995     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3996
3997     OpenDoor(DOOR_OPEN_ALL);
3998
3999     PlaySound(SND_GAME_STARTING);
4000
4001     if (setup.sound_music)
4002       PlayLevelMusic();
4003
4004     KeyboardAutoRepeatOffUnlessAutoplay();
4005
4006 #if DEBUG_INIT_PLAYER
4007     if (options.debug)
4008     {
4009       printf("Player status (final):\n");
4010
4011       for (i = 0; i < MAX_PLAYERS; i++)
4012       {
4013         struct PlayerInfo *player = &stored_player[i];
4014
4015         printf("- player %d: present == %d, connected == %d, active == %d",
4016                i + 1,
4017                player->present,
4018                player->connected,
4019                player->active);
4020
4021         if (local_player == player)
4022           printf(" (local player)");
4023
4024         printf("\n");
4025       }
4026     }
4027 #endif
4028   }
4029
4030   UnmapAllGadgets();
4031
4032   MapGameButtons();
4033   MapTapeButtons();
4034
4035   if (!game.restart_level && !tape.playing)
4036   {
4037     LevelStats_incPlayed(level_nr);
4038
4039     SaveLevelSetup_SeriesInfo();
4040   }
4041
4042   game.restart_level = FALSE;
4043
4044   SaveEngineSnapshotToListInitial();
4045 }
4046
4047 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4048                         int actual_player_x, int actual_player_y)
4049 {
4050   /* this is used for non-R'n'D game engines to update certain engine values */
4051
4052   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4053   {
4054     actual_player_x = correctLevelPosX_EM(actual_player_x);
4055     actual_player_y = correctLevelPosY_EM(actual_player_y);
4056   }
4057
4058   /* needed to determine if sounds are played within the visible screen area */
4059   scroll_x = actual_scroll_x;
4060   scroll_y = actual_scroll_y;
4061
4062   /* needed to get player position for "follow finger" playing input method */
4063   local_player->jx = actual_player_x;
4064   local_player->jy = actual_player_y;
4065 }
4066
4067 void InitMovDir(int x, int y)
4068 {
4069   int i, element = Feld[x][y];
4070   static int xy[4][2] =
4071   {
4072     {  0, +1 },
4073     { +1,  0 },
4074     {  0, -1 },
4075     { -1,  0 }
4076   };
4077   static int direction[3][4] =
4078   {
4079     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4080     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4081     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4082   };
4083
4084   switch (element)
4085   {
4086     case EL_BUG_RIGHT:
4087     case EL_BUG_UP:
4088     case EL_BUG_LEFT:
4089     case EL_BUG_DOWN:
4090       Feld[x][y] = EL_BUG;
4091       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4092       break;
4093
4094     case EL_SPACESHIP_RIGHT:
4095     case EL_SPACESHIP_UP:
4096     case EL_SPACESHIP_LEFT:
4097     case EL_SPACESHIP_DOWN:
4098       Feld[x][y] = EL_SPACESHIP;
4099       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4100       break;
4101
4102     case EL_BD_BUTTERFLY_RIGHT:
4103     case EL_BD_BUTTERFLY_UP:
4104     case EL_BD_BUTTERFLY_LEFT:
4105     case EL_BD_BUTTERFLY_DOWN:
4106       Feld[x][y] = EL_BD_BUTTERFLY;
4107       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4108       break;
4109
4110     case EL_BD_FIREFLY_RIGHT:
4111     case EL_BD_FIREFLY_UP:
4112     case EL_BD_FIREFLY_LEFT:
4113     case EL_BD_FIREFLY_DOWN:
4114       Feld[x][y] = EL_BD_FIREFLY;
4115       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4116       break;
4117
4118     case EL_PACMAN_RIGHT:
4119     case EL_PACMAN_UP:
4120     case EL_PACMAN_LEFT:
4121     case EL_PACMAN_DOWN:
4122       Feld[x][y] = EL_PACMAN;
4123       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4124       break;
4125
4126     case EL_YAMYAM_LEFT:
4127     case EL_YAMYAM_RIGHT:
4128     case EL_YAMYAM_UP:
4129     case EL_YAMYAM_DOWN:
4130       Feld[x][y] = EL_YAMYAM;
4131       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4132       break;
4133
4134     case EL_SP_SNIKSNAK:
4135       MovDir[x][y] = MV_UP;
4136       break;
4137
4138     case EL_SP_ELECTRON:
4139       MovDir[x][y] = MV_LEFT;
4140       break;
4141
4142     case EL_MOLE_LEFT:
4143     case EL_MOLE_RIGHT:
4144     case EL_MOLE_UP:
4145     case EL_MOLE_DOWN:
4146       Feld[x][y] = EL_MOLE;
4147       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4148       break;
4149
4150     default:
4151       if (IS_CUSTOM_ELEMENT(element))
4152       {
4153         struct ElementInfo *ei = &element_info[element];
4154         int move_direction_initial = ei->move_direction_initial;
4155         int move_pattern = ei->move_pattern;
4156
4157         if (move_direction_initial == MV_START_PREVIOUS)
4158         {
4159           if (MovDir[x][y] != MV_NONE)
4160             return;
4161
4162           move_direction_initial = MV_START_AUTOMATIC;
4163         }
4164
4165         if (move_direction_initial == MV_START_RANDOM)
4166           MovDir[x][y] = 1 << RND(4);
4167         else if (move_direction_initial & MV_ANY_DIRECTION)
4168           MovDir[x][y] = move_direction_initial;
4169         else if (move_pattern == MV_ALL_DIRECTIONS ||
4170                  move_pattern == MV_TURNING_LEFT ||
4171                  move_pattern == MV_TURNING_RIGHT ||
4172                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4173                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4174                  move_pattern == MV_TURNING_RANDOM)
4175           MovDir[x][y] = 1 << RND(4);
4176         else if (move_pattern == MV_HORIZONTAL)
4177           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4178         else if (move_pattern == MV_VERTICAL)
4179           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4180         else if (move_pattern & MV_ANY_DIRECTION)
4181           MovDir[x][y] = element_info[element].move_pattern;
4182         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4183                  move_pattern == MV_ALONG_RIGHT_SIDE)
4184         {
4185           /* use random direction as default start direction */
4186           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4187             MovDir[x][y] = 1 << RND(4);
4188
4189           for (i = 0; i < NUM_DIRECTIONS; i++)
4190           {
4191             int x1 = x + xy[i][0];
4192             int y1 = y + xy[i][1];
4193
4194             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4195             {
4196               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4197                 MovDir[x][y] = direction[0][i];
4198               else
4199                 MovDir[x][y] = direction[1][i];
4200
4201               break;
4202             }
4203           }
4204         }                
4205       }
4206       else
4207       {
4208         MovDir[x][y] = 1 << RND(4);
4209
4210         if (element != EL_BUG &&
4211             element != EL_SPACESHIP &&
4212             element != EL_BD_BUTTERFLY &&
4213             element != EL_BD_FIREFLY)
4214           break;
4215
4216         for (i = 0; i < NUM_DIRECTIONS; i++)
4217         {
4218           int x1 = x + xy[i][0];
4219           int y1 = y + xy[i][1];
4220
4221           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4222           {
4223             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4224             {
4225               MovDir[x][y] = direction[0][i];
4226               break;
4227             }
4228             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4229                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4230             {
4231               MovDir[x][y] = direction[1][i];
4232               break;
4233             }
4234           }
4235         }
4236       }
4237       break;
4238   }
4239
4240   GfxDir[x][y] = MovDir[x][y];
4241 }
4242
4243 void InitAmoebaNr(int x, int y)
4244 {
4245   int i;
4246   int group_nr = AmoebeNachbarNr(x, y);
4247
4248   if (group_nr == 0)
4249   {
4250     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4251     {
4252       if (AmoebaCnt[i] == 0)
4253       {
4254         group_nr = i;
4255         break;
4256       }
4257     }
4258   }
4259
4260   AmoebaNr[x][y] = group_nr;
4261   AmoebaCnt[group_nr]++;
4262   AmoebaCnt2[group_nr]++;
4263 }
4264
4265 static void PlayerWins(struct PlayerInfo *player)
4266 {
4267   player->LevelSolved = TRUE;
4268   player->GameOver = TRUE;
4269
4270   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4271                          level.native_em_level->lev->score : player->score);
4272
4273   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4274                                       TimeLeft);
4275   player->LevelSolved_CountingScore = player->score_final;
4276 }
4277
4278 void GameWon()
4279 {
4280   static int time, time_final;
4281   static int score, score_final;
4282   static int game_over_delay_1 = 0;
4283   static int game_over_delay_2 = 0;
4284   int game_over_delay_value_1 = 50;
4285   int game_over_delay_value_2 = 50;
4286
4287   if (!local_player->LevelSolved_GameWon)
4288   {
4289     int i;
4290
4291     /* do not start end game actions before the player stops moving (to exit) */
4292     if (local_player->MovPos)
4293       return;
4294
4295     local_player->LevelSolved_GameWon = TRUE;
4296     local_player->LevelSolved_SaveTape = tape.recording;
4297     local_player->LevelSolved_SaveScore = !tape.playing;
4298
4299     if (!tape.playing)
4300     {
4301       LevelStats_incSolved(level_nr);
4302
4303       SaveLevelSetup_SeriesInfo();
4304     }
4305
4306     if (tape.auto_play)         /* tape might already be stopped here */
4307       tape.auto_play_level_solved = TRUE;
4308
4309     TapeStop();
4310
4311     game_over_delay_1 = game_over_delay_value_1;
4312     game_over_delay_2 = game_over_delay_value_2;
4313
4314     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4315     score = score_final = local_player->score_final;
4316
4317     if (TimeLeft > 0)
4318     {
4319       time_final = 0;
4320       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4321     }
4322     else if (game.no_time_limit && TimePlayed < 999)
4323     {
4324       time_final = 999;
4325       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4326     }
4327
4328     local_player->score_final = score_final;
4329
4330     if (level_editor_test_game)
4331     {
4332       time = time_final;
4333       score = score_final;
4334
4335       local_player->LevelSolved_CountingTime = time;
4336       local_player->LevelSolved_CountingScore = score;
4337
4338       game_panel_controls[GAME_PANEL_TIME].value = time;
4339       game_panel_controls[GAME_PANEL_SCORE].value = score;
4340
4341       DisplayGameControlValues();
4342     }
4343
4344     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4345     {
4346       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4347       {
4348         /* close exit door after last player */
4349         if ((AllPlayersGone &&
4350              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4351               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4352               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4353             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4354             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4355         {
4356           int element = Feld[ExitX][ExitY];
4357
4358           Feld[ExitX][ExitY] =
4359             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4360              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4361              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4362              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4363              EL_EM_STEEL_EXIT_CLOSING);
4364
4365           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4366         }
4367
4368         /* player disappears */
4369         DrawLevelField(ExitX, ExitY);
4370       }
4371
4372       for (i = 0; i < MAX_PLAYERS; i++)
4373       {
4374         struct PlayerInfo *player = &stored_player[i];
4375
4376         if (player->present)
4377         {
4378           RemovePlayer(player);
4379
4380           /* player disappears */
4381           DrawLevelField(player->jx, player->jy);
4382         }
4383       }
4384     }
4385
4386     PlaySound(SND_GAME_WINNING);
4387   }
4388
4389   if (game_over_delay_1 > 0)
4390   {
4391     game_over_delay_1--;
4392
4393     return;
4394   }
4395
4396   if (time != time_final)
4397   {
4398     int time_to_go = ABS(time_final - time);
4399     int time_count_dir = (time < time_final ? +1 : -1);
4400     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4401
4402     time  += time_count_steps * time_count_dir;
4403     score += time_count_steps * level.score[SC_TIME_BONUS];
4404
4405     local_player->LevelSolved_CountingTime = time;
4406     local_player->LevelSolved_CountingScore = score;
4407
4408     game_panel_controls[GAME_PANEL_TIME].value = time;
4409     game_panel_controls[GAME_PANEL_SCORE].value = score;
4410
4411     DisplayGameControlValues();
4412
4413     if (time == time_final)
4414       StopSound(SND_GAME_LEVELTIME_BONUS);
4415     else if (setup.sound_loops)
4416       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4417     else
4418       PlaySound(SND_GAME_LEVELTIME_BONUS);
4419
4420     return;
4421   }
4422
4423   local_player->LevelSolved_PanelOff = TRUE;
4424
4425   if (game_over_delay_2 > 0)
4426   {
4427     game_over_delay_2--;
4428
4429     return;
4430   }
4431
4432   GameEnd();
4433 }
4434
4435 void GameEnd()
4436 {
4437   int hi_pos;
4438   boolean raise_level = FALSE;
4439
4440   local_player->LevelSolved_GameEnd = TRUE;
4441
4442   if (!global.use_envelope_request)
4443     CloseDoor(DOOR_CLOSE_1);
4444
4445   if (local_player->LevelSolved_SaveTape)
4446   {
4447     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4448   }
4449
4450   CloseDoor(DOOR_CLOSE_ALL);
4451
4452   if (level_editor_test_game)
4453   {
4454     SetGameStatus(GAME_MODE_MAIN);
4455
4456     DrawMainMenu();
4457
4458     return;
4459   }
4460
4461   if (!local_player->LevelSolved_SaveScore)
4462   {
4463     SetGameStatus(GAME_MODE_MAIN);
4464
4465     DrawMainMenu();
4466
4467     return;
4468   }
4469
4470   if (level_nr == leveldir_current->handicap_level)
4471   {
4472     leveldir_current->handicap_level++;
4473
4474     SaveLevelSetup_SeriesInfo();
4475   }
4476
4477   if (setup.increment_levels &&
4478       level_nr < leveldir_current->last_level)
4479     raise_level = TRUE;                 /* advance to next level */
4480
4481   if ((hi_pos = NewHiScore()) >= 0) 
4482   {
4483     SetGameStatus(GAME_MODE_SCORES);
4484
4485     DrawHallOfFame(hi_pos);
4486
4487     if (raise_level)
4488     {
4489       level_nr++;
4490       TapeErase();
4491     }
4492   }
4493   else
4494   {
4495     SetGameStatus(GAME_MODE_MAIN);
4496
4497     if (raise_level)
4498     {
4499       level_nr++;
4500       TapeErase();
4501     }
4502
4503     DrawMainMenu();
4504   }
4505 }
4506
4507 int NewHiScore()
4508 {
4509   int k, l;
4510   int position = -1;
4511   boolean one_score_entry_per_name = !program.many_scores_per_name;
4512
4513   LoadScore(level_nr);
4514
4515   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4516       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4517     return -1;
4518
4519   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4520   {
4521     if (local_player->score_final > highscore[k].Score)
4522     {
4523       /* player has made it to the hall of fame */
4524
4525       if (k < MAX_SCORE_ENTRIES - 1)
4526       {
4527         int m = MAX_SCORE_ENTRIES - 1;
4528
4529         if (one_score_entry_per_name)
4530         {
4531           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4532             if (strEqual(setup.player_name, highscore[l].Name))
4533               m = l;
4534
4535           if (m == k)   /* player's new highscore overwrites his old one */
4536             goto put_into_list;
4537         }
4538
4539         for (l = m; l > k; l--)
4540         {
4541           strcpy(highscore[l].Name, highscore[l - 1].Name);
4542           highscore[l].Score = highscore[l - 1].Score;
4543         }
4544       }
4545
4546       put_into_list:
4547
4548       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4549       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4550       highscore[k].Score = local_player->score_final; 
4551       position = k;
4552
4553       break;
4554     }
4555     else if (one_score_entry_per_name &&
4556              !strncmp(setup.player_name, highscore[k].Name,
4557                       MAX_PLAYER_NAME_LEN))
4558       break;    /* player already there with a higher score */
4559   }
4560
4561   if (position >= 0) 
4562     SaveScore(level_nr);
4563
4564   return position;
4565 }
4566
4567 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4568 {
4569   int element = Feld[x][y];
4570   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4571   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4572   int horiz_move = (dx != 0);
4573   int sign = (horiz_move ? dx : dy);
4574   int step = sign * element_info[element].move_stepsize;
4575
4576   /* special values for move stepsize for spring and things on conveyor belt */
4577   if (horiz_move)
4578   {
4579     if (CAN_FALL(element) &&
4580         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4581       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4582     else if (element == EL_SPRING)
4583       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4584   }
4585
4586   return step;
4587 }
4588
4589 inline static int getElementMoveStepsize(int x, int y)
4590 {
4591   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4592 }
4593
4594 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4595 {
4596   if (player->GfxAction != action || player->GfxDir != dir)
4597   {
4598     player->GfxAction = action;
4599     player->GfxDir = dir;
4600     player->Frame = 0;
4601     player->StepFrame = 0;
4602   }
4603 }
4604
4605 static void ResetGfxFrame(int x, int y)
4606 {
4607   // profiling showed that "autotest" spends 10~20% of its time in this function
4608   if (DrawingDeactivatedField())
4609     return;
4610
4611   int element = Feld[x][y];
4612   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4613
4614   if (graphic_info[graphic].anim_global_sync)
4615     GfxFrame[x][y] = FrameCounter;
4616   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4617     GfxFrame[x][y] = CustomValue[x][y];
4618   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4619     GfxFrame[x][y] = element_info[element].collect_score;
4620   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4621     GfxFrame[x][y] = ChangeDelay[x][y];
4622 }
4623
4624 static void ResetGfxAnimation(int x, int y)
4625 {
4626   GfxAction[x][y] = ACTION_DEFAULT;
4627   GfxDir[x][y] = MovDir[x][y];
4628   GfxFrame[x][y] = 0;
4629
4630   ResetGfxFrame(x, y);
4631 }
4632
4633 static void ResetRandomAnimationValue(int x, int y)
4634 {
4635   GfxRandom[x][y] = INIT_GFX_RANDOM();
4636 }
4637
4638 void InitMovingField(int x, int y, int direction)
4639 {
4640   int element = Feld[x][y];
4641   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4642   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4643   int newx = x + dx;
4644   int newy = y + dy;
4645   boolean is_moving_before, is_moving_after;
4646
4647   /* check if element was/is moving or being moved before/after mode change */
4648   is_moving_before = (WasJustMoving[x][y] != 0);
4649   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4650
4651   /* reset animation only for moving elements which change direction of moving
4652      or which just started or stopped moving
4653      (else CEs with property "can move" / "not moving" are reset each frame) */
4654   if (is_moving_before != is_moving_after ||
4655       direction != MovDir[x][y])
4656     ResetGfxAnimation(x, y);
4657
4658   MovDir[x][y] = direction;
4659   GfxDir[x][y] = direction;
4660
4661   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4662                      direction == MV_DOWN && CAN_FALL(element) ?
4663                      ACTION_FALLING : ACTION_MOVING);
4664
4665   /* this is needed for CEs with property "can move" / "not moving" */
4666
4667   if (is_moving_after)
4668   {
4669     if (Feld[newx][newy] == EL_EMPTY)
4670       Feld[newx][newy] = EL_BLOCKED;
4671
4672     MovDir[newx][newy] = MovDir[x][y];
4673
4674     CustomValue[newx][newy] = CustomValue[x][y];
4675
4676     GfxFrame[newx][newy] = GfxFrame[x][y];
4677     GfxRandom[newx][newy] = GfxRandom[x][y];
4678     GfxAction[newx][newy] = GfxAction[x][y];
4679     GfxDir[newx][newy] = GfxDir[x][y];
4680   }
4681 }
4682
4683 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4684 {
4685   int direction = MovDir[x][y];
4686   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4687   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4688
4689   *goes_to_x = newx;
4690   *goes_to_y = newy;
4691 }
4692
4693 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4694 {
4695   int oldx = x, oldy = y;
4696   int direction = MovDir[x][y];
4697
4698   if (direction == MV_LEFT)
4699     oldx++;
4700   else if (direction == MV_RIGHT)
4701     oldx--;
4702   else if (direction == MV_UP)
4703     oldy++;
4704   else if (direction == MV_DOWN)
4705     oldy--;
4706
4707   *comes_from_x = oldx;
4708   *comes_from_y = oldy;
4709 }
4710
4711 int MovingOrBlocked2Element(int x, int y)
4712 {
4713   int element = Feld[x][y];
4714
4715   if (element == EL_BLOCKED)
4716   {
4717     int oldx, oldy;
4718
4719     Blocked2Moving(x, y, &oldx, &oldy);
4720     return Feld[oldx][oldy];
4721   }
4722   else
4723     return element;
4724 }
4725
4726 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4727 {
4728   /* like MovingOrBlocked2Element(), but if element is moving
4729      and (x,y) is the field the moving element is just leaving,
4730      return EL_BLOCKED instead of the element value */
4731   int element = Feld[x][y];
4732
4733   if (IS_MOVING(x, y))
4734   {
4735     if (element == EL_BLOCKED)
4736     {
4737       int oldx, oldy;
4738
4739       Blocked2Moving(x, y, &oldx, &oldy);
4740       return Feld[oldx][oldy];
4741     }
4742     else
4743       return EL_BLOCKED;
4744   }
4745   else
4746     return element;
4747 }
4748
4749 static void RemoveField(int x, int y)
4750 {
4751   Feld[x][y] = EL_EMPTY;
4752
4753   MovPos[x][y] = 0;
4754   MovDir[x][y] = 0;
4755   MovDelay[x][y] = 0;
4756
4757   CustomValue[x][y] = 0;
4758
4759   AmoebaNr[x][y] = 0;
4760   ChangeDelay[x][y] = 0;
4761   ChangePage[x][y] = -1;
4762   Pushed[x][y] = FALSE;
4763
4764   GfxElement[x][y] = EL_UNDEFINED;
4765   GfxAction[x][y] = ACTION_DEFAULT;
4766   GfxDir[x][y] = MV_NONE;
4767 }
4768
4769 void RemoveMovingField(int x, int y)
4770 {
4771   int oldx = x, oldy = y, newx = x, newy = y;
4772   int element = Feld[x][y];
4773   int next_element = EL_UNDEFINED;
4774
4775   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4776     return;
4777
4778   if (IS_MOVING(x, y))
4779   {
4780     Moving2Blocked(x, y, &newx, &newy);
4781
4782     if (Feld[newx][newy] != EL_BLOCKED)
4783     {
4784       /* element is moving, but target field is not free (blocked), but
4785          already occupied by something different (example: acid pool);
4786          in this case, only remove the moving field, but not the target */
4787
4788       RemoveField(oldx, oldy);
4789
4790       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4791
4792       TEST_DrawLevelField(oldx, oldy);
4793
4794       return;
4795     }
4796   }
4797   else if (element == EL_BLOCKED)
4798   {
4799     Blocked2Moving(x, y, &oldx, &oldy);
4800     if (!IS_MOVING(oldx, oldy))
4801       return;
4802   }
4803
4804   if (element == EL_BLOCKED &&
4805       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4806        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4807        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4808        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4809        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4810        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4811     next_element = get_next_element(Feld[oldx][oldy]);
4812
4813   RemoveField(oldx, oldy);
4814   RemoveField(newx, newy);
4815
4816   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4817
4818   if (next_element != EL_UNDEFINED)
4819     Feld[oldx][oldy] = next_element;
4820
4821   TEST_DrawLevelField(oldx, oldy);
4822   TEST_DrawLevelField(newx, newy);
4823 }
4824
4825 void DrawDynamite(int x, int y)
4826 {
4827   int sx = SCREENX(x), sy = SCREENY(y);
4828   int graphic = el2img(Feld[x][y]);
4829   int frame;
4830
4831   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4832     return;
4833
4834   if (IS_WALKABLE_INSIDE(Back[x][y]))
4835     return;
4836
4837   if (Back[x][y])
4838     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4839   else if (Store[x][y])
4840     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4841
4842   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4843
4844   if (Back[x][y] || Store[x][y])
4845     DrawGraphicThruMask(sx, sy, graphic, frame);
4846   else
4847     DrawGraphic(sx, sy, graphic, frame);
4848 }
4849
4850 void CheckDynamite(int x, int y)
4851 {
4852   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4853   {
4854     MovDelay[x][y]--;
4855
4856     if (MovDelay[x][y] != 0)
4857     {
4858       DrawDynamite(x, y);
4859       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4860
4861       return;
4862     }
4863   }
4864
4865   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4866
4867   Bang(x, y);
4868 }
4869
4870 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4871 {
4872   boolean num_checked_players = 0;
4873   int i;
4874
4875   for (i = 0; i < MAX_PLAYERS; i++)
4876   {
4877     if (stored_player[i].active)
4878     {
4879       int sx = stored_player[i].jx;
4880       int sy = stored_player[i].jy;
4881
4882       if (num_checked_players == 0)
4883       {
4884         *sx1 = *sx2 = sx;
4885         *sy1 = *sy2 = sy;
4886       }
4887       else
4888       {
4889         *sx1 = MIN(*sx1, sx);
4890         *sy1 = MIN(*sy1, sy);
4891         *sx2 = MAX(*sx2, sx);
4892         *sy2 = MAX(*sy2, sy);
4893       }
4894
4895       num_checked_players++;
4896     }
4897   }
4898 }
4899
4900 static boolean checkIfAllPlayersFitToScreen_RND()
4901 {
4902   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4903
4904   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4905
4906   return (sx2 - sx1 < SCR_FIELDX &&
4907           sy2 - sy1 < SCR_FIELDY);
4908 }
4909
4910 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4911 {
4912   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4913
4914   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4915
4916   *sx = (sx1 + sx2) / 2;
4917   *sy = (sy1 + sy2) / 2;
4918 }
4919
4920 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4921                         boolean center_screen, boolean quick_relocation)
4922 {
4923   unsigned int frame_delay_value_old = GetVideoFrameDelay();
4924   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4925   boolean no_delay = (tape.warp_forward);
4926   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4927   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4928   int new_scroll_x, new_scroll_y;
4929
4930   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4931   {
4932     /* case 1: quick relocation inside visible screen (without scrolling) */
4933
4934     RedrawPlayfield();
4935
4936     return;
4937   }
4938
4939   if (!level.shifted_relocation || center_screen)
4940   {
4941     /* relocation _with_ centering of screen */
4942
4943     new_scroll_x = SCROLL_POSITION_X(x);
4944     new_scroll_y = SCROLL_POSITION_Y(y);
4945   }
4946   else
4947   {
4948     /* relocation _without_ centering of screen */
4949
4950     int center_scroll_x = SCROLL_POSITION_X(old_x);
4951     int center_scroll_y = SCROLL_POSITION_Y(old_y);
4952     int offset_x = x + (scroll_x - center_scroll_x);
4953     int offset_y = y + (scroll_y - center_scroll_y);
4954
4955     /* for new screen position, apply previous offset to center position */
4956     new_scroll_x = SCROLL_POSITION_X(offset_x);
4957     new_scroll_y = SCROLL_POSITION_Y(offset_y);
4958   }
4959
4960   if (quick_relocation)
4961   {
4962     /* case 2: quick relocation (redraw without visible scrolling) */
4963
4964     scroll_x = new_scroll_x;
4965     scroll_y = new_scroll_y;
4966
4967     RedrawPlayfield();
4968
4969     return;
4970   }
4971
4972   /* case 3: visible relocation (with scrolling to new position) */
4973
4974   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4975
4976   SetVideoFrameDelay(wait_delay_value);
4977
4978   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4979   {
4980     int dx = 0, dy = 0;
4981     int fx = FX, fy = FY;
4982
4983     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4984     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4985
4986     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4987       break;
4988
4989     scroll_x -= dx;
4990     scroll_y -= dy;
4991
4992     fx += dx * TILEX / 2;
4993     fy += dy * TILEY / 2;
4994
4995     ScrollLevel(dx, dy);
4996     DrawAllPlayers();
4997
4998     /* scroll in two steps of half tile size to make things smoother */
4999     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5000
5001     /* scroll second step to align at full tile size */
5002     BlitScreenToBitmap(window);
5003   }
5004
5005   DrawAllPlayers();
5006   BackToFront();
5007
5008   SetVideoFrameDelay(frame_delay_value_old);
5009 }
5010
5011 void RelocatePlayer(int jx, int jy, int el_player_raw)
5012 {
5013   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5014   int player_nr = GET_PLAYER_NR(el_player);
5015   struct PlayerInfo *player = &stored_player[player_nr];
5016   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5017   boolean no_delay = (tape.warp_forward);
5018   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5019   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5020   int old_jx = player->jx;
5021   int old_jy = player->jy;
5022   int old_element = Feld[old_jx][old_jy];
5023   int element = Feld[jx][jy];
5024   boolean player_relocated = (old_jx != jx || old_jy != jy);
5025
5026   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5027   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5028   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5029   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5030   int leave_side_horiz = move_dir_horiz;
5031   int leave_side_vert  = move_dir_vert;
5032   int enter_side = enter_side_horiz | enter_side_vert;
5033   int leave_side = leave_side_horiz | leave_side_vert;
5034
5035   if (player->GameOver)         /* do not reanimate dead player */
5036     return;
5037
5038   if (!player_relocated)        /* no need to relocate the player */
5039     return;
5040
5041   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5042   {
5043     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5044     DrawLevelField(jx, jy);
5045   }
5046
5047   if (player->present)
5048   {
5049     while (player->MovPos)
5050     {
5051       ScrollPlayer(player, SCROLL_GO_ON);
5052       ScrollScreen(NULL, SCROLL_GO_ON);
5053
5054       AdvanceFrameAndPlayerCounters(player->index_nr);
5055
5056       DrawPlayer(player);
5057
5058       BackToFront_WithFrameDelay(wait_delay_value);
5059     }
5060
5061     DrawPlayer(player);         /* needed here only to cleanup last field */
5062     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5063
5064     player->is_moving = FALSE;
5065   }
5066
5067   if (IS_CUSTOM_ELEMENT(old_element))
5068     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5069                                CE_LEFT_BY_PLAYER,
5070                                player->index_bit, leave_side);
5071
5072   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5073                                       CE_PLAYER_LEAVES_X,
5074                                       player->index_bit, leave_side);
5075
5076   Feld[jx][jy] = el_player;
5077   InitPlayerField(jx, jy, el_player, TRUE);
5078
5079   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5080      possible that the relocation target field did not contain a player element,
5081      but a walkable element, to which the new player was relocated -- in this
5082      case, restore that (already initialized!) element on the player field */
5083   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5084   {
5085     Feld[jx][jy] = element;     /* restore previously existing element */
5086   }
5087
5088   /* only visually relocate centered player */
5089   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5090                      FALSE, level.instant_relocation);
5091
5092   TestIfPlayerTouchesBadThing(jx, jy);
5093   TestIfPlayerTouchesCustomElement(jx, jy);
5094
5095   if (IS_CUSTOM_ELEMENT(element))
5096     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5097                                player->index_bit, enter_side);
5098
5099   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5100                                       player->index_bit, enter_side);
5101
5102   if (player->is_switching)
5103   {
5104     /* ensure that relocation while still switching an element does not cause
5105        a new element to be treated as also switched directly after relocation
5106        (this is important for teleporter switches that teleport the player to
5107        a place where another teleporter switch is in the same direction, which
5108        would then incorrectly be treated as immediately switched before the
5109        direction key that caused the switch was released) */
5110
5111     player->switch_x += jx - old_jx;
5112     player->switch_y += jy - old_jy;
5113   }
5114 }
5115
5116 void Explode(int ex, int ey, int phase, int mode)
5117 {
5118   int x, y;
5119   int last_phase;
5120   int border_element;
5121
5122   /* !!! eliminate this variable !!! */
5123   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5124
5125   if (game.explosions_delayed)
5126   {
5127     ExplodeField[ex][ey] = mode;
5128     return;
5129   }
5130
5131   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5132   {
5133     int center_element = Feld[ex][ey];
5134     int artwork_element, explosion_element;     /* set these values later */
5135
5136     /* remove things displayed in background while burning dynamite */
5137     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5138       Back[ex][ey] = 0;
5139
5140     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5141     {
5142       /* put moving element to center field (and let it explode there) */
5143       center_element = MovingOrBlocked2Element(ex, ey);
5144       RemoveMovingField(ex, ey);
5145       Feld[ex][ey] = center_element;
5146     }
5147
5148     /* now "center_element" is finally determined -- set related values now */
5149     artwork_element = center_element;           /* for custom player artwork */
5150     explosion_element = center_element;         /* for custom player artwork */
5151
5152     if (IS_PLAYER(ex, ey))
5153     {
5154       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5155
5156       artwork_element = stored_player[player_nr].artwork_element;
5157
5158       if (level.use_explosion_element[player_nr])
5159       {
5160         explosion_element = level.explosion_element[player_nr];
5161         artwork_element = explosion_element;
5162       }
5163     }
5164
5165     if (mode == EX_TYPE_NORMAL ||
5166         mode == EX_TYPE_CENTER ||
5167         mode == EX_TYPE_CROSS)
5168       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5169
5170     last_phase = element_info[explosion_element].explosion_delay + 1;
5171
5172     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5173     {
5174       int xx = x - ex + 1;
5175       int yy = y - ey + 1;
5176       int element;
5177
5178       if (!IN_LEV_FIELD(x, y) ||
5179           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5180           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5181         continue;
5182
5183       element = Feld[x][y];
5184
5185       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5186       {
5187         element = MovingOrBlocked2Element(x, y);
5188
5189         if (!IS_EXPLOSION_PROOF(element))
5190           RemoveMovingField(x, y);
5191       }
5192
5193       /* indestructible elements can only explode in center (but not flames) */
5194       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5195                                            mode == EX_TYPE_BORDER)) ||
5196           element == EL_FLAMES)
5197         continue;
5198
5199       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5200          behaviour, for example when touching a yamyam that explodes to rocks
5201          with active deadly shield, a rock is created under the player !!! */
5202       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5203 #if 0
5204       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5205           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5206            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5207 #else
5208       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5209 #endif
5210       {
5211         if (IS_ACTIVE_BOMB(element))
5212         {
5213           /* re-activate things under the bomb like gate or penguin */
5214           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5215           Back[x][y] = 0;
5216         }
5217
5218         continue;
5219       }
5220
5221       /* save walkable background elements while explosion on same tile */
5222       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5223           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5224         Back[x][y] = element;
5225
5226       /* ignite explodable elements reached by other explosion */
5227       if (element == EL_EXPLOSION)
5228         element = Store2[x][y];
5229
5230       if (AmoebaNr[x][y] &&
5231           (element == EL_AMOEBA_FULL ||
5232            element == EL_BD_AMOEBA ||
5233            element == EL_AMOEBA_GROWING))
5234       {
5235         AmoebaCnt[AmoebaNr[x][y]]--;
5236         AmoebaCnt2[AmoebaNr[x][y]]--;
5237       }
5238
5239       RemoveField(x, y);
5240
5241       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5242       {
5243         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5244
5245         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5246
5247         if (PLAYERINFO(ex, ey)->use_murphy)
5248           Store[x][y] = EL_EMPTY;
5249       }
5250
5251       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5252          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5253       else if (ELEM_IS_PLAYER(center_element))
5254         Store[x][y] = EL_EMPTY;
5255       else if (center_element == EL_YAMYAM)
5256         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5257       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5258         Store[x][y] = element_info[center_element].content.e[xx][yy];
5259 #if 1
5260       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5261          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5262          otherwise) -- FIX THIS !!! */
5263       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5264         Store[x][y] = element_info[element].content.e[1][1];
5265 #else
5266       else if (!CAN_EXPLODE(element))
5267         Store[x][y] = element_info[element].content.e[1][1];
5268 #endif
5269       else
5270         Store[x][y] = EL_EMPTY;
5271
5272       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5273           center_element == EL_AMOEBA_TO_DIAMOND)
5274         Store2[x][y] = element;
5275
5276       Feld[x][y] = EL_EXPLOSION;
5277       GfxElement[x][y] = artwork_element;
5278
5279       ExplodePhase[x][y] = 1;
5280       ExplodeDelay[x][y] = last_phase;
5281
5282       Stop[x][y] = TRUE;
5283     }
5284
5285     if (center_element == EL_YAMYAM)
5286       game.yamyam_content_nr =
5287         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5288
5289     return;
5290   }
5291
5292   if (Stop[ex][ey])
5293     return;
5294
5295   x = ex;
5296   y = ey;
5297
5298   if (phase == 1)
5299     GfxFrame[x][y] = 0;         /* restart explosion animation */
5300
5301   last_phase = ExplodeDelay[x][y];
5302
5303   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5304
5305   /* this can happen if the player leaves an explosion just in time */
5306   if (GfxElement[x][y] == EL_UNDEFINED)
5307     GfxElement[x][y] = EL_EMPTY;
5308
5309   border_element = Store2[x][y];
5310   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5311     border_element = StorePlayer[x][y];
5312
5313   if (phase == element_info[border_element].ignition_delay ||
5314       phase == last_phase)
5315   {
5316     boolean border_explosion = FALSE;
5317
5318     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5319         !PLAYER_EXPLOSION_PROTECTED(x, y))
5320     {
5321       KillPlayerUnlessExplosionProtected(x, y);
5322       border_explosion = TRUE;
5323     }
5324     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5325     {
5326       Feld[x][y] = Store2[x][y];
5327       Store2[x][y] = 0;
5328       Bang(x, y);
5329       border_explosion = TRUE;
5330     }
5331     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5332     {
5333       AmoebeUmwandeln(x, y);
5334       Store2[x][y] = 0;
5335       border_explosion = TRUE;
5336     }
5337
5338     /* if an element just explodes due to another explosion (chain-reaction),
5339        do not immediately end the new explosion when it was the last frame of
5340        the explosion (as it would be done in the following "if"-statement!) */
5341     if (border_explosion && phase == last_phase)
5342       return;
5343   }
5344
5345   if (phase == last_phase)
5346   {
5347     int element;
5348
5349     element = Feld[x][y] = Store[x][y];
5350     Store[x][y] = Store2[x][y] = 0;
5351     GfxElement[x][y] = EL_UNDEFINED;
5352
5353     /* player can escape from explosions and might therefore be still alive */
5354     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5355         element <= EL_PLAYER_IS_EXPLODING_4)
5356     {
5357       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5358       int explosion_element = EL_PLAYER_1 + player_nr;
5359       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5360       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5361
5362       if (level.use_explosion_element[player_nr])
5363         explosion_element = level.explosion_element[player_nr];
5364
5365       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5366                     element_info[explosion_element].content.e[xx][yy]);
5367     }
5368
5369     /* restore probably existing indestructible background element */
5370     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5371       element = Feld[x][y] = Back[x][y];
5372     Back[x][y] = 0;
5373
5374     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5375     GfxDir[x][y] = MV_NONE;
5376     ChangeDelay[x][y] = 0;
5377     ChangePage[x][y] = -1;
5378
5379     CustomValue[x][y] = 0;
5380
5381     InitField_WithBug2(x, y, FALSE);
5382
5383     TEST_DrawLevelField(x, y);
5384
5385     TestIfElementTouchesCustomElement(x, y);
5386
5387     if (GFX_CRUMBLED(element))
5388       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5389
5390     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5391       StorePlayer[x][y] = 0;
5392
5393     if (ELEM_IS_PLAYER(element))
5394       RelocatePlayer(x, y, element);
5395   }
5396   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5397   {
5398     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5399     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5400
5401     if (phase == delay)
5402       TEST_DrawLevelFieldCrumbled(x, y);
5403
5404     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5405     {
5406       DrawLevelElement(x, y, Back[x][y]);
5407       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5408     }
5409     else if (IS_WALKABLE_UNDER(Back[x][y]))
5410     {
5411       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5412       DrawLevelElementThruMask(x, y, Back[x][y]);
5413     }
5414     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5415       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5416   }
5417 }
5418
5419 void DynaExplode(int ex, int ey)
5420 {
5421   int i, j;
5422   int dynabomb_element = Feld[ex][ey];
5423   int dynabomb_size = 1;
5424   boolean dynabomb_xl = FALSE;
5425   struct PlayerInfo *player;
5426   static int xy[4][2] =
5427   {
5428     { 0, -1 },
5429     { -1, 0 },
5430     { +1, 0 },
5431     { 0, +1 }
5432   };
5433
5434   if (IS_ACTIVE_BOMB(dynabomb_element))
5435   {
5436     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5437     dynabomb_size = player->dynabomb_size;
5438     dynabomb_xl = player->dynabomb_xl;
5439     player->dynabombs_left++;
5440   }
5441
5442   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5443
5444   for (i = 0; i < NUM_DIRECTIONS; i++)
5445   {
5446     for (j = 1; j <= dynabomb_size; j++)
5447     {
5448       int x = ex + j * xy[i][0];
5449       int y = ey + j * xy[i][1];
5450       int element;
5451
5452       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5453         break;
5454
5455       element = Feld[x][y];
5456
5457       /* do not restart explosions of fields with active bombs */
5458       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5459         continue;
5460
5461       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5462
5463       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5464           !IS_DIGGABLE(element) && !dynabomb_xl)
5465         break;
5466     }
5467   }
5468 }
5469
5470 void Bang(int x, int y)
5471 {
5472   int element = MovingOrBlocked2Element(x, y);
5473   int explosion_type = EX_TYPE_NORMAL;
5474
5475   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5476   {
5477     struct PlayerInfo *player = PLAYERINFO(x, y);
5478
5479     element = Feld[x][y] = player->initial_element;
5480
5481     if (level.use_explosion_element[player->index_nr])
5482     {
5483       int explosion_element = level.explosion_element[player->index_nr];
5484
5485       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5486         explosion_type = EX_TYPE_CROSS;
5487       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5488         explosion_type = EX_TYPE_CENTER;
5489     }
5490   }
5491
5492   switch (element)
5493   {
5494     case EL_BUG:
5495     case EL_SPACESHIP:
5496     case EL_BD_BUTTERFLY:
5497     case EL_BD_FIREFLY:
5498     case EL_YAMYAM:
5499     case EL_DARK_YAMYAM:
5500     case EL_ROBOT:
5501     case EL_PACMAN:
5502     case EL_MOLE:
5503       RaiseScoreElement(element);
5504       break;
5505
5506     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5507     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5508     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5509     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5510     case EL_DYNABOMB_INCREASE_NUMBER:
5511     case EL_DYNABOMB_INCREASE_SIZE:
5512     case EL_DYNABOMB_INCREASE_POWER:
5513       explosion_type = EX_TYPE_DYNA;
5514       break;
5515
5516     case EL_DC_LANDMINE:
5517       explosion_type = EX_TYPE_CENTER;
5518       break;
5519
5520     case EL_PENGUIN:
5521     case EL_LAMP:
5522     case EL_LAMP_ACTIVE:
5523     case EL_AMOEBA_TO_DIAMOND:
5524       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5525         explosion_type = EX_TYPE_CENTER;
5526       break;
5527
5528     default:
5529       if (element_info[element].explosion_type == EXPLODES_CROSS)
5530         explosion_type = EX_TYPE_CROSS;
5531       else if (element_info[element].explosion_type == EXPLODES_1X1)
5532         explosion_type = EX_TYPE_CENTER;
5533       break;
5534   }
5535
5536   if (explosion_type == EX_TYPE_DYNA)
5537     DynaExplode(x, y);
5538   else
5539     Explode(x, y, EX_PHASE_START, explosion_type);
5540
5541   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5542 }
5543
5544 void SplashAcid(int x, int y)
5545 {
5546   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5547       (!IN_LEV_FIELD(x - 1, y - 2) ||
5548        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5549     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5550
5551   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5552       (!IN_LEV_FIELD(x + 1, y - 2) ||
5553        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5554     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5555
5556   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5557 }
5558
5559 static void InitBeltMovement()
5560 {
5561   static int belt_base_element[4] =
5562   {
5563     EL_CONVEYOR_BELT_1_LEFT,
5564     EL_CONVEYOR_BELT_2_LEFT,
5565     EL_CONVEYOR_BELT_3_LEFT,
5566     EL_CONVEYOR_BELT_4_LEFT
5567   };
5568   static int belt_base_active_element[4] =
5569   {
5570     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5571     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5572     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5573     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5574   };
5575
5576   int x, y, i, j;
5577
5578   /* set frame order for belt animation graphic according to belt direction */
5579   for (i = 0; i < NUM_BELTS; i++)
5580   {
5581     int belt_nr = i;
5582
5583     for (j = 0; j < NUM_BELT_PARTS; j++)
5584     {
5585       int element = belt_base_active_element[belt_nr] + j;
5586       int graphic_1 = el2img(element);
5587       int graphic_2 = el2panelimg(element);
5588
5589       if (game.belt_dir[i] == MV_LEFT)
5590       {
5591         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5592         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5593       }
5594       else
5595       {
5596         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5597         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5598       }
5599     }
5600   }
5601
5602   SCAN_PLAYFIELD(x, y)
5603   {
5604     int element = Feld[x][y];
5605
5606     for (i = 0; i < NUM_BELTS; i++)
5607     {
5608       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5609       {
5610         int e_belt_nr = getBeltNrFromBeltElement(element);
5611         int belt_nr = i;
5612
5613         if (e_belt_nr == belt_nr)
5614         {
5615           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5616
5617           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5618         }
5619       }
5620     }
5621   }
5622 }
5623
5624 static void ToggleBeltSwitch(int x, int y)
5625 {
5626   static int belt_base_element[4] =
5627   {
5628     EL_CONVEYOR_BELT_1_LEFT,
5629     EL_CONVEYOR_BELT_2_LEFT,
5630     EL_CONVEYOR_BELT_3_LEFT,
5631     EL_CONVEYOR_BELT_4_LEFT
5632   };
5633   static int belt_base_active_element[4] =
5634   {
5635     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5636     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5637     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5638     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5639   };
5640   static int belt_base_switch_element[4] =
5641   {
5642     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5643     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5644     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5645     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5646   };
5647   static int belt_move_dir[4] =
5648   {
5649     MV_LEFT,
5650     MV_NONE,
5651     MV_RIGHT,
5652     MV_NONE,
5653   };
5654
5655   int element = Feld[x][y];
5656   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5657   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5658   int belt_dir = belt_move_dir[belt_dir_nr];
5659   int xx, yy, i;
5660
5661   if (!IS_BELT_SWITCH(element))
5662     return;
5663
5664   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5665   game.belt_dir[belt_nr] = belt_dir;
5666
5667   if (belt_dir_nr == 3)
5668     belt_dir_nr = 1;
5669
5670   /* set frame order for belt animation graphic according to belt direction */
5671   for (i = 0; i < NUM_BELT_PARTS; i++)
5672   {
5673     int element = belt_base_active_element[belt_nr] + i;
5674     int graphic_1 = el2img(element);
5675     int graphic_2 = el2panelimg(element);
5676
5677     if (belt_dir == MV_LEFT)
5678     {
5679       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5680       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5681     }
5682     else
5683     {
5684       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5685       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5686     }
5687   }
5688
5689   SCAN_PLAYFIELD(xx, yy)
5690   {
5691     int element = Feld[xx][yy];
5692
5693     if (IS_BELT_SWITCH(element))
5694     {
5695       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5696
5697       if (e_belt_nr == belt_nr)
5698       {
5699         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5700         TEST_DrawLevelField(xx, yy);
5701       }
5702     }
5703     else if (IS_BELT(element) && belt_dir != MV_NONE)
5704     {
5705       int e_belt_nr = getBeltNrFromBeltElement(element);
5706
5707       if (e_belt_nr == belt_nr)
5708       {
5709         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5710
5711         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5712         TEST_DrawLevelField(xx, yy);
5713       }
5714     }
5715     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5716     {
5717       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5718
5719       if (e_belt_nr == belt_nr)
5720       {
5721         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5722
5723         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5724         TEST_DrawLevelField(xx, yy);
5725       }
5726     }
5727   }
5728 }
5729
5730 static void ToggleSwitchgateSwitch(int x, int y)
5731 {
5732   int xx, yy;
5733
5734   game.switchgate_pos = !game.switchgate_pos;
5735
5736   SCAN_PLAYFIELD(xx, yy)
5737   {
5738     int element = Feld[xx][yy];
5739
5740     if (element == EL_SWITCHGATE_SWITCH_UP)
5741     {
5742       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5743       TEST_DrawLevelField(xx, yy);
5744     }
5745     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5746     {
5747       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5748       TEST_DrawLevelField(xx, yy);
5749     }
5750     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5751     {
5752       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5753       TEST_DrawLevelField(xx, yy);
5754     }
5755     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5756     {
5757       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5758       TEST_DrawLevelField(xx, yy);
5759     }
5760     else if (element == EL_SWITCHGATE_OPEN ||
5761              element == EL_SWITCHGATE_OPENING)
5762     {
5763       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5764
5765       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5766     }
5767     else if (element == EL_SWITCHGATE_CLOSED ||
5768              element == EL_SWITCHGATE_CLOSING)
5769     {
5770       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5771
5772       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5773     }
5774   }
5775 }
5776
5777 static int getInvisibleActiveFromInvisibleElement(int element)
5778 {
5779   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5780           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5781           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5782           element);
5783 }
5784
5785 static int getInvisibleFromInvisibleActiveElement(int element)
5786 {
5787   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5788           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5789           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5790           element);
5791 }
5792
5793 static void RedrawAllLightSwitchesAndInvisibleElements()
5794 {
5795   int x, y;
5796
5797   SCAN_PLAYFIELD(x, y)
5798   {
5799     int element = Feld[x][y];
5800
5801     if (element == EL_LIGHT_SWITCH &&
5802         game.light_time_left > 0)
5803     {
5804       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5805       TEST_DrawLevelField(x, y);
5806     }
5807     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5808              game.light_time_left == 0)
5809     {
5810       Feld[x][y] = EL_LIGHT_SWITCH;
5811       TEST_DrawLevelField(x, y);
5812     }
5813     else if (element == EL_EMC_DRIPPER &&
5814              game.light_time_left > 0)
5815     {
5816       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5817       TEST_DrawLevelField(x, y);
5818     }
5819     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5820              game.light_time_left == 0)
5821     {
5822       Feld[x][y] = EL_EMC_DRIPPER;
5823       TEST_DrawLevelField(x, y);
5824     }
5825     else if (element == EL_INVISIBLE_STEELWALL ||
5826              element == EL_INVISIBLE_WALL ||
5827              element == EL_INVISIBLE_SAND)
5828     {
5829       if (game.light_time_left > 0)
5830         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5831
5832       TEST_DrawLevelField(x, y);
5833
5834       /* uncrumble neighbour fields, if needed */
5835       if (element == EL_INVISIBLE_SAND)
5836         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5837     }
5838     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5839              element == EL_INVISIBLE_WALL_ACTIVE ||
5840              element == EL_INVISIBLE_SAND_ACTIVE)
5841     {
5842       if (game.light_time_left == 0)
5843         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5844
5845       TEST_DrawLevelField(x, y);
5846
5847       /* re-crumble neighbour fields, if needed */
5848       if (element == EL_INVISIBLE_SAND)
5849         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5850     }
5851   }
5852 }
5853
5854 static void RedrawAllInvisibleElementsForLenses()
5855 {
5856   int x, y;
5857
5858   SCAN_PLAYFIELD(x, y)
5859   {
5860     int element = Feld[x][y];
5861
5862     if (element == EL_EMC_DRIPPER &&
5863         game.lenses_time_left > 0)
5864     {
5865       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5866       TEST_DrawLevelField(x, y);
5867     }
5868     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5869              game.lenses_time_left == 0)
5870     {
5871       Feld[x][y] = EL_EMC_DRIPPER;
5872       TEST_DrawLevelField(x, y);
5873     }
5874     else if (element == EL_INVISIBLE_STEELWALL ||
5875              element == EL_INVISIBLE_WALL ||
5876              element == EL_INVISIBLE_SAND)
5877     {
5878       if (game.lenses_time_left > 0)
5879         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5880
5881       TEST_DrawLevelField(x, y);
5882
5883       /* uncrumble neighbour fields, if needed */
5884       if (element == EL_INVISIBLE_SAND)
5885         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5886     }
5887     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5888              element == EL_INVISIBLE_WALL_ACTIVE ||
5889              element == EL_INVISIBLE_SAND_ACTIVE)
5890     {
5891       if (game.lenses_time_left == 0)
5892         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5893
5894       TEST_DrawLevelField(x, y);
5895
5896       /* re-crumble neighbour fields, if needed */
5897       if (element == EL_INVISIBLE_SAND)
5898         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5899     }
5900   }
5901 }
5902
5903 static void RedrawAllInvisibleElementsForMagnifier()
5904 {
5905   int x, y;
5906
5907   SCAN_PLAYFIELD(x, y)
5908   {
5909     int element = Feld[x][y];
5910
5911     if (element == EL_EMC_FAKE_GRASS &&
5912         game.magnify_time_left > 0)
5913     {
5914       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5915       TEST_DrawLevelField(x, y);
5916     }
5917     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5918              game.magnify_time_left == 0)
5919     {
5920       Feld[x][y] = EL_EMC_FAKE_GRASS;
5921       TEST_DrawLevelField(x, y);
5922     }
5923     else if (IS_GATE_GRAY(element) &&
5924              game.magnify_time_left > 0)
5925     {
5926       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5927                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5928                     IS_EM_GATE_GRAY(element) ?
5929                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5930                     IS_EMC_GATE_GRAY(element) ?
5931                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5932                     IS_DC_GATE_GRAY(element) ?
5933                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5934                     element);
5935       TEST_DrawLevelField(x, y);
5936     }
5937     else if (IS_GATE_GRAY_ACTIVE(element) &&
5938              game.magnify_time_left == 0)
5939     {
5940       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5941                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5942                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5943                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5944                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5945                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5946                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5947                     EL_DC_GATE_WHITE_GRAY :
5948                     element);
5949       TEST_DrawLevelField(x, y);
5950     }
5951   }
5952 }
5953
5954 static void ToggleLightSwitch(int x, int y)
5955 {
5956   int element = Feld[x][y];
5957
5958   game.light_time_left =
5959     (element == EL_LIGHT_SWITCH ?
5960      level.time_light * FRAMES_PER_SECOND : 0);
5961
5962   RedrawAllLightSwitchesAndInvisibleElements();
5963 }
5964
5965 static void ActivateTimegateSwitch(int x, int y)
5966 {
5967   int xx, yy;
5968
5969   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5970
5971   SCAN_PLAYFIELD(xx, yy)
5972   {
5973     int element = Feld[xx][yy];
5974
5975     if (element == EL_TIMEGATE_CLOSED ||
5976         element == EL_TIMEGATE_CLOSING)
5977     {
5978       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5979       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5980     }
5981
5982     /*
5983     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5984     {
5985       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5986       TEST_DrawLevelField(xx, yy);
5987     }
5988     */
5989
5990   }
5991
5992   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5993                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5994 }
5995
5996 void Impact(int x, int y)
5997 {
5998   boolean last_line = (y == lev_fieldy - 1);
5999   boolean object_hit = FALSE;
6000   boolean impact = (last_line || object_hit);
6001   int element = Feld[x][y];
6002   int smashed = EL_STEELWALL;
6003
6004   if (!last_line)       /* check if element below was hit */
6005   {
6006     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6007       return;
6008
6009     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6010                                          MovDir[x][y + 1] != MV_DOWN ||
6011                                          MovPos[x][y + 1] <= TILEY / 2));
6012
6013     /* do not smash moving elements that left the smashed field in time */
6014     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6015         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6016       object_hit = FALSE;
6017
6018 #if USE_QUICKSAND_IMPACT_BUGFIX
6019     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6020     {
6021       RemoveMovingField(x, y + 1);
6022       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6023       Feld[x][y + 2] = EL_ROCK;
6024       TEST_DrawLevelField(x, y + 2);
6025
6026       object_hit = TRUE;
6027     }
6028
6029     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6030     {
6031       RemoveMovingField(x, y + 1);
6032       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6033       Feld[x][y + 2] = EL_ROCK;
6034       TEST_DrawLevelField(x, y + 2);
6035
6036       object_hit = TRUE;
6037     }
6038 #endif
6039
6040     if (object_hit)
6041       smashed = MovingOrBlocked2Element(x, y + 1);
6042
6043     impact = (last_line || object_hit);
6044   }
6045
6046   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6047   {
6048     SplashAcid(x, y + 1);
6049     return;
6050   }
6051
6052   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6053   /* only reset graphic animation if graphic really changes after impact */
6054   if (impact &&
6055       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6056   {
6057     ResetGfxAnimation(x, y);
6058     TEST_DrawLevelField(x, y);
6059   }
6060
6061   if (impact && CAN_EXPLODE_IMPACT(element))
6062   {
6063     Bang(x, y);
6064     return;
6065   }
6066   else if (impact && element == EL_PEARL &&
6067            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6068   {
6069     ResetGfxAnimation(x, y);
6070
6071     Feld[x][y] = EL_PEARL_BREAKING;
6072     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6073     return;
6074   }
6075   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6076   {
6077     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6078
6079     return;
6080   }
6081
6082   if (impact && element == EL_AMOEBA_DROP)
6083   {
6084     if (object_hit && IS_PLAYER(x, y + 1))
6085       KillPlayerUnlessEnemyProtected(x, y + 1);
6086     else if (object_hit && smashed == EL_PENGUIN)
6087       Bang(x, y + 1);
6088     else
6089     {
6090       Feld[x][y] = EL_AMOEBA_GROWING;
6091       Store[x][y] = EL_AMOEBA_WET;
6092
6093       ResetRandomAnimationValue(x, y);
6094     }
6095     return;
6096   }
6097
6098   if (object_hit)               /* check which object was hit */
6099   {
6100     if ((CAN_PASS_MAGIC_WALL(element) && 
6101          (smashed == EL_MAGIC_WALL ||
6102           smashed == EL_BD_MAGIC_WALL)) ||
6103         (CAN_PASS_DC_MAGIC_WALL(element) &&
6104          smashed == EL_DC_MAGIC_WALL))
6105     {
6106       int xx, yy;
6107       int activated_magic_wall =
6108         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6109          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6110          EL_DC_MAGIC_WALL_ACTIVE);
6111
6112       /* activate magic wall / mill */
6113       SCAN_PLAYFIELD(xx, yy)
6114       {
6115         if (Feld[xx][yy] == smashed)
6116           Feld[xx][yy] = activated_magic_wall;
6117       }
6118
6119       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6120       game.magic_wall_active = TRUE;
6121
6122       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6123                             SND_MAGIC_WALL_ACTIVATING :
6124                             smashed == EL_BD_MAGIC_WALL ?
6125                             SND_BD_MAGIC_WALL_ACTIVATING :
6126                             SND_DC_MAGIC_WALL_ACTIVATING));
6127     }
6128
6129     if (IS_PLAYER(x, y + 1))
6130     {
6131       if (CAN_SMASH_PLAYER(element))
6132       {
6133         KillPlayerUnlessEnemyProtected(x, y + 1);
6134         return;
6135       }
6136     }
6137     else if (smashed == EL_PENGUIN)
6138     {
6139       if (CAN_SMASH_PLAYER(element))
6140       {
6141         Bang(x, y + 1);
6142         return;
6143       }
6144     }
6145     else if (element == EL_BD_DIAMOND)
6146     {
6147       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6148       {
6149         Bang(x, y + 1);
6150         return;
6151       }
6152     }
6153     else if (((element == EL_SP_INFOTRON ||
6154                element == EL_SP_ZONK) &&
6155               (smashed == EL_SP_SNIKSNAK ||
6156                smashed == EL_SP_ELECTRON ||
6157                smashed == EL_SP_DISK_ORANGE)) ||
6158              (element == EL_SP_INFOTRON &&
6159               smashed == EL_SP_DISK_YELLOW))
6160     {
6161       Bang(x, y + 1);
6162       return;
6163     }
6164     else if (CAN_SMASH_EVERYTHING(element))
6165     {
6166       if (IS_CLASSIC_ENEMY(smashed) ||
6167           CAN_EXPLODE_SMASHED(smashed))
6168       {
6169         Bang(x, y + 1);
6170         return;
6171       }
6172       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6173       {
6174         if (smashed == EL_LAMP ||
6175             smashed == EL_LAMP_ACTIVE)
6176         {
6177           Bang(x, y + 1);
6178           return;
6179         }
6180         else if (smashed == EL_NUT)
6181         {
6182           Feld[x][y + 1] = EL_NUT_BREAKING;
6183           PlayLevelSound(x, y, SND_NUT_BREAKING);
6184           RaiseScoreElement(EL_NUT);
6185           return;
6186         }
6187         else if (smashed == EL_PEARL)
6188         {
6189           ResetGfxAnimation(x, y);
6190
6191           Feld[x][y + 1] = EL_PEARL_BREAKING;
6192           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6193           return;
6194         }
6195         else if (smashed == EL_DIAMOND)
6196         {
6197           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6198           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6199           return;
6200         }
6201         else if (IS_BELT_SWITCH(smashed))
6202         {
6203           ToggleBeltSwitch(x, y + 1);
6204         }
6205         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6206                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6207                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6208                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6209         {
6210           ToggleSwitchgateSwitch(x, y + 1);
6211         }
6212         else if (smashed == EL_LIGHT_SWITCH ||
6213                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6214         {
6215           ToggleLightSwitch(x, y + 1);
6216         }
6217         else
6218         {
6219           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6220
6221           CheckElementChangeBySide(x, y + 1, smashed, element,
6222                                    CE_SWITCHED, CH_SIDE_TOP);
6223           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6224                                             CH_SIDE_TOP);
6225         }
6226       }
6227       else
6228       {
6229         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6230       }
6231     }
6232   }
6233
6234   /* play sound of magic wall / mill */
6235   if (!last_line &&
6236       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6237        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6238        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6239   {
6240     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6241       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6242     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6243       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6244     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6245       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6246
6247     return;
6248   }
6249
6250   /* play sound of object that hits the ground */
6251   if (last_line || object_hit)
6252     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6253 }
6254
6255 inline static void TurnRoundExt(int x, int y)
6256 {
6257   static struct
6258   {
6259     int dx, dy;
6260   } move_xy[] =
6261   {
6262     {  0,  0 },
6263     { -1,  0 },
6264     { +1,  0 },
6265     {  0,  0 },
6266     {  0, -1 },
6267     {  0,  0 }, { 0, 0 }, { 0, 0 },
6268     {  0, +1 }
6269   };
6270   static struct
6271   {
6272     int left, right, back;
6273   } turn[] =
6274   {
6275     { 0,        0,              0        },
6276     { MV_DOWN,  MV_UP,          MV_RIGHT },
6277     { MV_UP,    MV_DOWN,        MV_LEFT  },
6278     { 0,        0,              0        },
6279     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6280     { 0,        0,              0        },
6281     { 0,        0,              0        },
6282     { 0,        0,              0        },
6283     { MV_RIGHT, MV_LEFT,        MV_UP    }
6284   };
6285
6286   int element = Feld[x][y];
6287   int move_pattern = element_info[element].move_pattern;
6288
6289   int old_move_dir = MovDir[x][y];
6290   int left_dir  = turn[old_move_dir].left;
6291   int right_dir = turn[old_move_dir].right;
6292   int back_dir  = turn[old_move_dir].back;
6293
6294   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6295   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6296   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6297   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6298
6299   int left_x  = x + left_dx,  left_y  = y + left_dy;
6300   int right_x = x + right_dx, right_y = y + right_dy;
6301   int move_x  = x + move_dx,  move_y  = y + move_dy;
6302
6303   int xx, yy;
6304
6305   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6306   {
6307     TestIfBadThingTouchesOtherBadThing(x, y);
6308
6309     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6310       MovDir[x][y] = right_dir;
6311     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6312       MovDir[x][y] = left_dir;
6313
6314     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6315       MovDelay[x][y] = 9;
6316     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6317       MovDelay[x][y] = 1;
6318   }
6319   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6320   {
6321     TestIfBadThingTouchesOtherBadThing(x, y);
6322
6323     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6324       MovDir[x][y] = left_dir;
6325     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6326       MovDir[x][y] = right_dir;
6327
6328     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6329       MovDelay[x][y] = 9;
6330     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6331       MovDelay[x][y] = 1;
6332   }
6333   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6334   {
6335     TestIfBadThingTouchesOtherBadThing(x, y);
6336
6337     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6338       MovDir[x][y] = left_dir;
6339     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6340       MovDir[x][y] = right_dir;
6341
6342     if (MovDir[x][y] != old_move_dir)
6343       MovDelay[x][y] = 9;
6344   }
6345   else if (element == EL_YAMYAM)
6346   {
6347     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6348     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6349
6350     if (can_turn_left && can_turn_right)
6351       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6352     else if (can_turn_left)
6353       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6354     else if (can_turn_right)
6355       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6356     else
6357       MovDir[x][y] = back_dir;
6358
6359     MovDelay[x][y] = 16 + 16 * RND(3);
6360   }
6361   else if (element == EL_DARK_YAMYAM)
6362   {
6363     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6364                                                          left_x, left_y);
6365     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6366                                                          right_x, right_y);
6367
6368     if (can_turn_left && can_turn_right)
6369       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6370     else if (can_turn_left)
6371       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6372     else if (can_turn_right)
6373       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6374     else
6375       MovDir[x][y] = back_dir;
6376
6377     MovDelay[x][y] = 16 + 16 * RND(3);
6378   }
6379   else if (element == EL_PACMAN)
6380   {
6381     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6382     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6383
6384     if (can_turn_left && can_turn_right)
6385       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6386     else if (can_turn_left)
6387       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6388     else if (can_turn_right)
6389       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6390     else
6391       MovDir[x][y] = back_dir;
6392
6393     MovDelay[x][y] = 6 + RND(40);
6394   }
6395   else if (element == EL_PIG)
6396   {
6397     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6398     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6399     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6400     boolean should_turn_left, should_turn_right, should_move_on;
6401     int rnd_value = 24;
6402     int rnd = RND(rnd_value);
6403
6404     should_turn_left = (can_turn_left &&
6405                         (!can_move_on ||
6406                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6407                                                    y + back_dy + left_dy)));
6408     should_turn_right = (can_turn_right &&
6409                          (!can_move_on ||
6410                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6411                                                     y + back_dy + right_dy)));
6412     should_move_on = (can_move_on &&
6413                       (!can_turn_left ||
6414                        !can_turn_right ||
6415                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6416                                                  y + move_dy + left_dy) ||
6417                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6418                                                  y + move_dy + right_dy)));
6419
6420     if (should_turn_left || should_turn_right || should_move_on)
6421     {
6422       if (should_turn_left && should_turn_right && should_move_on)
6423         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6424                         rnd < 2 * rnd_value / 3 ? right_dir :
6425                         old_move_dir);
6426       else if (should_turn_left && should_turn_right)
6427         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6428       else if (should_turn_left && should_move_on)
6429         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6430       else if (should_turn_right && should_move_on)
6431         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6432       else if (should_turn_left)
6433         MovDir[x][y] = left_dir;
6434       else if (should_turn_right)
6435         MovDir[x][y] = right_dir;
6436       else if (should_move_on)
6437         MovDir[x][y] = old_move_dir;
6438     }
6439     else if (can_move_on && rnd > rnd_value / 8)
6440       MovDir[x][y] = old_move_dir;
6441     else if (can_turn_left && can_turn_right)
6442       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6443     else if (can_turn_left && rnd > rnd_value / 8)
6444       MovDir[x][y] = left_dir;
6445     else if (can_turn_right && rnd > rnd_value/8)
6446       MovDir[x][y] = right_dir;
6447     else
6448       MovDir[x][y] = back_dir;
6449
6450     xx = x + move_xy[MovDir[x][y]].dx;
6451     yy = y + move_xy[MovDir[x][y]].dy;
6452
6453     if (!IN_LEV_FIELD(xx, yy) ||
6454         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6455       MovDir[x][y] = old_move_dir;
6456
6457     MovDelay[x][y] = 0;
6458   }
6459   else if (element == EL_DRAGON)
6460   {
6461     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6462     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6463     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6464     int rnd_value = 24;
6465     int rnd = RND(rnd_value);
6466
6467     if (can_move_on && rnd > rnd_value / 8)
6468       MovDir[x][y] = old_move_dir;
6469     else if (can_turn_left && can_turn_right)
6470       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6471     else if (can_turn_left && rnd > rnd_value / 8)
6472       MovDir[x][y] = left_dir;
6473     else if (can_turn_right && rnd > rnd_value / 8)
6474       MovDir[x][y] = right_dir;
6475     else
6476       MovDir[x][y] = back_dir;
6477
6478     xx = x + move_xy[MovDir[x][y]].dx;
6479     yy = y + move_xy[MovDir[x][y]].dy;
6480
6481     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6482       MovDir[x][y] = old_move_dir;
6483
6484     MovDelay[x][y] = 0;
6485   }
6486   else if (element == EL_MOLE)
6487   {
6488     boolean can_move_on =
6489       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6490                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6491                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6492     if (!can_move_on)
6493     {
6494       boolean can_turn_left =
6495         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6496                               IS_AMOEBOID(Feld[left_x][left_y])));
6497
6498       boolean can_turn_right =
6499         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6500                               IS_AMOEBOID(Feld[right_x][right_y])));
6501
6502       if (can_turn_left && can_turn_right)
6503         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6504       else if (can_turn_left)
6505         MovDir[x][y] = left_dir;
6506       else
6507         MovDir[x][y] = right_dir;
6508     }
6509
6510     if (MovDir[x][y] != old_move_dir)
6511       MovDelay[x][y] = 9;
6512   }
6513   else if (element == EL_BALLOON)
6514   {
6515     MovDir[x][y] = game.wind_direction;
6516     MovDelay[x][y] = 0;
6517   }
6518   else if (element == EL_SPRING)
6519   {
6520     if (MovDir[x][y] & MV_HORIZONTAL)
6521     {
6522       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6523           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6524       {
6525         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6526         ResetGfxAnimation(move_x, move_y);
6527         TEST_DrawLevelField(move_x, move_y);
6528
6529         MovDir[x][y] = back_dir;
6530       }
6531       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6532                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6533         MovDir[x][y] = MV_NONE;
6534     }
6535
6536     MovDelay[x][y] = 0;
6537   }
6538   else if (element == EL_ROBOT ||
6539            element == EL_SATELLITE ||
6540            element == EL_PENGUIN ||
6541            element == EL_EMC_ANDROID)
6542   {
6543     int attr_x = -1, attr_y = -1;
6544
6545     if (AllPlayersGone)
6546     {
6547       attr_x = ExitX;
6548       attr_y = ExitY;
6549     }
6550     else
6551     {
6552       int i;
6553
6554       for (i = 0; i < MAX_PLAYERS; i++)
6555       {
6556         struct PlayerInfo *player = &stored_player[i];
6557         int jx = player->jx, jy = player->jy;
6558
6559         if (!player->active)
6560           continue;
6561
6562         if (attr_x == -1 ||
6563             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6564         {
6565           attr_x = jx;
6566           attr_y = jy;
6567         }
6568       }
6569     }
6570
6571     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6572         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6573          game.engine_version < VERSION_IDENT(3,1,0,0)))
6574     {
6575       attr_x = ZX;
6576       attr_y = ZY;
6577     }
6578
6579     if (element == EL_PENGUIN)
6580     {
6581       int i;
6582       static int xy[4][2] =
6583       {
6584         { 0, -1 },
6585         { -1, 0 },
6586         { +1, 0 },
6587         { 0, +1 }
6588       };
6589
6590       for (i = 0; i < NUM_DIRECTIONS; i++)
6591       {
6592         int ex = x + xy[i][0];
6593         int ey = y + xy[i][1];
6594
6595         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6596                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6597                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6598                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6599         {
6600           attr_x = ex;
6601           attr_y = ey;
6602           break;
6603         }
6604       }
6605     }
6606
6607     MovDir[x][y] = MV_NONE;
6608     if (attr_x < x)
6609       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6610     else if (attr_x > x)
6611       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6612     if (attr_y < y)
6613       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6614     else if (attr_y > y)
6615       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6616
6617     if (element == EL_ROBOT)
6618     {
6619       int newx, newy;
6620
6621       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6622         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6623       Moving2Blocked(x, y, &newx, &newy);
6624
6625       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6626         MovDelay[x][y] = 8 + 8 * !RND(3);
6627       else
6628         MovDelay[x][y] = 16;
6629     }
6630     else if (element == EL_PENGUIN)
6631     {
6632       int newx, newy;
6633
6634       MovDelay[x][y] = 1;
6635
6636       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6637       {
6638         boolean first_horiz = RND(2);
6639         int new_move_dir = MovDir[x][y];
6640
6641         MovDir[x][y] =
6642           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6643         Moving2Blocked(x, y, &newx, &newy);
6644
6645         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6646           return;
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] = old_move_dir;
6656         return;
6657       }
6658     }
6659     else if (element == EL_SATELLITE)
6660     {
6661       int newx, newy;
6662
6663       MovDelay[x][y] = 1;
6664
6665       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6666       {
6667         boolean first_horiz = RND(2);
6668         int new_move_dir = MovDir[x][y];
6669
6670         MovDir[x][y] =
6671           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6672         Moving2Blocked(x, y, &newx, &newy);
6673
6674         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6675           return;
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] = old_move_dir;
6685         return;
6686       }
6687     }
6688     else if (element == EL_EMC_ANDROID)
6689     {
6690       static int check_pos[16] =
6691       {
6692         -1,             /*  0 => (invalid)          */
6693         7,              /*  1 => MV_LEFT            */
6694         3,              /*  2 => MV_RIGHT           */
6695         -1,             /*  3 => (invalid)          */
6696         1,              /*  4 =>            MV_UP   */
6697         0,              /*  5 => MV_LEFT  | MV_UP   */
6698         2,              /*  6 => MV_RIGHT | MV_UP   */
6699         -1,             /*  7 => (invalid)          */
6700         5,              /*  8 =>            MV_DOWN */
6701         6,              /*  9 => MV_LEFT  | MV_DOWN */
6702         4,              /* 10 => MV_RIGHT | MV_DOWN */
6703         -1,             /* 11 => (invalid)          */
6704         -1,             /* 12 => (invalid)          */
6705         -1,             /* 13 => (invalid)          */
6706         -1,             /* 14 => (invalid)          */
6707         -1,             /* 15 => (invalid)          */
6708       };
6709       static struct
6710       {
6711         int dx, dy;
6712         int dir;
6713       } check_xy[8] =
6714       {
6715         { -1, -1,       MV_LEFT  | MV_UP   },
6716         {  0, -1,                  MV_UP   },
6717         { +1, -1,       MV_RIGHT | MV_UP   },
6718         { +1,  0,       MV_RIGHT           },
6719         { +1, +1,       MV_RIGHT | MV_DOWN },
6720         {  0, +1,                  MV_DOWN },
6721         { -1, +1,       MV_LEFT  | MV_DOWN },
6722         { -1,  0,       MV_LEFT            },
6723       };
6724       int start_pos, check_order;
6725       boolean can_clone = FALSE;
6726       int i;
6727
6728       /* check if there is any free field around current position */
6729       for (i = 0; i < 8; i++)
6730       {
6731         int newx = x + check_xy[i].dx;
6732         int newy = y + check_xy[i].dy;
6733
6734         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6735         {
6736           can_clone = TRUE;
6737
6738           break;
6739         }
6740       }
6741
6742       if (can_clone)            /* randomly find an element to clone */
6743       {
6744         can_clone = FALSE;
6745
6746         start_pos = check_pos[RND(8)];
6747         check_order = (RND(2) ? -1 : +1);
6748
6749         for (i = 0; i < 8; i++)
6750         {
6751           int pos_raw = start_pos + i * check_order;
6752           int pos = (pos_raw + 8) % 8;
6753           int newx = x + check_xy[pos].dx;
6754           int newy = y + check_xy[pos].dy;
6755
6756           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6757           {
6758             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6759             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6760
6761             Store[x][y] = Feld[newx][newy];
6762
6763             can_clone = TRUE;
6764
6765             break;
6766           }
6767         }
6768       }
6769
6770       if (can_clone)            /* randomly find a direction to move */
6771       {
6772         can_clone = FALSE;
6773
6774         start_pos = check_pos[RND(8)];
6775         check_order = (RND(2) ? -1 : +1);
6776
6777         for (i = 0; i < 8; i++)
6778         {
6779           int pos_raw = start_pos + i * check_order;
6780           int pos = (pos_raw + 8) % 8;
6781           int newx = x + check_xy[pos].dx;
6782           int newy = y + check_xy[pos].dy;
6783           int new_move_dir = check_xy[pos].dir;
6784
6785           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6786           {
6787             MovDir[x][y] = new_move_dir;
6788             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6789
6790             can_clone = TRUE;
6791
6792             break;
6793           }
6794         }
6795       }
6796
6797       if (can_clone)            /* cloning and moving successful */
6798         return;
6799
6800       /* cannot clone -- try to move towards player */
6801
6802       start_pos = check_pos[MovDir[x][y] & 0x0f];
6803       check_order = (RND(2) ? -1 : +1);
6804
6805       for (i = 0; i < 3; i++)
6806       {
6807         /* first check start_pos, then previous/next or (next/previous) pos */
6808         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6809         int pos = (pos_raw + 8) % 8;
6810         int newx = x + check_xy[pos].dx;
6811         int newy = y + check_xy[pos].dy;
6812         int new_move_dir = check_xy[pos].dir;
6813
6814         if (IS_PLAYER(newx, newy))
6815           break;
6816
6817         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6818         {
6819           MovDir[x][y] = new_move_dir;
6820           MovDelay[x][y] = level.android_move_time * 8 + 1;
6821
6822           break;
6823         }
6824       }
6825     }
6826   }
6827   else if (move_pattern == MV_TURNING_LEFT ||
6828            move_pattern == MV_TURNING_RIGHT ||
6829            move_pattern == MV_TURNING_LEFT_RIGHT ||
6830            move_pattern == MV_TURNING_RIGHT_LEFT ||
6831            move_pattern == MV_TURNING_RANDOM ||
6832            move_pattern == MV_ALL_DIRECTIONS)
6833   {
6834     boolean can_turn_left =
6835       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6836     boolean can_turn_right =
6837       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6838
6839     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6840       return;
6841
6842     if (move_pattern == MV_TURNING_LEFT)
6843       MovDir[x][y] = left_dir;
6844     else if (move_pattern == MV_TURNING_RIGHT)
6845       MovDir[x][y] = right_dir;
6846     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6847       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6848     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6849       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6850     else if (move_pattern == MV_TURNING_RANDOM)
6851       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6852                       can_turn_right && !can_turn_left ? right_dir :
6853                       RND(2) ? left_dir : right_dir);
6854     else if (can_turn_left && can_turn_right)
6855       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6856     else if (can_turn_left)
6857       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6858     else if (can_turn_right)
6859       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6860     else
6861       MovDir[x][y] = back_dir;
6862
6863     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6864   }
6865   else if (move_pattern == MV_HORIZONTAL ||
6866            move_pattern == MV_VERTICAL)
6867   {
6868     if (move_pattern & old_move_dir)
6869       MovDir[x][y] = back_dir;
6870     else if (move_pattern == MV_HORIZONTAL)
6871       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6872     else if (move_pattern == MV_VERTICAL)
6873       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6874
6875     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6876   }
6877   else if (move_pattern & MV_ANY_DIRECTION)
6878   {
6879     MovDir[x][y] = move_pattern;
6880     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6881   }
6882   else if (move_pattern & MV_WIND_DIRECTION)
6883   {
6884     MovDir[x][y] = game.wind_direction;
6885     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6886   }
6887   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6888   {
6889     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6890       MovDir[x][y] = left_dir;
6891     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6892       MovDir[x][y] = right_dir;
6893
6894     if (MovDir[x][y] != old_move_dir)
6895       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6896   }
6897   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6898   {
6899     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6900       MovDir[x][y] = right_dir;
6901     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6902       MovDir[x][y] = left_dir;
6903
6904     if (MovDir[x][y] != old_move_dir)
6905       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6906   }
6907   else if (move_pattern == MV_TOWARDS_PLAYER ||
6908            move_pattern == MV_AWAY_FROM_PLAYER)
6909   {
6910     int attr_x = -1, attr_y = -1;
6911     int newx, newy;
6912     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6913
6914     if (AllPlayersGone)
6915     {
6916       attr_x = ExitX;
6917       attr_y = ExitY;
6918     }
6919     else
6920     {
6921       int i;
6922
6923       for (i = 0; i < MAX_PLAYERS; i++)
6924       {
6925         struct PlayerInfo *player = &stored_player[i];
6926         int jx = player->jx, jy = player->jy;
6927
6928         if (!player->active)
6929           continue;
6930
6931         if (attr_x == -1 ||
6932             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6933         {
6934           attr_x = jx;
6935           attr_y = jy;
6936         }
6937       }
6938     }
6939
6940     MovDir[x][y] = MV_NONE;
6941     if (attr_x < x)
6942       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6943     else if (attr_x > x)
6944       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6945     if (attr_y < y)
6946       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6947     else if (attr_y > y)
6948       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6949
6950     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6951
6952     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6953     {
6954       boolean first_horiz = RND(2);
6955       int new_move_dir = MovDir[x][y];
6956
6957       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6958       {
6959         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6960         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6961
6962         return;
6963       }
6964
6965       MovDir[x][y] =
6966         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6967       Moving2Blocked(x, y, &newx, &newy);
6968
6969       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6970         return;
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] = old_move_dir;
6980     }
6981   }
6982   else if (move_pattern == MV_WHEN_PUSHED ||
6983            move_pattern == MV_WHEN_DROPPED)
6984   {
6985     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6986       MovDir[x][y] = MV_NONE;
6987
6988     MovDelay[x][y] = 0;
6989   }
6990   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6991   {
6992     static int test_xy[7][2] =
6993     {
6994       { 0, -1 },
6995       { -1, 0 },
6996       { +1, 0 },
6997       { 0, +1 },
6998       { 0, -1 },
6999       { -1, 0 },
7000       { +1, 0 },
7001     };
7002     static int test_dir[7] =
7003     {
7004       MV_UP,
7005       MV_LEFT,
7006       MV_RIGHT,
7007       MV_DOWN,
7008       MV_UP,
7009       MV_LEFT,
7010       MV_RIGHT,
7011     };
7012     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7013     int move_preference = -1000000;     /* start with very low preference */
7014     int new_move_dir = MV_NONE;
7015     int start_test = RND(4);
7016     int i;
7017
7018     for (i = 0; i < NUM_DIRECTIONS; i++)
7019     {
7020       int move_dir = test_dir[start_test + i];
7021       int move_dir_preference;
7022
7023       xx = x + test_xy[start_test + i][0];
7024       yy = y + test_xy[start_test + i][1];
7025
7026       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7027           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7028       {
7029         new_move_dir = move_dir;
7030
7031         break;
7032       }
7033
7034       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7035         continue;
7036
7037       move_dir_preference = -1 * RunnerVisit[xx][yy];
7038       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7039         move_dir_preference = PlayerVisit[xx][yy];
7040
7041       if (move_dir_preference > move_preference)
7042       {
7043         /* prefer field that has not been visited for the longest time */
7044         move_preference = move_dir_preference;
7045         new_move_dir = move_dir;
7046       }
7047       else if (move_dir_preference == move_preference &&
7048                move_dir == old_move_dir)
7049       {
7050         /* prefer last direction when all directions are preferred equally */
7051         move_preference = move_dir_preference;
7052         new_move_dir = move_dir;
7053       }
7054     }
7055
7056     MovDir[x][y] = new_move_dir;
7057     if (old_move_dir != new_move_dir)
7058       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7059   }
7060 }
7061
7062 static void TurnRound(int x, int y)
7063 {
7064   int direction = MovDir[x][y];
7065
7066   TurnRoundExt(x, y);
7067
7068   GfxDir[x][y] = MovDir[x][y];
7069
7070   if (direction != MovDir[x][y])
7071     GfxFrame[x][y] = 0;
7072
7073   if (MovDelay[x][y])
7074     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7075
7076   ResetGfxFrame(x, y);
7077 }
7078
7079 static boolean JustBeingPushed(int x, int y)
7080 {
7081   int i;
7082
7083   for (i = 0; i < MAX_PLAYERS; i++)
7084   {
7085     struct PlayerInfo *player = &stored_player[i];
7086
7087     if (player->active && player->is_pushing && player->MovPos)
7088     {
7089       int next_jx = player->jx + (player->jx - player->last_jx);
7090       int next_jy = player->jy + (player->jy - player->last_jy);
7091
7092       if (x == next_jx && y == next_jy)
7093         return TRUE;
7094     }
7095   }
7096
7097   return FALSE;
7098 }
7099
7100 void StartMoving(int x, int y)
7101 {
7102   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7103   int element = Feld[x][y];
7104
7105   if (Stop[x][y])
7106     return;
7107
7108   if (MovDelay[x][y] == 0)
7109     GfxAction[x][y] = ACTION_DEFAULT;
7110
7111   if (CAN_FALL(element) && y < lev_fieldy - 1)
7112   {
7113     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7114         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7115       if (JustBeingPushed(x, y))
7116         return;
7117
7118     if (element == EL_QUICKSAND_FULL)
7119     {
7120       if (IS_FREE(x, y + 1))
7121       {
7122         InitMovingField(x, y, MV_DOWN);
7123         started_moving = TRUE;
7124
7125         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7126 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7127         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7128           Store[x][y] = EL_ROCK;
7129 #else
7130         Store[x][y] = EL_ROCK;
7131 #endif
7132
7133         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7134       }
7135       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7136       {
7137         if (!MovDelay[x][y])
7138         {
7139           MovDelay[x][y] = TILEY + 1;
7140
7141           ResetGfxAnimation(x, y);
7142           ResetGfxAnimation(x, y + 1);
7143         }
7144
7145         if (MovDelay[x][y])
7146         {
7147           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7148           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7149
7150           MovDelay[x][y]--;
7151           if (MovDelay[x][y])
7152             return;
7153         }
7154
7155         Feld[x][y] = EL_QUICKSAND_EMPTY;
7156         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7157         Store[x][y + 1] = Store[x][y];
7158         Store[x][y] = 0;
7159
7160         PlayLevelSoundAction(x, y, ACTION_FILLING);
7161       }
7162       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7163       {
7164         if (!MovDelay[x][y])
7165         {
7166           MovDelay[x][y] = TILEY + 1;
7167
7168           ResetGfxAnimation(x, y);
7169           ResetGfxAnimation(x, y + 1);
7170         }
7171
7172         if (MovDelay[x][y])
7173         {
7174           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7175           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7176
7177           MovDelay[x][y]--;
7178           if (MovDelay[x][y])
7179             return;
7180         }
7181
7182         Feld[x][y] = EL_QUICKSAND_EMPTY;
7183         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7184         Store[x][y + 1] = Store[x][y];
7185         Store[x][y] = 0;
7186
7187         PlayLevelSoundAction(x, y, ACTION_FILLING);
7188       }
7189     }
7190     else if (element == EL_QUICKSAND_FAST_FULL)
7191     {
7192       if (IS_FREE(x, y + 1))
7193       {
7194         InitMovingField(x, y, MV_DOWN);
7195         started_moving = TRUE;
7196
7197         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7198 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7199         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7200           Store[x][y] = EL_ROCK;
7201 #else
7202         Store[x][y] = EL_ROCK;
7203 #endif
7204
7205         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7206       }
7207       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7208       {
7209         if (!MovDelay[x][y])
7210         {
7211           MovDelay[x][y] = TILEY + 1;
7212
7213           ResetGfxAnimation(x, y);
7214           ResetGfxAnimation(x, y + 1);
7215         }
7216
7217         if (MovDelay[x][y])
7218         {
7219           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7220           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7221
7222           MovDelay[x][y]--;
7223           if (MovDelay[x][y])
7224             return;
7225         }
7226
7227         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7228         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7229         Store[x][y + 1] = Store[x][y];
7230         Store[x][y] = 0;
7231
7232         PlayLevelSoundAction(x, y, ACTION_FILLING);
7233       }
7234       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7235       {
7236         if (!MovDelay[x][y])
7237         {
7238           MovDelay[x][y] = TILEY + 1;
7239
7240           ResetGfxAnimation(x, y);
7241           ResetGfxAnimation(x, y + 1);
7242         }
7243
7244         if (MovDelay[x][y])
7245         {
7246           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7247           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7248
7249           MovDelay[x][y]--;
7250           if (MovDelay[x][y])
7251             return;
7252         }
7253
7254         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7255         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7256         Store[x][y + 1] = Store[x][y];
7257         Store[x][y] = 0;
7258
7259         PlayLevelSoundAction(x, y, ACTION_FILLING);
7260       }
7261     }
7262     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7263              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7264     {
7265       InitMovingField(x, y, MV_DOWN);
7266       started_moving = TRUE;
7267
7268       Feld[x][y] = EL_QUICKSAND_FILLING;
7269       Store[x][y] = element;
7270
7271       PlayLevelSoundAction(x, y, ACTION_FILLING);
7272     }
7273     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7274              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7275     {
7276       InitMovingField(x, y, MV_DOWN);
7277       started_moving = TRUE;
7278
7279       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7280       Store[x][y] = element;
7281
7282       PlayLevelSoundAction(x, y, ACTION_FILLING);
7283     }
7284     else if (element == EL_MAGIC_WALL_FULL)
7285     {
7286       if (IS_FREE(x, y + 1))
7287       {
7288         InitMovingField(x, y, MV_DOWN);
7289         started_moving = TRUE;
7290
7291         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7292         Store[x][y] = EL_CHANGED(Store[x][y]);
7293       }
7294       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7295       {
7296         if (!MovDelay[x][y])
7297           MovDelay[x][y] = TILEY / 4 + 1;
7298
7299         if (MovDelay[x][y])
7300         {
7301           MovDelay[x][y]--;
7302           if (MovDelay[x][y])
7303             return;
7304         }
7305
7306         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7307         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7308         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7309         Store[x][y] = 0;
7310       }
7311     }
7312     else if (element == EL_BD_MAGIC_WALL_FULL)
7313     {
7314       if (IS_FREE(x, y + 1))
7315       {
7316         InitMovingField(x, y, MV_DOWN);
7317         started_moving = TRUE;
7318
7319         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7320         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7321       }
7322       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7323       {
7324         if (!MovDelay[x][y])
7325           MovDelay[x][y] = TILEY / 4 + 1;
7326
7327         if (MovDelay[x][y])
7328         {
7329           MovDelay[x][y]--;
7330           if (MovDelay[x][y])
7331             return;
7332         }
7333
7334         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7335         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7336         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7337         Store[x][y] = 0;
7338       }
7339     }
7340     else if (element == EL_DC_MAGIC_WALL_FULL)
7341     {
7342       if (IS_FREE(x, y + 1))
7343       {
7344         InitMovingField(x, y, MV_DOWN);
7345         started_moving = TRUE;
7346
7347         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7348         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7349       }
7350       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7351       {
7352         if (!MovDelay[x][y])
7353           MovDelay[x][y] = TILEY / 4 + 1;
7354
7355         if (MovDelay[x][y])
7356         {
7357           MovDelay[x][y]--;
7358           if (MovDelay[x][y])
7359             return;
7360         }
7361
7362         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7363         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7364         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7365         Store[x][y] = 0;
7366       }
7367     }
7368     else if ((CAN_PASS_MAGIC_WALL(element) &&
7369               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7370                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7371              (CAN_PASS_DC_MAGIC_WALL(element) &&
7372               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7373
7374     {
7375       InitMovingField(x, y, MV_DOWN);
7376       started_moving = TRUE;
7377
7378       Feld[x][y] =
7379         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7380          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7381          EL_DC_MAGIC_WALL_FILLING);
7382       Store[x][y] = element;
7383     }
7384     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7385     {
7386       SplashAcid(x, y + 1);
7387
7388       InitMovingField(x, y, MV_DOWN);
7389       started_moving = TRUE;
7390
7391       Store[x][y] = EL_ACID;
7392     }
7393     else if (
7394              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7395               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7396              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7397               CAN_FALL(element) && WasJustFalling[x][y] &&
7398               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7399
7400              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7401               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7402               (Feld[x][y + 1] == EL_BLOCKED)))
7403     {
7404       /* this is needed for a special case not covered by calling "Impact()"
7405          from "ContinueMoving()": if an element moves to a tile directly below
7406          another element which was just falling on that tile (which was empty
7407          in the previous frame), the falling element above would just stop
7408          instead of smashing the element below (in previous version, the above
7409          element was just checked for "moving" instead of "falling", resulting
7410          in incorrect smashes caused by horizontal movement of the above
7411          element; also, the case of the player being the element to smash was
7412          simply not covered here... :-/ ) */
7413
7414       CheckCollision[x][y] = 0;
7415       CheckImpact[x][y] = 0;
7416
7417       Impact(x, y);
7418     }
7419     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7420     {
7421       if (MovDir[x][y] == MV_NONE)
7422       {
7423         InitMovingField(x, y, MV_DOWN);
7424         started_moving = TRUE;
7425       }
7426     }
7427     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7428     {
7429       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7430         MovDir[x][y] = MV_DOWN;
7431
7432       InitMovingField(x, y, MV_DOWN);
7433       started_moving = TRUE;
7434     }
7435     else if (element == EL_AMOEBA_DROP)
7436     {
7437       Feld[x][y] = EL_AMOEBA_GROWING;
7438       Store[x][y] = EL_AMOEBA_WET;
7439     }
7440     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7441               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7442              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7443              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7444     {
7445       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7446                                 (IS_FREE(x - 1, y + 1) ||
7447                                  Feld[x - 1][y + 1] == EL_ACID));
7448       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7449                                 (IS_FREE(x + 1, y + 1) ||
7450                                  Feld[x + 1][y + 1] == EL_ACID));
7451       boolean can_fall_any  = (can_fall_left || can_fall_right);
7452       boolean can_fall_both = (can_fall_left && can_fall_right);
7453       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7454
7455       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7456       {
7457         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7458           can_fall_right = FALSE;
7459         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7460           can_fall_left = FALSE;
7461         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7462           can_fall_right = FALSE;
7463         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7464           can_fall_left = FALSE;
7465
7466         can_fall_any  = (can_fall_left || can_fall_right);
7467         can_fall_both = FALSE;
7468       }
7469
7470       if (can_fall_both)
7471       {
7472         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7473           can_fall_right = FALSE;       /* slip down on left side */
7474         else
7475           can_fall_left = !(can_fall_right = RND(2));
7476
7477         can_fall_both = FALSE;
7478       }
7479
7480       if (can_fall_any)
7481       {
7482         /* if not determined otherwise, prefer left side for slipping down */
7483         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7484         started_moving = TRUE;
7485       }
7486     }
7487     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7488     {
7489       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7490       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7491       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7492       int belt_dir = game.belt_dir[belt_nr];
7493
7494       if ((belt_dir == MV_LEFT  && left_is_free) ||
7495           (belt_dir == MV_RIGHT && right_is_free))
7496       {
7497         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7498
7499         InitMovingField(x, y, belt_dir);
7500         started_moving = TRUE;
7501
7502         Pushed[x][y] = TRUE;
7503         Pushed[nextx][y] = TRUE;
7504
7505         GfxAction[x][y] = ACTION_DEFAULT;
7506       }
7507       else
7508       {
7509         MovDir[x][y] = 0;       /* if element was moving, stop it */
7510       }
7511     }
7512   }
7513
7514   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7515   if (CAN_MOVE(element) && !started_moving)
7516   {
7517     int move_pattern = element_info[element].move_pattern;
7518     int newx, newy;
7519
7520     Moving2Blocked(x, y, &newx, &newy);
7521
7522     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7523       return;
7524
7525     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7526         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7527     {
7528       WasJustMoving[x][y] = 0;
7529       CheckCollision[x][y] = 0;
7530
7531       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7532
7533       if (Feld[x][y] != element)        /* element has changed */
7534         return;
7535     }
7536
7537     if (!MovDelay[x][y])        /* start new movement phase */
7538     {
7539       /* all objects that can change their move direction after each step
7540          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7541
7542       if (element != EL_YAMYAM &&
7543           element != EL_DARK_YAMYAM &&
7544           element != EL_PACMAN &&
7545           !(move_pattern & MV_ANY_DIRECTION) &&
7546           move_pattern != MV_TURNING_LEFT &&
7547           move_pattern != MV_TURNING_RIGHT &&
7548           move_pattern != MV_TURNING_LEFT_RIGHT &&
7549           move_pattern != MV_TURNING_RIGHT_LEFT &&
7550           move_pattern != MV_TURNING_RANDOM)
7551       {
7552         TurnRound(x, y);
7553
7554         if (MovDelay[x][y] && (element == EL_BUG ||
7555                                element == EL_SPACESHIP ||
7556                                element == EL_SP_SNIKSNAK ||
7557                                element == EL_SP_ELECTRON ||
7558                                element == EL_MOLE))
7559           TEST_DrawLevelField(x, y);
7560       }
7561     }
7562
7563     if (MovDelay[x][y])         /* wait some time before next movement */
7564     {
7565       MovDelay[x][y]--;
7566
7567       if (element == EL_ROBOT ||
7568           element == EL_YAMYAM ||
7569           element == EL_DARK_YAMYAM)
7570       {
7571         DrawLevelElementAnimationIfNeeded(x, y, element);
7572         PlayLevelSoundAction(x, y, ACTION_WAITING);
7573       }
7574       else if (element == EL_SP_ELECTRON)
7575         DrawLevelElementAnimationIfNeeded(x, y, element);
7576       else if (element == EL_DRAGON)
7577       {
7578         int i;
7579         int dir = MovDir[x][y];
7580         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7581         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7582         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7583                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7584                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7585                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7586         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7587
7588         GfxAction[x][y] = ACTION_ATTACKING;
7589
7590         if (IS_PLAYER(x, y))
7591           DrawPlayerField(x, y);
7592         else
7593           TEST_DrawLevelField(x, y);
7594
7595         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7596
7597         for (i = 1; i <= 3; i++)
7598         {
7599           int xx = x + i * dx;
7600           int yy = y + i * dy;
7601           int sx = SCREENX(xx);
7602           int sy = SCREENY(yy);
7603           int flame_graphic = graphic + (i - 1);
7604
7605           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7606             break;
7607
7608           if (MovDelay[x][y])
7609           {
7610             int flamed = MovingOrBlocked2Element(xx, yy);
7611
7612             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7613               Bang(xx, yy);
7614             else
7615               RemoveMovingField(xx, yy);
7616
7617             ChangeDelay[xx][yy] = 0;
7618
7619             Feld[xx][yy] = EL_FLAMES;
7620
7621             if (IN_SCR_FIELD(sx, sy))
7622             {
7623               TEST_DrawLevelFieldCrumbled(xx, yy);
7624               DrawGraphic(sx, sy, flame_graphic, frame);
7625             }
7626           }
7627           else
7628           {
7629             if (Feld[xx][yy] == EL_FLAMES)
7630               Feld[xx][yy] = EL_EMPTY;
7631             TEST_DrawLevelField(xx, yy);
7632           }
7633         }
7634       }
7635
7636       if (MovDelay[x][y])       /* element still has to wait some time */
7637       {
7638         PlayLevelSoundAction(x, y, ACTION_WAITING);
7639
7640         return;
7641       }
7642     }
7643
7644     /* now make next step */
7645
7646     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7647
7648     if (DONT_COLLIDE_WITH(element) &&
7649         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7650         !PLAYER_ENEMY_PROTECTED(newx, newy))
7651     {
7652       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7653
7654       return;
7655     }
7656
7657     else if (CAN_MOVE_INTO_ACID(element) &&
7658              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7659              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7660              (MovDir[x][y] == MV_DOWN ||
7661               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7662     {
7663       SplashAcid(newx, newy);
7664       Store[x][y] = EL_ACID;
7665     }
7666     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7667     {
7668       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7669           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7670           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7671           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7672       {
7673         RemoveField(x, y);
7674         TEST_DrawLevelField(x, y);
7675
7676         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7677         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7678           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7679
7680         local_player->friends_still_needed--;
7681         if (!local_player->friends_still_needed &&
7682             !local_player->GameOver && AllPlayersGone)
7683           PlayerWins(local_player);
7684
7685         return;
7686       }
7687       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7688       {
7689         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7690           TEST_DrawLevelField(newx, newy);
7691         else
7692           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7693       }
7694       else if (!IS_FREE(newx, newy))
7695       {
7696         GfxAction[x][y] = ACTION_WAITING;
7697
7698         if (IS_PLAYER(x, y))
7699           DrawPlayerField(x, y);
7700         else
7701           TEST_DrawLevelField(x, y);
7702
7703         return;
7704       }
7705     }
7706     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7707     {
7708       if (IS_FOOD_PIG(Feld[newx][newy]))
7709       {
7710         if (IS_MOVING(newx, newy))
7711           RemoveMovingField(newx, newy);
7712         else
7713         {
7714           Feld[newx][newy] = EL_EMPTY;
7715           TEST_DrawLevelField(newx, newy);
7716         }
7717
7718         PlayLevelSound(x, y, SND_PIG_DIGGING);
7719       }
7720       else if (!IS_FREE(newx, newy))
7721       {
7722         if (IS_PLAYER(x, y))
7723           DrawPlayerField(x, y);
7724         else
7725           TEST_DrawLevelField(x, y);
7726
7727         return;
7728       }
7729     }
7730     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7731     {
7732       if (Store[x][y] != EL_EMPTY)
7733       {
7734         boolean can_clone = FALSE;
7735         int xx, yy;
7736
7737         /* check if element to clone is still there */
7738         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7739         {
7740           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7741           {
7742             can_clone = TRUE;
7743
7744             break;
7745           }
7746         }
7747
7748         /* cannot clone or target field not free anymore -- do not clone */
7749         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7750           Store[x][y] = EL_EMPTY;
7751       }
7752
7753       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7754       {
7755         if (IS_MV_DIAGONAL(MovDir[x][y]))
7756         {
7757           int diagonal_move_dir = MovDir[x][y];
7758           int stored = Store[x][y];
7759           int change_delay = 8;
7760           int graphic;
7761
7762           /* android is moving diagonally */
7763
7764           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7765
7766           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7767           GfxElement[x][y] = EL_EMC_ANDROID;
7768           GfxAction[x][y] = ACTION_SHRINKING;
7769           GfxDir[x][y] = diagonal_move_dir;
7770           ChangeDelay[x][y] = change_delay;
7771
7772           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7773                                    GfxDir[x][y]);
7774
7775           DrawLevelGraphicAnimation(x, y, graphic);
7776           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7777
7778           if (Feld[newx][newy] == EL_ACID)
7779           {
7780             SplashAcid(newx, newy);
7781
7782             return;
7783           }
7784
7785           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7786
7787           Store[newx][newy] = EL_EMC_ANDROID;
7788           GfxElement[newx][newy] = EL_EMC_ANDROID;
7789           GfxAction[newx][newy] = ACTION_GROWING;
7790           GfxDir[newx][newy] = diagonal_move_dir;
7791           ChangeDelay[newx][newy] = change_delay;
7792
7793           graphic = el_act_dir2img(GfxElement[newx][newy],
7794                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7795
7796           DrawLevelGraphicAnimation(newx, newy, graphic);
7797           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7798
7799           return;
7800         }
7801         else
7802         {
7803           Feld[newx][newy] = EL_EMPTY;
7804           TEST_DrawLevelField(newx, newy);
7805
7806           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7807         }
7808       }
7809       else if (!IS_FREE(newx, newy))
7810       {
7811         return;
7812       }
7813     }
7814     else if (IS_CUSTOM_ELEMENT(element) &&
7815              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7816     {
7817       if (!DigFieldByCE(newx, newy, element))
7818         return;
7819
7820       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7821       {
7822         RunnerVisit[x][y] = FrameCounter;
7823         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7824       }
7825     }
7826     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7827     {
7828       if (!IS_FREE(newx, newy))
7829       {
7830         if (IS_PLAYER(x, y))
7831           DrawPlayerField(x, y);
7832         else
7833           TEST_DrawLevelField(x, y);
7834
7835         return;
7836       }
7837       else
7838       {
7839         boolean wanna_flame = !RND(10);
7840         int dx = newx - x, dy = newy - y;
7841         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7842         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7843         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7844                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7845         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7846                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7847
7848         if ((wanna_flame ||
7849              IS_CLASSIC_ENEMY(element1) ||
7850              IS_CLASSIC_ENEMY(element2)) &&
7851             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7852             element1 != EL_FLAMES && element2 != EL_FLAMES)
7853         {
7854           ResetGfxAnimation(x, y);
7855           GfxAction[x][y] = ACTION_ATTACKING;
7856
7857           if (IS_PLAYER(x, y))
7858             DrawPlayerField(x, y);
7859           else
7860             TEST_DrawLevelField(x, y);
7861
7862           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7863
7864           MovDelay[x][y] = 50;
7865
7866           Feld[newx][newy] = EL_FLAMES;
7867           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7868             Feld[newx1][newy1] = EL_FLAMES;
7869           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7870             Feld[newx2][newy2] = EL_FLAMES;
7871
7872           return;
7873         }
7874       }
7875     }
7876     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7877              Feld[newx][newy] == EL_DIAMOND)
7878     {
7879       if (IS_MOVING(newx, newy))
7880         RemoveMovingField(newx, newy);
7881       else
7882       {
7883         Feld[newx][newy] = EL_EMPTY;
7884         TEST_DrawLevelField(newx, newy);
7885       }
7886
7887       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7888     }
7889     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7890              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7891     {
7892       if (AmoebaNr[newx][newy])
7893       {
7894         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7895         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7896             Feld[newx][newy] == EL_BD_AMOEBA)
7897           AmoebaCnt[AmoebaNr[newx][newy]]--;
7898       }
7899
7900       if (IS_MOVING(newx, newy))
7901       {
7902         RemoveMovingField(newx, newy);
7903       }
7904       else
7905       {
7906         Feld[newx][newy] = EL_EMPTY;
7907         TEST_DrawLevelField(newx, newy);
7908       }
7909
7910       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7911     }
7912     else if ((element == EL_PACMAN || element == EL_MOLE)
7913              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7914     {
7915       if (AmoebaNr[newx][newy])
7916       {
7917         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7918         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7919             Feld[newx][newy] == EL_BD_AMOEBA)
7920           AmoebaCnt[AmoebaNr[newx][newy]]--;
7921       }
7922
7923       if (element == EL_MOLE)
7924       {
7925         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7926         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7927
7928         ResetGfxAnimation(x, y);
7929         GfxAction[x][y] = ACTION_DIGGING;
7930         TEST_DrawLevelField(x, y);
7931
7932         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7933
7934         return;                         /* wait for shrinking amoeba */
7935       }
7936       else      /* element == EL_PACMAN */
7937       {
7938         Feld[newx][newy] = EL_EMPTY;
7939         TEST_DrawLevelField(newx, newy);
7940         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7941       }
7942     }
7943     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7944              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7945               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7946     {
7947       /* wait for shrinking amoeba to completely disappear */
7948       return;
7949     }
7950     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7951     {
7952       /* object was running against a wall */
7953
7954       TurnRound(x, y);
7955
7956       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7957         DrawLevelElementAnimation(x, y, element);
7958
7959       if (DONT_TOUCH(element))
7960         TestIfBadThingTouchesPlayer(x, y);
7961
7962       return;
7963     }
7964
7965     InitMovingField(x, y, MovDir[x][y]);
7966
7967     PlayLevelSoundAction(x, y, ACTION_MOVING);
7968   }
7969
7970   if (MovDir[x][y])
7971     ContinueMoving(x, y);
7972 }
7973
7974 void ContinueMoving(int x, int y)
7975 {
7976   int element = Feld[x][y];
7977   struct ElementInfo *ei = &element_info[element];
7978   int direction = MovDir[x][y];
7979   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7980   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7981   int newx = x + dx, newy = y + dy;
7982   int stored = Store[x][y];
7983   int stored_new = Store[newx][newy];
7984   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7985   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7986   boolean last_line = (newy == lev_fieldy - 1);
7987
7988   MovPos[x][y] += getElementMoveStepsize(x, y);
7989
7990   if (pushed_by_player) /* special case: moving object pushed by player */
7991     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7992
7993   if (ABS(MovPos[x][y]) < TILEX)
7994   {
7995     TEST_DrawLevelField(x, y);
7996
7997     return;     /* element is still moving */
7998   }
7999
8000   /* element reached destination field */
8001
8002   Feld[x][y] = EL_EMPTY;
8003   Feld[newx][newy] = element;
8004   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8005
8006   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8007   {
8008     element = Feld[newx][newy] = EL_ACID;
8009   }
8010   else if (element == EL_MOLE)
8011   {
8012     Feld[x][y] = EL_SAND;
8013
8014     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8015   }
8016   else if (element == EL_QUICKSAND_FILLING)
8017   {
8018     element = Feld[newx][newy] = get_next_element(element);
8019     Store[newx][newy] = Store[x][y];
8020   }
8021   else if (element == EL_QUICKSAND_EMPTYING)
8022   {
8023     Feld[x][y] = get_next_element(element);
8024     element = Feld[newx][newy] = Store[x][y];
8025   }
8026   else if (element == EL_QUICKSAND_FAST_FILLING)
8027   {
8028     element = Feld[newx][newy] = get_next_element(element);
8029     Store[newx][newy] = Store[x][y];
8030   }
8031   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8032   {
8033     Feld[x][y] = get_next_element(element);
8034     element = Feld[newx][newy] = Store[x][y];
8035   }
8036   else if (element == EL_MAGIC_WALL_FILLING)
8037   {
8038     element = Feld[newx][newy] = get_next_element(element);
8039     if (!game.magic_wall_active)
8040       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8041     Store[newx][newy] = Store[x][y];
8042   }
8043   else if (element == EL_MAGIC_WALL_EMPTYING)
8044   {
8045     Feld[x][y] = get_next_element(element);
8046     if (!game.magic_wall_active)
8047       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8048     element = Feld[newx][newy] = Store[x][y];
8049
8050     InitField(newx, newy, FALSE);
8051   }
8052   else if (element == EL_BD_MAGIC_WALL_FILLING)
8053   {
8054     element = Feld[newx][newy] = get_next_element(element);
8055     if (!game.magic_wall_active)
8056       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8057     Store[newx][newy] = Store[x][y];
8058   }
8059   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8060   {
8061     Feld[x][y] = get_next_element(element);
8062     if (!game.magic_wall_active)
8063       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8064     element = Feld[newx][newy] = Store[x][y];
8065
8066     InitField(newx, newy, FALSE);
8067   }
8068   else if (element == EL_DC_MAGIC_WALL_FILLING)
8069   {
8070     element = Feld[newx][newy] = get_next_element(element);
8071     if (!game.magic_wall_active)
8072       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8073     Store[newx][newy] = Store[x][y];
8074   }
8075   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8076   {
8077     Feld[x][y] = get_next_element(element);
8078     if (!game.magic_wall_active)
8079       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8080     element = Feld[newx][newy] = Store[x][y];
8081
8082     InitField(newx, newy, FALSE);
8083   }
8084   else if (element == EL_AMOEBA_DROPPING)
8085   {
8086     Feld[x][y] = get_next_element(element);
8087     element = Feld[newx][newy] = Store[x][y];
8088   }
8089   else if (element == EL_SOKOBAN_OBJECT)
8090   {
8091     if (Back[x][y])
8092       Feld[x][y] = Back[x][y];
8093
8094     if (Back[newx][newy])
8095       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8096
8097     Back[x][y] = Back[newx][newy] = 0;
8098   }
8099
8100   Store[x][y] = EL_EMPTY;
8101   MovPos[x][y] = 0;
8102   MovDir[x][y] = 0;
8103   MovDelay[x][y] = 0;
8104
8105   MovDelay[newx][newy] = 0;
8106
8107   if (CAN_CHANGE_OR_HAS_ACTION(element))
8108   {
8109     /* copy element change control values to new field */
8110     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8111     ChangePage[newx][newy]  = ChangePage[x][y];
8112     ChangeCount[newx][newy] = ChangeCount[x][y];
8113     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8114   }
8115
8116   CustomValue[newx][newy] = CustomValue[x][y];
8117
8118   ChangeDelay[x][y] = 0;
8119   ChangePage[x][y] = -1;
8120   ChangeCount[x][y] = 0;
8121   ChangeEvent[x][y] = -1;
8122
8123   CustomValue[x][y] = 0;
8124
8125   /* copy animation control values to new field */
8126   GfxFrame[newx][newy]  = GfxFrame[x][y];
8127   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8128   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8129   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8130
8131   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8132
8133   /* some elements can leave other elements behind after moving */
8134   if (ei->move_leave_element != EL_EMPTY &&
8135       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8136       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8137   {
8138     int move_leave_element = ei->move_leave_element;
8139
8140     /* this makes it possible to leave the removed element again */
8141     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8142       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8143
8144     Feld[x][y] = move_leave_element;
8145
8146     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8147       MovDir[x][y] = direction;
8148
8149     InitField(x, y, FALSE);
8150
8151     if (GFX_CRUMBLED(Feld[x][y]))
8152       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8153
8154     if (ELEM_IS_PLAYER(move_leave_element))
8155       RelocatePlayer(x, y, move_leave_element);
8156   }
8157
8158   /* do this after checking for left-behind element */
8159   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8160
8161   if (!CAN_MOVE(element) ||
8162       (CAN_FALL(element) && direction == MV_DOWN &&
8163        (element == EL_SPRING ||
8164         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8165         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8166     GfxDir[x][y] = MovDir[newx][newy] = 0;
8167
8168   TEST_DrawLevelField(x, y);
8169   TEST_DrawLevelField(newx, newy);
8170
8171   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8172
8173   /* prevent pushed element from moving on in pushed direction */
8174   if (pushed_by_player && CAN_MOVE(element) &&
8175       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8176       !(element_info[element].move_pattern & direction))
8177     TurnRound(newx, newy);
8178
8179   /* prevent elements on conveyor belt from moving on in last direction */
8180   if (pushed_by_conveyor && CAN_FALL(element) &&
8181       direction & MV_HORIZONTAL)
8182     MovDir[newx][newy] = 0;
8183
8184   if (!pushed_by_player)
8185   {
8186     int nextx = newx + dx, nexty = newy + dy;
8187     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8188
8189     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8190
8191     if (CAN_FALL(element) && direction == MV_DOWN)
8192       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8193
8194     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8195       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8196
8197     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8198       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8199   }
8200
8201   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8202   {
8203     TestIfBadThingTouchesPlayer(newx, newy);
8204     TestIfBadThingTouchesFriend(newx, newy);
8205
8206     if (!IS_CUSTOM_ELEMENT(element))
8207       TestIfBadThingTouchesOtherBadThing(newx, newy);
8208   }
8209   else if (element == EL_PENGUIN)
8210     TestIfFriendTouchesBadThing(newx, newy);
8211
8212   if (DONT_GET_HIT_BY(element))
8213   {
8214     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8215   }
8216
8217   /* give the player one last chance (one more frame) to move away */
8218   if (CAN_FALL(element) && direction == MV_DOWN &&
8219       (last_line || (!IS_FREE(x, newy + 1) &&
8220                      (!IS_PLAYER(x, newy + 1) ||
8221                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8222     Impact(x, newy);
8223
8224   if (pushed_by_player && !game.use_change_when_pushing_bug)
8225   {
8226     int push_side = MV_DIR_OPPOSITE(direction);
8227     struct PlayerInfo *player = PLAYERINFO(x, y);
8228
8229     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8230                                player->index_bit, push_side);
8231     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8232                                         player->index_bit, push_side);
8233   }
8234
8235   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8236     MovDelay[newx][newy] = 1;
8237
8238   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8239
8240   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8241   TestIfElementHitsCustomElement(newx, newy, direction);
8242   TestIfPlayerTouchesCustomElement(newx, newy);
8243   TestIfElementTouchesCustomElement(newx, newy);
8244
8245   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8246       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8247     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8248                              MV_DIR_OPPOSITE(direction));
8249 }
8250
8251 int AmoebeNachbarNr(int ax, int ay)
8252 {
8253   int i;
8254   int element = Feld[ax][ay];
8255   int group_nr = 0;
8256   static int xy[4][2] =
8257   {
8258     { 0, -1 },
8259     { -1, 0 },
8260     { +1, 0 },
8261     { 0, +1 }
8262   };
8263
8264   for (i = 0; i < NUM_DIRECTIONS; i++)
8265   {
8266     int x = ax + xy[i][0];
8267     int y = ay + xy[i][1];
8268
8269     if (!IN_LEV_FIELD(x, y))
8270       continue;
8271
8272     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8273       group_nr = AmoebaNr[x][y];
8274   }
8275
8276   return group_nr;
8277 }
8278
8279 void AmoebenVereinigen(int ax, int ay)
8280 {
8281   int i, x, y, xx, yy;
8282   int new_group_nr = AmoebaNr[ax][ay];
8283   static int xy[4][2] =
8284   {
8285     { 0, -1 },
8286     { -1, 0 },
8287     { +1, 0 },
8288     { 0, +1 }
8289   };
8290
8291   if (new_group_nr == 0)
8292     return;
8293
8294   for (i = 0; i < NUM_DIRECTIONS; i++)
8295   {
8296     x = ax + xy[i][0];
8297     y = ay + xy[i][1];
8298
8299     if (!IN_LEV_FIELD(x, y))
8300       continue;
8301
8302     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8303          Feld[x][y] == EL_BD_AMOEBA ||
8304          Feld[x][y] == EL_AMOEBA_DEAD) &&
8305         AmoebaNr[x][y] != new_group_nr)
8306     {
8307       int old_group_nr = AmoebaNr[x][y];
8308
8309       if (old_group_nr == 0)
8310         return;
8311
8312       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8313       AmoebaCnt[old_group_nr] = 0;
8314       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8315       AmoebaCnt2[old_group_nr] = 0;
8316
8317       SCAN_PLAYFIELD(xx, yy)
8318       {
8319         if (AmoebaNr[xx][yy] == old_group_nr)
8320           AmoebaNr[xx][yy] = new_group_nr;
8321       }
8322     }
8323   }
8324 }
8325
8326 void AmoebeUmwandeln(int ax, int ay)
8327 {
8328   int i, x, y;
8329
8330   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8331   {
8332     int group_nr = AmoebaNr[ax][ay];
8333
8334 #ifdef DEBUG
8335     if (group_nr == 0)
8336     {
8337       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8338       printf("AmoebeUmwandeln(): This should never happen!\n");
8339       return;
8340     }
8341 #endif
8342
8343     SCAN_PLAYFIELD(x, y)
8344     {
8345       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8346       {
8347         AmoebaNr[x][y] = 0;
8348         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8349       }
8350     }
8351
8352     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8353                             SND_AMOEBA_TURNING_TO_GEM :
8354                             SND_AMOEBA_TURNING_TO_ROCK));
8355     Bang(ax, ay);
8356   }
8357   else
8358   {
8359     static int xy[4][2] =
8360     {
8361       { 0, -1 },
8362       { -1, 0 },
8363       { +1, 0 },
8364       { 0, +1 }
8365     };
8366
8367     for (i = 0; i < NUM_DIRECTIONS; i++)
8368     {
8369       x = ax + xy[i][0];
8370       y = ay + xy[i][1];
8371
8372       if (!IN_LEV_FIELD(x, y))
8373         continue;
8374
8375       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8376       {
8377         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8378                               SND_AMOEBA_TURNING_TO_GEM :
8379                               SND_AMOEBA_TURNING_TO_ROCK));
8380         Bang(x, y);
8381       }
8382     }
8383   }
8384 }
8385
8386 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8387 {
8388   int x, y;
8389   int group_nr = AmoebaNr[ax][ay];
8390   boolean done = FALSE;
8391
8392 #ifdef DEBUG
8393   if (group_nr == 0)
8394   {
8395     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8396     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8397     return;
8398   }
8399 #endif
8400
8401   SCAN_PLAYFIELD(x, y)
8402   {
8403     if (AmoebaNr[x][y] == group_nr &&
8404         (Feld[x][y] == EL_AMOEBA_DEAD ||
8405          Feld[x][y] == EL_BD_AMOEBA ||
8406          Feld[x][y] == EL_AMOEBA_GROWING))
8407     {
8408       AmoebaNr[x][y] = 0;
8409       Feld[x][y] = new_element;
8410       InitField(x, y, FALSE);
8411       TEST_DrawLevelField(x, y);
8412       done = TRUE;
8413     }
8414   }
8415
8416   if (done)
8417     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8418                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8419                             SND_BD_AMOEBA_TURNING_TO_GEM));
8420 }
8421
8422 void AmoebeWaechst(int x, int y)
8423 {
8424   static unsigned int sound_delay = 0;
8425   static unsigned int sound_delay_value = 0;
8426
8427   if (!MovDelay[x][y])          /* start new growing cycle */
8428   {
8429     MovDelay[x][y] = 7;
8430
8431     if (DelayReached(&sound_delay, sound_delay_value))
8432     {
8433       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8434       sound_delay_value = 30;
8435     }
8436   }
8437
8438   if (MovDelay[x][y])           /* wait some time before growing bigger */
8439   {
8440     MovDelay[x][y]--;
8441     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8442     {
8443       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8444                                            6 - MovDelay[x][y]);
8445
8446       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8447     }
8448
8449     if (!MovDelay[x][y])
8450     {
8451       Feld[x][y] = Store[x][y];
8452       Store[x][y] = 0;
8453       TEST_DrawLevelField(x, y);
8454     }
8455   }
8456 }
8457
8458 void AmoebaDisappearing(int x, int y)
8459 {
8460   static unsigned int sound_delay = 0;
8461   static unsigned int sound_delay_value = 0;
8462
8463   if (!MovDelay[x][y])          /* start new shrinking cycle */
8464   {
8465     MovDelay[x][y] = 7;
8466
8467     if (DelayReached(&sound_delay, sound_delay_value))
8468       sound_delay_value = 30;
8469   }
8470
8471   if (MovDelay[x][y])           /* wait some time before shrinking */
8472   {
8473     MovDelay[x][y]--;
8474     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8475     {
8476       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8477                                            6 - MovDelay[x][y]);
8478
8479       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8480     }
8481
8482     if (!MovDelay[x][y])
8483     {
8484       Feld[x][y] = EL_EMPTY;
8485       TEST_DrawLevelField(x, y);
8486
8487       /* don't let mole enter this field in this cycle;
8488          (give priority to objects falling to this field from above) */
8489       Stop[x][y] = TRUE;
8490     }
8491   }
8492 }
8493
8494 void AmoebeAbleger(int ax, int ay)
8495 {
8496   int i;
8497   int element = Feld[ax][ay];
8498   int graphic = el2img(element);
8499   int newax = ax, neway = ay;
8500   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8501   static int xy[4][2] =
8502   {
8503     { 0, -1 },
8504     { -1, 0 },
8505     { +1, 0 },
8506     { 0, +1 }
8507   };
8508
8509   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8510   {
8511     Feld[ax][ay] = EL_AMOEBA_DEAD;
8512     TEST_DrawLevelField(ax, ay);
8513     return;
8514   }
8515
8516   if (IS_ANIMATED(graphic))
8517     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8518
8519   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8520     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8521
8522   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8523   {
8524     MovDelay[ax][ay]--;
8525     if (MovDelay[ax][ay])
8526       return;
8527   }
8528
8529   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8530   {
8531     int start = RND(4);
8532     int x = ax + xy[start][0];
8533     int y = ay + xy[start][1];
8534
8535     if (!IN_LEV_FIELD(x, y))
8536       return;
8537
8538     if (IS_FREE(x, y) ||
8539         CAN_GROW_INTO(Feld[x][y]) ||
8540         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8541         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8542     {
8543       newax = x;
8544       neway = y;
8545     }
8546
8547     if (newax == ax && neway == ay)
8548       return;
8549   }
8550   else                          /* normal or "filled" (BD style) amoeba */
8551   {
8552     int start = RND(4);
8553     boolean waiting_for_player = FALSE;
8554
8555     for (i = 0; i < NUM_DIRECTIONS; i++)
8556     {
8557       int j = (start + i) % 4;
8558       int x = ax + xy[j][0];
8559       int y = ay + xy[j][1];
8560
8561       if (!IN_LEV_FIELD(x, y))
8562         continue;
8563
8564       if (IS_FREE(x, y) ||
8565           CAN_GROW_INTO(Feld[x][y]) ||
8566           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8567           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8568       {
8569         newax = x;
8570         neway = y;
8571         break;
8572       }
8573       else if (IS_PLAYER(x, y))
8574         waiting_for_player = TRUE;
8575     }
8576
8577     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8578     {
8579       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8580       {
8581         Feld[ax][ay] = EL_AMOEBA_DEAD;
8582         TEST_DrawLevelField(ax, ay);
8583         AmoebaCnt[AmoebaNr[ax][ay]]--;
8584
8585         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8586         {
8587           if (element == EL_AMOEBA_FULL)
8588             AmoebeUmwandeln(ax, ay);
8589           else if (element == EL_BD_AMOEBA)
8590             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8591         }
8592       }
8593       return;
8594     }
8595     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8596     {
8597       /* amoeba gets larger by growing in some direction */
8598
8599       int new_group_nr = AmoebaNr[ax][ay];
8600
8601 #ifdef DEBUG
8602   if (new_group_nr == 0)
8603   {
8604     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8605     printf("AmoebeAbleger(): This should never happen!\n");
8606     return;
8607   }
8608 #endif
8609
8610       AmoebaNr[newax][neway] = new_group_nr;
8611       AmoebaCnt[new_group_nr]++;
8612       AmoebaCnt2[new_group_nr]++;
8613
8614       /* if amoeba touches other amoeba(s) after growing, unify them */
8615       AmoebenVereinigen(newax, neway);
8616
8617       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8618       {
8619         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8620         return;
8621       }
8622     }
8623   }
8624
8625   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8626       (neway == lev_fieldy - 1 && newax != ax))
8627   {
8628     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8629     Store[newax][neway] = element;
8630   }
8631   else if (neway == ay || element == EL_EMC_DRIPPER)
8632   {
8633     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8634
8635     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8636   }
8637   else
8638   {
8639     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8640     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8641     Store[ax][ay] = EL_AMOEBA_DROP;
8642     ContinueMoving(ax, ay);
8643     return;
8644   }
8645
8646   TEST_DrawLevelField(newax, neway);
8647 }
8648
8649 void Life(int ax, int ay)
8650 {
8651   int x1, y1, x2, y2;
8652   int life_time = 40;
8653   int element = Feld[ax][ay];
8654   int graphic = el2img(element);
8655   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8656                          level.biomaze);
8657   boolean changed = FALSE;
8658
8659   if (IS_ANIMATED(graphic))
8660     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8661
8662   if (Stop[ax][ay])
8663     return;
8664
8665   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8666     MovDelay[ax][ay] = life_time;
8667
8668   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8669   {
8670     MovDelay[ax][ay]--;
8671     if (MovDelay[ax][ay])
8672       return;
8673   }
8674
8675   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8676   {
8677     int xx = ax+x1, yy = ay+y1;
8678     int nachbarn = 0;
8679
8680     if (!IN_LEV_FIELD(xx, yy))
8681       continue;
8682
8683     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8684     {
8685       int x = xx+x2, y = yy+y2;
8686
8687       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8688         continue;
8689
8690       if (((Feld[x][y] == element ||
8691             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8692            !Stop[x][y]) ||
8693           (IS_FREE(x, y) && Stop[x][y]))
8694         nachbarn++;
8695     }
8696
8697     if (xx == ax && yy == ay)           /* field in the middle */
8698     {
8699       if (nachbarn < life_parameter[0] ||
8700           nachbarn > life_parameter[1])
8701       {
8702         Feld[xx][yy] = EL_EMPTY;
8703         if (!Stop[xx][yy])
8704           TEST_DrawLevelField(xx, yy);
8705         Stop[xx][yy] = TRUE;
8706         changed = TRUE;
8707       }
8708     }
8709     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8710     {                                   /* free border field */
8711       if (nachbarn >= life_parameter[2] &&
8712           nachbarn <= life_parameter[3])
8713       {
8714         Feld[xx][yy] = element;
8715         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8716         if (!Stop[xx][yy])
8717           TEST_DrawLevelField(xx, yy);
8718         Stop[xx][yy] = TRUE;
8719         changed = TRUE;
8720       }
8721     }
8722   }
8723
8724   if (changed)
8725     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8726                    SND_GAME_OF_LIFE_GROWING);
8727 }
8728
8729 static void InitRobotWheel(int x, int y)
8730 {
8731   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8732 }
8733
8734 static void RunRobotWheel(int x, int y)
8735 {
8736   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8737 }
8738
8739 static void StopRobotWheel(int x, int y)
8740 {
8741   if (ZX == x && ZY == y)
8742   {
8743     ZX = ZY = -1;
8744
8745     game.robot_wheel_active = FALSE;
8746   }
8747 }
8748
8749 static void InitTimegateWheel(int x, int y)
8750 {
8751   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8752 }
8753
8754 static void RunTimegateWheel(int x, int y)
8755 {
8756   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8757 }
8758
8759 static void InitMagicBallDelay(int x, int y)
8760 {
8761   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8762 }
8763
8764 static void ActivateMagicBall(int bx, int by)
8765 {
8766   int x, y;
8767
8768   if (level.ball_random)
8769   {
8770     int pos_border = RND(8);    /* select one of the eight border elements */
8771     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8772     int xx = pos_content % 3;
8773     int yy = pos_content / 3;
8774
8775     x = bx - 1 + xx;
8776     y = by - 1 + yy;
8777
8778     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8779       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8780   }
8781   else
8782   {
8783     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8784     {
8785       int xx = x - bx + 1;
8786       int yy = y - by + 1;
8787
8788       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8789         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8790     }
8791   }
8792
8793   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8794 }
8795
8796 void CheckExit(int x, int y)
8797 {
8798   if (local_player->gems_still_needed > 0 ||
8799       local_player->sokobanfields_still_needed > 0 ||
8800       local_player->lights_still_needed > 0)
8801   {
8802     int element = Feld[x][y];
8803     int graphic = el2img(element);
8804
8805     if (IS_ANIMATED(graphic))
8806       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8807
8808     return;
8809   }
8810
8811   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8812     return;
8813
8814   Feld[x][y] = EL_EXIT_OPENING;
8815
8816   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8817 }
8818
8819 void CheckExitEM(int x, int y)
8820 {
8821   if (local_player->gems_still_needed > 0 ||
8822       local_player->sokobanfields_still_needed > 0 ||
8823       local_player->lights_still_needed > 0)
8824   {
8825     int element = Feld[x][y];
8826     int graphic = el2img(element);
8827
8828     if (IS_ANIMATED(graphic))
8829       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8830
8831     return;
8832   }
8833
8834   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8835     return;
8836
8837   Feld[x][y] = EL_EM_EXIT_OPENING;
8838
8839   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8840 }
8841
8842 void CheckExitSteel(int x, int y)
8843 {
8844   if (local_player->gems_still_needed > 0 ||
8845       local_player->sokobanfields_still_needed > 0 ||
8846       local_player->lights_still_needed > 0)
8847   {
8848     int element = Feld[x][y];
8849     int graphic = el2img(element);
8850
8851     if (IS_ANIMATED(graphic))
8852       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8853
8854     return;
8855   }
8856
8857   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8858     return;
8859
8860   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8861
8862   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8863 }
8864
8865 void CheckExitSteelEM(int x, int y)
8866 {
8867   if (local_player->gems_still_needed > 0 ||
8868       local_player->sokobanfields_still_needed > 0 ||
8869       local_player->lights_still_needed > 0)
8870   {
8871     int element = Feld[x][y];
8872     int graphic = el2img(element);
8873
8874     if (IS_ANIMATED(graphic))
8875       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8876
8877     return;
8878   }
8879
8880   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8881     return;
8882
8883   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8884
8885   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8886 }
8887
8888 void CheckExitSP(int x, int y)
8889 {
8890   if (local_player->gems_still_needed > 0)
8891   {
8892     int element = Feld[x][y];
8893     int graphic = el2img(element);
8894
8895     if (IS_ANIMATED(graphic))
8896       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8897
8898     return;
8899   }
8900
8901   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8902     return;
8903
8904   Feld[x][y] = EL_SP_EXIT_OPENING;
8905
8906   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8907 }
8908
8909 static void CloseAllOpenTimegates()
8910 {
8911   int x, y;
8912
8913   SCAN_PLAYFIELD(x, y)
8914   {
8915     int element = Feld[x][y];
8916
8917     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8918     {
8919       Feld[x][y] = EL_TIMEGATE_CLOSING;
8920
8921       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8922     }
8923   }
8924 }
8925
8926 void DrawTwinkleOnField(int x, int y)
8927 {
8928   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8929     return;
8930
8931   if (Feld[x][y] == EL_BD_DIAMOND)
8932     return;
8933
8934   if (MovDelay[x][y] == 0)      /* next animation frame */
8935     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8936
8937   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8938   {
8939     MovDelay[x][y]--;
8940
8941     DrawLevelElementAnimation(x, y, Feld[x][y]);
8942
8943     if (MovDelay[x][y] != 0)
8944     {
8945       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8946                                            10 - MovDelay[x][y]);
8947
8948       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8949     }
8950   }
8951 }
8952
8953 void MauerWaechst(int x, int y)
8954 {
8955   int delay = 6;
8956
8957   if (!MovDelay[x][y])          /* next animation frame */
8958     MovDelay[x][y] = 3 * delay;
8959
8960   if (MovDelay[x][y])           /* wait some time before next frame */
8961   {
8962     MovDelay[x][y]--;
8963
8964     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8965     {
8966       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8967       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8968
8969       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8970     }
8971
8972     if (!MovDelay[x][y])
8973     {
8974       if (MovDir[x][y] == MV_LEFT)
8975       {
8976         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8977           TEST_DrawLevelField(x - 1, y);
8978       }
8979       else if (MovDir[x][y] == MV_RIGHT)
8980       {
8981         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8982           TEST_DrawLevelField(x + 1, y);
8983       }
8984       else if (MovDir[x][y] == MV_UP)
8985       {
8986         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8987           TEST_DrawLevelField(x, y - 1);
8988       }
8989       else
8990       {
8991         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8992           TEST_DrawLevelField(x, y + 1);
8993       }
8994
8995       Feld[x][y] = Store[x][y];
8996       Store[x][y] = 0;
8997       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8998       TEST_DrawLevelField(x, y);
8999     }
9000   }
9001 }
9002
9003 void MauerAbleger(int ax, int ay)
9004 {
9005   int element = Feld[ax][ay];
9006   int graphic = el2img(element);
9007   boolean oben_frei = FALSE, unten_frei = FALSE;
9008   boolean links_frei = FALSE, rechts_frei = FALSE;
9009   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9010   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9011   boolean new_wall = FALSE;
9012
9013   if (IS_ANIMATED(graphic))
9014     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9015
9016   if (!MovDelay[ax][ay])        /* start building new wall */
9017     MovDelay[ax][ay] = 6;
9018
9019   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9020   {
9021     MovDelay[ax][ay]--;
9022     if (MovDelay[ax][ay])
9023       return;
9024   }
9025
9026   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9027     oben_frei = TRUE;
9028   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9029     unten_frei = TRUE;
9030   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9031     links_frei = TRUE;
9032   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9033     rechts_frei = TRUE;
9034
9035   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9036       element == EL_EXPANDABLE_WALL_ANY)
9037   {
9038     if (oben_frei)
9039     {
9040       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9041       Store[ax][ay-1] = element;
9042       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9043       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9044         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9045                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9046       new_wall = TRUE;
9047     }
9048     if (unten_frei)
9049     {
9050       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9051       Store[ax][ay+1] = element;
9052       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9053       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9054         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9055                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9056       new_wall = TRUE;
9057     }
9058   }
9059
9060   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9061       element == EL_EXPANDABLE_WALL_ANY ||
9062       element == EL_EXPANDABLE_WALL ||
9063       element == EL_BD_EXPANDABLE_WALL)
9064   {
9065     if (links_frei)
9066     {
9067       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9068       Store[ax-1][ay] = element;
9069       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9070       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9071         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9072                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9073       new_wall = TRUE;
9074     }
9075
9076     if (rechts_frei)
9077     {
9078       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9079       Store[ax+1][ay] = element;
9080       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9081       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9082         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9083                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9084       new_wall = TRUE;
9085     }
9086   }
9087
9088   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9089     TEST_DrawLevelField(ax, ay);
9090
9091   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9092     oben_massiv = TRUE;
9093   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9094     unten_massiv = TRUE;
9095   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9096     links_massiv = TRUE;
9097   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9098     rechts_massiv = TRUE;
9099
9100   if (((oben_massiv && unten_massiv) ||
9101        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9102        element == EL_EXPANDABLE_WALL) &&
9103       ((links_massiv && rechts_massiv) ||
9104        element == EL_EXPANDABLE_WALL_VERTICAL))
9105     Feld[ax][ay] = EL_WALL;
9106
9107   if (new_wall)
9108     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9109 }
9110
9111 void MauerAblegerStahl(int ax, int ay)
9112 {
9113   int element = Feld[ax][ay];
9114   int graphic = el2img(element);
9115   boolean oben_frei = FALSE, unten_frei = FALSE;
9116   boolean links_frei = FALSE, rechts_frei = FALSE;
9117   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9118   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9119   boolean new_wall = FALSE;
9120
9121   if (IS_ANIMATED(graphic))
9122     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9123
9124   if (!MovDelay[ax][ay])        /* start building new wall */
9125     MovDelay[ax][ay] = 6;
9126
9127   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9128   {
9129     MovDelay[ax][ay]--;
9130     if (MovDelay[ax][ay])
9131       return;
9132   }
9133
9134   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9135     oben_frei = TRUE;
9136   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9137     unten_frei = TRUE;
9138   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9139     links_frei = TRUE;
9140   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9141     rechts_frei = TRUE;
9142
9143   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9144       element == EL_EXPANDABLE_STEELWALL_ANY)
9145   {
9146     if (oben_frei)
9147     {
9148       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9149       Store[ax][ay-1] = element;
9150       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9151       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9152         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9153                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9154       new_wall = TRUE;
9155     }
9156     if (unten_frei)
9157     {
9158       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9159       Store[ax][ay+1] = element;
9160       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9161       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9162         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9163                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9164       new_wall = TRUE;
9165     }
9166   }
9167
9168   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9169       element == EL_EXPANDABLE_STEELWALL_ANY)
9170   {
9171     if (links_frei)
9172     {
9173       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9174       Store[ax-1][ay] = element;
9175       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9176       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9177         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9178                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9179       new_wall = TRUE;
9180     }
9181
9182     if (rechts_frei)
9183     {
9184       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9185       Store[ax+1][ay] = element;
9186       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9187       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9188         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9189                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9190       new_wall = TRUE;
9191     }
9192   }
9193
9194   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9195     oben_massiv = TRUE;
9196   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9197     unten_massiv = TRUE;
9198   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9199     links_massiv = TRUE;
9200   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9201     rechts_massiv = TRUE;
9202
9203   if (((oben_massiv && unten_massiv) ||
9204        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9205       ((links_massiv && rechts_massiv) ||
9206        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9207     Feld[ax][ay] = EL_STEELWALL;
9208
9209   if (new_wall)
9210     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9211 }
9212
9213 void CheckForDragon(int x, int y)
9214 {
9215   int i, j;
9216   boolean dragon_found = FALSE;
9217   static int xy[4][2] =
9218   {
9219     { 0, -1 },
9220     { -1, 0 },
9221     { +1, 0 },
9222     { 0, +1 }
9223   };
9224
9225   for (i = 0; i < NUM_DIRECTIONS; i++)
9226   {
9227     for (j = 0; j < 4; j++)
9228     {
9229       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9230
9231       if (IN_LEV_FIELD(xx, yy) &&
9232           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9233       {
9234         if (Feld[xx][yy] == EL_DRAGON)
9235           dragon_found = TRUE;
9236       }
9237       else
9238         break;
9239     }
9240   }
9241
9242   if (!dragon_found)
9243   {
9244     for (i = 0; i < NUM_DIRECTIONS; i++)
9245     {
9246       for (j = 0; j < 3; j++)
9247       {
9248         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9249   
9250         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9251         {
9252           Feld[xx][yy] = EL_EMPTY;
9253           TEST_DrawLevelField(xx, yy);
9254         }
9255         else
9256           break;
9257       }
9258     }
9259   }
9260 }
9261
9262 static void InitBuggyBase(int x, int y)
9263 {
9264   int element = Feld[x][y];
9265   int activating_delay = FRAMES_PER_SECOND / 4;
9266
9267   ChangeDelay[x][y] =
9268     (element == EL_SP_BUGGY_BASE ?
9269      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9270      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9271      activating_delay :
9272      element == EL_SP_BUGGY_BASE_ACTIVE ?
9273      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9274 }
9275
9276 static void WarnBuggyBase(int x, int y)
9277 {
9278   int i;
9279   static int xy[4][2] =
9280   {
9281     { 0, -1 },
9282     { -1, 0 },
9283     { +1, 0 },
9284     { 0, +1 }
9285   };
9286
9287   for (i = 0; i < NUM_DIRECTIONS; i++)
9288   {
9289     int xx = x + xy[i][0];
9290     int yy = y + xy[i][1];
9291
9292     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9293     {
9294       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9295
9296       break;
9297     }
9298   }
9299 }
9300
9301 static void InitTrap(int x, int y)
9302 {
9303   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9304 }
9305
9306 static void ActivateTrap(int x, int y)
9307 {
9308   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9309 }
9310
9311 static void ChangeActiveTrap(int x, int y)
9312 {
9313   int graphic = IMG_TRAP_ACTIVE;
9314
9315   /* if new animation frame was drawn, correct crumbled sand border */
9316   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9317     TEST_DrawLevelFieldCrumbled(x, y);
9318 }
9319
9320 static int getSpecialActionElement(int element, int number, int base_element)
9321 {
9322   return (element != EL_EMPTY ? element :
9323           number != -1 ? base_element + number - 1 :
9324           EL_EMPTY);
9325 }
9326
9327 static int getModifiedActionNumber(int value_old, int operator, int operand,
9328                                    int value_min, int value_max)
9329 {
9330   int value_new = (operator == CA_MODE_SET      ? operand :
9331                    operator == CA_MODE_ADD      ? value_old + operand :
9332                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9333                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9334                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9335                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9336                    value_old);
9337
9338   return (value_new < value_min ? value_min :
9339           value_new > value_max ? value_max :
9340           value_new);
9341 }
9342
9343 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9344 {
9345   struct ElementInfo *ei = &element_info[element];
9346   struct ElementChangeInfo *change = &ei->change_page[page];
9347   int target_element = change->target_element;
9348   int action_type = change->action_type;
9349   int action_mode = change->action_mode;
9350   int action_arg = change->action_arg;
9351   int action_element = change->action_element;
9352   int i;
9353
9354   if (!change->has_action)
9355     return;
9356
9357   /* ---------- determine action paramater values -------------------------- */
9358
9359   int level_time_value =
9360     (level.time > 0 ? TimeLeft :
9361      TimePlayed);
9362
9363   int action_arg_element_raw =
9364     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9365      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9366      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9367      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9368      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9369      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9370      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9371      EL_EMPTY);
9372   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9373
9374   int action_arg_direction =
9375     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9376      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9377      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9378      change->actual_trigger_side :
9379      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9380      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9381      MV_NONE);
9382
9383   int action_arg_number_min =
9384     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9385      CA_ARG_MIN);
9386
9387   int action_arg_number_max =
9388     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9389      action_type == CA_SET_LEVEL_GEMS ? 999 :
9390      action_type == CA_SET_LEVEL_TIME ? 9999 :
9391      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9392      action_type == CA_SET_CE_VALUE ? 9999 :
9393      action_type == CA_SET_CE_SCORE ? 9999 :
9394      CA_ARG_MAX);
9395
9396   int action_arg_number_reset =
9397     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9398      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9399      action_type == CA_SET_LEVEL_TIME ? level.time :
9400      action_type == CA_SET_LEVEL_SCORE ? 0 :
9401      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9402      action_type == CA_SET_CE_SCORE ? 0 :
9403      0);
9404
9405   int action_arg_number =
9406     (action_arg <= CA_ARG_MAX ? action_arg :
9407      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9408      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9409      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9410      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9411      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9412      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9413      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9414      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9415      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9416      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9417      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9418      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9419      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9420      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9421      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9422      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9423      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9424      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9425      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9426      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9427      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9428      -1);
9429
9430   int action_arg_number_old =
9431     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9432      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9433      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9434      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9435      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9436      0);
9437
9438   int action_arg_number_new =
9439     getModifiedActionNumber(action_arg_number_old,
9440                             action_mode, action_arg_number,
9441                             action_arg_number_min, action_arg_number_max);
9442
9443   int trigger_player_bits =
9444     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9445      change->actual_trigger_player_bits : change->trigger_player);
9446
9447   int action_arg_player_bits =
9448     (action_arg >= CA_ARG_PLAYER_1 &&
9449      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9450      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9451      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9452      PLAYER_BITS_ANY);
9453
9454   /* ---------- execute action  -------------------------------------------- */
9455
9456   switch (action_type)
9457   {
9458     case CA_NO_ACTION:
9459     {
9460       return;
9461     }
9462
9463     /* ---------- level actions  ------------------------------------------- */
9464
9465     case CA_RESTART_LEVEL:
9466     {
9467       game.restart_level = TRUE;
9468
9469       break;
9470     }
9471
9472     case CA_SHOW_ENVELOPE:
9473     {
9474       int element = getSpecialActionElement(action_arg_element,
9475                                             action_arg_number, EL_ENVELOPE_1);
9476
9477       if (IS_ENVELOPE(element))
9478         local_player->show_envelope = element;
9479
9480       break;
9481     }
9482
9483     case CA_SET_LEVEL_TIME:
9484     {
9485       if (level.time > 0)       /* only modify limited time value */
9486       {
9487         TimeLeft = action_arg_number_new;
9488
9489         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9490
9491         DisplayGameControlValues();
9492
9493         if (!TimeLeft && setup.time_limit)
9494           for (i = 0; i < MAX_PLAYERS; i++)
9495             KillPlayer(&stored_player[i]);
9496       }
9497
9498       break;
9499     }
9500
9501     case CA_SET_LEVEL_SCORE:
9502     {
9503       local_player->score = action_arg_number_new;
9504
9505       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9506
9507       DisplayGameControlValues();
9508
9509       break;
9510     }
9511
9512     case CA_SET_LEVEL_GEMS:
9513     {
9514       local_player->gems_still_needed = action_arg_number_new;
9515
9516       game.snapshot.collected_item = TRUE;
9517
9518       game_panel_controls[GAME_PANEL_GEMS].value =
9519         local_player->gems_still_needed;
9520
9521       DisplayGameControlValues();
9522
9523       break;
9524     }
9525
9526     case CA_SET_LEVEL_WIND:
9527     {
9528       game.wind_direction = action_arg_direction;
9529
9530       break;
9531     }
9532
9533     case CA_SET_LEVEL_RANDOM_SEED:
9534     {
9535       /* ensure that setting a new random seed while playing is predictable */
9536       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9537
9538       break;
9539     }
9540
9541     /* ---------- player actions  ------------------------------------------ */
9542
9543     case CA_MOVE_PLAYER:
9544     {
9545       /* automatically move to the next field in specified direction */
9546       for (i = 0; i < MAX_PLAYERS; i++)
9547         if (trigger_player_bits & (1 << i))
9548           stored_player[i].programmed_action = action_arg_direction;
9549
9550       break;
9551     }
9552
9553     case CA_EXIT_PLAYER:
9554     {
9555       for (i = 0; i < MAX_PLAYERS; i++)
9556         if (action_arg_player_bits & (1 << i))
9557           PlayerWins(&stored_player[i]);
9558
9559       break;
9560     }
9561
9562     case CA_KILL_PLAYER:
9563     {
9564       for (i = 0; i < MAX_PLAYERS; i++)
9565         if (action_arg_player_bits & (1 << i))
9566           KillPlayer(&stored_player[i]);
9567
9568       break;
9569     }
9570
9571     case CA_SET_PLAYER_KEYS:
9572     {
9573       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9574       int element = getSpecialActionElement(action_arg_element,
9575                                             action_arg_number, EL_KEY_1);
9576
9577       if (IS_KEY(element))
9578       {
9579         for (i = 0; i < MAX_PLAYERS; i++)
9580         {
9581           if (trigger_player_bits & (1 << i))
9582           {
9583             stored_player[i].key[KEY_NR(element)] = key_state;
9584
9585             DrawGameDoorValues();
9586           }
9587         }
9588       }
9589
9590       break;
9591     }
9592
9593     case CA_SET_PLAYER_SPEED:
9594     {
9595       for (i = 0; i < MAX_PLAYERS; i++)
9596       {
9597         if (trigger_player_bits & (1 << i))
9598         {
9599           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9600
9601           if (action_arg == CA_ARG_SPEED_FASTER &&
9602               stored_player[i].cannot_move)
9603           {
9604             action_arg_number = STEPSIZE_VERY_SLOW;
9605           }
9606           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9607                    action_arg == CA_ARG_SPEED_FASTER)
9608           {
9609             action_arg_number = 2;
9610             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9611                            CA_MODE_MULTIPLY);
9612           }
9613           else if (action_arg == CA_ARG_NUMBER_RESET)
9614           {
9615             action_arg_number = level.initial_player_stepsize[i];
9616           }
9617
9618           move_stepsize =
9619             getModifiedActionNumber(move_stepsize,
9620                                     action_mode,
9621                                     action_arg_number,
9622                                     action_arg_number_min,
9623                                     action_arg_number_max);
9624
9625           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9626         }
9627       }
9628
9629       break;
9630     }
9631
9632     case CA_SET_PLAYER_SHIELD:
9633     {
9634       for (i = 0; i < MAX_PLAYERS; i++)
9635       {
9636         if (trigger_player_bits & (1 << i))
9637         {
9638           if (action_arg == CA_ARG_SHIELD_OFF)
9639           {
9640             stored_player[i].shield_normal_time_left = 0;
9641             stored_player[i].shield_deadly_time_left = 0;
9642           }
9643           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9644           {
9645             stored_player[i].shield_normal_time_left = 999999;
9646           }
9647           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9648           {
9649             stored_player[i].shield_normal_time_left = 999999;
9650             stored_player[i].shield_deadly_time_left = 999999;
9651           }
9652         }
9653       }
9654
9655       break;
9656     }
9657
9658     case CA_SET_PLAYER_GRAVITY:
9659     {
9660       for (i = 0; i < MAX_PLAYERS; i++)
9661       {
9662         if (trigger_player_bits & (1 << i))
9663         {
9664           stored_player[i].gravity =
9665             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9666              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9667              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9668              stored_player[i].gravity);
9669         }
9670       }
9671
9672       break;
9673     }
9674
9675     case CA_SET_PLAYER_ARTWORK:
9676     {
9677       for (i = 0; i < MAX_PLAYERS; i++)
9678       {
9679         if (trigger_player_bits & (1 << i))
9680         {
9681           int artwork_element = action_arg_element;
9682
9683           if (action_arg == CA_ARG_ELEMENT_RESET)
9684             artwork_element =
9685               (level.use_artwork_element[i] ? level.artwork_element[i] :
9686                stored_player[i].element_nr);
9687
9688           if (stored_player[i].artwork_element != artwork_element)
9689             stored_player[i].Frame = 0;
9690
9691           stored_player[i].artwork_element = artwork_element;
9692
9693           SetPlayerWaiting(&stored_player[i], FALSE);
9694
9695           /* set number of special actions for bored and sleeping animation */
9696           stored_player[i].num_special_action_bored =
9697             get_num_special_action(artwork_element,
9698                                    ACTION_BORING_1, ACTION_BORING_LAST);
9699           stored_player[i].num_special_action_sleeping =
9700             get_num_special_action(artwork_element,
9701                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9702         }
9703       }
9704
9705       break;
9706     }
9707
9708     case CA_SET_PLAYER_INVENTORY:
9709     {
9710       for (i = 0; i < MAX_PLAYERS; i++)
9711       {
9712         struct PlayerInfo *player = &stored_player[i];
9713         int j, k;
9714
9715         if (trigger_player_bits & (1 << i))
9716         {
9717           int inventory_element = action_arg_element;
9718
9719           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9720               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9721               action_arg == CA_ARG_ELEMENT_ACTION)
9722           {
9723             int element = inventory_element;
9724             int collect_count = element_info[element].collect_count_initial;
9725
9726             if (!IS_CUSTOM_ELEMENT(element))
9727               collect_count = 1;
9728
9729             if (collect_count == 0)
9730               player->inventory_infinite_element = element;
9731             else
9732               for (k = 0; k < collect_count; k++)
9733                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9734                   player->inventory_element[player->inventory_size++] =
9735                     element;
9736           }
9737           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9738                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9739                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9740           {
9741             if (player->inventory_infinite_element != EL_UNDEFINED &&
9742                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9743                                      action_arg_element_raw))
9744               player->inventory_infinite_element = EL_UNDEFINED;
9745
9746             for (k = 0, j = 0; j < player->inventory_size; j++)
9747             {
9748               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9749                                         action_arg_element_raw))
9750                 player->inventory_element[k++] = player->inventory_element[j];
9751             }
9752
9753             player->inventory_size = k;
9754           }
9755           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9756           {
9757             if (player->inventory_size > 0)
9758             {
9759               for (j = 0; j < player->inventory_size - 1; j++)
9760                 player->inventory_element[j] = player->inventory_element[j + 1];
9761
9762               player->inventory_size--;
9763             }
9764           }
9765           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9766           {
9767             if (player->inventory_size > 0)
9768               player->inventory_size--;
9769           }
9770           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9771           {
9772             player->inventory_infinite_element = EL_UNDEFINED;
9773             player->inventory_size = 0;
9774           }
9775           else if (action_arg == CA_ARG_INVENTORY_RESET)
9776           {
9777             player->inventory_infinite_element = EL_UNDEFINED;
9778             player->inventory_size = 0;
9779
9780             if (level.use_initial_inventory[i])
9781             {
9782               for (j = 0; j < level.initial_inventory_size[i]; j++)
9783               {
9784                 int element = level.initial_inventory_content[i][j];
9785                 int collect_count = element_info[element].collect_count_initial;
9786
9787                 if (!IS_CUSTOM_ELEMENT(element))
9788                   collect_count = 1;
9789
9790                 if (collect_count == 0)
9791                   player->inventory_infinite_element = element;
9792                 else
9793                   for (k = 0; k < collect_count; k++)
9794                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9795                       player->inventory_element[player->inventory_size++] =
9796                         element;
9797               }
9798             }
9799           }
9800         }
9801       }
9802
9803       break;
9804     }
9805
9806     /* ---------- CE actions  ---------------------------------------------- */
9807
9808     case CA_SET_CE_VALUE:
9809     {
9810       int last_ce_value = CustomValue[x][y];
9811
9812       CustomValue[x][y] = action_arg_number_new;
9813
9814       if (CustomValue[x][y] != last_ce_value)
9815       {
9816         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9817         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9818
9819         if (CustomValue[x][y] == 0)
9820         {
9821           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9822           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9823         }
9824       }
9825
9826       break;
9827     }
9828
9829     case CA_SET_CE_SCORE:
9830     {
9831       int last_ce_score = ei->collect_score;
9832
9833       ei->collect_score = action_arg_number_new;
9834
9835       if (ei->collect_score != last_ce_score)
9836       {
9837         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9838         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9839
9840         if (ei->collect_score == 0)
9841         {
9842           int xx, yy;
9843
9844           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9845           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9846
9847           /*
9848             This is a very special case that seems to be a mixture between
9849             CheckElementChange() and CheckTriggeredElementChange(): while
9850             the first one only affects single elements that are triggered
9851             directly, the second one affects multiple elements in the playfield
9852             that are triggered indirectly by another element. This is a third
9853             case: Changing the CE score always affects multiple identical CEs,
9854             so every affected CE must be checked, not only the single CE for
9855             which the CE score was changed in the first place (as every instance
9856             of that CE shares the same CE score, and therefore also can change)!
9857           */
9858           SCAN_PLAYFIELD(xx, yy)
9859           {
9860             if (Feld[xx][yy] == element)
9861               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9862                                  CE_SCORE_GETS_ZERO);
9863           }
9864         }
9865       }
9866
9867       break;
9868     }
9869
9870     case CA_SET_CE_ARTWORK:
9871     {
9872       int artwork_element = action_arg_element;
9873       boolean reset_frame = FALSE;
9874       int xx, yy;
9875
9876       if (action_arg == CA_ARG_ELEMENT_RESET)
9877         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9878                            element);
9879
9880       if (ei->gfx_element != artwork_element)
9881         reset_frame = TRUE;
9882
9883       ei->gfx_element = artwork_element;
9884
9885       SCAN_PLAYFIELD(xx, yy)
9886       {
9887         if (Feld[xx][yy] == element)
9888         {
9889           if (reset_frame)
9890           {
9891             ResetGfxAnimation(xx, yy);
9892             ResetRandomAnimationValue(xx, yy);
9893           }
9894
9895           TEST_DrawLevelField(xx, yy);
9896         }
9897       }
9898
9899       break;
9900     }
9901
9902     /* ---------- engine actions  ------------------------------------------ */
9903
9904     case CA_SET_ENGINE_SCAN_MODE:
9905     {
9906       InitPlayfieldScanMode(action_arg);
9907
9908       break;
9909     }
9910
9911     default:
9912       break;
9913   }
9914 }
9915
9916 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9917 {
9918   int old_element = Feld[x][y];
9919   int new_element = GetElementFromGroupElement(element);
9920   int previous_move_direction = MovDir[x][y];
9921   int last_ce_value = CustomValue[x][y];
9922   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9923   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9924   boolean add_player_onto_element = (new_element_is_player &&
9925                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9926                                      IS_WALKABLE(old_element));
9927
9928   if (!add_player_onto_element)
9929   {
9930     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9931       RemoveMovingField(x, y);
9932     else
9933       RemoveField(x, y);
9934
9935     Feld[x][y] = new_element;
9936
9937     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9938       MovDir[x][y] = previous_move_direction;
9939
9940     if (element_info[new_element].use_last_ce_value)
9941       CustomValue[x][y] = last_ce_value;
9942
9943     InitField_WithBug1(x, y, FALSE);
9944
9945     new_element = Feld[x][y];   /* element may have changed */
9946
9947     ResetGfxAnimation(x, y);
9948     ResetRandomAnimationValue(x, y);
9949
9950     TEST_DrawLevelField(x, y);
9951
9952     if (GFX_CRUMBLED(new_element))
9953       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9954   }
9955
9956   /* check if element under the player changes from accessible to unaccessible
9957      (needed for special case of dropping element which then changes) */
9958   /* (must be checked after creating new element for walkable group elements) */
9959   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9960       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9961   {
9962     Bang(x, y);
9963
9964     return;
9965   }
9966
9967   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9968   if (new_element_is_player)
9969     RelocatePlayer(x, y, new_element);
9970
9971   if (is_change)
9972     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9973
9974   TestIfBadThingTouchesPlayer(x, y);
9975   TestIfPlayerTouchesCustomElement(x, y);
9976   TestIfElementTouchesCustomElement(x, y);
9977 }
9978
9979 static void CreateField(int x, int y, int element)
9980 {
9981   CreateFieldExt(x, y, element, FALSE);
9982 }
9983
9984 static void CreateElementFromChange(int x, int y, int element)
9985 {
9986   element = GET_VALID_RUNTIME_ELEMENT(element);
9987
9988   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9989   {
9990     int old_element = Feld[x][y];
9991
9992     /* prevent changed element from moving in same engine frame
9993        unless both old and new element can either fall or move */
9994     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9995         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9996       Stop[x][y] = TRUE;
9997   }
9998
9999   CreateFieldExt(x, y, element, TRUE);
10000 }
10001
10002 static boolean ChangeElement(int x, int y, int element, int page)
10003 {
10004   struct ElementInfo *ei = &element_info[element];
10005   struct ElementChangeInfo *change = &ei->change_page[page];
10006   int ce_value = CustomValue[x][y];
10007   int ce_score = ei->collect_score;
10008   int target_element;
10009   int old_element = Feld[x][y];
10010
10011   /* always use default change event to prevent running into a loop */
10012   if (ChangeEvent[x][y] == -1)
10013     ChangeEvent[x][y] = CE_DELAY;
10014
10015   if (ChangeEvent[x][y] == CE_DELAY)
10016   {
10017     /* reset actual trigger element, trigger player and action element */
10018     change->actual_trigger_element = EL_EMPTY;
10019     change->actual_trigger_player = EL_EMPTY;
10020     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10021     change->actual_trigger_side = CH_SIDE_NONE;
10022     change->actual_trigger_ce_value = 0;
10023     change->actual_trigger_ce_score = 0;
10024   }
10025
10026   /* do not change elements more than a specified maximum number of changes */
10027   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10028     return FALSE;
10029
10030   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10031
10032   if (change->explode)
10033   {
10034     Bang(x, y);
10035
10036     return TRUE;
10037   }
10038
10039   if (change->use_target_content)
10040   {
10041     boolean complete_replace = TRUE;
10042     boolean can_replace[3][3];
10043     int xx, yy;
10044
10045     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10046     {
10047       boolean is_empty;
10048       boolean is_walkable;
10049       boolean is_diggable;
10050       boolean is_collectible;
10051       boolean is_removable;
10052       boolean is_destructible;
10053       int ex = x + xx - 1;
10054       int ey = y + yy - 1;
10055       int content_element = change->target_content.e[xx][yy];
10056       int e;
10057
10058       can_replace[xx][yy] = TRUE;
10059
10060       if (ex == x && ey == y)   /* do not check changing element itself */
10061         continue;
10062
10063       if (content_element == EL_EMPTY_SPACE)
10064       {
10065         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10066
10067         continue;
10068       }
10069
10070       if (!IN_LEV_FIELD(ex, ey))
10071       {
10072         can_replace[xx][yy] = FALSE;
10073         complete_replace = FALSE;
10074
10075         continue;
10076       }
10077
10078       e = Feld[ex][ey];
10079
10080       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10081         e = MovingOrBlocked2Element(ex, ey);
10082
10083       is_empty = (IS_FREE(ex, ey) ||
10084                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10085
10086       is_walkable     = (is_empty || IS_WALKABLE(e));
10087       is_diggable     = (is_empty || IS_DIGGABLE(e));
10088       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10089       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10090       is_removable    = (is_diggable || is_collectible);
10091
10092       can_replace[xx][yy] =
10093         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10094           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10095           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10096           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10097           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10098           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10099          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10100
10101       if (!can_replace[xx][yy])
10102         complete_replace = FALSE;
10103     }
10104
10105     if (!change->only_if_complete || complete_replace)
10106     {
10107       boolean something_has_changed = FALSE;
10108
10109       if (change->only_if_complete && change->use_random_replace &&
10110           RND(100) < change->random_percentage)
10111         return FALSE;
10112
10113       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10114       {
10115         int ex = x + xx - 1;
10116         int ey = y + yy - 1;
10117         int content_element;
10118
10119         if (can_replace[xx][yy] && (!change->use_random_replace ||
10120                                     RND(100) < change->random_percentage))
10121         {
10122           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10123             RemoveMovingField(ex, ey);
10124
10125           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10126
10127           content_element = change->target_content.e[xx][yy];
10128           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10129                                               ce_value, ce_score);
10130
10131           CreateElementFromChange(ex, ey, target_element);
10132
10133           something_has_changed = TRUE;
10134
10135           /* for symmetry reasons, freeze newly created border elements */
10136           if (ex != x || ey != y)
10137             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10138         }
10139       }
10140
10141       if (something_has_changed)
10142       {
10143         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10144         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10145       }
10146     }
10147   }
10148   else
10149   {
10150     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10151                                         ce_value, ce_score);
10152
10153     if (element == EL_DIAGONAL_GROWING ||
10154         element == EL_DIAGONAL_SHRINKING)
10155     {
10156       target_element = Store[x][y];
10157
10158       Store[x][y] = EL_EMPTY;
10159     }
10160
10161     CreateElementFromChange(x, y, target_element);
10162
10163     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10164     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10165   }
10166
10167   /* this uses direct change before indirect change */
10168   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10169
10170   return TRUE;
10171 }
10172
10173 static void HandleElementChange(int x, int y, int page)
10174 {
10175   int element = MovingOrBlocked2Element(x, y);
10176   struct ElementInfo *ei = &element_info[element];
10177   struct ElementChangeInfo *change = &ei->change_page[page];
10178   boolean handle_action_before_change = FALSE;
10179
10180 #ifdef DEBUG
10181   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10182       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10183   {
10184     printf("\n\n");
10185     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10186            x, y, element, element_info[element].token_name);
10187     printf("HandleElementChange(): This should never happen!\n");
10188     printf("\n\n");
10189   }
10190 #endif
10191
10192   /* this can happen with classic bombs on walkable, changing elements */
10193   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10194   {
10195     return;
10196   }
10197
10198   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10199   {
10200     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10201
10202     if (change->can_change)
10203     {
10204       /* !!! not clear why graphic animation should be reset at all here !!! */
10205       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10206       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10207
10208       /*
10209         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10210
10211         When using an animation frame delay of 1 (this only happens with
10212         "sp_zonk.moving.left/right" in the classic graphics), the default
10213         (non-moving) animation shows wrong animation frames (while the
10214         moving animation, like "sp_zonk.moving.left/right", is correct,
10215         so this graphical bug never shows up with the classic graphics).
10216         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10217         be drawn instead of the correct frames 0,1,2,3. This is caused by
10218         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10219         an element change: First when the change delay ("ChangeDelay[][]")
10220         counter has reached zero after decrementing, then a second time in
10221         the next frame (after "GfxFrame[][]" was already incremented) when
10222         "ChangeDelay[][]" is reset to the initial delay value again.
10223
10224         This causes frame 0 to be drawn twice, while the last frame won't
10225         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10226
10227         As some animations may already be cleverly designed around this bug
10228         (at least the "Snake Bite" snake tail animation does this), it cannot
10229         simply be fixed here without breaking such existing animations.
10230         Unfortunately, it cannot easily be detected if a graphics set was
10231         designed "before" or "after" the bug was fixed. As a workaround,
10232         a new graphics set option "game.graphics_engine_version" was added
10233         to be able to specify the game's major release version for which the
10234         graphics set was designed, which can then be used to decide if the
10235         bugfix should be used (version 4 and above) or not (version 3 or
10236         below, or if no version was specified at all, as with old sets).
10237
10238         (The wrong/fixed animation frames can be tested with the test level set
10239         "test_gfxframe" and level "000", which contains a specially prepared
10240         custom element at level position (x/y) == (11/9) which uses the zonk
10241         animation mentioned above. Using "game.graphics_engine_version: 4"
10242         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10243         This can also be seen from the debug output for this test element.)
10244       */
10245
10246       /* when a custom element is about to change (for example by change delay),
10247          do not reset graphic animation when the custom element is moving */
10248       if (game.graphics_engine_version < 4 &&
10249           !IS_MOVING(x, y))
10250       {
10251         ResetGfxAnimation(x, y);
10252         ResetRandomAnimationValue(x, y);
10253       }
10254
10255       if (change->pre_change_function)
10256         change->pre_change_function(x, y);
10257     }
10258   }
10259
10260   ChangeDelay[x][y]--;
10261
10262   if (ChangeDelay[x][y] != 0)           /* continue element change */
10263   {
10264     if (change->can_change)
10265     {
10266       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10267
10268       if (IS_ANIMATED(graphic))
10269         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10270
10271       if (change->change_function)
10272         change->change_function(x, y);
10273     }
10274   }
10275   else                                  /* finish element change */
10276   {
10277     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10278     {
10279       page = ChangePage[x][y];
10280       ChangePage[x][y] = -1;
10281
10282       change = &ei->change_page[page];
10283     }
10284
10285     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10286     {
10287       ChangeDelay[x][y] = 1;            /* try change after next move step */
10288       ChangePage[x][y] = page;          /* remember page to use for change */
10289
10290       return;
10291     }
10292
10293     /* special case: set new level random seed before changing element */
10294     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10295       handle_action_before_change = TRUE;
10296
10297     if (change->has_action && handle_action_before_change)
10298       ExecuteCustomElementAction(x, y, element, page);
10299
10300     if (change->can_change)
10301     {
10302       if (ChangeElement(x, y, element, page))
10303       {
10304         if (change->post_change_function)
10305           change->post_change_function(x, y);
10306       }
10307     }
10308
10309     if (change->has_action && !handle_action_before_change)
10310       ExecuteCustomElementAction(x, y, element, page);
10311   }
10312 }
10313
10314 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10315                                               int trigger_element,
10316                                               int trigger_event,
10317                                               int trigger_player,
10318                                               int trigger_side,
10319                                               int trigger_page)
10320 {
10321   boolean change_done_any = FALSE;
10322   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10323   int i;
10324
10325   if (!(trigger_events[trigger_element][trigger_event]))
10326     return FALSE;
10327
10328   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10329
10330   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10331   {
10332     int element = EL_CUSTOM_START + i;
10333     boolean change_done = FALSE;
10334     int p;
10335
10336     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10337         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10338       continue;
10339
10340     for (p = 0; p < element_info[element].num_change_pages; p++)
10341     {
10342       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10343
10344       if (change->can_change_or_has_action &&
10345           change->has_event[trigger_event] &&
10346           change->trigger_side & trigger_side &&
10347           change->trigger_player & trigger_player &&
10348           change->trigger_page & trigger_page_bits &&
10349           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10350       {
10351         change->actual_trigger_element = trigger_element;
10352         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10353         change->actual_trigger_player_bits = trigger_player;
10354         change->actual_trigger_side = trigger_side;
10355         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10356         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10357
10358         if ((change->can_change && !change_done) || change->has_action)
10359         {
10360           int x, y;
10361
10362           SCAN_PLAYFIELD(x, y)
10363           {
10364             if (Feld[x][y] == element)
10365             {
10366               if (change->can_change && !change_done)
10367               {
10368                 /* if element already changed in this frame, not only prevent
10369                    another element change (checked in ChangeElement()), but
10370                    also prevent additional element actions for this element */
10371
10372                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10373                     !level.use_action_after_change_bug)
10374                   continue;
10375
10376                 ChangeDelay[x][y] = 1;
10377                 ChangeEvent[x][y] = trigger_event;
10378
10379                 HandleElementChange(x, y, p);
10380               }
10381               else if (change->has_action)
10382               {
10383                 /* if element already changed in this frame, not only prevent
10384                    another element change (checked in ChangeElement()), but
10385                    also prevent additional element actions for this element */
10386
10387                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10388                     !level.use_action_after_change_bug)
10389                   continue;
10390
10391                 ExecuteCustomElementAction(x, y, element, p);
10392                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10393               }
10394             }
10395           }
10396
10397           if (change->can_change)
10398           {
10399             change_done = TRUE;
10400             change_done_any = TRUE;
10401           }
10402         }
10403       }
10404     }
10405   }
10406
10407   RECURSION_LOOP_DETECTION_END();
10408
10409   return change_done_any;
10410 }
10411
10412 static boolean CheckElementChangeExt(int x, int y,
10413                                      int element,
10414                                      int trigger_element,
10415                                      int trigger_event,
10416                                      int trigger_player,
10417                                      int trigger_side)
10418 {
10419   boolean change_done = FALSE;
10420   int p;
10421
10422   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10423       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10424     return FALSE;
10425
10426   if (Feld[x][y] == EL_BLOCKED)
10427   {
10428     Blocked2Moving(x, y, &x, &y);
10429     element = Feld[x][y];
10430   }
10431
10432   /* check if element has already changed or is about to change after moving */
10433   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10434        Feld[x][y] != element) ||
10435
10436       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10437        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10438         ChangePage[x][y] != -1)))
10439     return FALSE;
10440
10441   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10442
10443   for (p = 0; p < element_info[element].num_change_pages; p++)
10444   {
10445     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10446
10447     /* check trigger element for all events where the element that is checked
10448        for changing interacts with a directly adjacent element -- this is
10449        different to element changes that affect other elements to change on the
10450        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10451     boolean check_trigger_element =
10452       (trigger_event == CE_TOUCHING_X ||
10453        trigger_event == CE_HITTING_X ||
10454        trigger_event == CE_HIT_BY_X ||
10455        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10456
10457     if (change->can_change_or_has_action &&
10458         change->has_event[trigger_event] &&
10459         change->trigger_side & trigger_side &&
10460         change->trigger_player & trigger_player &&
10461         (!check_trigger_element ||
10462          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10463     {
10464       change->actual_trigger_element = trigger_element;
10465       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10466       change->actual_trigger_player_bits = trigger_player;
10467       change->actual_trigger_side = trigger_side;
10468       change->actual_trigger_ce_value = CustomValue[x][y];
10469       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10470
10471       /* special case: trigger element not at (x,y) position for some events */
10472       if (check_trigger_element)
10473       {
10474         static struct
10475         {
10476           int dx, dy;
10477         } move_xy[] =
10478           {
10479             {  0,  0 },
10480             { -1,  0 },
10481             { +1,  0 },
10482             {  0,  0 },
10483             {  0, -1 },
10484             {  0,  0 }, { 0, 0 }, { 0, 0 },
10485             {  0, +1 }
10486           };
10487
10488         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10489         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10490
10491         change->actual_trigger_ce_value = CustomValue[xx][yy];
10492         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10493       }
10494
10495       if (change->can_change && !change_done)
10496       {
10497         ChangeDelay[x][y] = 1;
10498         ChangeEvent[x][y] = trigger_event;
10499
10500         HandleElementChange(x, y, p);
10501
10502         change_done = TRUE;
10503       }
10504       else if (change->has_action)
10505       {
10506         ExecuteCustomElementAction(x, y, element, p);
10507         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10508       }
10509     }
10510   }
10511
10512   RECURSION_LOOP_DETECTION_END();
10513
10514   return change_done;
10515 }
10516
10517 static void PlayPlayerSound(struct PlayerInfo *player)
10518 {
10519   int jx = player->jx, jy = player->jy;
10520   int sound_element = player->artwork_element;
10521   int last_action = player->last_action_waiting;
10522   int action = player->action_waiting;
10523
10524   if (player->is_waiting)
10525   {
10526     if (action != last_action)
10527       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10528     else
10529       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10530   }
10531   else
10532   {
10533     if (action != last_action)
10534       StopSound(element_info[sound_element].sound[last_action]);
10535
10536     if (last_action == ACTION_SLEEPING)
10537       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10538   }
10539 }
10540
10541 static void PlayAllPlayersSound()
10542 {
10543   int i;
10544
10545   for (i = 0; i < MAX_PLAYERS; i++)
10546     if (stored_player[i].active)
10547       PlayPlayerSound(&stored_player[i]);
10548 }
10549
10550 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10551 {
10552   boolean last_waiting = player->is_waiting;
10553   int move_dir = player->MovDir;
10554
10555   player->dir_waiting = move_dir;
10556   player->last_action_waiting = player->action_waiting;
10557
10558   if (is_waiting)
10559   {
10560     if (!last_waiting)          /* not waiting -> waiting */
10561     {
10562       player->is_waiting = TRUE;
10563
10564       player->frame_counter_bored =
10565         FrameCounter +
10566         game.player_boring_delay_fixed +
10567         GetSimpleRandom(game.player_boring_delay_random);
10568       player->frame_counter_sleeping =
10569         FrameCounter +
10570         game.player_sleeping_delay_fixed +
10571         GetSimpleRandom(game.player_sleeping_delay_random);
10572
10573       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10574     }
10575
10576     if (game.player_sleeping_delay_fixed +
10577         game.player_sleeping_delay_random > 0 &&
10578         player->anim_delay_counter == 0 &&
10579         player->post_delay_counter == 0 &&
10580         FrameCounter >= player->frame_counter_sleeping)
10581       player->is_sleeping = TRUE;
10582     else if (game.player_boring_delay_fixed +
10583              game.player_boring_delay_random > 0 &&
10584              FrameCounter >= player->frame_counter_bored)
10585       player->is_bored = TRUE;
10586
10587     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10588                               player->is_bored ? ACTION_BORING :
10589                               ACTION_WAITING);
10590
10591     if (player->is_sleeping && player->use_murphy)
10592     {
10593       /* special case for sleeping Murphy when leaning against non-free tile */
10594
10595       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10596           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10597            !IS_MOVING(player->jx - 1, player->jy)))
10598         move_dir = MV_LEFT;
10599       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10600                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10601                 !IS_MOVING(player->jx + 1, player->jy)))
10602         move_dir = MV_RIGHT;
10603       else
10604         player->is_sleeping = FALSE;
10605
10606       player->dir_waiting = move_dir;
10607     }
10608
10609     if (player->is_sleeping)
10610     {
10611       if (player->num_special_action_sleeping > 0)
10612       {
10613         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10614         {
10615           int last_special_action = player->special_action_sleeping;
10616           int num_special_action = player->num_special_action_sleeping;
10617           int special_action =
10618             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10619              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10620              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10621              last_special_action + 1 : ACTION_SLEEPING);
10622           int special_graphic =
10623             el_act_dir2img(player->artwork_element, special_action, move_dir);
10624
10625           player->anim_delay_counter =
10626             graphic_info[special_graphic].anim_delay_fixed +
10627             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10628           player->post_delay_counter =
10629             graphic_info[special_graphic].post_delay_fixed +
10630             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10631
10632           player->special_action_sleeping = special_action;
10633         }
10634
10635         if (player->anim_delay_counter > 0)
10636         {
10637           player->action_waiting = player->special_action_sleeping;
10638           player->anim_delay_counter--;
10639         }
10640         else if (player->post_delay_counter > 0)
10641         {
10642           player->post_delay_counter--;
10643         }
10644       }
10645     }
10646     else if (player->is_bored)
10647     {
10648       if (player->num_special_action_bored > 0)
10649       {
10650         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10651         {
10652           int special_action =
10653             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10654           int special_graphic =
10655             el_act_dir2img(player->artwork_element, special_action, move_dir);
10656
10657           player->anim_delay_counter =
10658             graphic_info[special_graphic].anim_delay_fixed +
10659             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10660           player->post_delay_counter =
10661             graphic_info[special_graphic].post_delay_fixed +
10662             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10663
10664           player->special_action_bored = special_action;
10665         }
10666
10667         if (player->anim_delay_counter > 0)
10668         {
10669           player->action_waiting = player->special_action_bored;
10670           player->anim_delay_counter--;
10671         }
10672         else if (player->post_delay_counter > 0)
10673         {
10674           player->post_delay_counter--;
10675         }
10676       }
10677     }
10678   }
10679   else if (last_waiting)        /* waiting -> not waiting */
10680   {
10681     player->is_waiting = FALSE;
10682     player->is_bored = FALSE;
10683     player->is_sleeping = FALSE;
10684
10685     player->frame_counter_bored = -1;
10686     player->frame_counter_sleeping = -1;
10687
10688     player->anim_delay_counter = 0;
10689     player->post_delay_counter = 0;
10690
10691     player->dir_waiting = player->MovDir;
10692     player->action_waiting = ACTION_DEFAULT;
10693
10694     player->special_action_bored = ACTION_DEFAULT;
10695     player->special_action_sleeping = ACTION_DEFAULT;
10696   }
10697 }
10698
10699 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10700 {
10701   if ((!player->is_moving  && player->was_moving) ||
10702       (player->MovPos == 0 && player->was_moving) ||
10703       (player->is_snapping && !player->was_snapping) ||
10704       (player->is_dropping && !player->was_dropping))
10705   {
10706     if (!CheckSaveEngineSnapshotToList())
10707       return;
10708
10709     player->was_moving = FALSE;
10710     player->was_snapping = TRUE;
10711     player->was_dropping = TRUE;
10712   }
10713   else
10714   {
10715     if (player->is_moving)
10716       player->was_moving = TRUE;
10717
10718     if (!player->is_snapping)
10719       player->was_snapping = FALSE;
10720
10721     if (!player->is_dropping)
10722       player->was_dropping = FALSE;
10723   }
10724 }
10725
10726 static void CheckSingleStepMode(struct PlayerInfo *player)
10727 {
10728   if (tape.single_step && tape.recording && !tape.pausing)
10729   {
10730     /* as it is called "single step mode", just return to pause mode when the
10731        player stopped moving after one tile (or never starts moving at all) */
10732     if (!player->is_moving &&
10733         !player->is_pushing &&
10734         !player->is_dropping_pressed)
10735     {
10736       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10737       SnapField(player, 0, 0);                  /* stop snapping */
10738     }
10739   }
10740
10741   CheckSaveEngineSnapshot(player);
10742 }
10743
10744 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10745 {
10746   int left      = player_action & JOY_LEFT;
10747   int right     = player_action & JOY_RIGHT;
10748   int up        = player_action & JOY_UP;
10749   int down      = player_action & JOY_DOWN;
10750   int button1   = player_action & JOY_BUTTON_1;
10751   int button2   = player_action & JOY_BUTTON_2;
10752   int dx        = (left ? -1 : right ? 1 : 0);
10753   int dy        = (up   ? -1 : down  ? 1 : 0);
10754
10755   if (!player->active || tape.pausing)
10756     return 0;
10757
10758   if (player_action)
10759   {
10760     if (button1)
10761       SnapField(player, dx, dy);
10762     else
10763     {
10764       if (button2)
10765         DropElement(player);
10766
10767       MovePlayer(player, dx, dy);
10768     }
10769
10770     CheckSingleStepMode(player);
10771
10772     SetPlayerWaiting(player, FALSE);
10773
10774     return player_action;
10775   }
10776   else
10777   {
10778     /* no actions for this player (no input at player's configured device) */
10779
10780     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10781     SnapField(player, 0, 0);
10782     CheckGravityMovementWhenNotMoving(player);
10783
10784     if (player->MovPos == 0)
10785       SetPlayerWaiting(player, TRUE);
10786
10787     if (player->MovPos == 0)    /* needed for tape.playing */
10788       player->is_moving = FALSE;
10789
10790     player->is_dropping = FALSE;
10791     player->is_dropping_pressed = FALSE;
10792     player->drop_pressed_delay = 0;
10793
10794     CheckSingleStepMode(player);
10795
10796     return 0;
10797   }
10798 }
10799
10800 static void CheckLevelTime()
10801 {
10802   int i;
10803
10804   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10805   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10806   {
10807     if (level.native_em_level->lev->home == 0)  /* all players at home */
10808     {
10809       PlayerWins(local_player);
10810
10811       AllPlayersGone = TRUE;
10812
10813       level.native_em_level->lev->home = -1;
10814     }
10815
10816     if (level.native_em_level->ply[0]->alive == 0 &&
10817         level.native_em_level->ply[1]->alive == 0 &&
10818         level.native_em_level->ply[2]->alive == 0 &&
10819         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10820       AllPlayersGone = TRUE;
10821   }
10822   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10823   {
10824     if (game_sp.LevelSolved &&
10825         !game_sp.GameOver)                              /* game won */
10826     {
10827       PlayerWins(local_player);
10828
10829       game_sp.GameOver = TRUE;
10830
10831       AllPlayersGone = TRUE;
10832     }
10833
10834     if (game_sp.GameOver)                               /* game lost */
10835       AllPlayersGone = TRUE;
10836   }
10837
10838   if (TimeFrames >= FRAMES_PER_SECOND)
10839   {
10840     TimeFrames = 0;
10841     TapeTime++;
10842
10843     for (i = 0; i < MAX_PLAYERS; i++)
10844     {
10845       struct PlayerInfo *player = &stored_player[i];
10846
10847       if (SHIELD_ON(player))
10848       {
10849         player->shield_normal_time_left--;
10850
10851         if (player->shield_deadly_time_left > 0)
10852           player->shield_deadly_time_left--;
10853       }
10854     }
10855
10856     if (!local_player->LevelSolved && !level.use_step_counter)
10857     {
10858       TimePlayed++;
10859
10860       if (TimeLeft > 0)
10861       {
10862         TimeLeft--;
10863
10864         if (TimeLeft <= 10 && setup.time_limit)
10865           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10866
10867         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10868            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10869
10870         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10871
10872         if (!TimeLeft && setup.time_limit)
10873         {
10874           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10875             level.native_em_level->lev->killed_out_of_time = TRUE;
10876           else
10877             for (i = 0; i < MAX_PLAYERS; i++)
10878               KillPlayer(&stored_player[i]);
10879         }
10880       }
10881       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10882       {
10883         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10884       }
10885
10886       level.native_em_level->lev->time =
10887         (game.no_time_limit ? TimePlayed : TimeLeft);
10888     }
10889
10890     if (tape.recording || tape.playing)
10891       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10892   }
10893
10894   if (tape.recording || tape.playing)
10895     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10896
10897   UpdateAndDisplayGameControlValues();
10898 }
10899
10900 void AdvanceFrameAndPlayerCounters(int player_nr)
10901 {
10902   int i;
10903
10904   /* advance frame counters (global frame counter and time frame counter) */
10905   FrameCounter++;
10906   TimeFrames++;
10907
10908   /* advance player counters (counters for move delay, move animation etc.) */
10909   for (i = 0; i < MAX_PLAYERS; i++)
10910   {
10911     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10912     int move_delay_value = stored_player[i].move_delay_value;
10913     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10914
10915     if (!advance_player_counters)       /* not all players may be affected */
10916       continue;
10917
10918     if (move_frames == 0)       /* less than one move per game frame */
10919     {
10920       int stepsize = TILEX / move_delay_value;
10921       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10922       int count = (stored_player[i].is_moving ?
10923                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10924
10925       if (count % delay == 0)
10926         move_frames = 1;
10927     }
10928
10929     stored_player[i].Frame += move_frames;
10930
10931     if (stored_player[i].MovPos != 0)
10932       stored_player[i].StepFrame += move_frames;
10933
10934     if (stored_player[i].move_delay > 0)
10935       stored_player[i].move_delay--;
10936
10937     /* due to bugs in previous versions, counter must count up, not down */
10938     if (stored_player[i].push_delay != -1)
10939       stored_player[i].push_delay++;
10940
10941     if (stored_player[i].drop_delay > 0)
10942       stored_player[i].drop_delay--;
10943
10944     if (stored_player[i].is_dropping_pressed)
10945       stored_player[i].drop_pressed_delay++;
10946   }
10947 }
10948
10949 void StartGameActions(boolean init_network_game, boolean record_tape,
10950                       int random_seed)
10951 {
10952   unsigned int new_random_seed = InitRND(random_seed);
10953
10954   if (record_tape)
10955     TapeStartRecording(new_random_seed);
10956
10957 #if defined(NETWORK_AVALIABLE)
10958   if (init_network_game)
10959   {
10960     SendToServer_StartPlaying();
10961
10962     return;
10963   }
10964 #endif
10965
10966   InitGame();
10967 }
10968
10969 void GameActionsExt()
10970 {
10971 #if 0
10972   static unsigned int game_frame_delay = 0;
10973 #endif
10974   unsigned int game_frame_delay_value;
10975   byte *recorded_player_action;
10976   byte summarized_player_action = 0;
10977   byte tape_action[MAX_PLAYERS];
10978   int i;
10979
10980   /* detect endless loops, caused by custom element programming */
10981   if (recursion_loop_detected && recursion_loop_depth == 0)
10982   {
10983     char *message = getStringCat3("Internal Error! Element ",
10984                                   EL_NAME(recursion_loop_element),
10985                                   " caused endless loop! Quit the game?");
10986
10987     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10988           EL_NAME(recursion_loop_element));
10989
10990     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10991
10992     recursion_loop_detected = FALSE;    /* if game should be continued */
10993
10994     free(message);
10995
10996     return;
10997   }
10998
10999   if (game.restart_level)
11000     StartGameActions(options.network, setup.autorecord, level.random_seed);
11001
11002   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11003   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11004   {
11005     if (level.native_em_level->lev->home == 0)  /* all players at home */
11006     {
11007       PlayerWins(local_player);
11008
11009       AllPlayersGone = TRUE;
11010
11011       level.native_em_level->lev->home = -1;
11012     }
11013
11014     if (level.native_em_level->ply[0]->alive == 0 &&
11015         level.native_em_level->ply[1]->alive == 0 &&
11016         level.native_em_level->ply[2]->alive == 0 &&
11017         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11018       AllPlayersGone = TRUE;
11019   }
11020   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11021   {
11022     if (game_sp.LevelSolved &&
11023         !game_sp.GameOver)                              /* game won */
11024     {
11025       PlayerWins(local_player);
11026
11027       game_sp.GameOver = TRUE;
11028
11029       AllPlayersGone = TRUE;
11030     }
11031
11032     if (game_sp.GameOver)                               /* game lost */
11033       AllPlayersGone = TRUE;
11034   }
11035
11036   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11037     GameWon();
11038
11039   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11040     TapeStop();
11041
11042   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11043     return;
11044
11045   game_frame_delay_value =
11046     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11047
11048   if (tape.playing && tape.warp_forward && !tape.pausing)
11049     game_frame_delay_value = 0;
11050
11051   SetVideoFrameDelay(game_frame_delay_value);
11052
11053 #if 0
11054 #if 0
11055   /* ---------- main game synchronization point ---------- */
11056
11057   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11058
11059   printf("::: skip == %d\n", skip);
11060
11061 #else
11062   /* ---------- main game synchronization point ---------- */
11063
11064   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11065 #endif
11066 #endif
11067
11068   if (network_playing && !network_player_action_received)
11069   {
11070     /* try to get network player actions in time */
11071
11072 #if defined(NETWORK_AVALIABLE)
11073     /* last chance to get network player actions without main loop delay */
11074     HandleNetworking();
11075 #endif
11076
11077     /* game was quit by network peer */
11078     if (game_status != GAME_MODE_PLAYING)
11079       return;
11080
11081     if (!network_player_action_received)
11082       return;           /* failed to get network player actions in time */
11083
11084     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11085   }
11086
11087   if (tape.pausing)
11088     return;
11089
11090   /* at this point we know that we really continue executing the game */
11091
11092   network_player_action_received = FALSE;
11093
11094   /* when playing tape, read previously recorded player input from tape data */
11095   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11096
11097   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11098   if (tape.pausing)
11099     return;
11100
11101   if (tape.set_centered_player)
11102   {
11103     game.centered_player_nr_next = tape.centered_player_nr_next;
11104     game.set_centered_player = TRUE;
11105   }
11106
11107   for (i = 0; i < MAX_PLAYERS; i++)
11108   {
11109     summarized_player_action |= stored_player[i].action;
11110
11111     if (!network_playing && (game.team_mode || tape.playing))
11112       stored_player[i].effective_action = stored_player[i].action;
11113   }
11114
11115 #if defined(NETWORK_AVALIABLE)
11116   if (network_playing)
11117     SendToServer_MovePlayer(summarized_player_action);
11118 #endif
11119
11120   // summarize all actions at local players mapped input device position
11121   // (this allows using different input devices in single player mode)
11122   if (!options.network && !game.team_mode)
11123     stored_player[map_player_action[local_player->index_nr]].effective_action =
11124       summarized_player_action;
11125
11126   if (tape.recording &&
11127       setup.team_mode &&
11128       setup.input_on_focus &&
11129       game.centered_player_nr != -1)
11130   {
11131     for (i = 0; i < MAX_PLAYERS; i++)
11132       stored_player[i].effective_action =
11133         (i == game.centered_player_nr ? summarized_player_action : 0);
11134   }
11135
11136   if (recorded_player_action != NULL)
11137     for (i = 0; i < MAX_PLAYERS; i++)
11138       stored_player[i].effective_action = recorded_player_action[i];
11139
11140   for (i = 0; i < MAX_PLAYERS; i++)
11141   {
11142     tape_action[i] = stored_player[i].effective_action;
11143
11144     /* (this may happen in the RND game engine if a player was not present on
11145        the playfield on level start, but appeared later from a custom element */
11146     if (setup.team_mode &&
11147         tape.recording &&
11148         tape_action[i] &&
11149         !tape.player_participates[i])
11150       tape.player_participates[i] = TRUE;
11151   }
11152
11153   /* only record actions from input devices, but not programmed actions */
11154   if (tape.recording)
11155     TapeRecordAction(tape_action);
11156
11157 #if USE_NEW_PLAYER_ASSIGNMENTS
11158   // !!! also map player actions in single player mode !!!
11159   // if (game.team_mode)
11160   if (1)
11161   {
11162     byte mapped_action[MAX_PLAYERS];
11163
11164 #if DEBUG_PLAYER_ACTIONS
11165     printf(":::");
11166     for (i = 0; i < MAX_PLAYERS; i++)
11167       printf(" %d, ", stored_player[i].effective_action);
11168 #endif
11169
11170     for (i = 0; i < MAX_PLAYERS; i++)
11171       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11172
11173     for (i = 0; i < MAX_PLAYERS; i++)
11174       stored_player[i].effective_action = mapped_action[i];
11175
11176 #if DEBUG_PLAYER_ACTIONS
11177     printf(" =>");
11178     for (i = 0; i < MAX_PLAYERS; i++)
11179       printf(" %d, ", stored_player[i].effective_action);
11180     printf("\n");
11181 #endif
11182   }
11183 #if DEBUG_PLAYER_ACTIONS
11184   else
11185   {
11186     printf(":::");
11187     for (i = 0; i < MAX_PLAYERS; i++)
11188       printf(" %d, ", stored_player[i].effective_action);
11189     printf("\n");
11190   }
11191 #endif
11192 #endif
11193
11194   for (i = 0; i < MAX_PLAYERS; i++)
11195   {
11196     // allow engine snapshot in case of changed movement attempt
11197     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11198         (stored_player[i].effective_action & KEY_MOTION))
11199       game.snapshot.changed_action = TRUE;
11200
11201     // allow engine snapshot in case of snapping/dropping attempt
11202     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11203         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11204       game.snapshot.changed_action = TRUE;
11205
11206     game.snapshot.last_action[i] = stored_player[i].effective_action;
11207   }
11208
11209   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11210   {
11211     GameActions_EM_Main();
11212   }
11213   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11214   {
11215     GameActions_SP_Main();
11216   }
11217   else
11218   {
11219     GameActions_RND_Main();
11220   }
11221
11222   BlitScreenToBitmap(backbuffer);
11223
11224   CheckLevelTime();
11225
11226   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11227
11228   if (global.show_frames_per_second)
11229   {
11230     static unsigned int fps_counter = 0;
11231     static int fps_frames = 0;
11232     unsigned int fps_delay_ms = Counter() - fps_counter;
11233
11234     fps_frames++;
11235
11236     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11237     {
11238       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11239
11240       fps_frames = 0;
11241       fps_counter = Counter();
11242
11243       /* always draw FPS to screen after FPS value was updated */
11244       redraw_mask |= REDRAW_FPS;
11245     }
11246
11247     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11248     if (GetDrawDeactivationMask() == REDRAW_NONE)
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   for (i = 0; i < MAX_PLAYERS; i++)
11295   {
11296     if (stored_player[i].force_dropping)
11297       stored_player[i].action |= KEY_BUTTON_DROP;
11298
11299     stored_player[i].force_dropping = FALSE;
11300   }
11301 }
11302
11303 void GameActions_RND_Main()
11304 {
11305   GameActions_RND();
11306 }
11307
11308 void GameActions_RND()
11309 {
11310   int magic_wall_x = 0, magic_wall_y = 0;
11311   int i, x, y, element, graphic, last_gfx_frame;
11312
11313   InitPlayfieldScanModeVars();
11314
11315   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11316   {
11317     SCAN_PLAYFIELD(x, y)
11318     {
11319       ChangeCount[x][y] = 0;
11320       ChangeEvent[x][y] = -1;
11321     }
11322   }
11323
11324   if (game.set_centered_player)
11325   {
11326     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11327
11328     /* switching to "all players" only possible if all players fit to screen */
11329     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11330     {
11331       game.centered_player_nr_next = game.centered_player_nr;
11332       game.set_centered_player = FALSE;
11333     }
11334
11335     /* do not switch focus to non-existing (or non-active) player */
11336     if (game.centered_player_nr_next >= 0 &&
11337         !stored_player[game.centered_player_nr_next].active)
11338     {
11339       game.centered_player_nr_next = game.centered_player_nr;
11340       game.set_centered_player = FALSE;
11341     }
11342   }
11343
11344   if (game.set_centered_player &&
11345       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11346   {
11347     int sx, sy;
11348
11349     if (game.centered_player_nr_next == -1)
11350     {
11351       setScreenCenteredToAllPlayers(&sx, &sy);
11352     }
11353     else
11354     {
11355       sx = stored_player[game.centered_player_nr_next].jx;
11356       sy = stored_player[game.centered_player_nr_next].jy;
11357     }
11358
11359     game.centered_player_nr = game.centered_player_nr_next;
11360     game.set_centered_player = FALSE;
11361
11362     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11363     DrawGameDoorValues();
11364   }
11365
11366   for (i = 0; i < MAX_PLAYERS; i++)
11367   {
11368     int actual_player_action = stored_player[i].effective_action;
11369
11370 #if 1
11371     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11372        - rnd_equinox_tetrachloride 048
11373        - rnd_equinox_tetrachloride_ii 096
11374        - rnd_emanuel_schmieg 002
11375        - doctor_sloan_ww 001, 020
11376     */
11377     if (stored_player[i].MovPos == 0)
11378       CheckGravityMovement(&stored_player[i]);
11379 #endif
11380
11381     /* overwrite programmed action with tape action */
11382     if (stored_player[i].programmed_action)
11383       actual_player_action = stored_player[i].programmed_action;
11384
11385     PlayerActions(&stored_player[i], actual_player_action);
11386
11387     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11388   }
11389
11390   ScrollScreen(NULL, SCROLL_GO_ON);
11391
11392   /* for backwards compatibility, the following code emulates a fixed bug that
11393      occured when pushing elements (causing elements that just made their last
11394      pushing step to already (if possible) make their first falling step in the
11395      same game frame, which is bad); this code is also needed to use the famous
11396      "spring push bug" which is used in older levels and might be wanted to be
11397      used also in newer levels, but in this case the buggy pushing code is only
11398      affecting the "spring" element and no other elements */
11399
11400   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11401   {
11402     for (i = 0; i < MAX_PLAYERS; i++)
11403     {
11404       struct PlayerInfo *player = &stored_player[i];
11405       int x = player->jx;
11406       int y = player->jy;
11407
11408       if (player->active && player->is_pushing && player->is_moving &&
11409           IS_MOVING(x, y) &&
11410           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11411            Feld[x][y] == EL_SPRING))
11412       {
11413         ContinueMoving(x, y);
11414
11415         /* continue moving after pushing (this is actually a bug) */
11416         if (!IS_MOVING(x, y))
11417           Stop[x][y] = FALSE;
11418       }
11419     }
11420   }
11421
11422   SCAN_PLAYFIELD(x, y)
11423   {
11424     ChangeCount[x][y] = 0;
11425     ChangeEvent[x][y] = -1;
11426
11427     /* this must be handled before main playfield loop */
11428     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11429     {
11430       MovDelay[x][y]--;
11431       if (MovDelay[x][y] <= 0)
11432         RemoveField(x, y);
11433     }
11434
11435     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11436     {
11437       MovDelay[x][y]--;
11438       if (MovDelay[x][y] <= 0)
11439       {
11440         RemoveField(x, y);
11441         TEST_DrawLevelField(x, y);
11442
11443         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11444       }
11445     }
11446
11447 #if DEBUG
11448     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11449     {
11450       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11451       printf("GameActions(): This should never happen!\n");
11452
11453       ChangePage[x][y] = -1;
11454     }
11455 #endif
11456
11457     Stop[x][y] = FALSE;
11458     if (WasJustMoving[x][y] > 0)
11459       WasJustMoving[x][y]--;
11460     if (WasJustFalling[x][y] > 0)
11461       WasJustFalling[x][y]--;
11462     if (CheckCollision[x][y] > 0)
11463       CheckCollision[x][y]--;
11464     if (CheckImpact[x][y] > 0)
11465       CheckImpact[x][y]--;
11466
11467     GfxFrame[x][y]++;
11468
11469     /* reset finished pushing action (not done in ContinueMoving() to allow
11470        continuous pushing animation for elements with zero push delay) */
11471     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11472     {
11473       ResetGfxAnimation(x, y);
11474       TEST_DrawLevelField(x, y);
11475     }
11476
11477 #if DEBUG
11478     if (IS_BLOCKED(x, y))
11479     {
11480       int oldx, oldy;
11481
11482       Blocked2Moving(x, y, &oldx, &oldy);
11483       if (!IS_MOVING(oldx, oldy))
11484       {
11485         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11486         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11487         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11488         printf("GameActions(): This should never happen!\n");
11489       }
11490     }
11491 #endif
11492   }
11493
11494   SCAN_PLAYFIELD(x, y)
11495   {
11496     element = Feld[x][y];
11497     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11498     last_gfx_frame = GfxFrame[x][y];
11499
11500     ResetGfxFrame(x, y);
11501
11502     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11503       DrawLevelGraphicAnimation(x, y, graphic);
11504
11505     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11506         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11507       ResetRandomAnimationValue(x, y);
11508
11509     SetRandomAnimationValue(x, y);
11510
11511     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11512
11513     if (IS_INACTIVE(element))
11514     {
11515       if (IS_ANIMATED(graphic))
11516         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11517
11518       continue;
11519     }
11520
11521     /* this may take place after moving, so 'element' may have changed */
11522     if (IS_CHANGING(x, y) &&
11523         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11524     {
11525       int page = element_info[element].event_page_nr[CE_DELAY];
11526
11527       HandleElementChange(x, y, page);
11528
11529       element = Feld[x][y];
11530       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11531     }
11532
11533     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11534     {
11535       StartMoving(x, y);
11536
11537       element = Feld[x][y];
11538       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11539
11540       if (IS_ANIMATED(graphic) &&
11541           !IS_MOVING(x, y) &&
11542           !Stop[x][y])
11543         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11544
11545       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11546         TEST_DrawTwinkleOnField(x, y);
11547     }
11548     else if (element == EL_ACID)
11549     {
11550       if (!Stop[x][y])
11551         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11552     }
11553     else if ((element == EL_EXIT_OPEN ||
11554               element == EL_EM_EXIT_OPEN ||
11555               element == EL_SP_EXIT_OPEN ||
11556               element == EL_STEEL_EXIT_OPEN ||
11557               element == EL_EM_STEEL_EXIT_OPEN ||
11558               element == EL_SP_TERMINAL ||
11559               element == EL_SP_TERMINAL_ACTIVE ||
11560               element == EL_EXTRA_TIME ||
11561               element == EL_SHIELD_NORMAL ||
11562               element == EL_SHIELD_DEADLY) &&
11563              IS_ANIMATED(graphic))
11564       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11565     else if (IS_MOVING(x, y))
11566       ContinueMoving(x, y);
11567     else if (IS_ACTIVE_BOMB(element))
11568       CheckDynamite(x, y);
11569     else if (element == EL_AMOEBA_GROWING)
11570       AmoebeWaechst(x, y);
11571     else if (element == EL_AMOEBA_SHRINKING)
11572       AmoebaDisappearing(x, y);
11573
11574 #if !USE_NEW_AMOEBA_CODE
11575     else if (IS_AMOEBALIVE(element))
11576       AmoebeAbleger(x, y);
11577 #endif
11578
11579     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11580       Life(x, y);
11581     else if (element == EL_EXIT_CLOSED)
11582       CheckExit(x, y);
11583     else if (element == EL_EM_EXIT_CLOSED)
11584       CheckExitEM(x, y);
11585     else if (element == EL_STEEL_EXIT_CLOSED)
11586       CheckExitSteel(x, y);
11587     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11588       CheckExitSteelEM(x, y);
11589     else if (element == EL_SP_EXIT_CLOSED)
11590       CheckExitSP(x, y);
11591     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11592              element == EL_EXPANDABLE_STEELWALL_GROWING)
11593       MauerWaechst(x, y);
11594     else if (element == EL_EXPANDABLE_WALL ||
11595              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11596              element == EL_EXPANDABLE_WALL_VERTICAL ||
11597              element == EL_EXPANDABLE_WALL_ANY ||
11598              element == EL_BD_EXPANDABLE_WALL)
11599       MauerAbleger(x, y);
11600     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11601              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11602              element == EL_EXPANDABLE_STEELWALL_ANY)
11603       MauerAblegerStahl(x, y);
11604     else if (element == EL_FLAMES)
11605       CheckForDragon(x, y);
11606     else if (element == EL_EXPLOSION)
11607       ; /* drawing of correct explosion animation is handled separately */
11608     else if (element == EL_ELEMENT_SNAPPING ||
11609              element == EL_DIAGONAL_SHRINKING ||
11610              element == EL_DIAGONAL_GROWING)
11611     {
11612       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11613
11614       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11615     }
11616     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11617       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11618
11619     if (IS_BELT_ACTIVE(element))
11620       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11621
11622     if (game.magic_wall_active)
11623     {
11624       int jx = local_player->jx, jy = local_player->jy;
11625
11626       /* play the element sound at the position nearest to the player */
11627       if ((element == EL_MAGIC_WALL_FULL ||
11628            element == EL_MAGIC_WALL_ACTIVE ||
11629            element == EL_MAGIC_WALL_EMPTYING ||
11630            element == EL_BD_MAGIC_WALL_FULL ||
11631            element == EL_BD_MAGIC_WALL_ACTIVE ||
11632            element == EL_BD_MAGIC_WALL_EMPTYING ||
11633            element == EL_DC_MAGIC_WALL_FULL ||
11634            element == EL_DC_MAGIC_WALL_ACTIVE ||
11635            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11636           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11637       {
11638         magic_wall_x = x;
11639         magic_wall_y = y;
11640       }
11641     }
11642   }
11643
11644 #if USE_NEW_AMOEBA_CODE
11645   /* new experimental amoeba growth stuff */
11646   if (!(FrameCounter % 8))
11647   {
11648     static unsigned int random = 1684108901;
11649
11650     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11651     {
11652       x = RND(lev_fieldx);
11653       y = RND(lev_fieldy);
11654       element = Feld[x][y];
11655
11656       if (!IS_PLAYER(x,y) &&
11657           (element == EL_EMPTY ||
11658            CAN_GROW_INTO(element) ||
11659            element == EL_QUICKSAND_EMPTY ||
11660            element == EL_QUICKSAND_FAST_EMPTY ||
11661            element == EL_ACID_SPLASH_LEFT ||
11662            element == EL_ACID_SPLASH_RIGHT))
11663       {
11664         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11665             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11666             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11667             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11668           Feld[x][y] = EL_AMOEBA_DROP;
11669       }
11670
11671       random = random * 129 + 1;
11672     }
11673   }
11674 #endif
11675
11676   game.explosions_delayed = FALSE;
11677
11678   SCAN_PLAYFIELD(x, y)
11679   {
11680     element = Feld[x][y];
11681
11682     if (ExplodeField[x][y])
11683       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11684     else if (element == EL_EXPLOSION)
11685       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11686
11687     ExplodeField[x][y] = EX_TYPE_NONE;
11688   }
11689
11690   game.explosions_delayed = TRUE;
11691
11692   if (game.magic_wall_active)
11693   {
11694     if (!(game.magic_wall_time_left % 4))
11695     {
11696       int element = Feld[magic_wall_x][magic_wall_y];
11697
11698       if (element == EL_BD_MAGIC_WALL_FULL ||
11699           element == EL_BD_MAGIC_WALL_ACTIVE ||
11700           element == EL_BD_MAGIC_WALL_EMPTYING)
11701         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11702       else if (element == EL_DC_MAGIC_WALL_FULL ||
11703                element == EL_DC_MAGIC_WALL_ACTIVE ||
11704                element == EL_DC_MAGIC_WALL_EMPTYING)
11705         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11706       else
11707         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11708     }
11709
11710     if (game.magic_wall_time_left > 0)
11711     {
11712       game.magic_wall_time_left--;
11713
11714       if (!game.magic_wall_time_left)
11715       {
11716         SCAN_PLAYFIELD(x, y)
11717         {
11718           element = Feld[x][y];
11719
11720           if (element == EL_MAGIC_WALL_ACTIVE ||
11721               element == EL_MAGIC_WALL_FULL)
11722           {
11723             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11724             TEST_DrawLevelField(x, y);
11725           }
11726           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11727                    element == EL_BD_MAGIC_WALL_FULL)
11728           {
11729             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11730             TEST_DrawLevelField(x, y);
11731           }
11732           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11733                    element == EL_DC_MAGIC_WALL_FULL)
11734           {
11735             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11736             TEST_DrawLevelField(x, y);
11737           }
11738         }
11739
11740         game.magic_wall_active = FALSE;
11741       }
11742     }
11743   }
11744
11745   if (game.light_time_left > 0)
11746   {
11747     game.light_time_left--;
11748
11749     if (game.light_time_left == 0)
11750       RedrawAllLightSwitchesAndInvisibleElements();
11751   }
11752
11753   if (game.timegate_time_left > 0)
11754   {
11755     game.timegate_time_left--;
11756
11757     if (game.timegate_time_left == 0)
11758       CloseAllOpenTimegates();
11759   }
11760
11761   if (game.lenses_time_left > 0)
11762   {
11763     game.lenses_time_left--;
11764
11765     if (game.lenses_time_left == 0)
11766       RedrawAllInvisibleElementsForLenses();
11767   }
11768
11769   if (game.magnify_time_left > 0)
11770   {
11771     game.magnify_time_left--;
11772
11773     if (game.magnify_time_left == 0)
11774       RedrawAllInvisibleElementsForMagnifier();
11775   }
11776
11777   for (i = 0; i < MAX_PLAYERS; i++)
11778   {
11779     struct PlayerInfo *player = &stored_player[i];
11780
11781     if (SHIELD_ON(player))
11782     {
11783       if (player->shield_deadly_time_left)
11784         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11785       else if (player->shield_normal_time_left)
11786         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11787     }
11788   }
11789
11790 #if USE_DELAYED_GFX_REDRAW
11791   SCAN_PLAYFIELD(x, y)
11792   {
11793     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11794     {
11795       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11796          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11797
11798       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11799         DrawLevelField(x, y);
11800
11801       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11802         DrawLevelFieldCrumbled(x, y);
11803
11804       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11805         DrawLevelFieldCrumbledNeighbours(x, y);
11806
11807       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11808         DrawTwinkleOnField(x, y);
11809     }
11810
11811     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11812   }
11813 #endif
11814
11815   DrawAllPlayers();
11816   PlayAllPlayersSound();
11817
11818   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11819   {
11820     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11821
11822     local_player->show_envelope = 0;
11823   }
11824
11825   /* use random number generator in every frame to make it less predictable */
11826   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11827     RND(1);
11828 }
11829
11830 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11831 {
11832   int min_x = x, min_y = y, max_x = x, max_y = y;
11833   int i;
11834
11835   for (i = 0; i < MAX_PLAYERS; i++)
11836   {
11837     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11838
11839     if (!stored_player[i].active || &stored_player[i] == player)
11840       continue;
11841
11842     min_x = MIN(min_x, jx);
11843     min_y = MIN(min_y, jy);
11844     max_x = MAX(max_x, jx);
11845     max_y = MAX(max_y, jy);
11846   }
11847
11848   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11849 }
11850
11851 static boolean AllPlayersInVisibleScreen()
11852 {
11853   int i;
11854
11855   for (i = 0; i < MAX_PLAYERS; i++)
11856   {
11857     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11858
11859     if (!stored_player[i].active)
11860       continue;
11861
11862     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11863       return FALSE;
11864   }
11865
11866   return TRUE;
11867 }
11868
11869 void ScrollLevel(int dx, int dy)
11870 {
11871   int scroll_offset = 2 * TILEX_VAR;
11872   int x, y;
11873
11874   BlitBitmap(drawto_field, drawto_field,
11875              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11876              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11877              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11878              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11879              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11880              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11881
11882   if (dx != 0)
11883   {
11884     x = (dx == 1 ? BX1 : BX2);
11885     for (y = BY1; y <= BY2; y++)
11886       DrawScreenField(x, y);
11887   }
11888
11889   if (dy != 0)
11890   {
11891     y = (dy == 1 ? BY1 : BY2);
11892     for (x = BX1; x <= BX2; x++)
11893       DrawScreenField(x, y);
11894   }
11895
11896   redraw_mask |= REDRAW_FIELD;
11897 }
11898
11899 static boolean canFallDown(struct PlayerInfo *player)
11900 {
11901   int jx = player->jx, jy = player->jy;
11902
11903   return (IN_LEV_FIELD(jx, jy + 1) &&
11904           (IS_FREE(jx, jy + 1) ||
11905            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11906           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11907           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11908 }
11909
11910 static boolean canPassField(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 nextx = x + dx;
11916   int nexty = y + dy;
11917   int element = Feld[x][y];
11918
11919   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11920           !CAN_MOVE(element) &&
11921           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11922           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11923           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11924 }
11925
11926 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11927 {
11928   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11929   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11930   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11931   int newx = x + dx;
11932   int newy = y + dy;
11933
11934   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11935           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11936           (IS_DIGGABLE(Feld[newx][newy]) ||
11937            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11938            canPassField(newx, newy, move_dir)));
11939 }
11940
11941 static void CheckGravityMovement(struct PlayerInfo *player)
11942 {
11943   if (player->gravity && !player->programmed_action)
11944   {
11945     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11946     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11947     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11948     int jx = player->jx, jy = player->jy;
11949     boolean player_is_moving_to_valid_field =
11950       (!player_is_snapping &&
11951        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11952         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11953     boolean player_can_fall_down = canFallDown(player);
11954
11955     if (player_can_fall_down &&
11956         !player_is_moving_to_valid_field)
11957       player->programmed_action = MV_DOWN;
11958   }
11959 }
11960
11961 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11962 {
11963   return CheckGravityMovement(player);
11964
11965   if (player->gravity && !player->programmed_action)
11966   {
11967     int jx = player->jx, jy = player->jy;
11968     boolean field_under_player_is_free =
11969       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11970     boolean player_is_standing_on_valid_field =
11971       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11972        (IS_WALKABLE(Feld[jx][jy]) &&
11973         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11974
11975     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11976       player->programmed_action = MV_DOWN;
11977   }
11978 }
11979
11980 /*
11981   MovePlayerOneStep()
11982   -----------------------------------------------------------------------------
11983   dx, dy:               direction (non-diagonal) to try to move the player to
11984   real_dx, real_dy:     direction as read from input device (can be diagonal)
11985 */
11986
11987 boolean MovePlayerOneStep(struct PlayerInfo *player,
11988                           int dx, int dy, int real_dx, int real_dy)
11989 {
11990   int jx = player->jx, jy = player->jy;
11991   int new_jx = jx + dx, new_jy = jy + dy;
11992   int can_move;
11993   boolean player_can_move = !player->cannot_move;
11994
11995   if (!player->active || (!dx && !dy))
11996     return MP_NO_ACTION;
11997
11998   player->MovDir = (dx < 0 ? MV_LEFT :
11999                     dx > 0 ? MV_RIGHT :
12000                     dy < 0 ? MV_UP :
12001                     dy > 0 ? MV_DOWN :  MV_NONE);
12002
12003   if (!IN_LEV_FIELD(new_jx, new_jy))
12004     return MP_NO_ACTION;
12005
12006   if (!player_can_move)
12007   {
12008     if (player->MovPos == 0)
12009     {
12010       player->is_moving = FALSE;
12011       player->is_digging = FALSE;
12012       player->is_collecting = FALSE;
12013       player->is_snapping = FALSE;
12014       player->is_pushing = FALSE;
12015     }
12016   }
12017
12018   if (!options.network && game.centered_player_nr == -1 &&
12019       !AllPlayersInSight(player, new_jx, new_jy))
12020     return MP_NO_ACTION;
12021
12022   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12023   if (can_move != MP_MOVING)
12024     return can_move;
12025
12026   /* check if DigField() has caused relocation of the player */
12027   if (player->jx != jx || player->jy != jy)
12028     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12029
12030   StorePlayer[jx][jy] = 0;
12031   player->last_jx = jx;
12032   player->last_jy = jy;
12033   player->jx = new_jx;
12034   player->jy = new_jy;
12035   StorePlayer[new_jx][new_jy] = player->element_nr;
12036
12037   if (player->move_delay_value_next != -1)
12038   {
12039     player->move_delay_value = player->move_delay_value_next;
12040     player->move_delay_value_next = -1;
12041   }
12042
12043   player->MovPos =
12044     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12045
12046   player->step_counter++;
12047
12048   PlayerVisit[jx][jy] = FrameCounter;
12049
12050   player->is_moving = TRUE;
12051
12052 #if 1
12053   /* should better be called in MovePlayer(), but this breaks some tapes */
12054   ScrollPlayer(player, SCROLL_INIT);
12055 #endif
12056
12057   return MP_MOVING;
12058 }
12059
12060 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12061 {
12062   int jx = player->jx, jy = player->jy;
12063   int old_jx = jx, old_jy = jy;
12064   int moved = MP_NO_ACTION;
12065
12066   if (!player->active)
12067     return FALSE;
12068
12069   if (!dx && !dy)
12070   {
12071     if (player->MovPos == 0)
12072     {
12073       player->is_moving = FALSE;
12074       player->is_digging = FALSE;
12075       player->is_collecting = FALSE;
12076       player->is_snapping = FALSE;
12077       player->is_pushing = FALSE;
12078     }
12079
12080     return FALSE;
12081   }
12082
12083   if (player->move_delay > 0)
12084     return FALSE;
12085
12086   player->move_delay = -1;              /* set to "uninitialized" value */
12087
12088   /* store if player is automatically moved to next field */
12089   player->is_auto_moving = (player->programmed_action != MV_NONE);
12090
12091   /* remove the last programmed player action */
12092   player->programmed_action = 0;
12093
12094   if (player->MovPos)
12095   {
12096     /* should only happen if pre-1.2 tape recordings are played */
12097     /* this is only for backward compatibility */
12098
12099     int original_move_delay_value = player->move_delay_value;
12100
12101 #if DEBUG
12102     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12103            tape.counter);
12104 #endif
12105
12106     /* scroll remaining steps with finest movement resolution */
12107     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12108
12109     while (player->MovPos)
12110     {
12111       ScrollPlayer(player, SCROLL_GO_ON);
12112       ScrollScreen(NULL, SCROLL_GO_ON);
12113
12114       AdvanceFrameAndPlayerCounters(player->index_nr);
12115
12116       DrawAllPlayers();
12117       BackToFront_WithFrameDelay(0);
12118     }
12119
12120     player->move_delay_value = original_move_delay_value;
12121   }
12122
12123   player->is_active = FALSE;
12124
12125   if (player->last_move_dir & MV_HORIZONTAL)
12126   {
12127     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12128       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12129   }
12130   else
12131   {
12132     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12133       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12134   }
12135
12136   if (!moved && !player->is_active)
12137   {
12138     player->is_moving = FALSE;
12139     player->is_digging = FALSE;
12140     player->is_collecting = FALSE;
12141     player->is_snapping = FALSE;
12142     player->is_pushing = FALSE;
12143   }
12144
12145   jx = player->jx;
12146   jy = player->jy;
12147
12148   if (moved & MP_MOVING && !ScreenMovPos &&
12149       (player->index_nr == game.centered_player_nr ||
12150        game.centered_player_nr == -1))
12151   {
12152     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12153     int offset = game.scroll_delay_value;
12154
12155     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12156     {
12157       /* actual player has left the screen -- scroll in that direction */
12158       if (jx != old_jx)         /* player has moved horizontally */
12159         scroll_x += (jx - old_jx);
12160       else                      /* player has moved vertically */
12161         scroll_y += (jy - old_jy);
12162     }
12163     else
12164     {
12165       if (jx != old_jx)         /* player has moved horizontally */
12166       {
12167         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12168             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12169           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12170
12171         /* don't scroll over playfield boundaries */
12172         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12173           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12174
12175         /* don't scroll more than one field at a time */
12176         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12177
12178         /* don't scroll against the player's moving direction */
12179         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12180             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12181           scroll_x = old_scroll_x;
12182       }
12183       else                      /* player has moved vertically */
12184       {
12185         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12186             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12187           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12188
12189         /* don't scroll over playfield boundaries */
12190         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12191           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12192
12193         /* don't scroll more than one field at a time */
12194         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12195
12196         /* don't scroll against the player's moving direction */
12197         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12198             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12199           scroll_y = old_scroll_y;
12200       }
12201     }
12202
12203     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12204     {
12205       if (!options.network && game.centered_player_nr == -1 &&
12206           !AllPlayersInVisibleScreen())
12207       {
12208         scroll_x = old_scroll_x;
12209         scroll_y = old_scroll_y;
12210       }
12211       else
12212       {
12213         ScrollScreen(player, SCROLL_INIT);
12214         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12215       }
12216     }
12217   }
12218
12219   player->StepFrame = 0;
12220
12221   if (moved & MP_MOVING)
12222   {
12223     if (old_jx != jx && old_jy == jy)
12224       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12225     else if (old_jx == jx && old_jy != jy)
12226       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12227
12228     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12229
12230     player->last_move_dir = player->MovDir;
12231     player->is_moving = TRUE;
12232     player->is_snapping = FALSE;
12233     player->is_switching = FALSE;
12234     player->is_dropping = FALSE;
12235     player->is_dropping_pressed = FALSE;
12236     player->drop_pressed_delay = 0;
12237
12238 #if 0
12239     /* should better be called here than above, but this breaks some tapes */
12240     ScrollPlayer(player, SCROLL_INIT);
12241 #endif
12242   }
12243   else
12244   {
12245     CheckGravityMovementWhenNotMoving(player);
12246
12247     player->is_moving = FALSE;
12248
12249     /* at this point, the player is allowed to move, but cannot move right now
12250        (e.g. because of something blocking the way) -- ensure that the player
12251        is also allowed to move in the next frame (in old versions before 3.1.1,
12252        the player was forced to wait again for eight frames before next try) */
12253
12254     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12255       player->move_delay = 0;   /* allow direct movement in the next frame */
12256   }
12257
12258   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12259     player->move_delay = player->move_delay_value;
12260
12261   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12262   {
12263     TestIfPlayerTouchesBadThing(jx, jy);
12264     TestIfPlayerTouchesCustomElement(jx, jy);
12265   }
12266
12267   if (!player->active)
12268     RemovePlayer(player);
12269
12270   return moved;
12271 }
12272
12273 void ScrollPlayer(struct PlayerInfo *player, int mode)
12274 {
12275   int jx = player->jx, jy = player->jy;
12276   int last_jx = player->last_jx, last_jy = player->last_jy;
12277   int move_stepsize = TILEX / player->move_delay_value;
12278
12279   if (!player->active)
12280     return;
12281
12282   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12283     return;
12284
12285   if (mode == SCROLL_INIT)
12286   {
12287     player->actual_frame_counter = FrameCounter;
12288     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12289
12290     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12291         Feld[last_jx][last_jy] == EL_EMPTY)
12292     {
12293       int last_field_block_delay = 0;   /* start with no blocking at all */
12294       int block_delay_adjustment = player->block_delay_adjustment;
12295
12296       /* if player blocks last field, add delay for exactly one move */
12297       if (player->block_last_field)
12298       {
12299         last_field_block_delay += player->move_delay_value;
12300
12301         /* when blocking enabled, prevent moving up despite gravity */
12302         if (player->gravity && player->MovDir == MV_UP)
12303           block_delay_adjustment = -1;
12304       }
12305
12306       /* add block delay adjustment (also possible when not blocking) */
12307       last_field_block_delay += block_delay_adjustment;
12308
12309       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12310       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12311     }
12312
12313     if (player->MovPos != 0)    /* player has not yet reached destination */
12314       return;
12315   }
12316   else if (!FrameReached(&player->actual_frame_counter, 1))
12317     return;
12318
12319   if (player->MovPos != 0)
12320   {
12321     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12322     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12323
12324     /* before DrawPlayer() to draw correct player graphic for this case */
12325     if (player->MovPos == 0)
12326       CheckGravityMovement(player);
12327   }
12328
12329   if (player->MovPos == 0)      /* player reached destination field */
12330   {
12331     if (player->move_delay_reset_counter > 0)
12332     {
12333       player->move_delay_reset_counter--;
12334
12335       if (player->move_delay_reset_counter == 0)
12336       {
12337         /* continue with normal speed after quickly moving through gate */
12338         HALVE_PLAYER_SPEED(player);
12339
12340         /* be able to make the next move without delay */
12341         player->move_delay = 0;
12342       }
12343     }
12344
12345     player->last_jx = jx;
12346     player->last_jy = jy;
12347
12348     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12349         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12350         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12351         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12352         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12353         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12354         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12355         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12356     {
12357       DrawPlayer(player);       /* needed here only to cleanup last field */
12358       RemovePlayer(player);
12359
12360       if (local_player->friends_still_needed == 0 ||
12361           IS_SP_ELEMENT(Feld[jx][jy]))
12362         PlayerWins(player);
12363     }
12364
12365     /* this breaks one level: "machine", level 000 */
12366     {
12367       int move_direction = player->MovDir;
12368       int enter_side = MV_DIR_OPPOSITE(move_direction);
12369       int leave_side = move_direction;
12370       int old_jx = last_jx;
12371       int old_jy = last_jy;
12372       int old_element = Feld[old_jx][old_jy];
12373       int new_element = Feld[jx][jy];
12374
12375       if (IS_CUSTOM_ELEMENT(old_element))
12376         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12377                                    CE_LEFT_BY_PLAYER,
12378                                    player->index_bit, leave_side);
12379
12380       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12381                                           CE_PLAYER_LEAVES_X,
12382                                           player->index_bit, leave_side);
12383
12384       if (IS_CUSTOM_ELEMENT(new_element))
12385         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12386                                    player->index_bit, enter_side);
12387
12388       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12389                                           CE_PLAYER_ENTERS_X,
12390                                           player->index_bit, enter_side);
12391
12392       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12393                                         CE_MOVE_OF_X, move_direction);
12394     }
12395
12396     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12397     {
12398       TestIfPlayerTouchesBadThing(jx, jy);
12399       TestIfPlayerTouchesCustomElement(jx, jy);
12400
12401       /* needed because pushed element has not yet reached its destination,
12402          so it would trigger a change event at its previous field location */
12403       if (!player->is_pushing)
12404         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12405
12406       if (!player->active)
12407         RemovePlayer(player);
12408     }
12409
12410     if (!local_player->LevelSolved && level.use_step_counter)
12411     {
12412       int i;
12413
12414       TimePlayed++;
12415
12416       if (TimeLeft > 0)
12417       {
12418         TimeLeft--;
12419
12420         if (TimeLeft <= 10 && setup.time_limit)
12421           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12422
12423         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12424
12425         DisplayGameControlValues();
12426
12427         if (!TimeLeft && setup.time_limit)
12428           for (i = 0; i < MAX_PLAYERS; i++)
12429             KillPlayer(&stored_player[i]);
12430       }
12431       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12432       {
12433         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12434
12435         DisplayGameControlValues();
12436       }
12437     }
12438
12439     if (tape.single_step && tape.recording && !tape.pausing &&
12440         !player->programmed_action)
12441       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12442
12443     if (!player->programmed_action)
12444       CheckSaveEngineSnapshot(player);
12445   }
12446 }
12447
12448 void ScrollScreen(struct PlayerInfo *player, int mode)
12449 {
12450   static unsigned int screen_frame_counter = 0;
12451
12452   if (mode == SCROLL_INIT)
12453   {
12454     /* set scrolling step size according to actual player's moving speed */
12455     ScrollStepSize = TILEX / player->move_delay_value;
12456
12457     screen_frame_counter = FrameCounter;
12458     ScreenMovDir = player->MovDir;
12459     ScreenMovPos = player->MovPos;
12460     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12461     return;
12462   }
12463   else if (!FrameReached(&screen_frame_counter, 1))
12464     return;
12465
12466   if (ScreenMovPos)
12467   {
12468     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12469     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12470     redraw_mask |= REDRAW_FIELD;
12471   }
12472   else
12473     ScreenMovDir = MV_NONE;
12474 }
12475
12476 void TestIfPlayerTouchesCustomElement(int x, int y)
12477 {
12478   static int xy[4][2] =
12479   {
12480     { 0, -1 },
12481     { -1, 0 },
12482     { +1, 0 },
12483     { 0, +1 }
12484   };
12485   static int trigger_sides[4][2] =
12486   {
12487     /* center side       border side */
12488     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12489     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12490     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12491     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12492   };
12493   static int touch_dir[4] =
12494   {
12495     MV_LEFT | MV_RIGHT,
12496     MV_UP   | MV_DOWN,
12497     MV_UP   | MV_DOWN,
12498     MV_LEFT | MV_RIGHT
12499   };
12500   int center_element = Feld[x][y];      /* should always be non-moving! */
12501   int i;
12502
12503   for (i = 0; i < NUM_DIRECTIONS; i++)
12504   {
12505     int xx = x + xy[i][0];
12506     int yy = y + xy[i][1];
12507     int center_side = trigger_sides[i][0];
12508     int border_side = trigger_sides[i][1];
12509     int border_element;
12510
12511     if (!IN_LEV_FIELD(xx, yy))
12512       continue;
12513
12514     if (IS_PLAYER(x, y))                /* player found at center element */
12515     {
12516       struct PlayerInfo *player = PLAYERINFO(x, y);
12517
12518       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12519         border_element = Feld[xx][yy];          /* may be moving! */
12520       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12521         border_element = Feld[xx][yy];
12522       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12523         border_element = MovingOrBlocked2Element(xx, yy);
12524       else
12525         continue;               /* center and border element do not touch */
12526
12527       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12528                                  player->index_bit, border_side);
12529       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12530                                           CE_PLAYER_TOUCHES_X,
12531                                           player->index_bit, border_side);
12532
12533       {
12534         /* use player element that is initially defined in the level playfield,
12535            not the player element that corresponds to the runtime player number
12536            (example: a level that contains EL_PLAYER_3 as the only player would
12537            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12538         int player_element = PLAYERINFO(x, y)->initial_element;
12539
12540         CheckElementChangeBySide(xx, yy, border_element, player_element,
12541                                  CE_TOUCHING_X, border_side);
12542       }
12543     }
12544     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12545     {
12546       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12547
12548       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12549       {
12550         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12551           continue;             /* center and border element do not touch */
12552       }
12553
12554       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12555                                  player->index_bit, center_side);
12556       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12557                                           CE_PLAYER_TOUCHES_X,
12558                                           player->index_bit, center_side);
12559
12560       {
12561         /* use player element that is initially defined in the level playfield,
12562            not the player element that corresponds to the runtime player number
12563            (example: a level that contains EL_PLAYER_3 as the only player would
12564            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12565         int player_element = PLAYERINFO(xx, yy)->initial_element;
12566
12567         CheckElementChangeBySide(x, y, center_element, player_element,
12568                                  CE_TOUCHING_X, center_side);
12569       }
12570
12571       break;
12572     }
12573   }
12574 }
12575
12576 void TestIfElementTouchesCustomElement(int x, int y)
12577 {
12578   static int xy[4][2] =
12579   {
12580     { 0, -1 },
12581     { -1, 0 },
12582     { +1, 0 },
12583     { 0, +1 }
12584   };
12585   static int trigger_sides[4][2] =
12586   {
12587     /* center side      border side */
12588     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12589     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12590     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12591     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12592   };
12593   static int touch_dir[4] =
12594   {
12595     MV_LEFT | MV_RIGHT,
12596     MV_UP   | MV_DOWN,
12597     MV_UP   | MV_DOWN,
12598     MV_LEFT | MV_RIGHT
12599   };
12600   boolean change_center_element = FALSE;
12601   int center_element = Feld[x][y];      /* should always be non-moving! */
12602   int border_element_old[NUM_DIRECTIONS];
12603   int i;
12604
12605   for (i = 0; i < NUM_DIRECTIONS; i++)
12606   {
12607     int xx = x + xy[i][0];
12608     int yy = y + xy[i][1];
12609     int border_element;
12610
12611     border_element_old[i] = -1;
12612
12613     if (!IN_LEV_FIELD(xx, yy))
12614       continue;
12615
12616     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12617       border_element = Feld[xx][yy];    /* may be moving! */
12618     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12619       border_element = Feld[xx][yy];
12620     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12621       border_element = MovingOrBlocked2Element(xx, yy);
12622     else
12623       continue;                 /* center and border element do not touch */
12624
12625     border_element_old[i] = border_element;
12626   }
12627
12628   for (i = 0; i < NUM_DIRECTIONS; i++)
12629   {
12630     int xx = x + xy[i][0];
12631     int yy = y + xy[i][1];
12632     int center_side = trigger_sides[i][0];
12633     int border_element = border_element_old[i];
12634
12635     if (border_element == -1)
12636       continue;
12637
12638     /* check for change of border element */
12639     CheckElementChangeBySide(xx, yy, border_element, center_element,
12640                              CE_TOUCHING_X, center_side);
12641
12642     /* (center element cannot be player, so we dont have to check this here) */
12643   }
12644
12645   for (i = 0; i < NUM_DIRECTIONS; i++)
12646   {
12647     int xx = x + xy[i][0];
12648     int yy = y + xy[i][1];
12649     int border_side = trigger_sides[i][1];
12650     int border_element = border_element_old[i];
12651
12652     if (border_element == -1)
12653       continue;
12654
12655     /* check for change of center element (but change it only once) */
12656     if (!change_center_element)
12657       change_center_element =
12658         CheckElementChangeBySide(x, y, center_element, border_element,
12659                                  CE_TOUCHING_X, border_side);
12660
12661     if (IS_PLAYER(xx, yy))
12662     {
12663       /* use player element that is initially defined in the level playfield,
12664          not the player element that corresponds to the runtime player number
12665          (example: a level that contains EL_PLAYER_3 as the only player would
12666          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12667       int player_element = PLAYERINFO(xx, yy)->initial_element;
12668
12669       CheckElementChangeBySide(x, y, center_element, player_element,
12670                                CE_TOUCHING_X, border_side);
12671     }
12672   }
12673 }
12674
12675 void TestIfElementHitsCustomElement(int x, int y, int direction)
12676 {
12677   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12678   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12679   int hitx = x + dx, hity = y + dy;
12680   int hitting_element = Feld[x][y];
12681   int touched_element;
12682
12683   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12684     return;
12685
12686   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12687                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12688
12689   if (IN_LEV_FIELD(hitx, hity))
12690   {
12691     int opposite_direction = MV_DIR_OPPOSITE(direction);
12692     int hitting_side = direction;
12693     int touched_side = opposite_direction;
12694     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12695                           MovDir[hitx][hity] != direction ||
12696                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12697
12698     object_hit = TRUE;
12699
12700     if (object_hit)
12701     {
12702       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12703                                CE_HITTING_X, touched_side);
12704
12705       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12706                                CE_HIT_BY_X, hitting_side);
12707
12708       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12709                                CE_HIT_BY_SOMETHING, opposite_direction);
12710
12711       if (IS_PLAYER(hitx, hity))
12712       {
12713         /* use player element that is initially defined in the level playfield,
12714            not the player element that corresponds to the runtime player number
12715            (example: a level that contains EL_PLAYER_3 as the only player would
12716            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12717         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12718
12719         CheckElementChangeBySide(x, y, hitting_element, player_element,
12720                                  CE_HITTING_X, touched_side);
12721       }
12722     }
12723   }
12724
12725   /* "hitting something" is also true when hitting the playfield border */
12726   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12727                            CE_HITTING_SOMETHING, direction);
12728 }
12729
12730 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12731 {
12732   int i, kill_x = -1, kill_y = -1;
12733
12734   int bad_element = -1;
12735   static int test_xy[4][2] =
12736   {
12737     { 0, -1 },
12738     { -1, 0 },
12739     { +1, 0 },
12740     { 0, +1 }
12741   };
12742   static int test_dir[4] =
12743   {
12744     MV_UP,
12745     MV_LEFT,
12746     MV_RIGHT,
12747     MV_DOWN
12748   };
12749
12750   for (i = 0; i < NUM_DIRECTIONS; i++)
12751   {
12752     int test_x, test_y, test_move_dir, test_element;
12753
12754     test_x = good_x + test_xy[i][0];
12755     test_y = good_y + test_xy[i][1];
12756
12757     if (!IN_LEV_FIELD(test_x, test_y))
12758       continue;
12759
12760     test_move_dir =
12761       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12762
12763     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12764
12765     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12766        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12767     */
12768     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12769         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12770     {
12771       kill_x = test_x;
12772       kill_y = test_y;
12773       bad_element = test_element;
12774
12775       break;
12776     }
12777   }
12778
12779   if (kill_x != -1 || kill_y != -1)
12780   {
12781     if (IS_PLAYER(good_x, good_y))
12782     {
12783       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12784
12785       if (player->shield_deadly_time_left > 0 &&
12786           !IS_INDESTRUCTIBLE(bad_element))
12787         Bang(kill_x, kill_y);
12788       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12789         KillPlayer(player);
12790     }
12791     else
12792       Bang(good_x, good_y);
12793   }
12794 }
12795
12796 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12797 {
12798   int i, kill_x = -1, kill_y = -1;
12799   int bad_element = Feld[bad_x][bad_y];
12800   static int test_xy[4][2] =
12801   {
12802     { 0, -1 },
12803     { -1, 0 },
12804     { +1, 0 },
12805     { 0, +1 }
12806   };
12807   static int touch_dir[4] =
12808   {
12809     MV_LEFT | MV_RIGHT,
12810     MV_UP   | MV_DOWN,
12811     MV_UP   | MV_DOWN,
12812     MV_LEFT | MV_RIGHT
12813   };
12814   static int test_dir[4] =
12815   {
12816     MV_UP,
12817     MV_LEFT,
12818     MV_RIGHT,
12819     MV_DOWN
12820   };
12821
12822   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12823     return;
12824
12825   for (i = 0; i < NUM_DIRECTIONS; i++)
12826   {
12827     int test_x, test_y, test_move_dir, test_element;
12828
12829     test_x = bad_x + test_xy[i][0];
12830     test_y = bad_y + test_xy[i][1];
12831
12832     if (!IN_LEV_FIELD(test_x, test_y))
12833       continue;
12834
12835     test_move_dir =
12836       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12837
12838     test_element = Feld[test_x][test_y];
12839
12840     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12841        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12842     */
12843     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12844         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12845     {
12846       /* good thing is player or penguin that does not move away */
12847       if (IS_PLAYER(test_x, test_y))
12848       {
12849         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12850
12851         if (bad_element == EL_ROBOT && player->is_moving)
12852           continue;     /* robot does not kill player if he is moving */
12853
12854         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12855         {
12856           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12857             continue;           /* center and border element do not touch */
12858         }
12859
12860         kill_x = test_x;
12861         kill_y = test_y;
12862
12863         break;
12864       }
12865       else if (test_element == EL_PENGUIN)
12866       {
12867         kill_x = test_x;
12868         kill_y = test_y;
12869
12870         break;
12871       }
12872     }
12873   }
12874
12875   if (kill_x != -1 || kill_y != -1)
12876   {
12877     if (IS_PLAYER(kill_x, kill_y))
12878     {
12879       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12880
12881       if (player->shield_deadly_time_left > 0 &&
12882           !IS_INDESTRUCTIBLE(bad_element))
12883         Bang(bad_x, bad_y);
12884       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12885         KillPlayer(player);
12886     }
12887     else
12888       Bang(kill_x, kill_y);
12889   }
12890 }
12891
12892 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12893 {
12894   int bad_element = Feld[bad_x][bad_y];
12895   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12896   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12897   int test_x = bad_x + dx, test_y = bad_y + dy;
12898   int test_move_dir, test_element;
12899   int kill_x = -1, kill_y = -1;
12900
12901   if (!IN_LEV_FIELD(test_x, test_y))
12902     return;
12903
12904   test_move_dir =
12905     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12906
12907   test_element = Feld[test_x][test_y];
12908
12909   if (test_move_dir != bad_move_dir)
12910   {
12911     /* good thing can be player or penguin that does not move away */
12912     if (IS_PLAYER(test_x, test_y))
12913     {
12914       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12915
12916       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12917          player as being hit when he is moving towards the bad thing, because
12918          the "get hit by" condition would be lost after the player stops) */
12919       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12920         return;         /* player moves away from bad thing */
12921
12922       kill_x = test_x;
12923       kill_y = test_y;
12924     }
12925     else if (test_element == EL_PENGUIN)
12926     {
12927       kill_x = test_x;
12928       kill_y = test_y;
12929     }
12930   }
12931
12932   if (kill_x != -1 || kill_y != -1)
12933   {
12934     if (IS_PLAYER(kill_x, kill_y))
12935     {
12936       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12937
12938       if (player->shield_deadly_time_left > 0 &&
12939           !IS_INDESTRUCTIBLE(bad_element))
12940         Bang(bad_x, bad_y);
12941       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12942         KillPlayer(player);
12943     }
12944     else
12945       Bang(kill_x, kill_y);
12946   }
12947 }
12948
12949 void TestIfPlayerTouchesBadThing(int x, int y)
12950 {
12951   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12952 }
12953
12954 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12955 {
12956   TestIfGoodThingHitsBadThing(x, y, move_dir);
12957 }
12958
12959 void TestIfBadThingTouchesPlayer(int x, int y)
12960 {
12961   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12962 }
12963
12964 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12965 {
12966   TestIfBadThingHitsGoodThing(x, y, move_dir);
12967 }
12968
12969 void TestIfFriendTouchesBadThing(int x, int y)
12970 {
12971   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12972 }
12973
12974 void TestIfBadThingTouchesFriend(int x, int y)
12975 {
12976   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12977 }
12978
12979 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12980 {
12981   int i, kill_x = bad_x, kill_y = bad_y;
12982   static int xy[4][2] =
12983   {
12984     { 0, -1 },
12985     { -1, 0 },
12986     { +1, 0 },
12987     { 0, +1 }
12988   };
12989
12990   for (i = 0; i < NUM_DIRECTIONS; i++)
12991   {
12992     int x, y, element;
12993
12994     x = bad_x + xy[i][0];
12995     y = bad_y + xy[i][1];
12996     if (!IN_LEV_FIELD(x, y))
12997       continue;
12998
12999     element = Feld[x][y];
13000     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13001         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13002     {
13003       kill_x = x;
13004       kill_y = y;
13005       break;
13006     }
13007   }
13008
13009   if (kill_x != bad_x || kill_y != bad_y)
13010     Bang(bad_x, bad_y);
13011 }
13012
13013 void KillPlayer(struct PlayerInfo *player)
13014 {
13015   int jx = player->jx, jy = player->jy;
13016
13017   if (!player->active)
13018     return;
13019
13020 #if 0
13021   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13022          player->killed, player->active, player->reanimated);
13023 #endif
13024
13025   /* the following code was introduced to prevent an infinite loop when calling
13026      -> Bang()
13027      -> CheckTriggeredElementChangeExt()
13028      -> ExecuteCustomElementAction()
13029      -> KillPlayer()
13030      -> (infinitely repeating the above sequence of function calls)
13031      which occurs when killing the player while having a CE with the setting
13032      "kill player X when explosion of <player X>"; the solution using a new
13033      field "player->killed" was chosen for backwards compatibility, although
13034      clever use of the fields "player->active" etc. would probably also work */
13035 #if 1
13036   if (player->killed)
13037     return;
13038 #endif
13039
13040   player->killed = TRUE;
13041
13042   /* remove accessible field at the player's position */
13043   Feld[jx][jy] = EL_EMPTY;
13044
13045   /* deactivate shield (else Bang()/Explode() would not work right) */
13046   player->shield_normal_time_left = 0;
13047   player->shield_deadly_time_left = 0;
13048
13049 #if 0
13050   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13051          player->killed, player->active, player->reanimated);
13052 #endif
13053
13054   Bang(jx, jy);
13055
13056 #if 0
13057   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13058          player->killed, player->active, player->reanimated);
13059 #endif
13060
13061   if (player->reanimated)       /* killed player may have been reanimated */
13062     player->killed = player->reanimated = FALSE;
13063   else
13064     BuryPlayer(player);
13065 }
13066
13067 static void KillPlayerUnlessEnemyProtected(int x, int y)
13068 {
13069   if (!PLAYER_ENEMY_PROTECTED(x, y))
13070     KillPlayer(PLAYERINFO(x, y));
13071 }
13072
13073 static void KillPlayerUnlessExplosionProtected(int x, int y)
13074 {
13075   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13076     KillPlayer(PLAYERINFO(x, y));
13077 }
13078
13079 void BuryPlayer(struct PlayerInfo *player)
13080 {
13081   int jx = player->jx, jy = player->jy;
13082
13083   if (!player->active)
13084     return;
13085
13086   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13087   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13088
13089   player->GameOver = TRUE;
13090   RemovePlayer(player);
13091 }
13092
13093 void RemovePlayer(struct PlayerInfo *player)
13094 {
13095   int jx = player->jx, jy = player->jy;
13096   int i, found = FALSE;
13097
13098   player->present = FALSE;
13099   player->active = FALSE;
13100
13101   if (!ExplodeField[jx][jy])
13102     StorePlayer[jx][jy] = 0;
13103
13104   if (player->is_moving)
13105     TEST_DrawLevelField(player->last_jx, player->last_jy);
13106
13107   for (i = 0; i < MAX_PLAYERS; i++)
13108     if (stored_player[i].active)
13109       found = TRUE;
13110
13111   if (!found)
13112     AllPlayersGone = TRUE;
13113
13114   ExitX = ZX = jx;
13115   ExitY = ZY = jy;
13116 }
13117
13118 static void setFieldForSnapping(int x, int y, int element, int direction)
13119 {
13120   struct ElementInfo *ei = &element_info[element];
13121   int direction_bit = MV_DIR_TO_BIT(direction);
13122   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13123   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13124                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13125
13126   Feld[x][y] = EL_ELEMENT_SNAPPING;
13127   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13128
13129   ResetGfxAnimation(x, y);
13130
13131   GfxElement[x][y] = element;
13132   GfxAction[x][y] = action;
13133   GfxDir[x][y] = direction;
13134   GfxFrame[x][y] = -1;
13135 }
13136
13137 /*
13138   =============================================================================
13139   checkDiagonalPushing()
13140   -----------------------------------------------------------------------------
13141   check if diagonal input device direction results in pushing of object
13142   (by checking if the alternative direction is walkable, diggable, ...)
13143   =============================================================================
13144 */
13145
13146 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13147                                     int x, int y, int real_dx, int real_dy)
13148 {
13149   int jx, jy, dx, dy, xx, yy;
13150
13151   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13152     return TRUE;
13153
13154   /* diagonal direction: check alternative direction */
13155   jx = player->jx;
13156   jy = player->jy;
13157   dx = x - jx;
13158   dy = y - jy;
13159   xx = jx + (dx == 0 ? real_dx : 0);
13160   yy = jy + (dy == 0 ? real_dy : 0);
13161
13162   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13163 }
13164
13165 /*
13166   =============================================================================
13167   DigField()
13168   -----------------------------------------------------------------------------
13169   x, y:                 field next to player (non-diagonal) to try to dig to
13170   real_dx, real_dy:     direction as read from input device (can be diagonal)
13171   =============================================================================
13172 */
13173
13174 static int DigField(struct PlayerInfo *player,
13175                     int oldx, int oldy, int x, int y,
13176                     int real_dx, int real_dy, int mode)
13177 {
13178   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13179   boolean player_was_pushing = player->is_pushing;
13180   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13181   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13182   int jx = oldx, jy = oldy;
13183   int dx = x - jx, dy = y - jy;
13184   int nextx = x + dx, nexty = y + dy;
13185   int move_direction = (dx == -1 ? MV_LEFT  :
13186                         dx == +1 ? MV_RIGHT :
13187                         dy == -1 ? MV_UP    :
13188                         dy == +1 ? MV_DOWN  : MV_NONE);
13189   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13190   int dig_side = MV_DIR_OPPOSITE(move_direction);
13191   int old_element = Feld[jx][jy];
13192   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13193   int collect_count;
13194
13195   if (is_player)                /* function can also be called by EL_PENGUIN */
13196   {
13197     if (player->MovPos == 0)
13198     {
13199       player->is_digging = FALSE;
13200       player->is_collecting = FALSE;
13201     }
13202
13203     if (player->MovPos == 0)    /* last pushing move finished */
13204       player->is_pushing = FALSE;
13205
13206     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13207     {
13208       player->is_switching = FALSE;
13209       player->push_delay = -1;
13210
13211       return MP_NO_ACTION;
13212     }
13213   }
13214
13215   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13216     old_element = Back[jx][jy];
13217
13218   /* in case of element dropped at player position, check background */
13219   else if (Back[jx][jy] != EL_EMPTY &&
13220            game.engine_version >= VERSION_IDENT(2,2,0,0))
13221     old_element = Back[jx][jy];
13222
13223   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13224     return MP_NO_ACTION;        /* field has no opening in this direction */
13225
13226   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13227     return MP_NO_ACTION;        /* field has no opening in this direction */
13228
13229   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13230   {
13231     SplashAcid(x, y);
13232
13233     Feld[jx][jy] = player->artwork_element;
13234     InitMovingField(jx, jy, MV_DOWN);
13235     Store[jx][jy] = EL_ACID;
13236     ContinueMoving(jx, jy);
13237     BuryPlayer(player);
13238
13239     return MP_DONT_RUN_INTO;
13240   }
13241
13242   if (player_can_move && DONT_RUN_INTO(element))
13243   {
13244     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13245
13246     return MP_DONT_RUN_INTO;
13247   }
13248
13249   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13250     return MP_NO_ACTION;
13251
13252   collect_count = element_info[element].collect_count_initial;
13253
13254   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13255     return MP_NO_ACTION;
13256
13257   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13258     player_can_move = player_can_move_or_snap;
13259
13260   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13261       game.engine_version >= VERSION_IDENT(2,2,0,0))
13262   {
13263     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13264                                player->index_bit, dig_side);
13265     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13266                                         player->index_bit, dig_side);
13267
13268     if (element == EL_DC_LANDMINE)
13269       Bang(x, y);
13270
13271     if (Feld[x][y] != element)          /* field changed by snapping */
13272       return MP_ACTION;
13273
13274     return MP_NO_ACTION;
13275   }
13276
13277   if (player->gravity && is_player && !player->is_auto_moving &&
13278       canFallDown(player) && move_direction != MV_DOWN &&
13279       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13280     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13281
13282   if (player_can_move &&
13283       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13284   {
13285     int sound_element = SND_ELEMENT(element);
13286     int sound_action = ACTION_WALKING;
13287
13288     if (IS_RND_GATE(element))
13289     {
13290       if (!player->key[RND_GATE_NR(element)])
13291         return MP_NO_ACTION;
13292     }
13293     else if (IS_RND_GATE_GRAY(element))
13294     {
13295       if (!player->key[RND_GATE_GRAY_NR(element)])
13296         return MP_NO_ACTION;
13297     }
13298     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13299     {
13300       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13301         return MP_NO_ACTION;
13302     }
13303     else if (element == EL_EXIT_OPEN ||
13304              element == EL_EM_EXIT_OPEN ||
13305              element == EL_EM_EXIT_OPENING ||
13306              element == EL_STEEL_EXIT_OPEN ||
13307              element == EL_EM_STEEL_EXIT_OPEN ||
13308              element == EL_EM_STEEL_EXIT_OPENING ||
13309              element == EL_SP_EXIT_OPEN ||
13310              element == EL_SP_EXIT_OPENING)
13311     {
13312       sound_action = ACTION_PASSING;    /* player is passing exit */
13313     }
13314     else if (element == EL_EMPTY)
13315     {
13316       sound_action = ACTION_MOVING;             /* nothing to walk on */
13317     }
13318
13319     /* play sound from background or player, whatever is available */
13320     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13321       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13322     else
13323       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13324   }
13325   else if (player_can_move &&
13326            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13327   {
13328     if (!ACCESS_FROM(element, opposite_direction))
13329       return MP_NO_ACTION;      /* field not accessible from this direction */
13330
13331     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13332       return MP_NO_ACTION;
13333
13334     if (IS_EM_GATE(element))
13335     {
13336       if (!player->key[EM_GATE_NR(element)])
13337         return MP_NO_ACTION;
13338     }
13339     else if (IS_EM_GATE_GRAY(element))
13340     {
13341       if (!player->key[EM_GATE_GRAY_NR(element)])
13342         return MP_NO_ACTION;
13343     }
13344     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13345     {
13346       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13347         return MP_NO_ACTION;
13348     }
13349     else if (IS_EMC_GATE(element))
13350     {
13351       if (!player->key[EMC_GATE_NR(element)])
13352         return MP_NO_ACTION;
13353     }
13354     else if (IS_EMC_GATE_GRAY(element))
13355     {
13356       if (!player->key[EMC_GATE_GRAY_NR(element)])
13357         return MP_NO_ACTION;
13358     }
13359     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13360     {
13361       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13362         return MP_NO_ACTION;
13363     }
13364     else if (element == EL_DC_GATE_WHITE ||
13365              element == EL_DC_GATE_WHITE_GRAY ||
13366              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13367     {
13368       if (player->num_white_keys == 0)
13369         return MP_NO_ACTION;
13370
13371       player->num_white_keys--;
13372     }
13373     else if (IS_SP_PORT(element))
13374     {
13375       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13376           element == EL_SP_GRAVITY_PORT_RIGHT ||
13377           element == EL_SP_GRAVITY_PORT_UP ||
13378           element == EL_SP_GRAVITY_PORT_DOWN)
13379         player->gravity = !player->gravity;
13380       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13381                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13382                element == EL_SP_GRAVITY_ON_PORT_UP ||
13383                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13384         player->gravity = TRUE;
13385       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13386                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13387                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13388                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13389         player->gravity = FALSE;
13390     }
13391
13392     /* automatically move to the next field with double speed */
13393     player->programmed_action = move_direction;
13394
13395     if (player->move_delay_reset_counter == 0)
13396     {
13397       player->move_delay_reset_counter = 2;     /* two double speed steps */
13398
13399       DOUBLE_PLAYER_SPEED(player);
13400     }
13401
13402     PlayLevelSoundAction(x, y, ACTION_PASSING);
13403   }
13404   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13405   {
13406     RemoveField(x, y);
13407
13408     if (mode != DF_SNAP)
13409     {
13410       GfxElement[x][y] = GFX_ELEMENT(element);
13411       player->is_digging = TRUE;
13412     }
13413
13414     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13415
13416     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13417                                         player->index_bit, dig_side);
13418
13419     if (mode == DF_SNAP)
13420     {
13421       if (level.block_snap_field)
13422         setFieldForSnapping(x, y, element, move_direction);
13423       else
13424         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13425
13426       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13427                                           player->index_bit, dig_side);
13428     }
13429   }
13430   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13431   {
13432     RemoveField(x, y);
13433
13434     if (is_player && mode != DF_SNAP)
13435     {
13436       GfxElement[x][y] = element;
13437       player->is_collecting = TRUE;
13438     }
13439
13440     if (element == EL_SPEED_PILL)
13441     {
13442       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13443     }
13444     else if (element == EL_EXTRA_TIME && level.time > 0)
13445     {
13446       TimeLeft += level.extra_time;
13447
13448       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13449
13450       DisplayGameControlValues();
13451     }
13452     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13453     {
13454       player->shield_normal_time_left += level.shield_normal_time;
13455       if (element == EL_SHIELD_DEADLY)
13456         player->shield_deadly_time_left += level.shield_deadly_time;
13457     }
13458     else if (element == EL_DYNAMITE ||
13459              element == EL_EM_DYNAMITE ||
13460              element == EL_SP_DISK_RED)
13461     {
13462       if (player->inventory_size < MAX_INVENTORY_SIZE)
13463         player->inventory_element[player->inventory_size++] = element;
13464
13465       DrawGameDoorValues();
13466     }
13467     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13468     {
13469       player->dynabomb_count++;
13470       player->dynabombs_left++;
13471     }
13472     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13473     {
13474       player->dynabomb_size++;
13475     }
13476     else if (element == EL_DYNABOMB_INCREASE_POWER)
13477     {
13478       player->dynabomb_xl = TRUE;
13479     }
13480     else if (IS_KEY(element))
13481     {
13482       player->key[KEY_NR(element)] = TRUE;
13483
13484       DrawGameDoorValues();
13485     }
13486     else if (element == EL_DC_KEY_WHITE)
13487     {
13488       player->num_white_keys++;
13489
13490       /* display white keys? */
13491       /* DrawGameDoorValues(); */
13492     }
13493     else if (IS_ENVELOPE(element))
13494     {
13495       player->show_envelope = element;
13496     }
13497     else if (element == EL_EMC_LENSES)
13498     {
13499       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13500
13501       RedrawAllInvisibleElementsForLenses();
13502     }
13503     else if (element == EL_EMC_MAGNIFIER)
13504     {
13505       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13506
13507       RedrawAllInvisibleElementsForMagnifier();
13508     }
13509     else if (IS_DROPPABLE(element) ||
13510              IS_THROWABLE(element))     /* can be collected and dropped */
13511     {
13512       int i;
13513
13514       if (collect_count == 0)
13515         player->inventory_infinite_element = element;
13516       else
13517         for (i = 0; i < collect_count; i++)
13518           if (player->inventory_size < MAX_INVENTORY_SIZE)
13519             player->inventory_element[player->inventory_size++] = element;
13520
13521       DrawGameDoorValues();
13522     }
13523     else if (collect_count > 0)
13524     {
13525       local_player->gems_still_needed -= collect_count;
13526       if (local_player->gems_still_needed < 0)
13527         local_player->gems_still_needed = 0;
13528
13529       game.snapshot.collected_item = TRUE;
13530
13531       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13532
13533       DisplayGameControlValues();
13534     }
13535
13536     RaiseScoreElement(element);
13537     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13538
13539     if (is_player)
13540       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13541                                           player->index_bit, dig_side);
13542
13543     if (mode == DF_SNAP)
13544     {
13545       if (level.block_snap_field)
13546         setFieldForSnapping(x, y, element, move_direction);
13547       else
13548         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13549
13550       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13551                                           player->index_bit, dig_side);
13552     }
13553   }
13554   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13555   {
13556     if (mode == DF_SNAP && element != EL_BD_ROCK)
13557       return MP_NO_ACTION;
13558
13559     if (CAN_FALL(element) && dy)
13560       return MP_NO_ACTION;
13561
13562     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13563         !(element == EL_SPRING && level.use_spring_bug))
13564       return MP_NO_ACTION;
13565
13566     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13567         ((move_direction & MV_VERTICAL &&
13568           ((element_info[element].move_pattern & MV_LEFT &&
13569             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13570            (element_info[element].move_pattern & MV_RIGHT &&
13571             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13572          (move_direction & MV_HORIZONTAL &&
13573           ((element_info[element].move_pattern & MV_UP &&
13574             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13575            (element_info[element].move_pattern & MV_DOWN &&
13576             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13577       return MP_NO_ACTION;
13578
13579     /* do not push elements already moving away faster than player */
13580     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13581         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13582       return MP_NO_ACTION;
13583
13584     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13585     {
13586       if (player->push_delay_value == -1 || !player_was_pushing)
13587         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13588     }
13589     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13590     {
13591       if (player->push_delay_value == -1)
13592         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13593     }
13594     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13595     {
13596       if (!player->is_pushing)
13597         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13598     }
13599
13600     player->is_pushing = TRUE;
13601     player->is_active = TRUE;
13602
13603     if (!(IN_LEV_FIELD(nextx, nexty) &&
13604           (IS_FREE(nextx, nexty) ||
13605            (IS_SB_ELEMENT(element) &&
13606             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13607            (IS_CUSTOM_ELEMENT(element) &&
13608             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13609       return MP_NO_ACTION;
13610
13611     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13612       return MP_NO_ACTION;
13613
13614     if (player->push_delay == -1)       /* new pushing; restart delay */
13615       player->push_delay = 0;
13616
13617     if (player->push_delay < player->push_delay_value &&
13618         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13619         element != EL_SPRING && element != EL_BALLOON)
13620     {
13621       /* make sure that there is no move delay before next try to push */
13622       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13623         player->move_delay = 0;
13624
13625       return MP_NO_ACTION;
13626     }
13627
13628     if (IS_CUSTOM_ELEMENT(element) &&
13629         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13630     {
13631       if (!DigFieldByCE(nextx, nexty, element))
13632         return MP_NO_ACTION;
13633     }
13634
13635     if (IS_SB_ELEMENT(element))
13636     {
13637       if (element == EL_SOKOBAN_FIELD_FULL)
13638       {
13639         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13640         local_player->sokobanfields_still_needed++;
13641       }
13642
13643       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13644       {
13645         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13646         local_player->sokobanfields_still_needed--;
13647       }
13648
13649       Feld[x][y] = EL_SOKOBAN_OBJECT;
13650
13651       if (Back[x][y] == Back[nextx][nexty])
13652         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13653       else if (Back[x][y] != 0)
13654         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13655                                     ACTION_EMPTYING);
13656       else
13657         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13658                                     ACTION_FILLING);
13659
13660       if (local_player->sokobanfields_still_needed == 0 &&
13661           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13662       {
13663         PlayerWins(player);
13664
13665         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13666       }
13667     }
13668     else
13669       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13670
13671     InitMovingField(x, y, move_direction);
13672     GfxAction[x][y] = ACTION_PUSHING;
13673
13674     if (mode == DF_SNAP)
13675       ContinueMoving(x, y);
13676     else
13677       MovPos[x][y] = (dx != 0 ? dx : dy);
13678
13679     Pushed[x][y] = TRUE;
13680     Pushed[nextx][nexty] = TRUE;
13681
13682     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13683       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13684     else
13685       player->push_delay_value = -1;    /* get new value later */
13686
13687     /* check for element change _after_ element has been pushed */
13688     if (game.use_change_when_pushing_bug)
13689     {
13690       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13691                                  player->index_bit, dig_side);
13692       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13693                                           player->index_bit, dig_side);
13694     }
13695   }
13696   else if (IS_SWITCHABLE(element))
13697   {
13698     if (PLAYER_SWITCHING(player, x, y))
13699     {
13700       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13701                                           player->index_bit, dig_side);
13702
13703       return MP_ACTION;
13704     }
13705
13706     player->is_switching = TRUE;
13707     player->switch_x = x;
13708     player->switch_y = y;
13709
13710     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13711
13712     if (element == EL_ROBOT_WHEEL)
13713     {
13714       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13715       ZX = x;
13716       ZY = y;
13717
13718       game.robot_wheel_active = TRUE;
13719
13720       TEST_DrawLevelField(x, y);
13721     }
13722     else if (element == EL_SP_TERMINAL)
13723     {
13724       int xx, yy;
13725
13726       SCAN_PLAYFIELD(xx, yy)
13727       {
13728         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13729         {
13730           Bang(xx, yy);
13731         }
13732         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13733         {
13734           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13735
13736           ResetGfxAnimation(xx, yy);
13737           TEST_DrawLevelField(xx, yy);
13738         }
13739       }
13740     }
13741     else if (IS_BELT_SWITCH(element))
13742     {
13743       ToggleBeltSwitch(x, y);
13744     }
13745     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13746              element == EL_SWITCHGATE_SWITCH_DOWN ||
13747              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13748              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13749     {
13750       ToggleSwitchgateSwitch(x, y);
13751     }
13752     else if (element == EL_LIGHT_SWITCH ||
13753              element == EL_LIGHT_SWITCH_ACTIVE)
13754     {
13755       ToggleLightSwitch(x, y);
13756     }
13757     else if (element == EL_TIMEGATE_SWITCH ||
13758              element == EL_DC_TIMEGATE_SWITCH)
13759     {
13760       ActivateTimegateSwitch(x, y);
13761     }
13762     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13763              element == EL_BALLOON_SWITCH_RIGHT ||
13764              element == EL_BALLOON_SWITCH_UP    ||
13765              element == EL_BALLOON_SWITCH_DOWN  ||
13766              element == EL_BALLOON_SWITCH_NONE  ||
13767              element == EL_BALLOON_SWITCH_ANY)
13768     {
13769       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13770                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13771                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13772                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13773                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13774                              move_direction);
13775     }
13776     else if (element == EL_LAMP)
13777     {
13778       Feld[x][y] = EL_LAMP_ACTIVE;
13779       local_player->lights_still_needed--;
13780
13781       ResetGfxAnimation(x, y);
13782       TEST_DrawLevelField(x, y);
13783     }
13784     else if (element == EL_TIME_ORB_FULL)
13785     {
13786       Feld[x][y] = EL_TIME_ORB_EMPTY;
13787
13788       if (level.time > 0 || level.use_time_orb_bug)
13789       {
13790         TimeLeft += level.time_orb_time;
13791         game.no_time_limit = FALSE;
13792
13793         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13794
13795         DisplayGameControlValues();
13796       }
13797
13798       ResetGfxAnimation(x, y);
13799       TEST_DrawLevelField(x, y);
13800     }
13801     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13802              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13803     {
13804       int xx, yy;
13805
13806       game.ball_state = !game.ball_state;
13807
13808       SCAN_PLAYFIELD(xx, yy)
13809       {
13810         int e = Feld[xx][yy];
13811
13812         if (game.ball_state)
13813         {
13814           if (e == EL_EMC_MAGIC_BALL)
13815             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13816           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13817             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13818         }
13819         else
13820         {
13821           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13822             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13823           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13824             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13825         }
13826       }
13827     }
13828
13829     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13830                                         player->index_bit, dig_side);
13831
13832     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13833                                         player->index_bit, dig_side);
13834
13835     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13836                                         player->index_bit, dig_side);
13837
13838     return MP_ACTION;
13839   }
13840   else
13841   {
13842     if (!PLAYER_SWITCHING(player, x, y))
13843     {
13844       player->is_switching = TRUE;
13845       player->switch_x = x;
13846       player->switch_y = y;
13847
13848       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13849                                  player->index_bit, dig_side);
13850       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13851                                           player->index_bit, dig_side);
13852
13853       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13854                                  player->index_bit, dig_side);
13855       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13856                                           player->index_bit, dig_side);
13857     }
13858
13859     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13860                                player->index_bit, dig_side);
13861     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13862                                         player->index_bit, dig_side);
13863
13864     return MP_NO_ACTION;
13865   }
13866
13867   player->push_delay = -1;
13868
13869   if (is_player)                /* function can also be called by EL_PENGUIN */
13870   {
13871     if (Feld[x][y] != element)          /* really digged/collected something */
13872     {
13873       player->is_collecting = !player->is_digging;
13874       player->is_active = TRUE;
13875     }
13876   }
13877
13878   return MP_MOVING;
13879 }
13880
13881 static boolean DigFieldByCE(int x, int y, int digging_element)
13882 {
13883   int element = Feld[x][y];
13884
13885   if (!IS_FREE(x, y))
13886   {
13887     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13888                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13889                   ACTION_BREAKING);
13890
13891     /* no element can dig solid indestructible elements */
13892     if (IS_INDESTRUCTIBLE(element) &&
13893         !IS_DIGGABLE(element) &&
13894         !IS_COLLECTIBLE(element))
13895       return FALSE;
13896
13897     if (AmoebaNr[x][y] &&
13898         (element == EL_AMOEBA_FULL ||
13899          element == EL_BD_AMOEBA ||
13900          element == EL_AMOEBA_GROWING))
13901     {
13902       AmoebaCnt[AmoebaNr[x][y]]--;
13903       AmoebaCnt2[AmoebaNr[x][y]]--;
13904     }
13905
13906     if (IS_MOVING(x, y))
13907       RemoveMovingField(x, y);
13908     else
13909     {
13910       RemoveField(x, y);
13911       TEST_DrawLevelField(x, y);
13912     }
13913
13914     /* if digged element was about to explode, prevent the explosion */
13915     ExplodeField[x][y] = EX_TYPE_NONE;
13916
13917     PlayLevelSoundAction(x, y, action);
13918   }
13919
13920   Store[x][y] = EL_EMPTY;
13921
13922   /* this makes it possible to leave the removed element again */
13923   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13924     Store[x][y] = element;
13925
13926   return TRUE;
13927 }
13928
13929 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13930 {
13931   int jx = player->jx, jy = player->jy;
13932   int x = jx + dx, y = jy + dy;
13933   int snap_direction = (dx == -1 ? MV_LEFT  :
13934                         dx == +1 ? MV_RIGHT :
13935                         dy == -1 ? MV_UP    :
13936                         dy == +1 ? MV_DOWN  : MV_NONE);
13937   boolean can_continue_snapping = (level.continuous_snapping &&
13938                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13939
13940   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13941     return FALSE;
13942
13943   if (!player->active || !IN_LEV_FIELD(x, y))
13944     return FALSE;
13945
13946   if (dx && dy)
13947     return FALSE;
13948
13949   if (!dx && !dy)
13950   {
13951     if (player->MovPos == 0)
13952       player->is_pushing = FALSE;
13953
13954     player->is_snapping = FALSE;
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     return FALSE;
13964   }
13965
13966   /* prevent snapping with already pressed snap key when not allowed */
13967   if (player->is_snapping && !can_continue_snapping)
13968     return FALSE;
13969
13970   player->MovDir = snap_direction;
13971
13972   if (player->MovPos == 0)
13973   {
13974     player->is_moving = FALSE;
13975     player->is_digging = FALSE;
13976     player->is_collecting = FALSE;
13977   }
13978
13979   player->is_dropping = FALSE;
13980   player->is_dropping_pressed = FALSE;
13981   player->drop_pressed_delay = 0;
13982
13983   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13984     return FALSE;
13985
13986   player->is_snapping = TRUE;
13987   player->is_active = TRUE;
13988
13989   if (player->MovPos == 0)
13990   {
13991     player->is_moving = FALSE;
13992     player->is_digging = FALSE;
13993     player->is_collecting = FALSE;
13994   }
13995
13996   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13997     TEST_DrawLevelField(player->last_jx, player->last_jy);
13998
13999   TEST_DrawLevelField(x, y);
14000
14001   return TRUE;
14002 }
14003
14004 static boolean DropElement(struct PlayerInfo *player)
14005 {
14006   int old_element, new_element;
14007   int dropx = player->jx, dropy = player->jy;
14008   int drop_direction = player->MovDir;
14009   int drop_side = drop_direction;
14010   int drop_element = get_next_dropped_element(player);
14011
14012   /* do not drop an element on top of another element; when holding drop key
14013      pressed without moving, dropped element must move away before the next
14014      element can be dropped (this is especially important if the next element
14015      is dynamite, which can be placed on background for historical reasons) */
14016   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14017     return MP_ACTION;
14018
14019   if (IS_THROWABLE(drop_element))
14020   {
14021     dropx += GET_DX_FROM_DIR(drop_direction);
14022     dropy += GET_DY_FROM_DIR(drop_direction);
14023
14024     if (!IN_LEV_FIELD(dropx, dropy))
14025       return FALSE;
14026   }
14027
14028   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14029   new_element = drop_element;           /* default: no change when dropping */
14030
14031   /* check if player is active, not moving and ready to drop */
14032   if (!player->active || player->MovPos || player->drop_delay > 0)
14033     return FALSE;
14034
14035   /* check if player has anything that can be dropped */
14036   if (new_element == EL_UNDEFINED)
14037     return FALSE;
14038
14039   /* only set if player has anything that can be dropped */
14040   player->is_dropping_pressed = TRUE;
14041
14042   /* check if drop key was pressed long enough for EM style dynamite */
14043   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14044     return FALSE;
14045
14046   /* check if anything can be dropped at the current position */
14047   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14048     return FALSE;
14049
14050   /* collected custom elements can only be dropped on empty fields */
14051   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14052     return FALSE;
14053
14054   if (old_element != EL_EMPTY)
14055     Back[dropx][dropy] = old_element;   /* store old element on this field */
14056
14057   ResetGfxAnimation(dropx, dropy);
14058   ResetRandomAnimationValue(dropx, dropy);
14059
14060   if (player->inventory_size > 0 ||
14061       player->inventory_infinite_element != EL_UNDEFINED)
14062   {
14063     if (player->inventory_size > 0)
14064     {
14065       player->inventory_size--;
14066
14067       DrawGameDoorValues();
14068
14069       if (new_element == EL_DYNAMITE)
14070         new_element = EL_DYNAMITE_ACTIVE;
14071       else if (new_element == EL_EM_DYNAMITE)
14072         new_element = EL_EM_DYNAMITE_ACTIVE;
14073       else if (new_element == EL_SP_DISK_RED)
14074         new_element = EL_SP_DISK_RED_ACTIVE;
14075     }
14076
14077     Feld[dropx][dropy] = new_element;
14078
14079     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14080       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14081                           el2img(Feld[dropx][dropy]), 0);
14082
14083     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14084
14085     /* needed if previous element just changed to "empty" in the last frame */
14086     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14087
14088     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14089                                player->index_bit, drop_side);
14090     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14091                                         CE_PLAYER_DROPS_X,
14092                                         player->index_bit, drop_side);
14093
14094     TestIfElementTouchesCustomElement(dropx, dropy);
14095   }
14096   else          /* player is dropping a dyna bomb */
14097   {
14098     player->dynabombs_left--;
14099
14100     Feld[dropx][dropy] = new_element;
14101
14102     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14103       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14104                           el2img(Feld[dropx][dropy]), 0);
14105
14106     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14107   }
14108
14109   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14110     InitField_WithBug1(dropx, dropy, FALSE);
14111
14112   new_element = Feld[dropx][dropy];     /* element might have changed */
14113
14114   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14115       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14116   {
14117     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14118       MovDir[dropx][dropy] = drop_direction;
14119
14120     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14121
14122     /* do not cause impact style collision by dropping elements that can fall */
14123     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14124   }
14125
14126   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14127   player->is_dropping = TRUE;
14128
14129   player->drop_pressed_delay = 0;
14130   player->is_dropping_pressed = FALSE;
14131
14132   player->drop_x = dropx;
14133   player->drop_y = dropy;
14134
14135   return TRUE;
14136 }
14137
14138 /* ------------------------------------------------------------------------- */
14139 /* game sound playing functions                                              */
14140 /* ------------------------------------------------------------------------- */
14141
14142 static int *loop_sound_frame = NULL;
14143 static int *loop_sound_volume = NULL;
14144
14145 void InitPlayLevelSound()
14146 {
14147   int num_sounds = getSoundListSize();
14148
14149   checked_free(loop_sound_frame);
14150   checked_free(loop_sound_volume);
14151
14152   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14153   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14154 }
14155
14156 static void PlayLevelSound(int x, int y, int nr)
14157 {
14158   int sx = SCREENX(x), sy = SCREENY(y);
14159   int volume, stereo_position;
14160   int max_distance = 8;
14161   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14162
14163   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14164       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14165     return;
14166
14167   if (!IN_LEV_FIELD(x, y) ||
14168       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14169       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14170     return;
14171
14172   volume = SOUND_MAX_VOLUME;
14173
14174   if (!IN_SCR_FIELD(sx, sy))
14175   {
14176     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14177     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14178
14179     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14180   }
14181
14182   stereo_position = (SOUND_MAX_LEFT +
14183                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14184                      (SCR_FIELDX + 2 * max_distance));
14185
14186   if (IS_LOOP_SOUND(nr))
14187   {
14188     /* This assures that quieter loop sounds do not overwrite louder ones,
14189        while restarting sound volume comparison with each new game frame. */
14190
14191     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14192       return;
14193
14194     loop_sound_volume[nr] = volume;
14195     loop_sound_frame[nr] = FrameCounter;
14196   }
14197
14198   PlaySoundExt(nr, volume, stereo_position, type);
14199 }
14200
14201 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14202 {
14203   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14204                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14205                  y < LEVELY(BY1) ? LEVELY(BY1) :
14206                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14207                  sound_action);
14208 }
14209
14210 static void PlayLevelSoundAction(int x, int y, int action)
14211 {
14212   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14213 }
14214
14215 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14216 {
14217   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14218
14219   if (sound_effect != SND_UNDEFINED)
14220     PlayLevelSound(x, y, sound_effect);
14221 }
14222
14223 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14224                                               int action)
14225 {
14226   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14227
14228   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14229     PlayLevelSound(x, y, sound_effect);
14230 }
14231
14232 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14233 {
14234   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14235
14236   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14237     PlayLevelSound(x, y, sound_effect);
14238 }
14239
14240 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14241 {
14242   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14243
14244   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14245     StopSound(sound_effect);
14246 }
14247
14248 static int getLevelMusicNr()
14249 {
14250   if (levelset.music[level_nr] != MUS_UNDEFINED)
14251     return levelset.music[level_nr];            /* from config file */
14252   else
14253     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14254 }
14255
14256 static void FadeLevelSounds()
14257 {
14258   FadeSounds();
14259 }
14260
14261 static void FadeLevelMusic()
14262 {
14263   int music_nr = getLevelMusicNr();
14264   char *curr_music = getCurrentlyPlayingMusicFilename();
14265   char *next_music = getMusicInfoEntryFilename(music_nr);
14266
14267   if (!strEqual(curr_music, next_music))
14268     FadeMusic();
14269 }
14270
14271 void FadeLevelSoundsAndMusic()
14272 {
14273   FadeLevelSounds();
14274   FadeLevelMusic();
14275 }
14276
14277 static void PlayLevelMusic()
14278 {
14279   int music_nr = getLevelMusicNr();
14280   char *curr_music = getCurrentlyPlayingMusicFilename();
14281   char *next_music = getMusicInfoEntryFilename(music_nr);
14282
14283   if (!strEqual(curr_music, next_music))
14284     PlayMusic(music_nr);
14285 }
14286
14287 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14288 {
14289   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14290   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14291   int x = xx - 1 - offset;
14292   int y = yy - 1 - offset;
14293
14294   switch (sample)
14295   {
14296     case SAMPLE_blank:
14297       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14298       break;
14299
14300     case SAMPLE_roll:
14301       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14302       break;
14303
14304     case SAMPLE_stone:
14305       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14306       break;
14307
14308     case SAMPLE_nut:
14309       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14310       break;
14311
14312     case SAMPLE_crack:
14313       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14314       break;
14315
14316     case SAMPLE_bug:
14317       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14318       break;
14319
14320     case SAMPLE_tank:
14321       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14322       break;
14323
14324     case SAMPLE_android_clone:
14325       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14326       break;
14327
14328     case SAMPLE_android_move:
14329       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14330       break;
14331
14332     case SAMPLE_spring:
14333       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14334       break;
14335
14336     case SAMPLE_slurp:
14337       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14338       break;
14339
14340     case SAMPLE_eater:
14341       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14342       break;
14343
14344     case SAMPLE_eater_eat:
14345       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14346       break;
14347
14348     case SAMPLE_alien:
14349       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14350       break;
14351
14352     case SAMPLE_collect:
14353       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14354       break;
14355
14356     case SAMPLE_diamond:
14357       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14358       break;
14359
14360     case SAMPLE_squash:
14361       /* !!! CHECK THIS !!! */
14362 #if 1
14363       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14364 #else
14365       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14366 #endif
14367       break;
14368
14369     case SAMPLE_wonderfall:
14370       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14371       break;
14372
14373     case SAMPLE_drip:
14374       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14375       break;
14376
14377     case SAMPLE_push:
14378       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14379       break;
14380
14381     case SAMPLE_dirt:
14382       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14383       break;
14384
14385     case SAMPLE_acid:
14386       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14387       break;
14388
14389     case SAMPLE_ball:
14390       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14391       break;
14392
14393     case SAMPLE_grow:
14394       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14395       break;
14396
14397     case SAMPLE_wonder:
14398       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14399       break;
14400
14401     case SAMPLE_door:
14402       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14403       break;
14404
14405     case SAMPLE_exit_open:
14406       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14407       break;
14408
14409     case SAMPLE_exit_leave:
14410       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14411       break;
14412
14413     case SAMPLE_dynamite:
14414       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14415       break;
14416
14417     case SAMPLE_tick:
14418       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14419       break;
14420
14421     case SAMPLE_press:
14422       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14423       break;
14424
14425     case SAMPLE_wheel:
14426       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14427       break;
14428
14429     case SAMPLE_boom:
14430       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14431       break;
14432
14433     case SAMPLE_die:
14434       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14435       break;
14436
14437     case SAMPLE_time:
14438       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14439       break;
14440
14441     default:
14442       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14443       break;
14444   }
14445 }
14446
14447 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14448 {
14449   int element = map_element_SP_to_RND(element_sp);
14450   int action = map_action_SP_to_RND(action_sp);
14451   int offset = (setup.sp_show_border_elements ? 0 : 1);
14452   int x = xx - offset;
14453   int y = yy - offset;
14454
14455   PlayLevelSoundElementAction(x, y, element, action);
14456 }
14457
14458 void RaiseScore(int value)
14459 {
14460   local_player->score += value;
14461
14462   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14463
14464   DisplayGameControlValues();
14465 }
14466
14467 void RaiseScoreElement(int element)
14468 {
14469   switch (element)
14470   {
14471     case EL_EMERALD:
14472     case EL_BD_DIAMOND:
14473     case EL_EMERALD_YELLOW:
14474     case EL_EMERALD_RED:
14475     case EL_EMERALD_PURPLE:
14476     case EL_SP_INFOTRON:
14477       RaiseScore(level.score[SC_EMERALD]);
14478       break;
14479     case EL_DIAMOND:
14480       RaiseScore(level.score[SC_DIAMOND]);
14481       break;
14482     case EL_CRYSTAL:
14483       RaiseScore(level.score[SC_CRYSTAL]);
14484       break;
14485     case EL_PEARL:
14486       RaiseScore(level.score[SC_PEARL]);
14487       break;
14488     case EL_BUG:
14489     case EL_BD_BUTTERFLY:
14490     case EL_SP_ELECTRON:
14491       RaiseScore(level.score[SC_BUG]);
14492       break;
14493     case EL_SPACESHIP:
14494     case EL_BD_FIREFLY:
14495     case EL_SP_SNIKSNAK:
14496       RaiseScore(level.score[SC_SPACESHIP]);
14497       break;
14498     case EL_YAMYAM:
14499     case EL_DARK_YAMYAM:
14500       RaiseScore(level.score[SC_YAMYAM]);
14501       break;
14502     case EL_ROBOT:
14503       RaiseScore(level.score[SC_ROBOT]);
14504       break;
14505     case EL_PACMAN:
14506       RaiseScore(level.score[SC_PACMAN]);
14507       break;
14508     case EL_NUT:
14509       RaiseScore(level.score[SC_NUT]);
14510       break;
14511     case EL_DYNAMITE:
14512     case EL_EM_DYNAMITE:
14513     case EL_SP_DISK_RED:
14514     case EL_DYNABOMB_INCREASE_NUMBER:
14515     case EL_DYNABOMB_INCREASE_SIZE:
14516     case EL_DYNABOMB_INCREASE_POWER:
14517       RaiseScore(level.score[SC_DYNAMITE]);
14518       break;
14519     case EL_SHIELD_NORMAL:
14520     case EL_SHIELD_DEADLY:
14521       RaiseScore(level.score[SC_SHIELD]);
14522       break;
14523     case EL_EXTRA_TIME:
14524       RaiseScore(level.extra_time_score);
14525       break;
14526     case EL_KEY_1:
14527     case EL_KEY_2:
14528     case EL_KEY_3:
14529     case EL_KEY_4:
14530     case EL_EM_KEY_1:
14531     case EL_EM_KEY_2:
14532     case EL_EM_KEY_3:
14533     case EL_EM_KEY_4:
14534     case EL_EMC_KEY_5:
14535     case EL_EMC_KEY_6:
14536     case EL_EMC_KEY_7:
14537     case EL_EMC_KEY_8:
14538     case EL_DC_KEY_WHITE:
14539       RaiseScore(level.score[SC_KEY]);
14540       break;
14541     default:
14542       RaiseScore(element_info[element].collect_score);
14543       break;
14544   }
14545 }
14546
14547 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14548 {
14549   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14550   {
14551     /* closing door required in case of envelope style request dialogs */
14552     if (!skip_request)
14553       CloseDoor(DOOR_CLOSE_1);
14554
14555 #if defined(NETWORK_AVALIABLE)
14556     if (options.network)
14557       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14558     else
14559 #endif
14560     {
14561       if (quick_quit)
14562         FadeSkipNextFadeIn();
14563
14564       SetGameStatus(GAME_MODE_MAIN);
14565
14566       DrawMainMenu();
14567     }
14568   }
14569   else          /* continue playing the game */
14570   {
14571     if (tape.playing && tape.deactivate_display)
14572       TapeDeactivateDisplayOff(TRUE);
14573
14574     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14575
14576     if (tape.playing && tape.deactivate_display)
14577       TapeDeactivateDisplayOn();
14578   }
14579 }
14580
14581 void RequestQuitGame(boolean ask_if_really_quit)
14582 {
14583   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14584   boolean skip_request = AllPlayersGone || quick_quit;
14585
14586   RequestQuitGameExt(skip_request, quick_quit,
14587                      "Do you really want to quit the game?");
14588 }
14589
14590
14591 /* ------------------------------------------------------------------------- */
14592 /* random generator functions                                                */
14593 /* ------------------------------------------------------------------------- */
14594
14595 unsigned int InitEngineRandom_RND(int seed)
14596 {
14597   game.num_random_calls = 0;
14598
14599   return InitEngineRandom(seed);
14600 }
14601
14602 unsigned int RND(int max)
14603 {
14604   if (max > 0)
14605   {
14606     game.num_random_calls++;
14607
14608     return GetEngineRandom(max);
14609   }
14610
14611   return 0;
14612 }
14613
14614
14615 /* ------------------------------------------------------------------------- */
14616 /* game engine snapshot handling functions                                   */
14617 /* ------------------------------------------------------------------------- */
14618
14619 struct EngineSnapshotInfo
14620 {
14621   /* runtime values for custom element collect score */
14622   int collect_score[NUM_CUSTOM_ELEMENTS];
14623
14624   /* runtime values for group element choice position */
14625   int choice_pos[NUM_GROUP_ELEMENTS];
14626
14627   /* runtime values for belt position animations */
14628   int belt_graphic[4][NUM_BELT_PARTS];
14629   int belt_anim_mode[4][NUM_BELT_PARTS];
14630 };
14631
14632 static struct EngineSnapshotInfo engine_snapshot_rnd;
14633 static char *snapshot_level_identifier = NULL;
14634 static int snapshot_level_nr = -1;
14635
14636 static void SaveEngineSnapshotValues_RND()
14637 {
14638   static int belt_base_active_element[4] =
14639   {
14640     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14641     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14642     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14643     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14644   };
14645   int i, j;
14646
14647   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14648   {
14649     int element = EL_CUSTOM_START + i;
14650
14651     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14652   }
14653
14654   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14655   {
14656     int element = EL_GROUP_START + i;
14657
14658     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14659   }
14660
14661   for (i = 0; i < 4; i++)
14662   {
14663     for (j = 0; j < NUM_BELT_PARTS; j++)
14664     {
14665       int element = belt_base_active_element[i] + j;
14666       int graphic = el2img(element);
14667       int anim_mode = graphic_info[graphic].anim_mode;
14668
14669       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14670       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14671     }
14672   }
14673 }
14674
14675 static void LoadEngineSnapshotValues_RND()
14676 {
14677   unsigned int num_random_calls = game.num_random_calls;
14678   int i, j;
14679
14680   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14681   {
14682     int element = EL_CUSTOM_START + i;
14683
14684     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14685   }
14686
14687   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14688   {
14689     int element = EL_GROUP_START + i;
14690
14691     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14692   }
14693
14694   for (i = 0; i < 4; i++)
14695   {
14696     for (j = 0; j < NUM_BELT_PARTS; j++)
14697     {
14698       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14699       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14700
14701       graphic_info[graphic].anim_mode = anim_mode;
14702     }
14703   }
14704
14705   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14706   {
14707     InitRND(tape.random_seed);
14708     for (i = 0; i < num_random_calls; i++)
14709       RND(1);
14710   }
14711
14712   if (game.num_random_calls != num_random_calls)
14713   {
14714     Error(ERR_INFO, "number of random calls out of sync");
14715     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14716     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14717     Error(ERR_EXIT, "this should not happen -- please debug");
14718   }
14719 }
14720
14721 void FreeEngineSnapshotSingle()
14722 {
14723   FreeSnapshotSingle();
14724
14725   setString(&snapshot_level_identifier, NULL);
14726   snapshot_level_nr = -1;
14727 }
14728
14729 void FreeEngineSnapshotList()
14730 {
14731   FreeSnapshotList();
14732 }
14733
14734 ListNode *SaveEngineSnapshotBuffers()
14735 {
14736   ListNode *buffers = NULL;
14737
14738   /* copy some special values to a structure better suited for the snapshot */
14739
14740   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14741     SaveEngineSnapshotValues_RND();
14742   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14743     SaveEngineSnapshotValues_EM();
14744   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14745     SaveEngineSnapshotValues_SP(&buffers);
14746
14747   /* save values stored in special snapshot structure */
14748
14749   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14750     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14751   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14752     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14753   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14754     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14755
14756   /* save further RND engine values */
14757
14758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14761
14762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14766
14767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14772
14773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14774   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14776
14777   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14778
14779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14780
14781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14783
14784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14786   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14787   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14788   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14789   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14790   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14791   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14792   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14793   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14794   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14795   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14796   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14797   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14798   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14799   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14800   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14801   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14802
14803   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14804   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14805
14806   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14807   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14808   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14809
14810   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14811   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14812
14813   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14814   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14815   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14816   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14817   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14818
14819   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14820   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14821
14822 #if 0
14823   ListNode *node = engine_snapshot_list_rnd;
14824   int num_bytes = 0;
14825
14826   while (node != NULL)
14827   {
14828     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14829
14830     node = node->next;
14831   }
14832
14833   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14834 #endif
14835
14836   return buffers;
14837 }
14838
14839 void SaveEngineSnapshotSingle()
14840 {
14841   ListNode *buffers = SaveEngineSnapshotBuffers();
14842
14843   /* finally save all snapshot buffers to single snapshot */
14844   SaveSnapshotSingle(buffers);
14845
14846   /* save level identification information */
14847   setString(&snapshot_level_identifier, leveldir_current->identifier);
14848   snapshot_level_nr = level_nr;
14849 }
14850
14851 boolean CheckSaveEngineSnapshotToList()
14852 {
14853   boolean save_snapshot =
14854     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14855      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14856       game.snapshot.changed_action) ||
14857      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14858       game.snapshot.collected_item));
14859
14860   game.snapshot.changed_action = FALSE;
14861   game.snapshot.collected_item = FALSE;
14862   game.snapshot.save_snapshot = save_snapshot;
14863
14864   return save_snapshot;
14865 }
14866
14867 void SaveEngineSnapshotToList()
14868 {
14869   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14870       tape.quick_resume)
14871     return;
14872
14873   ListNode *buffers = SaveEngineSnapshotBuffers();
14874
14875   /* finally save all snapshot buffers to snapshot list */
14876   SaveSnapshotToList(buffers);
14877 }
14878
14879 void SaveEngineSnapshotToListInitial()
14880 {
14881   FreeEngineSnapshotList();
14882
14883   SaveEngineSnapshotToList();
14884 }
14885
14886 void LoadEngineSnapshotValues()
14887 {
14888   /* restore special values from snapshot structure */
14889
14890   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14891     LoadEngineSnapshotValues_RND();
14892   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14893     LoadEngineSnapshotValues_EM();
14894   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14895     LoadEngineSnapshotValues_SP();
14896 }
14897
14898 void LoadEngineSnapshotSingle()
14899 {
14900   LoadSnapshotSingle();
14901
14902   LoadEngineSnapshotValues();
14903 }
14904
14905 void LoadEngineSnapshot_Undo(int steps)
14906 {
14907   LoadSnapshotFromList_Older(steps);
14908
14909   LoadEngineSnapshotValues();
14910 }
14911
14912 void LoadEngineSnapshot_Redo(int steps)
14913 {
14914   LoadSnapshotFromList_Newer(steps);
14915
14916   LoadEngineSnapshotValues();
14917 }
14918
14919 boolean CheckEngineSnapshotSingle()
14920 {
14921   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14922           snapshot_level_nr == level_nr);
14923 }
14924
14925 boolean CheckEngineSnapshotList()
14926 {
14927   return CheckSnapshotList();
14928 }
14929
14930
14931 /* ---------- new game button stuff ---------------------------------------- */
14932
14933 static struct
14934 {
14935   int graphic;
14936   struct XY *pos;
14937   int gadget_id;
14938   char *infotext;
14939 } gamebutton_info[NUM_GAME_BUTTONS] =
14940 {
14941   {
14942     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14943     GAME_CTRL_ID_STOP,                  "stop game"
14944   },
14945   {
14946     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14947     GAME_CTRL_ID_PAUSE,                 "pause game"
14948   },
14949   {
14950     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14951     GAME_CTRL_ID_PLAY,                  "play game"
14952   },
14953   {
14954     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14955     GAME_CTRL_ID_UNDO,                  "undo step"
14956   },
14957   {
14958     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14959     GAME_CTRL_ID_REDO,                  "redo step"
14960   },
14961   {
14962     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14963     GAME_CTRL_ID_SAVE,                  "save game"
14964   },
14965   {
14966     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14967     GAME_CTRL_ID_PAUSE2,                "pause game"
14968   },
14969   {
14970     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14971     GAME_CTRL_ID_LOAD,                  "load game"
14972   },
14973   {
14974     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14975     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14976   },
14977   {
14978     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14979     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14980   },
14981   {
14982     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14983     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14984   }
14985 };
14986
14987 void CreateGameButtons()
14988 {
14989   int i;
14990
14991   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14992   {
14993     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14994     struct XY *pos = gamebutton_info[i].pos;
14995     struct GadgetInfo *gi;
14996     int button_type;
14997     boolean checked;
14998     unsigned int event_mask;
14999     int base_x = (tape.show_game_buttons ? VX : DX);
15000     int base_y = (tape.show_game_buttons ? VY : DY);
15001     int gd_x   = gfx->src_x;
15002     int gd_y   = gfx->src_y;
15003     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15004     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15005     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15006     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15007     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15008     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15009     int id = i;
15010
15011     if (gfx->bitmap == NULL)
15012     {
15013       game_gadget[id] = NULL;
15014
15015       continue;
15016     }
15017
15018     if (id == GAME_CTRL_ID_STOP ||
15019         id == GAME_CTRL_ID_PLAY ||
15020         id == GAME_CTRL_ID_SAVE ||
15021         id == GAME_CTRL_ID_LOAD)
15022     {
15023       button_type = GD_TYPE_NORMAL_BUTTON;
15024       checked = FALSE;
15025       event_mask = GD_EVENT_RELEASED;
15026     }
15027     else if (id == GAME_CTRL_ID_UNDO ||
15028              id == GAME_CTRL_ID_REDO)
15029     {
15030       button_type = GD_TYPE_NORMAL_BUTTON;
15031       checked = FALSE;
15032       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15033     }
15034     else
15035     {
15036       button_type = GD_TYPE_CHECK_BUTTON;
15037       checked =
15038         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15039          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15040          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15041       event_mask = GD_EVENT_PRESSED;
15042     }
15043
15044     gi = CreateGadget(GDI_CUSTOM_ID, id,
15045                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15046                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15047                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15048                       GDI_WIDTH, gfx->width,
15049                       GDI_HEIGHT, gfx->height,
15050                       GDI_TYPE, button_type,
15051                       GDI_STATE, GD_BUTTON_UNPRESSED,
15052                       GDI_CHECKED, checked,
15053                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15054                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15055                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15056                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15057                       GDI_DIRECT_DRAW, FALSE,
15058                       GDI_EVENT_MASK, event_mask,
15059                       GDI_CALLBACK_ACTION, HandleGameButtons,
15060                       GDI_END);
15061
15062     if (gi == NULL)
15063       Error(ERR_EXIT, "cannot create gadget");
15064
15065     game_gadget[id] = gi;
15066   }
15067 }
15068
15069 void FreeGameButtons()
15070 {
15071   int i;
15072
15073   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15074     FreeGadget(game_gadget[i]);
15075 }
15076
15077 static void UnmapGameButtonsAtSamePosition(int id)
15078 {
15079   int i;
15080
15081   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15082     if (i != id &&
15083         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15084         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15085       UnmapGadget(game_gadget[i]);
15086 }
15087
15088 static void UnmapGameButtonsAtSamePosition_All()
15089 {
15090   if (setup.show_snapshot_buttons)
15091   {
15092     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15093     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15094     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15095   }
15096   else
15097   {
15098     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15099     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15100     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15101   }
15102 }
15103
15104 static void MapGameButtonsAtSamePosition(int id)
15105 {
15106   int i;
15107
15108   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15109     if (i != id &&
15110         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15111         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15112       MapGadget(game_gadget[i]);
15113
15114   UnmapGameButtonsAtSamePosition_All();
15115 }
15116
15117 void MapUndoRedoButtons()
15118 {
15119   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15120   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15121
15122   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15123   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15124
15125   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15126 }
15127
15128 void UnmapUndoRedoButtons()
15129 {
15130   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15131   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15132
15133   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15134   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15135
15136   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15137 }
15138
15139 void MapGameButtons()
15140 {
15141   int i;
15142
15143   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15144     if (i != GAME_CTRL_ID_UNDO &&
15145         i != GAME_CTRL_ID_REDO)
15146       MapGadget(game_gadget[i]);
15147
15148   UnmapGameButtonsAtSamePosition_All();
15149
15150   RedrawGameButtons();
15151 }
15152
15153 void UnmapGameButtons()
15154 {
15155   int i;
15156
15157   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15158     UnmapGadget(game_gadget[i]);
15159 }
15160
15161 void RedrawGameButtons()
15162 {
15163   int i;
15164
15165   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15166     RedrawGadget(game_gadget[i]);
15167
15168   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15169   redraw_mask &= ~REDRAW_ALL;
15170 }
15171
15172 void GameUndoRedoExt()
15173 {
15174   ClearPlayerAction();
15175
15176   tape.pausing = TRUE;
15177
15178   RedrawPlayfield();
15179   UpdateAndDisplayGameControlValues();
15180
15181   DrawCompleteVideoDisplay();
15182   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15183   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15184   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15185
15186   BackToFront();
15187 }
15188
15189 void GameUndo(int steps)
15190 {
15191   if (!CheckEngineSnapshotList())
15192     return;
15193
15194   LoadEngineSnapshot_Undo(steps);
15195
15196   GameUndoRedoExt();
15197 }
15198
15199 void GameRedo(int steps)
15200 {
15201   if (!CheckEngineSnapshotList())
15202     return;
15203
15204   LoadEngineSnapshot_Redo(steps);
15205
15206   GameUndoRedoExt();
15207 }
15208
15209 static void HandleGameButtonsExt(int id, int button)
15210 {
15211   static boolean game_undo_executed = FALSE;
15212   int steps = BUTTON_STEPSIZE(button);
15213   boolean handle_game_buttons =
15214     (game_status == GAME_MODE_PLAYING ||
15215      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15216
15217   if (!handle_game_buttons)
15218     return;
15219
15220   switch (id)
15221   {
15222     case GAME_CTRL_ID_STOP:
15223       if (game_status == GAME_MODE_MAIN)
15224         break;
15225
15226       if (tape.playing)
15227         TapeStop();
15228       else
15229         RequestQuitGame(TRUE);
15230
15231       break;
15232
15233     case GAME_CTRL_ID_PAUSE:
15234     case GAME_CTRL_ID_PAUSE2:
15235       if (options.network && game_status == GAME_MODE_PLAYING)
15236       {
15237 #if defined(NETWORK_AVALIABLE)
15238         if (tape.pausing)
15239           SendToServer_ContinuePlaying();
15240         else
15241           SendToServer_PausePlaying();
15242 #endif
15243       }
15244       else
15245         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15246
15247       game_undo_executed = FALSE;
15248
15249       break;
15250
15251     case GAME_CTRL_ID_PLAY:
15252       if (game_status == GAME_MODE_MAIN)
15253       {
15254         StartGameActions(options.network, setup.autorecord, level.random_seed);
15255       }
15256       else if (tape.pausing)
15257       {
15258 #if defined(NETWORK_AVALIABLE)
15259         if (options.network)
15260           SendToServer_ContinuePlaying();
15261         else
15262 #endif
15263           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15264       }
15265       break;
15266
15267     case GAME_CTRL_ID_UNDO:
15268       // Important: When using "save snapshot when collecting an item" mode,
15269       // load last (current) snapshot for first "undo" after pressing "pause"
15270       // (else the last-but-one snapshot would be loaded, because the snapshot
15271       // pointer already points to the last snapshot when pressing "pause",
15272       // which is fine for "every step/move" mode, but not for "every collect")
15273       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15274           !game_undo_executed)
15275         steps--;
15276
15277       game_undo_executed = TRUE;
15278
15279       GameUndo(steps);
15280       break;
15281
15282     case GAME_CTRL_ID_REDO:
15283       GameRedo(steps);
15284       break;
15285
15286     case GAME_CTRL_ID_SAVE:
15287       TapeQuickSave();
15288       break;
15289
15290     case GAME_CTRL_ID_LOAD:
15291       TapeQuickLoad();
15292       break;
15293
15294     case SOUND_CTRL_ID_MUSIC:
15295       if (setup.sound_music)
15296       { 
15297         setup.sound_music = FALSE;
15298
15299         FadeMusic();
15300       }
15301       else if (audio.music_available)
15302       { 
15303         setup.sound = setup.sound_music = TRUE;
15304
15305         SetAudioMode(setup.sound);
15306
15307         PlayLevelMusic();
15308       }
15309       break;
15310
15311     case SOUND_CTRL_ID_LOOPS:
15312       if (setup.sound_loops)
15313         setup.sound_loops = FALSE;
15314       else if (audio.loops_available)
15315       {
15316         setup.sound = setup.sound_loops = TRUE;
15317
15318         SetAudioMode(setup.sound);
15319       }
15320       break;
15321
15322     case SOUND_CTRL_ID_SIMPLE:
15323       if (setup.sound_simple)
15324         setup.sound_simple = FALSE;
15325       else if (audio.sound_available)
15326       {
15327         setup.sound = setup.sound_simple = TRUE;
15328
15329         SetAudioMode(setup.sound);
15330       }
15331       break;
15332
15333     default:
15334       break;
15335   }
15336 }
15337
15338 static void HandleGameButtons(struct GadgetInfo *gi)
15339 {
15340   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15341 }
15342
15343 void HandleSoundButtonKeys(Key key)
15344 {
15345
15346   if (key == setup.shortcut.sound_simple)
15347     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15348   else if (key == setup.shortcut.sound_loops)
15349     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15350   else if (key == setup.shortcut.sound_music)
15351     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15352 }