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