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