fixed saving game engine snapshots (save after game actions are finished)
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_FRAME                        35
126 #define GAME_PANEL_SHIELD_NORMAL                36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
128 #define GAME_PANEL_SHIELD_DEADLY                38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
130 #define GAME_PANEL_EXIT                         40
131 #define GAME_PANEL_EMC_MAGIC_BALL               41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
133 #define GAME_PANEL_LIGHT_SWITCH                 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
135 #define GAME_PANEL_TIMEGATE_SWITCH              45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
137 #define GAME_PANEL_SWITCHGATE_SWITCH            47
138 #define GAME_PANEL_EMC_LENSES                   48
139 #define GAME_PANEL_EMC_LENSES_TIME              49
140 #define GAME_PANEL_EMC_MAGNIFIER                50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
142 #define GAME_PANEL_BALLOON_SWITCH               52
143 #define GAME_PANEL_DYNABOMB_NUMBER              53
144 #define GAME_PANEL_DYNABOMB_SIZE                54
145 #define GAME_PANEL_DYNABOMB_POWER               55
146 #define GAME_PANEL_PENGUINS                     56
147 #define GAME_PANEL_SOKOBAN_OBJECTS              57
148 #define GAME_PANEL_SOKOBAN_FIELDS               58
149 #define GAME_PANEL_ROBOT_WHEEL                  59
150 #define GAME_PANEL_CONVEYOR_BELT_1              60
151 #define GAME_PANEL_CONVEYOR_BELT_2              61
152 #define GAME_PANEL_CONVEYOR_BELT_3              62
153 #define GAME_PANEL_CONVEYOR_BELT_4              63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
158 #define GAME_PANEL_MAGIC_WALL                   68
159 #define GAME_PANEL_MAGIC_WALL_TIME              69
160 #define GAME_PANEL_GRAVITY_STATE                70
161 #define GAME_PANEL_GRAPHIC_1                    71
162 #define GAME_PANEL_GRAPHIC_2                    72
163 #define GAME_PANEL_GRAPHIC_3                    73
164 #define GAME_PANEL_GRAPHIC_4                    74
165 #define GAME_PANEL_GRAPHIC_5                    75
166 #define GAME_PANEL_GRAPHIC_6                    76
167 #define GAME_PANEL_GRAPHIC_7                    77
168 #define GAME_PANEL_GRAPHIC_8                    78
169 #define GAME_PANEL_ELEMENT_1                    79
170 #define GAME_PANEL_ELEMENT_2                    80
171 #define GAME_PANEL_ELEMENT_3                    81
172 #define GAME_PANEL_ELEMENT_4                    82
173 #define GAME_PANEL_ELEMENT_5                    83
174 #define GAME_PANEL_ELEMENT_6                    84
175 #define GAME_PANEL_ELEMENT_7                    85
176 #define GAME_PANEL_ELEMENT_8                    86
177 #define GAME_PANEL_ELEMENT_COUNT_1              87
178 #define GAME_PANEL_ELEMENT_COUNT_2              88
179 #define GAME_PANEL_ELEMENT_COUNT_3              89
180 #define GAME_PANEL_ELEMENT_COUNT_4              90
181 #define GAME_PANEL_ELEMENT_COUNT_5              91
182 #define GAME_PANEL_ELEMENT_COUNT_6              92
183 #define GAME_PANEL_ELEMENT_COUNT_7              93
184 #define GAME_PANEL_ELEMENT_COUNT_8              94
185 #define GAME_PANEL_CE_SCORE_1                   95
186 #define GAME_PANEL_CE_SCORE_2                   96
187 #define GAME_PANEL_CE_SCORE_3                   97
188 #define GAME_PANEL_CE_SCORE_4                   98
189 #define GAME_PANEL_CE_SCORE_5                   99
190 #define GAME_PANEL_CE_SCORE_6                   100
191 #define GAME_PANEL_CE_SCORE_7                   101
192 #define GAME_PANEL_CE_SCORE_8                   102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
201 #define GAME_PANEL_PLAYER_NAME                  111
202 #define GAME_PANEL_LEVEL_NAME                   112
203 #define GAME_PANEL_LEVEL_AUTHOR                 113
204
205 #define NUM_GAME_PANEL_CONTROLS                 114
206
207 struct GamePanelOrderInfo
208 {
209   int nr;
210   int sort_priority;
211 };
212
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
214
215 struct GamePanelControlInfo
216 {
217   int nr;
218
219   struct TextPosInfo *pos;
220   int type;
221
222   int value, last_value;
223   int frame, last_frame;
224   int gfx_frame;
225   int gfx_random;
226 };
227
228 static struct GamePanelControlInfo game_panel_controls[] =
229 {
230   {
231     GAME_PANEL_LEVEL_NUMBER,
232     &game.panel.level_number,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_PANEL_GEMS,
237     &game.panel.gems,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_INVENTORY_COUNT,
242     &game.panel.inventory_count,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_FIRST_1,
247     &game.panel.inventory_first[0],
248     TYPE_ELEMENT,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_2,
252     &game.panel.inventory_first[1],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_3,
257     &game.panel.inventory_first[2],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_4,
262     &game.panel.inventory_first[3],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_5,
267     &game.panel.inventory_first[4],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_6,
272     &game.panel.inventory_first[5],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_7,
277     &game.panel.inventory_first[6],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_8,
282     &game.panel.inventory_first[7],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_LAST_1,
287     &game.panel.inventory_last[0],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_2,
292     &game.panel.inventory_last[1],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_3,
297     &game.panel.inventory_last[2],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_4,
302     &game.panel.inventory_last[3],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_5,
307     &game.panel.inventory_last[4],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_6,
312     &game.panel.inventory_last[5],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_7,
317     &game.panel.inventory_last[6],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_8,
322     &game.panel.inventory_last[7],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_KEY_1,
327     &game.panel.key[0],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_2,
332     &game.panel.key[1],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_3,
337     &game.panel.key[2],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_4,
342     &game.panel.key[3],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_5,
347     &game.panel.key[4],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_6,
352     &game.panel.key[5],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_7,
357     &game.panel.key[6],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_8,
362     &game.panel.key[7],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_WHITE,
367     &game.panel.key_white,
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE_COUNT,
372     &game.panel.key_white_count,
373     TYPE_INTEGER,
374   },
375   {
376     GAME_PANEL_SCORE,
377     &game.panel.score,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_HIGHSCORE,
382     &game.panel.highscore,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_TIME,
387     &game.panel.time,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME_HH,
392     &game.panel.time_hh,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_MM,
397     &game.panel.time_mm,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_SS,
402     &game.panel.time_ss,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_FRAME,
407     &game.panel.frame,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_SHIELD_NORMAL,
412     &game.panel.shield_normal,
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_PANEL_SHIELD_NORMAL_TIME,
417     &game.panel.shield_normal_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_PANEL_SHIELD_DEADLY,
422     &game.panel.shield_deadly,
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_PANEL_SHIELD_DEADLY_TIME,
427     &game.panel.shield_deadly_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_PANEL_EXIT,
432     &game.panel.exit,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_PANEL_EMC_MAGIC_BALL,
437     &game.panel.emc_magic_ball,
438     TYPE_ELEMENT,
439   },
440   {
441     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442     &game.panel.emc_magic_ball_switch,
443     TYPE_ELEMENT,
444   },
445   {
446     GAME_PANEL_LIGHT_SWITCH,
447     &game.panel.light_switch,
448     TYPE_ELEMENT,
449   },
450   {
451     GAME_PANEL_LIGHT_SWITCH_TIME,
452     &game.panel.light_switch_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_PANEL_TIMEGATE_SWITCH,
457     &game.panel.timegate_switch,
458     TYPE_ELEMENT,
459   },
460   {
461     GAME_PANEL_TIMEGATE_SWITCH_TIME,
462     &game.panel.timegate_switch_time,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_PANEL_SWITCHGATE_SWITCH,
467     &game.panel.switchgate_switch,
468     TYPE_ELEMENT,
469   },
470   {
471     GAME_PANEL_EMC_LENSES,
472     &game.panel.emc_lenses,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_PANEL_EMC_LENSES_TIME,
477     &game.panel.emc_lenses_time,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_PANEL_EMC_MAGNIFIER,
482     &game.panel.emc_magnifier,
483     TYPE_ELEMENT,
484   },
485   {
486     GAME_PANEL_EMC_MAGNIFIER_TIME,
487     &game.panel.emc_magnifier_time,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_PANEL_BALLOON_SWITCH,
492     &game.panel.balloon_switch,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_PANEL_DYNABOMB_NUMBER,
497     &game.panel.dynabomb_number,
498     TYPE_INTEGER,
499   },
500   {
501     GAME_PANEL_DYNABOMB_SIZE,
502     &game.panel.dynabomb_size,
503     TYPE_INTEGER,
504   },
505   {
506     GAME_PANEL_DYNABOMB_POWER,
507     &game.panel.dynabomb_power,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_PANEL_PENGUINS,
512     &game.panel.penguins,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_PANEL_SOKOBAN_OBJECTS,
517     &game.panel.sokoban_objects,
518     TYPE_INTEGER,
519   },
520   {
521     GAME_PANEL_SOKOBAN_FIELDS,
522     &game.panel.sokoban_fields,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_PANEL_ROBOT_WHEEL,
527     &game.panel.robot_wheel,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_PANEL_CONVEYOR_BELT_1,
532     &game.panel.conveyor_belt[0],
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_PANEL_CONVEYOR_BELT_2,
537     &game.panel.conveyor_belt[1],
538     TYPE_ELEMENT,
539   },
540   {
541     GAME_PANEL_CONVEYOR_BELT_3,
542     &game.panel.conveyor_belt[2],
543     TYPE_ELEMENT,
544   },
545   {
546     GAME_PANEL_CONVEYOR_BELT_4,
547     &game.panel.conveyor_belt[3],
548     TYPE_ELEMENT,
549   },
550   {
551     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552     &game.panel.conveyor_belt_switch[0],
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557     &game.panel.conveyor_belt_switch[1],
558     TYPE_ELEMENT,
559   },
560   {
561     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562     &game.panel.conveyor_belt_switch[2],
563     TYPE_ELEMENT,
564   },
565   {
566     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567     &game.panel.conveyor_belt_switch[3],
568     TYPE_ELEMENT,
569   },
570   {
571     GAME_PANEL_MAGIC_WALL,
572     &game.panel.magic_wall,
573     TYPE_ELEMENT,
574   },
575   {
576     GAME_PANEL_MAGIC_WALL_TIME,
577     &game.panel.magic_wall_time,
578     TYPE_INTEGER,
579   },
580   {
581     GAME_PANEL_GRAVITY_STATE,
582     &game.panel.gravity_state,
583     TYPE_STRING,
584   },
585   {
586     GAME_PANEL_GRAPHIC_1,
587     &game.panel.graphic[0],
588     TYPE_ELEMENT,
589   },
590   {
591     GAME_PANEL_GRAPHIC_2,
592     &game.panel.graphic[1],
593     TYPE_ELEMENT,
594   },
595   {
596     GAME_PANEL_GRAPHIC_3,
597     &game.panel.graphic[2],
598     TYPE_ELEMENT,
599   },
600   {
601     GAME_PANEL_GRAPHIC_4,
602     &game.panel.graphic[3],
603     TYPE_ELEMENT,
604   },
605   {
606     GAME_PANEL_GRAPHIC_5,
607     &game.panel.graphic[4],
608     TYPE_ELEMENT,
609   },
610   {
611     GAME_PANEL_GRAPHIC_6,
612     &game.panel.graphic[5],
613     TYPE_ELEMENT,
614   },
615   {
616     GAME_PANEL_GRAPHIC_7,
617     &game.panel.graphic[6],
618     TYPE_ELEMENT,
619   },
620   {
621     GAME_PANEL_GRAPHIC_8,
622     &game.panel.graphic[7],
623     TYPE_ELEMENT,
624   },
625   {
626     GAME_PANEL_ELEMENT_1,
627     &game.panel.element[0],
628     TYPE_ELEMENT,
629   },
630   {
631     GAME_PANEL_ELEMENT_2,
632     &game.panel.element[1],
633     TYPE_ELEMENT,
634   },
635   {
636     GAME_PANEL_ELEMENT_3,
637     &game.panel.element[2],
638     TYPE_ELEMENT,
639   },
640   {
641     GAME_PANEL_ELEMENT_4,
642     &game.panel.element[3],
643     TYPE_ELEMENT,
644   },
645   {
646     GAME_PANEL_ELEMENT_5,
647     &game.panel.element[4],
648     TYPE_ELEMENT,
649   },
650   {
651     GAME_PANEL_ELEMENT_6,
652     &game.panel.element[5],
653     TYPE_ELEMENT,
654   },
655   {
656     GAME_PANEL_ELEMENT_7,
657     &game.panel.element[6],
658     TYPE_ELEMENT,
659   },
660   {
661     GAME_PANEL_ELEMENT_8,
662     &game.panel.element[7],
663     TYPE_ELEMENT,
664   },
665   {
666     GAME_PANEL_ELEMENT_COUNT_1,
667     &game.panel.element_count[0],
668     TYPE_INTEGER,
669   },
670   {
671     GAME_PANEL_ELEMENT_COUNT_2,
672     &game.panel.element_count[1],
673     TYPE_INTEGER,
674   },
675   {
676     GAME_PANEL_ELEMENT_COUNT_3,
677     &game.panel.element_count[2],
678     TYPE_INTEGER,
679   },
680   {
681     GAME_PANEL_ELEMENT_COUNT_4,
682     &game.panel.element_count[3],
683     TYPE_INTEGER,
684   },
685   {
686     GAME_PANEL_ELEMENT_COUNT_5,
687     &game.panel.element_count[4],
688     TYPE_INTEGER,
689   },
690   {
691     GAME_PANEL_ELEMENT_COUNT_6,
692     &game.panel.element_count[5],
693     TYPE_INTEGER,
694   },
695   {
696     GAME_PANEL_ELEMENT_COUNT_7,
697     &game.panel.element_count[6],
698     TYPE_INTEGER,
699   },
700   {
701     GAME_PANEL_ELEMENT_COUNT_8,
702     &game.panel.element_count[7],
703     TYPE_INTEGER,
704   },
705   {
706     GAME_PANEL_CE_SCORE_1,
707     &game.panel.ce_score[0],
708     TYPE_INTEGER,
709   },
710   {
711     GAME_PANEL_CE_SCORE_2,
712     &game.panel.ce_score[1],
713     TYPE_INTEGER,
714   },
715   {
716     GAME_PANEL_CE_SCORE_3,
717     &game.panel.ce_score[2],
718     TYPE_INTEGER,
719   },
720   {
721     GAME_PANEL_CE_SCORE_4,
722     &game.panel.ce_score[3],
723     TYPE_INTEGER,
724   },
725   {
726     GAME_PANEL_CE_SCORE_5,
727     &game.panel.ce_score[4],
728     TYPE_INTEGER,
729   },
730   {
731     GAME_PANEL_CE_SCORE_6,
732     &game.panel.ce_score[5],
733     TYPE_INTEGER,
734   },
735   {
736     GAME_PANEL_CE_SCORE_7,
737     &game.panel.ce_score[6],
738     TYPE_INTEGER,
739   },
740   {
741     GAME_PANEL_CE_SCORE_8,
742     &game.panel.ce_score[7],
743     TYPE_INTEGER,
744   },
745   {
746     GAME_PANEL_CE_SCORE_1_ELEMENT,
747     &game.panel.ce_score_element[0],
748     TYPE_ELEMENT,
749   },
750   {
751     GAME_PANEL_CE_SCORE_2_ELEMENT,
752     &game.panel.ce_score_element[1],
753     TYPE_ELEMENT,
754   },
755   {
756     GAME_PANEL_CE_SCORE_3_ELEMENT,
757     &game.panel.ce_score_element[2],
758     TYPE_ELEMENT,
759   },
760   {
761     GAME_PANEL_CE_SCORE_4_ELEMENT,
762     &game.panel.ce_score_element[3],
763     TYPE_ELEMENT,
764   },
765   {
766     GAME_PANEL_CE_SCORE_5_ELEMENT,
767     &game.panel.ce_score_element[4],
768     TYPE_ELEMENT,
769   },
770   {
771     GAME_PANEL_CE_SCORE_6_ELEMENT,
772     &game.panel.ce_score_element[5],
773     TYPE_ELEMENT,
774   },
775   {
776     GAME_PANEL_CE_SCORE_7_ELEMENT,
777     &game.panel.ce_score_element[6],
778     TYPE_ELEMENT,
779   },
780   {
781     GAME_PANEL_CE_SCORE_8_ELEMENT,
782     &game.panel.ce_score_element[7],
783     TYPE_ELEMENT,
784   },
785   {
786     GAME_PANEL_PLAYER_NAME,
787     &game.panel.player_name,
788     TYPE_STRING,
789   },
790   {
791     GAME_PANEL_LEVEL_NAME,
792     &game.panel.level_name,
793     TYPE_STRING,
794   },
795   {
796     GAME_PANEL_LEVEL_AUTHOR,
797     &game.panel.level_author,
798     TYPE_STRING,
799   },
800
801   {
802     -1,
803     NULL,
804     -1,
805   }
806 };
807
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING      3
810 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION   2
812 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
813
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF  -1
816 #define INITIAL_MOVE_DELAY_ON   0
817
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED    32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED   4
822 #define MOVE_DELAY_MAX_SPEED    1
823
824 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
826
827 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
829
830 /* values for other actions */
831 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
832 #define MOVE_STEPSIZE_MIN       (1)
833 #define MOVE_STEPSIZE_MAX       (TILEX)
834
835 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
836 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
837
838 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
839
840 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
841                                  RND(element_info[e].push_delay_random))
842 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
843                                  RND(element_info[e].drop_delay_random))
844 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
845                                  RND(element_info[e].move_delay_random))
846 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
847                                     (element_info[e].move_delay_random))
848 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
849                                  RND(element_info[e].ce_value_random_initial))
850 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
851 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
852                                  RND((c)->delay_random * (c)->delay_frames))
853 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
854                                  RND((c)->delay_random))
855
856
857 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
858          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
859
860 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
861         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
862          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
863          (be) + (e) - EL_SELF)
864
865 #define GET_PLAYER_FROM_BITS(p)                                         \
866         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
867
868 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
869         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
870          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
871          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
872          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
873          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
874          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
875          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
876          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
877          (e))
878
879 #define CAN_GROW_INTO(e)                                                \
880         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
881
882 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
883                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
884                                         (condition)))
885
886 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
887                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
888                                         (CAN_MOVE_INTO_ACID(e) &&       \
889                                          Feld[x][y] == EL_ACID) ||      \
890                                         (condition)))
891
892 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
893                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
894                                         (CAN_MOVE_INTO_ACID(e) &&       \
895                                          Feld[x][y] == EL_ACID) ||      \
896                                         (condition)))
897
898 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
899                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
900                                         (condition) ||                  \
901                                         (CAN_MOVE_INTO_ACID(e) &&       \
902                                          Feld[x][y] == EL_ACID) ||      \
903                                         (DONT_COLLIDE_WITH(e) &&        \
904                                          IS_PLAYER(x, y) &&             \
905                                          !PLAYER_ENEMY_PROTECTED(x, y))))
906
907 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
908         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
909
910 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
911         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
912
913 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
914         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
915
916 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
917         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
918                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
919
920 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
921         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
922
923 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
924         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
925
926 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
927         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
928
929 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
930         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
931
932 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
933         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
934
935 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
936         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
939                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
940                                                  IS_FOOD_PENGUIN(Feld[x][y])))
941 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
942         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
943
944 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
946
947 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
949
950 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
951         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
952                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
953
954 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
955
956 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
957                 (!IS_PLAYER(x, y) &&                                    \
958                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
959
960 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
961         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
962
963 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
964 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
965
966 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
967 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
969 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
970
971 /* game button identifiers */
972 #define GAME_CTRL_ID_STOP               0
973 #define GAME_CTRL_ID_PAUSE              1
974 #define GAME_CTRL_ID_PLAY               2
975 #define GAME_CTRL_ID_UNDO               3
976 #define GAME_CTRL_ID_REDO               4
977 #define GAME_CTRL_ID_SAVE               5
978 #define GAME_CTRL_ID_PAUSE2             6
979 #define GAME_CTRL_ID_LOAD               7
980 #define SOUND_CTRL_ID_MUSIC             8
981 #define SOUND_CTRL_ID_LOOPS             9
982 #define SOUND_CTRL_ID_SIMPLE            10
983
984 #define NUM_GAME_BUTTONS                11
985
986
987 /* forward declaration for internal use */
988
989 static void CreateField(int, int, int);
990
991 static void ResetGfxAnimation(int, int);
992
993 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
994 static void AdvanceFrameAndPlayerCounters(int);
995
996 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
997 static boolean MovePlayer(struct PlayerInfo *, int, int);
998 static void ScrollPlayer(struct PlayerInfo *, int);
999 static void ScrollScreen(struct PlayerInfo *, int);
1000
1001 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1002 static boolean DigFieldByCE(int, int, int);
1003 static boolean SnapField(struct PlayerInfo *, int, int);
1004 static boolean DropElement(struct PlayerInfo *);
1005
1006 static void InitBeltMovement(void);
1007 static void CloseAllOpenTimegates(void);
1008 static void CheckGravityMovement(struct PlayerInfo *);
1009 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1010 static void KillPlayerUnlessEnemyProtected(int, int);
1011 static void KillPlayerUnlessExplosionProtected(int, int);
1012
1013 static void TestIfPlayerTouchesCustomElement(int, int);
1014 static void TestIfElementTouchesCustomElement(int, int);
1015 static void TestIfElementHitsCustomElement(int, int, int);
1016
1017 static void HandleElementChange(int, int, int);
1018 static void ExecuteCustomElementAction(int, int, int, int);
1019 static boolean ChangeElement(int, int, int, int);
1020
1021 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1022 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1023         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1024 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1026 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1027         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1028 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1029         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1030
1031 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1032 #define CheckElementChange(x, y, e, te, ev)                             \
1033         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1034 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1035         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1036 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1037         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1038
1039 static void PlayLevelSound(int, int, int);
1040 static void PlayLevelSoundNearest(int, int, int);
1041 static void PlayLevelSoundAction(int, int, int);
1042 static void PlayLevelSoundElementAction(int, int, int, int);
1043 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1044 static void PlayLevelSoundActionIfLoop(int, int, int);
1045 static void StopLevelSoundActionIfLoop(int, int, int);
1046 static void PlayLevelMusic();
1047
1048 static void HandleGameButtons(struct GadgetInfo *);
1049
1050 int AmoebeNachbarNr(int, int);
1051 void AmoebeUmwandeln(int, int);
1052 void ContinueMoving(int, int);
1053 void Bang(int, int);
1054 void InitMovDir(int, int);
1055 void InitAmoebaNr(int, int);
1056 int NewHiScore(void);
1057
1058 void TestIfGoodThingHitsBadThing(int, int, int);
1059 void TestIfBadThingHitsGoodThing(int, int, int);
1060 void TestIfPlayerTouchesBadThing(int, int);
1061 void TestIfPlayerRunsIntoBadThing(int, int, int);
1062 void TestIfBadThingTouchesPlayer(int, int);
1063 void TestIfBadThingRunsIntoPlayer(int, int, int);
1064 void TestIfFriendTouchesBadThing(int, int);
1065 void TestIfBadThingTouchesFriend(int, int);
1066 void TestIfBadThingTouchesOtherBadThing(int, int);
1067 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1068
1069 void KillPlayer(struct PlayerInfo *);
1070 void BuryPlayer(struct PlayerInfo *);
1071 void RemovePlayer(struct PlayerInfo *);
1072
1073 static int getInvisibleActiveFromInvisibleElement(int);
1074 static int getInvisibleFromInvisibleActiveElement(int);
1075
1076 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1077
1078 /* for detection of endless loops, caused by custom element programming */
1079 /* (using maximal playfield width x 10 is just a rough approximation) */
1080 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1081
1082 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1083 {                                                                       \
1084   if (recursion_loop_detected)                                          \
1085     return (rc);                                                        \
1086                                                                         \
1087   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1088   {                                                                     \
1089     recursion_loop_detected = TRUE;                                     \
1090     recursion_loop_element = (e);                                       \
1091   }                                                                     \
1092                                                                         \
1093   recursion_loop_depth++;                                               \
1094 }
1095
1096 #define RECURSION_LOOP_DETECTION_END()                                  \
1097 {                                                                       \
1098   recursion_loop_depth--;                                               \
1099 }
1100
1101 static int recursion_loop_depth;
1102 static boolean recursion_loop_detected;
1103 static boolean recursion_loop_element;
1104
1105 static int map_player_action[MAX_PLAYERS];
1106
1107
1108 /* ------------------------------------------------------------------------- */
1109 /* definition of elements that automatically change to other elements after  */
1110 /* a specified time, eventually calling a function when changing             */
1111 /* ------------------------------------------------------------------------- */
1112
1113 /* forward declaration for changer functions */
1114 static void InitBuggyBase(int, int);
1115 static void WarnBuggyBase(int, int);
1116
1117 static void InitTrap(int, int);
1118 static void ActivateTrap(int, int);
1119 static void ChangeActiveTrap(int, int);
1120
1121 static void InitRobotWheel(int, int);
1122 static void RunRobotWheel(int, int);
1123 static void StopRobotWheel(int, int);
1124
1125 static void InitTimegateWheel(int, int);
1126 static void RunTimegateWheel(int, int);
1127
1128 static void InitMagicBallDelay(int, int);
1129 static void ActivateMagicBall(int, int);
1130
1131 struct ChangingElementInfo
1132 {
1133   int element;
1134   int target_element;
1135   int change_delay;
1136   void (*pre_change_function)(int x, int y);
1137   void (*change_function)(int x, int y);
1138   void (*post_change_function)(int x, int y);
1139 };
1140
1141 static struct ChangingElementInfo change_delay_list[] =
1142 {
1143   {
1144     EL_NUT_BREAKING,
1145     EL_EMERALD,
1146     6,
1147     NULL,
1148     NULL,
1149     NULL
1150   },
1151   {
1152     EL_PEARL_BREAKING,
1153     EL_EMPTY,
1154     8,
1155     NULL,
1156     NULL,
1157     NULL
1158   },
1159   {
1160     EL_EXIT_OPENING,
1161     EL_EXIT_OPEN,
1162     29,
1163     NULL,
1164     NULL,
1165     NULL
1166   },
1167   {
1168     EL_EXIT_CLOSING,
1169     EL_EXIT_CLOSED,
1170     29,
1171     NULL,
1172     NULL,
1173     NULL
1174   },
1175   {
1176     EL_STEEL_EXIT_OPENING,
1177     EL_STEEL_EXIT_OPEN,
1178     29,
1179     NULL,
1180     NULL,
1181     NULL
1182   },
1183   {
1184     EL_STEEL_EXIT_CLOSING,
1185     EL_STEEL_EXIT_CLOSED,
1186     29,
1187     NULL,
1188     NULL,
1189     NULL
1190   },
1191   {
1192     EL_EM_EXIT_OPENING,
1193     EL_EM_EXIT_OPEN,
1194     29,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_EM_EXIT_CLOSING,
1201     EL_EMPTY,
1202     29,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EM_STEEL_EXIT_OPENING,
1209     EL_EM_STEEL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EM_STEEL_EXIT_CLOSING,
1217     EL_STEELWALL,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_SP_EXIT_OPENING,
1225     EL_SP_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_SP_EXIT_CLOSING,
1233     EL_SP_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_SWITCHGATE_OPENING,
1241     EL_SWITCHGATE_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_SWITCHGATE_CLOSING,
1249     EL_SWITCHGATE_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_TIMEGATE_OPENING,
1257     EL_TIMEGATE_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_TIMEGATE_CLOSING,
1265     EL_TIMEGATE_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271
1272   {
1273     EL_ACID_SPLASH_LEFT,
1274     EL_EMPTY,
1275     8,
1276     NULL,
1277     NULL,
1278     NULL
1279   },
1280   {
1281     EL_ACID_SPLASH_RIGHT,
1282     EL_EMPTY,
1283     8,
1284     NULL,
1285     NULL,
1286     NULL
1287   },
1288   {
1289     EL_SP_BUGGY_BASE,
1290     EL_SP_BUGGY_BASE_ACTIVATING,
1291     0,
1292     InitBuggyBase,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_SP_BUGGY_BASE_ACTIVATING,
1298     EL_SP_BUGGY_BASE_ACTIVE,
1299     0,
1300     InitBuggyBase,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_BUGGY_BASE_ACTIVE,
1306     EL_SP_BUGGY_BASE,
1307     0,
1308     InitBuggyBase,
1309     WarnBuggyBase,
1310     NULL
1311   },
1312   {
1313     EL_TRAP,
1314     EL_TRAP_ACTIVE,
1315     0,
1316     InitTrap,
1317     NULL,
1318     ActivateTrap
1319   },
1320   {
1321     EL_TRAP_ACTIVE,
1322     EL_TRAP,
1323     31,
1324     NULL,
1325     ChangeActiveTrap,
1326     NULL
1327   },
1328   {
1329     EL_ROBOT_WHEEL_ACTIVE,
1330     EL_ROBOT_WHEEL,
1331     0,
1332     InitRobotWheel,
1333     RunRobotWheel,
1334     StopRobotWheel
1335   },
1336   {
1337     EL_TIMEGATE_SWITCH_ACTIVE,
1338     EL_TIMEGATE_SWITCH,
1339     0,
1340     InitTimegateWheel,
1341     RunTimegateWheel,
1342     NULL
1343   },
1344   {
1345     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1346     EL_DC_TIMEGATE_SWITCH,
1347     0,
1348     InitTimegateWheel,
1349     RunTimegateWheel,
1350     NULL
1351   },
1352   {
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     EL_EMC_MAGIC_BALL_ACTIVE,
1355     0,
1356     InitMagicBallDelay,
1357     NULL,
1358     ActivateMagicBall
1359   },
1360   {
1361     EL_EMC_SPRING_BUMPER_ACTIVE,
1362     EL_EMC_SPRING_BUMPER,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_DIAGONAL_SHRINKING,
1370     EL_UNDEFINED,
1371     0,
1372     NULL,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_DIAGONAL_GROWING,
1378     EL_UNDEFINED,
1379     0,
1380     NULL,
1381     NULL,
1382     NULL,
1383   },
1384
1385   {
1386     EL_UNDEFINED,
1387     EL_UNDEFINED,
1388     -1,
1389     NULL,
1390     NULL,
1391     NULL
1392   }
1393 };
1394
1395 struct
1396 {
1397   int element;
1398   int push_delay_fixed, push_delay_random;
1399 }
1400 push_delay_list[] =
1401 {
1402   { EL_SPRING,                  0, 0 },
1403   { EL_BALLOON,                 0, 0 },
1404
1405   { EL_SOKOBAN_OBJECT,          2, 0 },
1406   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1407   { EL_SATELLITE,               2, 0 },
1408   { EL_SP_DISK_YELLOW,          2, 0 },
1409
1410   { EL_UNDEFINED,               0, 0 },
1411 };
1412
1413 struct
1414 {
1415   int element;
1416   int move_stepsize;
1417 }
1418 move_stepsize_list[] =
1419 {
1420   { EL_AMOEBA_DROP,             2 },
1421   { EL_AMOEBA_DROPPING,         2 },
1422   { EL_QUICKSAND_FILLING,       1 },
1423   { EL_QUICKSAND_EMPTYING,      1 },
1424   { EL_QUICKSAND_FAST_FILLING,  2 },
1425   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1426   { EL_MAGIC_WALL_FILLING,      2 },
1427   { EL_MAGIC_WALL_EMPTYING,     2 },
1428   { EL_BD_MAGIC_WALL_FILLING,   2 },
1429   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1430   { EL_DC_MAGIC_WALL_FILLING,   2 },
1431   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1432
1433   { EL_UNDEFINED,               0 },
1434 };
1435
1436 struct
1437 {
1438   int element;
1439   int count;
1440 }
1441 collect_count_list[] =
1442 {
1443   { EL_EMERALD,                 1 },
1444   { EL_BD_DIAMOND,              1 },
1445   { EL_EMERALD_YELLOW,          1 },
1446   { EL_EMERALD_RED,             1 },
1447   { EL_EMERALD_PURPLE,          1 },
1448   { EL_DIAMOND,                 3 },
1449   { EL_SP_INFOTRON,             1 },
1450   { EL_PEARL,                   5 },
1451   { EL_CRYSTAL,                 8 },
1452
1453   { EL_UNDEFINED,               0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int direction;
1460 }
1461 access_direction_list[] =
1462 {
1463   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1464   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1465   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1466   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1467   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1468   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1469   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1470   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1471   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1472   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1473   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1474
1475   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1476   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1477   { EL_SP_PORT_UP,                                                   MV_DOWN },
1478   { EL_SP_PORT_DOWN,                                         MV_UP           },
1479   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1480   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1481   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1482   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1483   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1484   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1485   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1486   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1487   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1488   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1489   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1490   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1491   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1492   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1493   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1494
1495   { EL_UNDEFINED,                       MV_NONE                              }
1496 };
1497
1498 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1499
1500 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1501 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1502 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1503                                  IS_JUST_CHANGING(x, y))
1504
1505 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1506
1507 /* static variables for playfield scan mode (scanning forward or backward) */
1508 static int playfield_scan_start_x = 0;
1509 static int playfield_scan_start_y = 0;
1510 static int playfield_scan_delta_x = 1;
1511 static int playfield_scan_delta_y = 1;
1512
1513 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1514                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1515                                      (y) += playfield_scan_delta_y)     \
1516                                 for ((x) = playfield_scan_start_x;      \
1517                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1518                                      (x) += playfield_scan_delta_x)
1519
1520 #ifdef DEBUG
1521 void DEBUG_SetMaximumDynamite()
1522 {
1523   int i;
1524
1525   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1526     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1527       local_player->inventory_element[local_player->inventory_size++] =
1528         EL_DYNAMITE;
1529 }
1530 #endif
1531
1532 static void InitPlayfieldScanModeVars()
1533 {
1534   if (game.use_reverse_scan_direction)
1535   {
1536     playfield_scan_start_x = lev_fieldx - 1;
1537     playfield_scan_start_y = lev_fieldy - 1;
1538
1539     playfield_scan_delta_x = -1;
1540     playfield_scan_delta_y = -1;
1541   }
1542   else
1543   {
1544     playfield_scan_start_x = 0;
1545     playfield_scan_start_y = 0;
1546
1547     playfield_scan_delta_x = 1;
1548     playfield_scan_delta_y = 1;
1549   }
1550 }
1551
1552 static void InitPlayfieldScanMode(int mode)
1553 {
1554   game.use_reverse_scan_direction =
1555     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1556
1557   InitPlayfieldScanModeVars();
1558 }
1559
1560 static int get_move_delay_from_stepsize(int move_stepsize)
1561 {
1562   move_stepsize =
1563     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1564
1565   /* make sure that stepsize value is always a power of 2 */
1566   move_stepsize = (1 << log_2(move_stepsize));
1567
1568   return TILEX / move_stepsize;
1569 }
1570
1571 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1572                                boolean init_game)
1573 {
1574   int player_nr = player->index_nr;
1575   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1576   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1577
1578   /* do no immediately change move delay -- the player might just be moving */
1579   player->move_delay_value_next = move_delay;
1580
1581   /* information if player can move must be set separately */
1582   player->cannot_move = cannot_move;
1583
1584   if (init_game)
1585   {
1586     player->move_delay       = game.initial_move_delay[player_nr];
1587     player->move_delay_value = game.initial_move_delay_value[player_nr];
1588
1589     player->move_delay_value_next = -1;
1590
1591     player->move_delay_reset_counter = 0;
1592   }
1593 }
1594
1595 void GetPlayerConfig()
1596 {
1597   GameFrameDelay = setup.game_frame_delay;
1598
1599   if (!audio.sound_available)
1600     setup.sound_simple = FALSE;
1601
1602   if (!audio.loops_available)
1603     setup.sound_loops = FALSE;
1604
1605   if (!audio.music_available)
1606     setup.sound_music = FALSE;
1607
1608   if (!video.fullscreen_available)
1609     setup.fullscreen = FALSE;
1610
1611   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1612
1613   SetAudioMode(setup.sound);
1614   InitJoysticks();
1615 }
1616
1617 int GetElementFromGroupElement(int element)
1618 {
1619   if (IS_GROUP_ELEMENT(element))
1620   {
1621     struct ElementGroupInfo *group = element_info[element].group;
1622     int last_anim_random_frame = gfx.anim_random_frame;
1623     int element_pos;
1624
1625     if (group->choice_mode == ANIM_RANDOM)
1626       gfx.anim_random_frame = RND(group->num_elements_resolved);
1627
1628     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1629                                     group->choice_mode, 0,
1630                                     group->choice_pos);
1631
1632     if (group->choice_mode == ANIM_RANDOM)
1633       gfx.anim_random_frame = last_anim_random_frame;
1634
1635     group->choice_pos++;
1636
1637     element = group->element_resolved[element_pos];
1638   }
1639
1640   return element;
1641 }
1642
1643 static void InitPlayerField(int x, int y, int element, boolean init_game)
1644 {
1645   if (element == EL_SP_MURPHY)
1646   {
1647     if (init_game)
1648     {
1649       if (stored_player[0].present)
1650       {
1651         Feld[x][y] = EL_SP_MURPHY_CLONE;
1652
1653         return;
1654       }
1655       else
1656       {
1657         stored_player[0].initial_element = element;
1658         stored_player[0].use_murphy = TRUE;
1659
1660         if (!level.use_artwork_element[0])
1661           stored_player[0].artwork_element = EL_SP_MURPHY;
1662       }
1663
1664       Feld[x][y] = EL_PLAYER_1;
1665     }
1666   }
1667
1668   if (init_game)
1669   {
1670     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1671     int jx = player->jx, jy = player->jy;
1672
1673     player->present = TRUE;
1674
1675     player->block_last_field = (element == EL_SP_MURPHY ?
1676                                 level.sp_block_last_field :
1677                                 level.block_last_field);
1678
1679     /* ---------- initialize player's last field block delay --------------- */
1680
1681     /* always start with reliable default value (no adjustment needed) */
1682     player->block_delay_adjustment = 0;
1683
1684     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1685     if (player->block_last_field && element == EL_SP_MURPHY)
1686       player->block_delay_adjustment = 1;
1687
1688     /* special case 2: in game engines before 3.1.1, blocking was different */
1689     if (game.use_block_last_field_bug)
1690       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1691
1692     if (!options.network || player->connected)
1693     {
1694       player->active = TRUE;
1695
1696       /* remove potentially duplicate players */
1697       if (StorePlayer[jx][jy] == Feld[x][y])
1698         StorePlayer[jx][jy] = 0;
1699
1700       StorePlayer[x][y] = Feld[x][y];
1701
1702 #if DEBUG_INIT_PLAYER
1703       if (options.debug)
1704       {
1705         printf("- player element %d activated", player->element_nr);
1706         printf(" (local player is %d and currently %s)\n",
1707                local_player->element_nr,
1708                local_player->active ? "active" : "not active");
1709       }
1710     }
1711 #endif
1712
1713     Feld[x][y] = EL_EMPTY;
1714
1715     player->jx = player->last_jx = x;
1716     player->jy = player->last_jy = y;
1717   }
1718
1719   if (!init_game)
1720   {
1721     int player_nr = GET_PLAYER_NR(element);
1722     struct PlayerInfo *player = &stored_player[player_nr];
1723
1724     if (player->active && player->killed)
1725       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1726   }
1727 }
1728
1729 static void InitField(int x, int y, boolean init_game)
1730 {
1731   int element = Feld[x][y];
1732
1733   switch (element)
1734   {
1735     case EL_SP_MURPHY:
1736     case EL_PLAYER_1:
1737     case EL_PLAYER_2:
1738     case EL_PLAYER_3:
1739     case EL_PLAYER_4:
1740       InitPlayerField(x, y, element, init_game);
1741       break;
1742
1743     case EL_SOKOBAN_FIELD_PLAYER:
1744       element = Feld[x][y] = EL_PLAYER_1;
1745       InitField(x, y, init_game);
1746
1747       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1748       InitField(x, y, init_game);
1749       break;
1750
1751     case EL_SOKOBAN_FIELD_EMPTY:
1752       local_player->sokobanfields_still_needed++;
1753       break;
1754
1755     case EL_STONEBLOCK:
1756       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1758       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1759         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1764       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1765         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1766       break;
1767
1768     case EL_BUG:
1769     case EL_BUG_RIGHT:
1770     case EL_BUG_UP:
1771     case EL_BUG_LEFT:
1772     case EL_BUG_DOWN:
1773     case EL_SPACESHIP:
1774     case EL_SPACESHIP_RIGHT:
1775     case EL_SPACESHIP_UP:
1776     case EL_SPACESHIP_LEFT:
1777     case EL_SPACESHIP_DOWN:
1778     case EL_BD_BUTTERFLY:
1779     case EL_BD_BUTTERFLY_RIGHT:
1780     case EL_BD_BUTTERFLY_UP:
1781     case EL_BD_BUTTERFLY_LEFT:
1782     case EL_BD_BUTTERFLY_DOWN:
1783     case EL_BD_FIREFLY:
1784     case EL_BD_FIREFLY_RIGHT:
1785     case EL_BD_FIREFLY_UP:
1786     case EL_BD_FIREFLY_LEFT:
1787     case EL_BD_FIREFLY_DOWN:
1788     case EL_PACMAN_RIGHT:
1789     case EL_PACMAN_UP:
1790     case EL_PACMAN_LEFT:
1791     case EL_PACMAN_DOWN:
1792     case EL_YAMYAM:
1793     case EL_YAMYAM_LEFT:
1794     case EL_YAMYAM_RIGHT:
1795     case EL_YAMYAM_UP:
1796     case EL_YAMYAM_DOWN:
1797     case EL_DARK_YAMYAM:
1798     case EL_ROBOT:
1799     case EL_PACMAN:
1800     case EL_SP_SNIKSNAK:
1801     case EL_SP_ELECTRON:
1802     case EL_MOLE:
1803     case EL_MOLE_LEFT:
1804     case EL_MOLE_RIGHT:
1805     case EL_MOLE_UP:
1806     case EL_MOLE_DOWN:
1807       InitMovDir(x, y);
1808       break;
1809
1810     case EL_AMOEBA_FULL:
1811     case EL_BD_AMOEBA:
1812       InitAmoebaNr(x, y);
1813       break;
1814
1815     case EL_AMOEBA_DROP:
1816       if (y == lev_fieldy - 1)
1817       {
1818         Feld[x][y] = EL_AMOEBA_GROWING;
1819         Store[x][y] = EL_AMOEBA_WET;
1820       }
1821       break;
1822
1823     case EL_DYNAMITE_ACTIVE:
1824     case EL_SP_DISK_RED_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1828     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1829       MovDelay[x][y] = 96;
1830       break;
1831
1832     case EL_EM_DYNAMITE_ACTIVE:
1833       MovDelay[x][y] = 32;
1834       break;
1835
1836     case EL_LAMP:
1837       local_player->lights_still_needed++;
1838       break;
1839
1840     case EL_PENGUIN:
1841       local_player->friends_still_needed++;
1842       break;
1843
1844     case EL_PIG:
1845     case EL_DRAGON:
1846       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1847       break;
1848
1849     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1850     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1851     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1853     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1854     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1856     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1857     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1859     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1860     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1861       if (init_game)
1862       {
1863         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1865         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1866
1867         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1868         {
1869           game.belt_dir[belt_nr] = belt_dir;
1870           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1871         }
1872         else    /* more than one switch -- set it like the first switch */
1873         {
1874           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1875         }
1876       }
1877       break;
1878
1879     case EL_LIGHT_SWITCH_ACTIVE:
1880       if (init_game)
1881         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1882       break;
1883
1884     case EL_INVISIBLE_STEELWALL:
1885     case EL_INVISIBLE_WALL:
1886     case EL_INVISIBLE_SAND:
1887       if (game.light_time_left > 0 ||
1888           game.lenses_time_left > 0)
1889         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1890       break;
1891
1892     case EL_EMC_MAGIC_BALL:
1893       if (game.ball_state)
1894         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1895       break;
1896
1897     case EL_EMC_MAGIC_BALL_SWITCH:
1898       if (game.ball_state)
1899         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1900       break;
1901
1902     case EL_TRIGGER_PLAYER:
1903     case EL_TRIGGER_ELEMENT:
1904     case EL_TRIGGER_CE_VALUE:
1905     case EL_TRIGGER_CE_SCORE:
1906     case EL_SELF:
1907     case EL_ANY_ELEMENT:
1908     case EL_CURRENT_CE_VALUE:
1909     case EL_CURRENT_CE_SCORE:
1910     case EL_PREV_CE_1:
1911     case EL_PREV_CE_2:
1912     case EL_PREV_CE_3:
1913     case EL_PREV_CE_4:
1914     case EL_PREV_CE_5:
1915     case EL_PREV_CE_6:
1916     case EL_PREV_CE_7:
1917     case EL_PREV_CE_8:
1918     case EL_NEXT_CE_1:
1919     case EL_NEXT_CE_2:
1920     case EL_NEXT_CE_3:
1921     case EL_NEXT_CE_4:
1922     case EL_NEXT_CE_5:
1923     case EL_NEXT_CE_6:
1924     case EL_NEXT_CE_7:
1925     case EL_NEXT_CE_8:
1926       /* reference elements should not be used on the playfield */
1927       Feld[x][y] = EL_EMPTY;
1928       break;
1929
1930     default:
1931       if (IS_CUSTOM_ELEMENT(element))
1932       {
1933         if (CAN_MOVE(element))
1934           InitMovDir(x, y);
1935
1936         if (!element_info[element].use_last_ce_value || init_game)
1937           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1938       }
1939       else if (IS_GROUP_ELEMENT(element))
1940       {
1941         Feld[x][y] = GetElementFromGroupElement(element);
1942
1943         InitField(x, y, init_game);
1944       }
1945
1946       break;
1947   }
1948
1949   if (!init_game)
1950     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1951 }
1952
1953 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1954 {
1955   InitField(x, y, init_game);
1956
1957   /* not needed to call InitMovDir() -- already done by InitField()! */
1958   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1959       CAN_MOVE(Feld[x][y]))
1960     InitMovDir(x, y);
1961 }
1962
1963 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1964 {
1965   int old_element = Feld[x][y];
1966
1967   InitField(x, y, init_game);
1968
1969   /* not needed to call InitMovDir() -- already done by InitField()! */
1970   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971       CAN_MOVE(old_element) &&
1972       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1973     InitMovDir(x, y);
1974
1975   /* this case is in fact a combination of not less than three bugs:
1976      first, it calls InitMovDir() for elements that can move, although this is
1977      already done by InitField(); then, it checks the element that was at this
1978      field _before_ the call to InitField() (which can change it); lastly, it
1979      was not called for "mole with direction" elements, which were treated as
1980      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1981   */
1982 }
1983
1984 static int get_key_element_from_nr(int key_nr)
1985 {
1986   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1987                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1988                           EL_EM_KEY_1 : EL_KEY_1);
1989
1990   return key_base_element + key_nr;
1991 }
1992
1993 static int get_next_dropped_element(struct PlayerInfo *player)
1994 {
1995   return (player->inventory_size > 0 ?
1996           player->inventory_element[player->inventory_size - 1] :
1997           player->inventory_infinite_element != EL_UNDEFINED ?
1998           player->inventory_infinite_element :
1999           player->dynabombs_left > 0 ?
2000           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2001           EL_UNDEFINED);
2002 }
2003
2004 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2005 {
2006   /* pos >= 0: get element from bottom of the stack;
2007      pos <  0: get element from top of the stack */
2008
2009   if (pos < 0)
2010   {
2011     int min_inventory_size = -pos;
2012     int inventory_pos = player->inventory_size - min_inventory_size;
2013     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2014
2015     return (player->inventory_size >= min_inventory_size ?
2016             player->inventory_element[inventory_pos] :
2017             player->inventory_infinite_element != EL_UNDEFINED ?
2018             player->inventory_infinite_element :
2019             player->dynabombs_left >= min_dynabombs_left ?
2020             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2021             EL_UNDEFINED);
2022   }
2023   else
2024   {
2025     int min_dynabombs_left = pos + 1;
2026     int min_inventory_size = pos + 1 - player->dynabombs_left;
2027     int inventory_pos = pos - player->dynabombs_left;
2028
2029     return (player->inventory_infinite_element != EL_UNDEFINED ?
2030             player->inventory_infinite_element :
2031             player->dynabombs_left >= min_dynabombs_left ?
2032             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2033             player->inventory_size >= min_inventory_size ?
2034             player->inventory_element[inventory_pos] :
2035             EL_UNDEFINED);
2036   }
2037 }
2038
2039 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2040 {
2041   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2042   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2043   int compare_result;
2044
2045   if (gpo1->sort_priority != gpo2->sort_priority)
2046     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2047   else
2048     compare_result = gpo1->nr - gpo2->nr;
2049
2050   return compare_result;
2051 }
2052
2053 void InitGameControlValues()
2054 {
2055   int i;
2056
2057   for (i = 0; game_panel_controls[i].nr != -1; i++)
2058   {
2059     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2060     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2061     struct TextPosInfo *pos = gpc->pos;
2062     int nr = gpc->nr;
2063     int type = gpc->type;
2064
2065     if (nr != i)
2066     {
2067       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2068       Error(ERR_EXIT, "this should not happen -- please debug");
2069     }
2070
2071     /* force update of game controls after initialization */
2072     gpc->value = gpc->last_value = -1;
2073     gpc->frame = gpc->last_frame = -1;
2074     gpc->gfx_frame = -1;
2075
2076     /* determine panel value width for later calculation of alignment */
2077     if (type == TYPE_INTEGER || type == TYPE_STRING)
2078     {
2079       pos->width = pos->size * getFontWidth(pos->font);
2080       pos->height = getFontHeight(pos->font);
2081     }
2082     else if (type == TYPE_ELEMENT)
2083     {
2084       pos->width = pos->size;
2085       pos->height = pos->size;
2086     }
2087
2088     /* fill structure for game panel draw order */
2089     gpo->nr = gpc->nr;
2090     gpo->sort_priority = pos->sort_priority;
2091   }
2092
2093   /* sort game panel controls according to sort_priority and control number */
2094   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2095         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2096 }
2097
2098 void UpdatePlayfieldElementCount()
2099 {
2100   boolean use_element_count = FALSE;
2101   int i, j, x, y;
2102
2103   /* first check if it is needed at all to calculate playfield element count */
2104   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2105     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2106       use_element_count = TRUE;
2107
2108   if (!use_element_count)
2109     return;
2110
2111   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2112     element_info[i].element_count = 0;
2113
2114   SCAN_PLAYFIELD(x, y)
2115   {
2116     element_info[Feld[x][y]].element_count++;
2117   }
2118
2119   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2120     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2121       if (IS_IN_GROUP(j, i))
2122         element_info[EL_GROUP_START + i].element_count +=
2123           element_info[j].element_count;
2124 }
2125
2126 void UpdateGameControlValues()
2127 {
2128   int i, k;
2129   int time = (local_player->LevelSolved ?
2130               local_player->LevelSolved_CountingTime :
2131               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2132               level.native_em_level->lev->time :
2133               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2134               level.native_sp_level->game_sp->time_played :
2135               game.no_time_limit ? TimePlayed : TimeLeft);
2136   int score = (local_player->LevelSolved ?
2137                local_player->LevelSolved_CountingScore :
2138                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2139                level.native_em_level->lev->score :
2140                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2141                level.native_sp_level->game_sp->score :
2142                local_player->score);
2143   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2144               level.native_em_level->lev->required :
2145               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2146               level.native_sp_level->game_sp->infotrons_still_needed :
2147               local_player->gems_still_needed);
2148   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149                      level.native_em_level->lev->required > 0 :
2150                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2152                      local_player->gems_still_needed > 0 ||
2153                      local_player->sokobanfields_still_needed > 0 ||
2154                      local_player->lights_still_needed > 0);
2155
2156   UpdatePlayfieldElementCount();
2157
2158   /* update game panel control values */
2159
2160   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2161   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2162
2163   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2164   for (i = 0; i < MAX_NUM_KEYS; i++)
2165     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2167   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2168
2169   if (game.centered_player_nr == -1)
2170   {
2171     for (i = 0; i < MAX_PLAYERS; i++)
2172     {
2173       /* only one player in Supaplex game engine */
2174       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2175         break;
2176
2177       for (k = 0; k < MAX_NUM_KEYS; k++)
2178       {
2179         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2180         {
2181           if (level.native_em_level->ply[i]->keys & (1 << k))
2182             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2183               get_key_element_from_nr(k);
2184         }
2185         else if (stored_player[i].key[k])
2186           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2187             get_key_element_from_nr(k);
2188       }
2189
2190       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2191         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2192           level.native_em_level->ply[i]->dynamite;
2193       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2194         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2195           level.native_sp_level->game_sp->red_disk_count;
2196       else
2197         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2198           stored_player[i].inventory_size;
2199
2200       if (stored_player[i].num_white_keys > 0)
2201         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2202           EL_DC_KEY_WHITE;
2203
2204       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2205         stored_player[i].num_white_keys;
2206     }
2207   }
2208   else
2209   {
2210     int player_nr = game.centered_player_nr;
2211
2212     for (k = 0; k < MAX_NUM_KEYS; k++)
2213     {
2214       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2215       {
2216         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2217           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2218             get_key_element_from_nr(k);
2219       }
2220       else if (stored_player[player_nr].key[k])
2221         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2222           get_key_element_from_nr(k);
2223     }
2224
2225     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2227         level.native_em_level->ply[player_nr]->dynamite;
2228     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2229       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2230         level.native_sp_level->game_sp->red_disk_count;
2231     else
2232       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2233         stored_player[player_nr].inventory_size;
2234
2235     if (stored_player[player_nr].num_white_keys > 0)
2236       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2237
2238     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2239       stored_player[player_nr].num_white_keys;
2240   }
2241
2242   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2243   {
2244     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2245       get_inventory_element_from_pos(local_player, i);
2246     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2247       get_inventory_element_from_pos(local_player, -i - 1);
2248   }
2249
2250   game_panel_controls[GAME_PANEL_SCORE].value = score;
2251   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2252
2253   game_panel_controls[GAME_PANEL_TIME].value = time;
2254
2255   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2256   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2257   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2258
2259   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2260
2261   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2262     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2263      EL_EMPTY);
2264   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2265     local_player->shield_normal_time_left;
2266   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2267     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2268      EL_EMPTY);
2269   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2270     local_player->shield_deadly_time_left;
2271
2272   game_panel_controls[GAME_PANEL_EXIT].value =
2273     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2274
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2277   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2278     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2279      EL_EMC_MAGIC_BALL_SWITCH);
2280
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2282     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2283   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2284     game.light_time_left;
2285
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2287     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2288   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2289     game.timegate_time_left;
2290
2291   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2292     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2293
2294   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2295     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2296   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2297     game.lenses_time_left;
2298
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2300     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2301   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2302     game.magnify_time_left;
2303
2304   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2305     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2306      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2307      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2308      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2309      EL_BALLOON_SWITCH_NONE);
2310
2311   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2312     local_player->dynabomb_count;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2314     local_player->dynabomb_size;
2315   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2316     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2317
2318   game_panel_controls[GAME_PANEL_PENGUINS].value =
2319     local_player->friends_still_needed;
2320
2321   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2322     local_player->sokobanfields_still_needed;
2323   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2324     local_player->sokobanfields_still_needed;
2325
2326   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2327     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2328
2329   for (i = 0; i < NUM_BELTS; i++)
2330   {
2331     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2332       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2333        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2334     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2335       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2336   }
2337
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2339     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2340   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2341     game.magic_wall_time_left;
2342
2343   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2344     local_player->gravity;
2345
2346   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2347     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2348
2349   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2350     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2351       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2352        game.panel.element[i].id : EL_UNDEFINED);
2353
2354   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2355     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2356       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2357        element_info[game.panel.element_count[i].id].element_count : 0);
2358
2359   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2360     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2361       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2362        element_info[game.panel.ce_score[i].id].collect_score : 0);
2363
2364   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2365     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2366       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2367        element_info[game.panel.ce_score_element[i].id].collect_score :
2368        EL_UNDEFINED);
2369
2370   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2372   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2373
2374   /* update game panel control frames */
2375
2376   for (i = 0; game_panel_controls[i].nr != -1; i++)
2377   {
2378     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2379
2380     if (gpc->type == TYPE_ELEMENT)
2381     {
2382       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2383       {
2384         int last_anim_random_frame = gfx.anim_random_frame;
2385         int element = gpc->value;
2386         int graphic = el2panelimg(element);
2387
2388         if (gpc->value != gpc->last_value)
2389         {
2390           gpc->gfx_frame = 0;
2391           gpc->gfx_random = INIT_GFX_RANDOM();
2392         }
2393         else
2394         {
2395           gpc->gfx_frame++;
2396
2397           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2398               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2399             gpc->gfx_random = INIT_GFX_RANDOM();
2400         }
2401
2402         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2403           gfx.anim_random_frame = gpc->gfx_random;
2404
2405         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2406           gpc->gfx_frame = element_info[element].collect_score;
2407
2408         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2409                                               gpc->gfx_frame);
2410
2411         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2412           gfx.anim_random_frame = last_anim_random_frame;
2413       }
2414     }
2415   }
2416 }
2417
2418 void DisplayGameControlValues()
2419 {
2420   boolean redraw_panel = FALSE;
2421   int i;
2422
2423   for (i = 0; game_panel_controls[i].nr != -1; i++)
2424   {
2425     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2426
2427     if (PANEL_DEACTIVATED(gpc->pos))
2428       continue;
2429
2430     if (gpc->value == gpc->last_value &&
2431         gpc->frame == gpc->last_frame)
2432       continue;
2433
2434     redraw_panel = TRUE;
2435   }
2436
2437   if (!redraw_panel)
2438     return;
2439
2440   /* copy default game door content to main double buffer */
2441
2442   /* !!! CHECK AGAIN !!! */
2443   SetPanelBackground();
2444   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2445   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2446
2447   /* redraw game control buttons */
2448   RedrawGameButtons();
2449
2450   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2451
2452   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2453   {
2454     int nr = game_panel_order[i].nr;
2455     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2456     struct TextPosInfo *pos = gpc->pos;
2457     int type = gpc->type;
2458     int value = gpc->value;
2459     int frame = gpc->frame;
2460     int size = pos->size;
2461     int font = pos->font;
2462     boolean draw_masked = pos->draw_masked;
2463     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2464
2465     if (PANEL_DEACTIVATED(pos))
2466       continue;
2467
2468     gpc->last_value = value;
2469     gpc->last_frame = frame;
2470
2471     if (type == TYPE_INTEGER)
2472     {
2473       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2474           nr == GAME_PANEL_TIME)
2475       {
2476         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2477
2478         if (use_dynamic_size)           /* use dynamic number of digits */
2479         {
2480           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2481           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2482           int size2 = size1 + 1;
2483           int font1 = pos->font;
2484           int font2 = pos->font_alt;
2485
2486           size = (value < value_change ? size1 : size2);
2487           font = (value < value_change ? font1 : font2);
2488         }
2489       }
2490
2491       /* correct text size if "digits" is zero or less */
2492       if (size <= 0)
2493         size = strlen(int2str(value, size));
2494
2495       /* dynamically correct text alignment */
2496       pos->width = size * getFontWidth(font);
2497
2498       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2499                   int2str(value, size), font, mask_mode);
2500     }
2501     else if (type == TYPE_ELEMENT)
2502     {
2503       int element, graphic;
2504       Bitmap *src_bitmap;
2505       int src_x, src_y;
2506       int width, height;
2507       int dst_x = PANEL_XPOS(pos);
2508       int dst_y = PANEL_YPOS(pos);
2509
2510       if (value != EL_UNDEFINED && value != EL_EMPTY)
2511       {
2512         element = value;
2513         graphic = el2panelimg(value);
2514
2515         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2516
2517         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2518           size = TILESIZE;
2519
2520         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2521                               &src_x, &src_y);
2522
2523         width  = graphic_info[graphic].width  * size / TILESIZE;
2524         height = graphic_info[graphic].height * size / TILESIZE;
2525
2526         if (draw_masked)
2527           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2528                            dst_x, dst_y);
2529         else
2530           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2531                      dst_x, dst_y);
2532       }
2533     }
2534     else if (type == TYPE_STRING)
2535     {
2536       boolean active = (value != 0);
2537       char *state_normal = "off";
2538       char *state_active = "on";
2539       char *state = (active ? state_active : state_normal);
2540       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2541                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2542                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2543                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2544
2545       if (nr == GAME_PANEL_GRAVITY_STATE)
2546       {
2547         int font1 = pos->font;          /* (used for normal state) */
2548         int font2 = pos->font_alt;      /* (used for active state) */
2549
2550         font = (active ? font2 : font1);
2551       }
2552
2553       if (s != NULL)
2554       {
2555         char *s_cut;
2556
2557         if (size <= 0)
2558         {
2559           /* don't truncate output if "chars" is zero or less */
2560           size = strlen(s);
2561
2562           /* dynamically correct text alignment */
2563           pos->width = size * getFontWidth(font);
2564         }
2565
2566         s_cut = getStringCopyN(s, size);
2567
2568         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2569                     s_cut, font, mask_mode);
2570
2571         free(s_cut);
2572       }
2573     }
2574
2575     redraw_mask |= REDRAW_DOOR_1;
2576   }
2577
2578   SetGameStatus(GAME_MODE_PLAYING);
2579 }
2580
2581 void UpdateAndDisplayGameControlValues()
2582 {
2583   if (tape.deactivate_display)
2584     return;
2585
2586   UpdateGameControlValues();
2587   DisplayGameControlValues();
2588 }
2589
2590 void UpdateGameDoorValues()
2591 {
2592   UpdateGameControlValues();
2593 }
2594
2595 void DrawGameDoorValues()
2596 {
2597   DisplayGameControlValues();
2598 }
2599
2600
2601 /*
2602   =============================================================================
2603   InitGameEngine()
2604   -----------------------------------------------------------------------------
2605   initialize game engine due to level / tape version number
2606   =============================================================================
2607 */
2608
2609 static void InitGameEngine()
2610 {
2611   int i, j, k, l, x, y;
2612
2613   /* set game engine from tape file when re-playing, else from level file */
2614   game.engine_version = (tape.playing ? tape.engine_version :
2615                          level.game_version);
2616
2617   /* set single or multi-player game mode (needed for re-playing tapes) */
2618   game.team_mode = setup.team_mode;
2619
2620   if (tape.playing)
2621   {
2622     int num_players = 0;
2623
2624     for (i = 0; i < MAX_PLAYERS; i++)
2625       if (tape.player_participates[i])
2626         num_players++;
2627
2628     /* multi-player tapes contain input data for more than one player */
2629     game.team_mode = (num_players > 1);
2630   }
2631
2632   /* ---------------------------------------------------------------------- */
2633   /* set flags for bugs and changes according to active game engine version */
2634   /* ---------------------------------------------------------------------- */
2635
2636   /*
2637     Summary of bugfix/change:
2638     Fixed handling for custom elements that change when pushed by the player.
2639
2640     Fixed/changed in version:
2641     3.1.0
2642
2643     Description:
2644     Before 3.1.0, custom elements that "change when pushing" changed directly
2645     after the player started pushing them (until then handled in "DigField()").
2646     Since 3.1.0, these custom elements are not changed until the "pushing"
2647     move of the element is finished (now handled in "ContinueMoving()").
2648
2649     Affected levels/tapes:
2650     The first condition is generally needed for all levels/tapes before version
2651     3.1.0, which might use the old behaviour before it was changed; known tapes
2652     that are affected are some tapes from the level set "Walpurgis Gardens" by
2653     Jamie Cullen.
2654     The second condition is an exception from the above case and is needed for
2655     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2656     above (including some development versions of 3.1.0), but before it was
2657     known that this change would break tapes like the above and was fixed in
2658     3.1.1, so that the changed behaviour was active although the engine version
2659     while recording maybe was before 3.1.0. There is at least one tape that is
2660     affected by this exception, which is the tape for the one-level set "Bug
2661     Machine" by Juergen Bonhagen.
2662   */
2663
2664   game.use_change_when_pushing_bug =
2665     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2666      !(tape.playing &&
2667        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2668        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2669
2670   /*
2671     Summary of bugfix/change:
2672     Fixed handling for blocking the field the player leaves when moving.
2673
2674     Fixed/changed in version:
2675     3.1.1
2676
2677     Description:
2678     Before 3.1.1, when "block last field when moving" was enabled, the field
2679     the player is leaving when moving was blocked for the time of the move,
2680     and was directly unblocked afterwards. This resulted in the last field
2681     being blocked for exactly one less than the number of frames of one player
2682     move. Additionally, even when blocking was disabled, the last field was
2683     blocked for exactly one frame.
2684     Since 3.1.1, due to changes in player movement handling, the last field
2685     is not blocked at all when blocking is disabled. When blocking is enabled,
2686     the last field is blocked for exactly the number of frames of one player
2687     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2688     last field is blocked for exactly one more than the number of frames of
2689     one player move.
2690
2691     Affected levels/tapes:
2692     (!!! yet to be determined -- probably many !!!)
2693   */
2694
2695   game.use_block_last_field_bug =
2696     (game.engine_version < VERSION_IDENT(3,1,1,0));
2697
2698   /* ---------------------------------------------------------------------- */
2699
2700   /* set maximal allowed number of custom element changes per game frame */
2701   game.max_num_changes_per_frame = 1;
2702
2703   /* default scan direction: scan playfield from top/left to bottom/right */
2704   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2705
2706   /* dynamically adjust element properties according to game engine version */
2707   InitElementPropertiesEngine(game.engine_version);
2708
2709 #if 0
2710   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2711   printf("          tape version == %06d [%s] [file: %06d]\n",
2712          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2713          tape.file_version);
2714   printf("       => game.engine_version == %06d\n", game.engine_version);
2715 #endif
2716
2717   /* ---------- initialize player's initial move delay --------------------- */
2718
2719   /* dynamically adjust player properties according to level information */
2720   for (i = 0; i < MAX_PLAYERS; i++)
2721     game.initial_move_delay_value[i] =
2722       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2723
2724   /* dynamically adjust player properties according to game engine version */
2725   for (i = 0; i < MAX_PLAYERS; i++)
2726     game.initial_move_delay[i] =
2727       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2728        game.initial_move_delay_value[i] : 0);
2729
2730   /* ---------- initialize player's initial push delay --------------------- */
2731
2732   /* dynamically adjust player properties according to game engine version */
2733   game.initial_push_delay_value =
2734     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2735
2736   /* ---------- initialize changing elements ------------------------------- */
2737
2738   /* initialize changing elements information */
2739   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2740   {
2741     struct ElementInfo *ei = &element_info[i];
2742
2743     /* this pointer might have been changed in the level editor */
2744     ei->change = &ei->change_page[0];
2745
2746     if (!IS_CUSTOM_ELEMENT(i))
2747     {
2748       ei->change->target_element = EL_EMPTY_SPACE;
2749       ei->change->delay_fixed = 0;
2750       ei->change->delay_random = 0;
2751       ei->change->delay_frames = 1;
2752     }
2753
2754     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2755     {
2756       ei->has_change_event[j] = FALSE;
2757
2758       ei->event_page_nr[j] = 0;
2759       ei->event_page[j] = &ei->change_page[0];
2760     }
2761   }
2762
2763   /* add changing elements from pre-defined list */
2764   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2765   {
2766     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2767     struct ElementInfo *ei = &element_info[ch_delay->element];
2768
2769     ei->change->target_element       = ch_delay->target_element;
2770     ei->change->delay_fixed          = ch_delay->change_delay;
2771
2772     ei->change->pre_change_function  = ch_delay->pre_change_function;
2773     ei->change->change_function      = ch_delay->change_function;
2774     ei->change->post_change_function = ch_delay->post_change_function;
2775
2776     ei->change->can_change = TRUE;
2777     ei->change->can_change_or_has_action = TRUE;
2778
2779     ei->has_change_event[CE_DELAY] = TRUE;
2780
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2782     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2783   }
2784
2785   /* ---------- initialize internal run-time variables --------------------- */
2786
2787   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2788   {
2789     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2790
2791     for (j = 0; j < ei->num_change_pages; j++)
2792     {
2793       ei->change_page[j].can_change_or_has_action =
2794         (ei->change_page[j].can_change |
2795          ei->change_page[j].has_action);
2796     }
2797   }
2798
2799   /* add change events from custom element configuration */
2800   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2801   {
2802     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2803
2804     for (j = 0; j < ei->num_change_pages; j++)
2805     {
2806       if (!ei->change_page[j].can_change_or_has_action)
2807         continue;
2808
2809       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2810       {
2811         /* only add event page for the first page found with this event */
2812         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2813         {
2814           ei->has_change_event[k] = TRUE;
2815
2816           ei->event_page_nr[k] = j;
2817           ei->event_page[k] = &ei->change_page[j];
2818         }
2819       }
2820     }
2821   }
2822
2823   /* ---------- initialize reference elements in change conditions --------- */
2824
2825   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2826   {
2827     int element = EL_CUSTOM_START + i;
2828     struct ElementInfo *ei = &element_info[element];
2829
2830     for (j = 0; j < ei->num_change_pages; j++)
2831     {
2832       int trigger_element = ei->change_page[j].initial_trigger_element;
2833
2834       if (trigger_element >= EL_PREV_CE_8 &&
2835           trigger_element <= EL_NEXT_CE_8)
2836         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2837
2838       ei->change_page[j].trigger_element = trigger_element;
2839     }
2840   }
2841
2842   /* ---------- initialize run-time trigger player and element ------------- */
2843
2844   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2845   {
2846     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2847
2848     for (j = 0; j < ei->num_change_pages; j++)
2849     {
2850       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2852       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2853       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2854       ei->change_page[j].actual_trigger_ce_value = 0;
2855       ei->change_page[j].actual_trigger_ce_score = 0;
2856     }
2857   }
2858
2859   /* ---------- initialize trigger events ---------------------------------- */
2860
2861   /* initialize trigger events information */
2862   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2863     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2864       trigger_events[i][j] = FALSE;
2865
2866   /* add trigger events from element change event properties */
2867   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2868   {
2869     struct ElementInfo *ei = &element_info[i];
2870
2871     for (j = 0; j < ei->num_change_pages; j++)
2872     {
2873       if (!ei->change_page[j].can_change_or_has_action)
2874         continue;
2875
2876       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2877       {
2878         int trigger_element = ei->change_page[j].trigger_element;
2879
2880         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2881         {
2882           if (ei->change_page[j].has_event[k])
2883           {
2884             if (IS_GROUP_ELEMENT(trigger_element))
2885             {
2886               struct ElementGroupInfo *group =
2887                 element_info[trigger_element].group;
2888
2889               for (l = 0; l < group->num_elements_resolved; l++)
2890                 trigger_events[group->element_resolved[l]][k] = TRUE;
2891             }
2892             else if (trigger_element == EL_ANY_ELEMENT)
2893               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2894                 trigger_events[l][k] = TRUE;
2895             else
2896               trigger_events[trigger_element][k] = TRUE;
2897           }
2898         }
2899       }
2900     }
2901   }
2902
2903   /* ---------- initialize push delay -------------------------------------- */
2904
2905   /* initialize push delay values to default */
2906   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2907   {
2908     if (!IS_CUSTOM_ELEMENT(i))
2909     {
2910       /* set default push delay values (corrected since version 3.0.7-1) */
2911       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2912       {
2913         element_info[i].push_delay_fixed = 2;
2914         element_info[i].push_delay_random = 8;
2915       }
2916       else
2917       {
2918         element_info[i].push_delay_fixed = 8;
2919         element_info[i].push_delay_random = 8;
2920       }
2921     }
2922   }
2923
2924   /* set push delay value for certain elements from pre-defined list */
2925   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2926   {
2927     int e = push_delay_list[i].element;
2928
2929     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2930     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2931   }
2932
2933   /* set push delay value for Supaplex elements for newer engine versions */
2934   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2935   {
2936     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2937     {
2938       if (IS_SP_ELEMENT(i))
2939       {
2940         /* set SP push delay to just enough to push under a falling zonk */
2941         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2942
2943         element_info[i].push_delay_fixed  = delay;
2944         element_info[i].push_delay_random = 0;
2945       }
2946     }
2947   }
2948
2949   /* ---------- initialize move stepsize ----------------------------------- */
2950
2951   /* initialize move stepsize values to default */
2952   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2953     if (!IS_CUSTOM_ELEMENT(i))
2954       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2955
2956   /* set move stepsize value for certain elements from pre-defined list */
2957   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2958   {
2959     int e = move_stepsize_list[i].element;
2960
2961     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2962   }
2963
2964   /* ---------- initialize collect score ----------------------------------- */
2965
2966   /* initialize collect score values for custom elements from initial value */
2967   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2968     if (IS_CUSTOM_ELEMENT(i))
2969       element_info[i].collect_score = element_info[i].collect_score_initial;
2970
2971   /* ---------- initialize collect count ----------------------------------- */
2972
2973   /* initialize collect count values for non-custom elements */
2974   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2975     if (!IS_CUSTOM_ELEMENT(i))
2976       element_info[i].collect_count_initial = 0;
2977
2978   /* add collect count values for all elements from pre-defined list */
2979   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2980     element_info[collect_count_list[i].element].collect_count_initial =
2981       collect_count_list[i].count;
2982
2983   /* ---------- initialize access direction -------------------------------- */
2984
2985   /* initialize access direction values to default (access from every side) */
2986   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2987     if (!IS_CUSTOM_ELEMENT(i))
2988       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2989
2990   /* set access direction value for certain elements from pre-defined list */
2991   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2992     element_info[access_direction_list[i].element].access_direction =
2993       access_direction_list[i].direction;
2994
2995   /* ---------- initialize explosion content ------------------------------- */
2996   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2997   {
2998     if (IS_CUSTOM_ELEMENT(i))
2999       continue;
3000
3001     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3002     {
3003       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3004
3005       element_info[i].content.e[x][y] =
3006         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3007          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3008          i == EL_PLAYER_3 ? EL_EMERALD :
3009          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3010          i == EL_MOLE ? EL_EMERALD_RED :
3011          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3012          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3013          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3014          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3015          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3016          i == EL_WALL_EMERALD ? EL_EMERALD :
3017          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3018          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3019          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3020          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3021          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3022          i == EL_WALL_PEARL ? EL_PEARL :
3023          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3024          EL_EMPTY);
3025     }
3026   }
3027
3028   /* ---------- initialize recursion detection ------------------------------ */
3029   recursion_loop_depth = 0;
3030   recursion_loop_detected = FALSE;
3031   recursion_loop_element = EL_UNDEFINED;
3032
3033   /* ---------- initialize graphics engine ---------------------------------- */
3034   game.scroll_delay_value =
3035     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3036      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3037   game.scroll_delay_value =
3038     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3039
3040   /* ---------- initialize game engine snapshots ---------------------------- */
3041   for (i = 0; i < MAX_PLAYERS; i++)
3042     game.snapshot.last_action[i] = 0;
3043   game.snapshot.changed_action = FALSE;
3044   game.snapshot.collected_item = FALSE;
3045   game.snapshot.mode =
3046     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3047      SNAPSHOT_MODE_EVERY_STEP :
3048      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3049      SNAPSHOT_MODE_EVERY_MOVE :
3050      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3051      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3052   game.snapshot.save_snapshot = FALSE;
3053 }
3054
3055 int get_num_special_action(int element, int action_first, int action_last)
3056 {
3057   int num_special_action = 0;
3058   int i, j;
3059
3060   for (i = action_first; i <= action_last; i++)
3061   {
3062     boolean found = FALSE;
3063
3064     for (j = 0; j < NUM_DIRECTIONS; j++)
3065       if (el_act_dir2img(element, i, j) !=
3066           el_act_dir2img(element, ACTION_DEFAULT, j))
3067         found = TRUE;
3068
3069     if (found)
3070       num_special_action++;
3071     else
3072       break;
3073   }
3074
3075   return num_special_action;
3076 }
3077
3078
3079 /*
3080   =============================================================================
3081   InitGame()
3082   -----------------------------------------------------------------------------
3083   initialize and start new game
3084   =============================================================================
3085 */
3086
3087 void InitGame()
3088 {
3089   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3090   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3091   int fade_mask = REDRAW_FIELD;
3092
3093   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3094   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3095   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3096   int initial_move_dir = MV_DOWN;
3097   int i, j, x, y;
3098
3099   // required here to update video display before fading (FIX THIS)
3100   DrawMaskedBorder(REDRAW_DOOR_2);
3101
3102   if (!game.restart_level)
3103     CloseDoor(DOOR_CLOSE_1);
3104
3105   SetGameStatus(GAME_MODE_PLAYING);
3106
3107   if (level_editor_test_game)
3108     FadeSkipNextFadeIn();
3109   else
3110     FadeSetEnterScreen();
3111
3112   if (CheckIfGlobalBorderHasChanged())
3113     fade_mask = REDRAW_ALL;
3114
3115   FadeSoundsAndMusic();
3116
3117   ExpireSoundLoops(TRUE);
3118
3119   FadeOut(fade_mask);
3120
3121   /* needed if different viewport properties defined for playing */
3122   ChangeViewportPropertiesIfNeeded();
3123
3124   ClearField();
3125
3126   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3127
3128   DrawCompleteVideoDisplay();
3129
3130   InitGameEngine();
3131   InitGameControlValues();
3132
3133   /* don't play tapes over network */
3134   network_playing = (options.network && !tape.playing);
3135
3136   for (i = 0; i < MAX_PLAYERS; i++)
3137   {
3138     struct PlayerInfo *player = &stored_player[i];
3139
3140     player->index_nr = i;
3141     player->index_bit = (1 << i);
3142     player->element_nr = EL_PLAYER_1 + i;
3143
3144     player->present = FALSE;
3145     player->active = FALSE;
3146     player->mapped = FALSE;
3147
3148     player->killed = FALSE;
3149     player->reanimated = FALSE;
3150
3151     player->action = 0;
3152     player->effective_action = 0;
3153     player->programmed_action = 0;
3154
3155     player->score = 0;
3156     player->score_final = 0;
3157
3158     player->gems_still_needed = level.gems_needed;
3159     player->sokobanfields_still_needed = 0;
3160     player->lights_still_needed = 0;
3161     player->friends_still_needed = 0;
3162
3163     for (j = 0; j < MAX_NUM_KEYS; j++)
3164       player->key[j] = FALSE;
3165
3166     player->num_white_keys = 0;
3167
3168     player->dynabomb_count = 0;
3169     player->dynabomb_size = 1;
3170     player->dynabombs_left = 0;
3171     player->dynabomb_xl = FALSE;
3172
3173     player->MovDir = initial_move_dir;
3174     player->MovPos = 0;
3175     player->GfxPos = 0;
3176     player->GfxDir = initial_move_dir;
3177     player->GfxAction = ACTION_DEFAULT;
3178     player->Frame = 0;
3179     player->StepFrame = 0;
3180
3181     player->initial_element = player->element_nr;
3182     player->artwork_element =
3183       (level.use_artwork_element[i] ? level.artwork_element[i] :
3184        player->element_nr);
3185     player->use_murphy = FALSE;
3186
3187     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3188     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3189
3190     player->gravity = level.initial_player_gravity[i];
3191
3192     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3193
3194     player->actual_frame_counter = 0;
3195
3196     player->step_counter = 0;
3197
3198     player->last_move_dir = initial_move_dir;
3199
3200     player->is_active = FALSE;
3201
3202     player->is_waiting = FALSE;
3203     player->is_moving = FALSE;
3204     player->is_auto_moving = FALSE;
3205     player->is_digging = FALSE;
3206     player->is_snapping = FALSE;
3207     player->is_collecting = FALSE;
3208     player->is_pushing = FALSE;
3209     player->is_switching = FALSE;
3210     player->is_dropping = FALSE;
3211     player->is_dropping_pressed = FALSE;
3212
3213     player->is_bored = FALSE;
3214     player->is_sleeping = FALSE;
3215
3216     player->was_waiting = TRUE;
3217     player->was_moving = FALSE;
3218     player->was_snapping = FALSE;
3219     player->was_dropping = FALSE;
3220
3221     player->frame_counter_bored = -1;
3222     player->frame_counter_sleeping = -1;
3223
3224     player->anim_delay_counter = 0;
3225     player->post_delay_counter = 0;
3226
3227     player->dir_waiting = initial_move_dir;
3228     player->action_waiting = ACTION_DEFAULT;
3229     player->last_action_waiting = ACTION_DEFAULT;
3230     player->special_action_bored = ACTION_DEFAULT;
3231     player->special_action_sleeping = ACTION_DEFAULT;
3232
3233     player->switch_x = -1;
3234     player->switch_y = -1;
3235
3236     player->drop_x = -1;
3237     player->drop_y = -1;
3238
3239     player->show_envelope = 0;
3240
3241     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3242
3243     player->push_delay       = -1;      /* initialized when pushing starts */
3244     player->push_delay_value = game.initial_push_delay_value;
3245
3246     player->drop_delay = 0;
3247     player->drop_pressed_delay = 0;
3248
3249     player->last_jx = -1;
3250     player->last_jy = -1;
3251     player->jx = -1;
3252     player->jy = -1;
3253
3254     player->shield_normal_time_left = 0;
3255     player->shield_deadly_time_left = 0;
3256
3257     player->inventory_infinite_element = EL_UNDEFINED;
3258     player->inventory_size = 0;
3259
3260     if (level.use_initial_inventory[i])
3261     {
3262       for (j = 0; j < level.initial_inventory_size[i]; j++)
3263       {
3264         int element = level.initial_inventory_content[i][j];
3265         int collect_count = element_info[element].collect_count_initial;
3266         int k;
3267
3268         if (!IS_CUSTOM_ELEMENT(element))
3269           collect_count = 1;
3270
3271         if (collect_count == 0)
3272           player->inventory_infinite_element = element;
3273         else
3274           for (k = 0; k < collect_count; k++)
3275             if (player->inventory_size < MAX_INVENTORY_SIZE)
3276               player->inventory_element[player->inventory_size++] = element;
3277       }
3278     }
3279
3280     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3281     SnapField(player, 0, 0);
3282
3283     player->LevelSolved = FALSE;
3284     player->GameOver = FALSE;
3285
3286     player->LevelSolved_GameWon = FALSE;
3287     player->LevelSolved_GameEnd = FALSE;
3288     player->LevelSolved_PanelOff = FALSE;
3289     player->LevelSolved_SaveTape = FALSE;
3290     player->LevelSolved_SaveScore = FALSE;
3291     player->LevelSolved_CountingTime = 0;
3292     player->LevelSolved_CountingScore = 0;
3293
3294     map_player_action[i] = i;
3295   }
3296
3297   network_player_action_received = FALSE;
3298
3299 #if defined(NETWORK_AVALIABLE)
3300   /* initial null action */
3301   if (network_playing)
3302     SendToServer_MovePlayer(MV_NONE);
3303 #endif
3304
3305   ZX = ZY = -1;
3306   ExitX = ExitY = -1;
3307
3308   FrameCounter = 0;
3309   TimeFrames = 0;
3310   TimePlayed = 0;
3311   TimeLeft = level.time;
3312   TapeTime = 0;
3313
3314   ScreenMovDir = MV_NONE;
3315   ScreenMovPos = 0;
3316   ScreenGfxPos = 0;
3317
3318   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3319
3320   AllPlayersGone = FALSE;
3321
3322   game.no_time_limit = (level.time == 0);
3323
3324   game.yamyam_content_nr = 0;
3325   game.robot_wheel_active = FALSE;
3326   game.magic_wall_active = FALSE;
3327   game.magic_wall_time_left = 0;
3328   game.light_time_left = 0;
3329   game.timegate_time_left = 0;
3330   game.switchgate_pos = 0;
3331   game.wind_direction = level.wind_direction_initial;
3332
3333   game.lenses_time_left = 0;
3334   game.magnify_time_left = 0;
3335
3336   game.ball_state = level.ball_state_initial;
3337   game.ball_content_nr = 0;
3338
3339   game.envelope_active = FALSE;
3340
3341   /* set focus to local player for network games, else to all players */
3342   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3343   game.centered_player_nr_next = game.centered_player_nr;
3344   game.set_centered_player = FALSE;
3345
3346   if (network_playing && tape.recording)
3347   {
3348     /* store client dependent player focus when recording network games */
3349     tape.centered_player_nr_next = game.centered_player_nr_next;
3350     tape.set_centered_player = TRUE;
3351   }
3352
3353   for (i = 0; i < NUM_BELTS; i++)
3354   {
3355     game.belt_dir[i] = MV_NONE;
3356     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3357   }
3358
3359   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3360     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3361
3362 #if DEBUG_INIT_PLAYER
3363   if (options.debug)
3364   {
3365     printf("Player status at level initialization:\n");
3366   }
3367 #endif
3368
3369   SCAN_PLAYFIELD(x, y)
3370   {
3371     Feld[x][y] = level.field[x][y];
3372     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3373     ChangeDelay[x][y] = 0;
3374     ChangePage[x][y] = -1;
3375     CustomValue[x][y] = 0;              /* initialized in InitField() */
3376     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3377     AmoebaNr[x][y] = 0;
3378     WasJustMoving[x][y] = 0;
3379     WasJustFalling[x][y] = 0;
3380     CheckCollision[x][y] = 0;
3381     CheckImpact[x][y] = 0;
3382     Stop[x][y] = FALSE;
3383     Pushed[x][y] = FALSE;
3384
3385     ChangeCount[x][y] = 0;
3386     ChangeEvent[x][y] = -1;
3387
3388     ExplodePhase[x][y] = 0;
3389     ExplodeDelay[x][y] = 0;
3390     ExplodeField[x][y] = EX_TYPE_NONE;
3391
3392     RunnerVisit[x][y] = 0;
3393     PlayerVisit[x][y] = 0;
3394
3395     GfxFrame[x][y] = 0;
3396     GfxRandom[x][y] = INIT_GFX_RANDOM();
3397     GfxElement[x][y] = EL_UNDEFINED;
3398     GfxAction[x][y] = ACTION_DEFAULT;
3399     GfxDir[x][y] = MV_NONE;
3400     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3401   }
3402
3403   SCAN_PLAYFIELD(x, y)
3404   {
3405     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3406       emulate_bd = FALSE;
3407     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3408       emulate_sb = FALSE;
3409     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3410       emulate_sp = FALSE;
3411
3412     InitField(x, y, TRUE);
3413
3414     ResetGfxAnimation(x, y);
3415   }
3416
3417   InitBeltMovement();
3418
3419   for (i = 0; i < MAX_PLAYERS; i++)
3420   {
3421     struct PlayerInfo *player = &stored_player[i];
3422
3423     /* set number of special actions for bored and sleeping animation */
3424     player->num_special_action_bored =
3425       get_num_special_action(player->artwork_element,
3426                              ACTION_BORING_1, ACTION_BORING_LAST);
3427     player->num_special_action_sleeping =
3428       get_num_special_action(player->artwork_element,
3429                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3430   }
3431
3432   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3433                     emulate_sb ? EMU_SOKOBAN :
3434                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3435
3436   /* initialize type of slippery elements */
3437   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3438   {
3439     if (!IS_CUSTOM_ELEMENT(i))
3440     {
3441       /* default: elements slip down either to the left or right randomly */
3442       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3443
3444       /* SP style elements prefer to slip down on the left side */
3445       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3446         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3447
3448       /* BD style elements prefer to slip down on the left side */
3449       if (game.emulation == EMU_BOULDERDASH)
3450         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3451     }
3452   }
3453
3454   /* initialize explosion and ignition delay */
3455   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3456   {
3457     if (!IS_CUSTOM_ELEMENT(i))
3458     {
3459       int num_phase = 8;
3460       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3461                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3462                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3463       int last_phase = (num_phase + 1) * delay;
3464       int half_phase = (num_phase / 2) * delay;
3465
3466       element_info[i].explosion_delay = last_phase - 1;
3467       element_info[i].ignition_delay = half_phase;
3468
3469       if (i == EL_BLACK_ORB)
3470         element_info[i].ignition_delay = 1;
3471     }
3472   }
3473
3474   /* correct non-moving belts to start moving left */
3475   for (i = 0; i < NUM_BELTS; i++)
3476     if (game.belt_dir[i] == MV_NONE)
3477       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3478
3479 #if USE_NEW_PLAYER_ASSIGNMENTS
3480   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3481   /* choose default local player */
3482   local_player = &stored_player[0];
3483
3484   for (i = 0; i < MAX_PLAYERS; i++)
3485     stored_player[i].connected = FALSE;
3486
3487   local_player->connected = TRUE;
3488   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3489
3490   if (tape.playing)
3491   {
3492     for (i = 0; i < MAX_PLAYERS; i++)
3493       stored_player[i].connected = tape.player_participates[i];
3494   }
3495   else if (game.team_mode && !options.network)
3496   {
3497     /* try to guess locally connected team mode players (needed for correct
3498        assignment of player figures from level to locally playing players) */
3499
3500     for (i = 0; i < MAX_PLAYERS; i++)
3501       if (setup.input[i].use_joystick ||
3502           setup.input[i].key.left != KSYM_UNDEFINED)
3503         stored_player[i].connected = TRUE;
3504   }
3505
3506 #if DEBUG_INIT_PLAYER
3507   if (options.debug)
3508   {
3509     printf("Player status after level initialization:\n");
3510
3511     for (i = 0; i < MAX_PLAYERS; i++)
3512     {
3513       struct PlayerInfo *player = &stored_player[i];
3514
3515       printf("- player %d: present == %d, connected == %d, active == %d",
3516              i + 1,
3517              player->present,
3518              player->connected,
3519              player->active);
3520
3521       if (local_player == player)
3522         printf(" (local player)");
3523
3524       printf("\n");
3525     }
3526   }
3527 #endif
3528
3529 #if DEBUG_INIT_PLAYER
3530   if (options.debug)
3531     printf("Reassigning players ...\n");
3532 #endif
3533
3534   /* check if any connected player was not found in playfield */
3535   for (i = 0; i < MAX_PLAYERS; i++)
3536   {
3537     struct PlayerInfo *player = &stored_player[i];
3538
3539     if (player->connected && !player->present)
3540     {
3541       struct PlayerInfo *field_player = NULL;
3542
3543 #if DEBUG_INIT_PLAYER
3544       if (options.debug)
3545         printf("- looking for field player for player %d ...\n", i + 1);
3546 #endif
3547
3548       /* assign first free player found that is present in the playfield */
3549
3550       /* first try: look for unmapped playfield player that is not connected */
3551       for (j = 0; j < MAX_PLAYERS; j++)
3552         if (field_player == NULL &&
3553             stored_player[j].present &&
3554             !stored_player[j].mapped &&
3555             !stored_player[j].connected)
3556           field_player = &stored_player[j];
3557
3558       /* second try: look for *any* unmapped playfield player */
3559       for (j = 0; j < MAX_PLAYERS; j++)
3560         if (field_player == NULL &&
3561             stored_player[j].present &&
3562             !stored_player[j].mapped)
3563           field_player = &stored_player[j];
3564
3565       if (field_player != NULL)
3566       {
3567         int jx = field_player->jx, jy = field_player->jy;
3568
3569 #if DEBUG_INIT_PLAYER
3570         if (options.debug)
3571           printf("- found player %d\n", field_player->index_nr + 1);
3572 #endif
3573
3574         player->present = FALSE;
3575         player->active = FALSE;
3576
3577         field_player->present = TRUE;
3578         field_player->active = TRUE;
3579
3580         /*
3581         player->initial_element = field_player->initial_element;
3582         player->artwork_element = field_player->artwork_element;
3583
3584         player->block_last_field       = field_player->block_last_field;
3585         player->block_delay_adjustment = field_player->block_delay_adjustment;
3586         */
3587
3588         StorePlayer[jx][jy] = field_player->element_nr;
3589
3590         field_player->jx = field_player->last_jx = jx;
3591         field_player->jy = field_player->last_jy = jy;
3592
3593         if (local_player == player)
3594           local_player = field_player;
3595
3596         map_player_action[field_player->index_nr] = i;
3597
3598         field_player->mapped = TRUE;
3599
3600 #if DEBUG_INIT_PLAYER
3601         if (options.debug)
3602           printf("- map_player_action[%d] == %d\n",
3603                  field_player->index_nr + 1, i + 1);
3604 #endif
3605       }
3606     }
3607
3608     if (player->connected && player->present)
3609       player->mapped = TRUE;
3610   }
3611
3612 #if DEBUG_INIT_PLAYER
3613   if (options.debug)
3614   {
3615     printf("Player status after player assignment (first stage):\n");
3616
3617     for (i = 0; i < MAX_PLAYERS; i++)
3618     {
3619       struct PlayerInfo *player = &stored_player[i];
3620
3621       printf("- player %d: present == %d, connected == %d, active == %d",
3622              i + 1,
3623              player->present,
3624              player->connected,
3625              player->active);
3626
3627       if (local_player == player)
3628         printf(" (local player)");
3629
3630       printf("\n");
3631     }
3632   }
3633 #endif
3634
3635 #else
3636
3637   /* check if any connected player was not found in playfield */
3638   for (i = 0; i < MAX_PLAYERS; i++)
3639   {
3640     struct PlayerInfo *player = &stored_player[i];
3641
3642     if (player->connected && !player->present)
3643     {
3644       for (j = 0; j < MAX_PLAYERS; j++)
3645       {
3646         struct PlayerInfo *field_player = &stored_player[j];
3647         int jx = field_player->jx, jy = field_player->jy;
3648
3649         /* assign first free player found that is present in the playfield */
3650         if (field_player->present && !field_player->connected)
3651         {
3652           player->present = TRUE;
3653           player->active = TRUE;
3654
3655           field_player->present = FALSE;
3656           field_player->active = FALSE;
3657
3658           player->initial_element = field_player->initial_element;
3659           player->artwork_element = field_player->artwork_element;
3660
3661           player->block_last_field       = field_player->block_last_field;
3662           player->block_delay_adjustment = field_player->block_delay_adjustment;
3663
3664           StorePlayer[jx][jy] = player->element_nr;
3665
3666           player->jx = player->last_jx = jx;
3667           player->jy = player->last_jy = jy;
3668
3669           break;
3670         }
3671       }
3672     }
3673   }
3674 #endif
3675
3676 #if 0
3677   printf("::: local_player->present == %d\n", local_player->present);
3678 #endif
3679
3680   if (tape.playing)
3681   {
3682     /* when playing a tape, eliminate all players who do not participate */
3683
3684 #if USE_NEW_PLAYER_ASSIGNMENTS
3685
3686     if (!game.team_mode)
3687     {
3688       for (i = 0; i < MAX_PLAYERS; i++)
3689       {
3690         if (stored_player[i].active &&
3691             !tape.player_participates[map_player_action[i]])
3692         {
3693           struct PlayerInfo *player = &stored_player[i];
3694           int jx = player->jx, jy = player->jy;
3695
3696 #if DEBUG_INIT_PLAYER
3697           if (options.debug)
3698             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3699 #endif
3700
3701           player->active = FALSE;
3702           StorePlayer[jx][jy] = 0;
3703           Feld[jx][jy] = EL_EMPTY;
3704         }
3705       }
3706     }
3707
3708 #else
3709
3710     for (i = 0; i < MAX_PLAYERS; i++)
3711     {
3712       if (stored_player[i].active &&
3713           !tape.player_participates[i])
3714       {
3715         struct PlayerInfo *player = &stored_player[i];
3716         int jx = player->jx, jy = player->jy;
3717
3718         player->active = FALSE;
3719         StorePlayer[jx][jy] = 0;
3720         Feld[jx][jy] = EL_EMPTY;
3721       }
3722     }
3723 #endif
3724   }
3725   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3726   {
3727     /* when in single player mode, eliminate all but the first active player */
3728
3729     for (i = 0; i < MAX_PLAYERS; i++)
3730     {
3731       if (stored_player[i].active)
3732       {
3733         for (j = i + 1; j < MAX_PLAYERS; j++)
3734         {
3735           if (stored_player[j].active)
3736           {
3737             struct PlayerInfo *player = &stored_player[j];
3738             int jx = player->jx, jy = player->jy;
3739
3740             player->active = FALSE;
3741             player->present = FALSE;
3742
3743             StorePlayer[jx][jy] = 0;
3744             Feld[jx][jy] = EL_EMPTY;
3745           }
3746         }
3747       }
3748     }
3749   }
3750
3751   /* when recording the game, store which players take part in the game */
3752   if (tape.recording)
3753   {
3754 #if USE_NEW_PLAYER_ASSIGNMENTS
3755     for (i = 0; i < MAX_PLAYERS; i++)
3756       if (stored_player[i].connected)
3757         tape.player_participates[i] = TRUE;
3758 #else
3759     for (i = 0; i < MAX_PLAYERS; i++)
3760       if (stored_player[i].active)
3761         tape.player_participates[i] = TRUE;
3762 #endif
3763   }
3764
3765 #if DEBUG_INIT_PLAYER
3766   if (options.debug)
3767   {
3768     printf("Player status after player assignment (final stage):\n");
3769
3770     for (i = 0; i < MAX_PLAYERS; i++)
3771     {
3772       struct PlayerInfo *player = &stored_player[i];
3773
3774       printf("- player %d: present == %d, connected == %d, active == %d",
3775              i + 1,
3776              player->present,
3777              player->connected,
3778              player->active);
3779
3780       if (local_player == player)
3781         printf(" (local player)");
3782
3783       printf("\n");
3784     }
3785   }
3786 #endif
3787
3788   if (BorderElement == EL_EMPTY)
3789   {
3790     SBX_Left = 0;
3791     SBX_Right = lev_fieldx - SCR_FIELDX;
3792     SBY_Upper = 0;
3793     SBY_Lower = lev_fieldy - SCR_FIELDY;
3794   }
3795   else
3796   {
3797     SBX_Left = -1;
3798     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3799     SBY_Upper = -1;
3800     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3801   }
3802
3803   if (full_lev_fieldx <= SCR_FIELDX)
3804     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3805   if (full_lev_fieldy <= SCR_FIELDY)
3806     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3807
3808   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3809     SBX_Left--;
3810   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3811     SBY_Upper--;
3812
3813   /* if local player not found, look for custom element that might create
3814      the player (make some assumptions about the right custom element) */
3815   if (!local_player->present)
3816   {
3817     int start_x = 0, start_y = 0;
3818     int found_rating = 0;
3819     int found_element = EL_UNDEFINED;
3820     int player_nr = local_player->index_nr;
3821
3822     SCAN_PLAYFIELD(x, y)
3823     {
3824       int element = Feld[x][y];
3825       int content;
3826       int xx, yy;
3827       boolean is_player;
3828
3829       if (level.use_start_element[player_nr] &&
3830           level.start_element[player_nr] == element &&
3831           found_rating < 4)
3832       {
3833         start_x = x;
3834         start_y = y;
3835
3836         found_rating = 4;
3837         found_element = element;
3838       }
3839
3840       if (!IS_CUSTOM_ELEMENT(element))
3841         continue;
3842
3843       if (CAN_CHANGE(element))
3844       {
3845         for (i = 0; i < element_info[element].num_change_pages; i++)
3846         {
3847           /* check for player created from custom element as single target */
3848           content = element_info[element].change_page[i].target_element;
3849           is_player = ELEM_IS_PLAYER(content);
3850
3851           if (is_player && (found_rating < 3 ||
3852                             (found_rating == 3 && element < found_element)))
3853           {
3854             start_x = x;
3855             start_y = y;
3856
3857             found_rating = 3;
3858             found_element = element;
3859           }
3860         }
3861       }
3862
3863       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3864       {
3865         /* check for player created from custom element as explosion content */
3866         content = element_info[element].content.e[xx][yy];
3867         is_player = ELEM_IS_PLAYER(content);
3868
3869         if (is_player && (found_rating < 2 ||
3870                           (found_rating == 2 && element < found_element)))
3871         {
3872           start_x = x + xx - 1;
3873           start_y = y + yy - 1;
3874
3875           found_rating = 2;
3876           found_element = element;
3877         }
3878
3879         if (!CAN_CHANGE(element))
3880           continue;
3881
3882         for (i = 0; i < element_info[element].num_change_pages; i++)
3883         {
3884           /* check for player created from custom element as extended target */
3885           content =
3886             element_info[element].change_page[i].target_content.e[xx][yy];
3887
3888           is_player = ELEM_IS_PLAYER(content);
3889
3890           if (is_player && (found_rating < 1 ||
3891                             (found_rating == 1 && element < found_element)))
3892           {
3893             start_x = x + xx - 1;
3894             start_y = y + yy - 1;
3895
3896             found_rating = 1;
3897             found_element = element;
3898           }
3899         }
3900       }
3901     }
3902
3903     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3904                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3905                 start_x - MIDPOSX);
3906
3907     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3908                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3909                 start_y - MIDPOSY);
3910   }
3911   else
3912   {
3913     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3914                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3915                 local_player->jx - MIDPOSX);
3916
3917     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3918                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3919                 local_player->jy - MIDPOSY);
3920   }
3921
3922   /* !!! FIX THIS (START) !!! */
3923   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3924   {
3925     InitGameEngine_EM();
3926   }
3927   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3928   {
3929     InitGameEngine_SP();
3930   }
3931   else
3932   {
3933     DrawLevel(REDRAW_FIELD);
3934     DrawAllPlayers();
3935
3936     /* after drawing the level, correct some elements */
3937     if (game.timegate_time_left == 0)
3938       CloseAllOpenTimegates();
3939   }
3940
3941   /* blit playfield from scroll buffer to normal back buffer for fading in */
3942   BlitScreenToBitmap(backbuffer);
3943   /* !!! FIX THIS (END) !!! */
3944
3945   DrawMaskedBorder(fade_mask);
3946
3947   FadeIn(fade_mask);
3948
3949 #if 1
3950   // full screen redraw is required at this point in the following cases:
3951   // - special editor door undrawn when game was started from level editor
3952   // - drawing area (playfield) was changed and has to be removed completely
3953   redraw_mask = REDRAW_ALL;
3954   BackToFront();
3955 #endif
3956
3957   if (!game.restart_level)
3958   {
3959     /* copy default game door content to main double buffer */
3960
3961     /* !!! CHECK AGAIN !!! */
3962     SetPanelBackground();
3963     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3964     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3965   }
3966
3967   SetPanelBackground();
3968   SetDrawBackgroundMask(REDRAW_DOOR_1);
3969
3970   UpdateAndDisplayGameControlValues();
3971
3972   if (!game.restart_level)
3973   {
3974     UnmapGameButtons();
3975     UnmapTapeButtons();
3976
3977     FreeGameButtons();
3978     CreateGameButtons();
3979
3980     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3981     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3982     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3983
3984     MapGameButtons();
3985     MapTapeButtons();
3986
3987     /* copy actual game door content to door double buffer for OpenDoor() */
3988     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3989
3990     OpenDoor(DOOR_OPEN_ALL);
3991
3992     PlaySound(SND_GAME_STARTING);
3993
3994     if (setup.sound_music)
3995       PlayLevelMusic();
3996
3997     KeyboardAutoRepeatOffUnlessAutoplay();
3998
3999 #if DEBUG_INIT_PLAYER
4000     if (options.debug)
4001     {
4002       printf("Player status (final):\n");
4003
4004       for (i = 0; i < MAX_PLAYERS; i++)
4005       {
4006         struct PlayerInfo *player = &stored_player[i];
4007
4008         printf("- player %d: present == %d, connected == %d, active == %d",
4009                i + 1,
4010                player->present,
4011                player->connected,
4012                player->active);
4013
4014         if (local_player == player)
4015           printf(" (local player)");
4016
4017         printf("\n");
4018       }
4019     }
4020 #endif
4021   }
4022
4023   UnmapAllGadgets();
4024
4025   MapGameButtons();
4026   MapTapeButtons();
4027
4028   if (!game.restart_level && !tape.playing)
4029   {
4030     LevelStats_incPlayed(level_nr);
4031
4032     SaveLevelSetup_SeriesInfo();
4033   }
4034
4035   game.restart_level = FALSE;
4036
4037   SaveEngineSnapshotToListInitial();
4038 }
4039
4040 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4041 {
4042   /* this is used for non-R'n'D game engines to update certain engine values */
4043
4044   /* needed to determine if sounds are played within the visible screen area */
4045   scroll_x = actual_scroll_x;
4046   scroll_y = actual_scroll_y;
4047 }
4048
4049 void InitMovDir(int x, int y)
4050 {
4051   int i, element = Feld[x][y];
4052   static int xy[4][2] =
4053   {
4054     {  0, +1 },
4055     { +1,  0 },
4056     {  0, -1 },
4057     { -1,  0 }
4058   };
4059   static int direction[3][4] =
4060   {
4061     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4062     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4063     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4064   };
4065
4066   switch (element)
4067   {
4068     case EL_BUG_RIGHT:
4069     case EL_BUG_UP:
4070     case EL_BUG_LEFT:
4071     case EL_BUG_DOWN:
4072       Feld[x][y] = EL_BUG;
4073       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4074       break;
4075
4076     case EL_SPACESHIP_RIGHT:
4077     case EL_SPACESHIP_UP:
4078     case EL_SPACESHIP_LEFT:
4079     case EL_SPACESHIP_DOWN:
4080       Feld[x][y] = EL_SPACESHIP;
4081       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4082       break;
4083
4084     case EL_BD_BUTTERFLY_RIGHT:
4085     case EL_BD_BUTTERFLY_UP:
4086     case EL_BD_BUTTERFLY_LEFT:
4087     case EL_BD_BUTTERFLY_DOWN:
4088       Feld[x][y] = EL_BD_BUTTERFLY;
4089       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4090       break;
4091
4092     case EL_BD_FIREFLY_RIGHT:
4093     case EL_BD_FIREFLY_UP:
4094     case EL_BD_FIREFLY_LEFT:
4095     case EL_BD_FIREFLY_DOWN:
4096       Feld[x][y] = EL_BD_FIREFLY;
4097       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4098       break;
4099
4100     case EL_PACMAN_RIGHT:
4101     case EL_PACMAN_UP:
4102     case EL_PACMAN_LEFT:
4103     case EL_PACMAN_DOWN:
4104       Feld[x][y] = EL_PACMAN;
4105       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4106       break;
4107
4108     case EL_YAMYAM_LEFT:
4109     case EL_YAMYAM_RIGHT:
4110     case EL_YAMYAM_UP:
4111     case EL_YAMYAM_DOWN:
4112       Feld[x][y] = EL_YAMYAM;
4113       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4114       break;
4115
4116     case EL_SP_SNIKSNAK:
4117       MovDir[x][y] = MV_UP;
4118       break;
4119
4120     case EL_SP_ELECTRON:
4121       MovDir[x][y] = MV_LEFT;
4122       break;
4123
4124     case EL_MOLE_LEFT:
4125     case EL_MOLE_RIGHT:
4126     case EL_MOLE_UP:
4127     case EL_MOLE_DOWN:
4128       Feld[x][y] = EL_MOLE;
4129       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4130       break;
4131
4132     default:
4133       if (IS_CUSTOM_ELEMENT(element))
4134       {
4135         struct ElementInfo *ei = &element_info[element];
4136         int move_direction_initial = ei->move_direction_initial;
4137         int move_pattern = ei->move_pattern;
4138
4139         if (move_direction_initial == MV_START_PREVIOUS)
4140         {
4141           if (MovDir[x][y] != MV_NONE)
4142             return;
4143
4144           move_direction_initial = MV_START_AUTOMATIC;
4145         }
4146
4147         if (move_direction_initial == MV_START_RANDOM)
4148           MovDir[x][y] = 1 << RND(4);
4149         else if (move_direction_initial & MV_ANY_DIRECTION)
4150           MovDir[x][y] = move_direction_initial;
4151         else if (move_pattern == MV_ALL_DIRECTIONS ||
4152                  move_pattern == MV_TURNING_LEFT ||
4153                  move_pattern == MV_TURNING_RIGHT ||
4154                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4155                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4156                  move_pattern == MV_TURNING_RANDOM)
4157           MovDir[x][y] = 1 << RND(4);
4158         else if (move_pattern == MV_HORIZONTAL)
4159           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4160         else if (move_pattern == MV_VERTICAL)
4161           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4162         else if (move_pattern & MV_ANY_DIRECTION)
4163           MovDir[x][y] = element_info[element].move_pattern;
4164         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4165                  move_pattern == MV_ALONG_RIGHT_SIDE)
4166         {
4167           /* use random direction as default start direction */
4168           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4169             MovDir[x][y] = 1 << RND(4);
4170
4171           for (i = 0; i < NUM_DIRECTIONS; i++)
4172           {
4173             int x1 = x + xy[i][0];
4174             int y1 = y + xy[i][1];
4175
4176             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4177             {
4178               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4179                 MovDir[x][y] = direction[0][i];
4180               else
4181                 MovDir[x][y] = direction[1][i];
4182
4183               break;
4184             }
4185           }
4186         }                
4187       }
4188       else
4189       {
4190         MovDir[x][y] = 1 << RND(4);
4191
4192         if (element != EL_BUG &&
4193             element != EL_SPACESHIP &&
4194             element != EL_BD_BUTTERFLY &&
4195             element != EL_BD_FIREFLY)
4196           break;
4197
4198         for (i = 0; i < NUM_DIRECTIONS; i++)
4199         {
4200           int x1 = x + xy[i][0];
4201           int y1 = y + xy[i][1];
4202
4203           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4204           {
4205             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4206             {
4207               MovDir[x][y] = direction[0][i];
4208               break;
4209             }
4210             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4211                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4212             {
4213               MovDir[x][y] = direction[1][i];
4214               break;
4215             }
4216           }
4217         }
4218       }
4219       break;
4220   }
4221
4222   GfxDir[x][y] = MovDir[x][y];
4223 }
4224
4225 void InitAmoebaNr(int x, int y)
4226 {
4227   int i;
4228   int group_nr = AmoebeNachbarNr(x, y);
4229
4230   if (group_nr == 0)
4231   {
4232     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4233     {
4234       if (AmoebaCnt[i] == 0)
4235       {
4236         group_nr = i;
4237         break;
4238       }
4239     }
4240   }
4241
4242   AmoebaNr[x][y] = group_nr;
4243   AmoebaCnt[group_nr]++;
4244   AmoebaCnt2[group_nr]++;
4245 }
4246
4247 static void PlayerWins(struct PlayerInfo *player)
4248 {
4249   player->LevelSolved = TRUE;
4250   player->GameOver = TRUE;
4251
4252   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4253                          level.native_em_level->lev->score : player->score);
4254
4255   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4256                                       TimeLeft);
4257   player->LevelSolved_CountingScore = player->score_final;
4258 }
4259
4260 void GameWon()
4261 {
4262   static int time, time_final;
4263   static int score, score_final;
4264   static int game_over_delay_1 = 0;
4265   static int game_over_delay_2 = 0;
4266   int game_over_delay_value_1 = 50;
4267   int game_over_delay_value_2 = 50;
4268
4269   if (!local_player->LevelSolved_GameWon)
4270   {
4271     int i;
4272
4273     /* do not start end game actions before the player stops moving (to exit) */
4274     if (local_player->MovPos)
4275       return;
4276
4277     local_player->LevelSolved_GameWon = TRUE;
4278     local_player->LevelSolved_SaveTape = tape.recording;
4279     local_player->LevelSolved_SaveScore = !tape.playing;
4280
4281     if (!tape.playing)
4282     {
4283       LevelStats_incSolved(level_nr);
4284
4285       SaveLevelSetup_SeriesInfo();
4286     }
4287
4288     if (tape.auto_play)         /* tape might already be stopped here */
4289       tape.auto_play_level_solved = TRUE;
4290
4291     TapeStop();
4292
4293     game_over_delay_1 = game_over_delay_value_1;
4294     game_over_delay_2 = game_over_delay_value_2;
4295
4296     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4297     score = score_final = local_player->score_final;
4298
4299     if (TimeLeft > 0)
4300     {
4301       time_final = 0;
4302       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4303     }
4304     else if (game.no_time_limit && TimePlayed < 999)
4305     {
4306       time_final = 999;
4307       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4308     }
4309
4310     local_player->score_final = score_final;
4311
4312     if (level_editor_test_game)
4313     {
4314       time = time_final;
4315       score = score_final;
4316
4317       local_player->LevelSolved_CountingTime = time;
4318       local_player->LevelSolved_CountingScore = score;
4319
4320       game_panel_controls[GAME_PANEL_TIME].value = time;
4321       game_panel_controls[GAME_PANEL_SCORE].value = score;
4322
4323       DisplayGameControlValues();
4324     }
4325
4326     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4327     {
4328       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4329       {
4330         /* close exit door after last player */
4331         if ((AllPlayersGone &&
4332              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4333               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4334               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4335             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4336             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4337         {
4338           int element = Feld[ExitX][ExitY];
4339
4340           Feld[ExitX][ExitY] =
4341             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4342              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4343              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4344              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4345              EL_EM_STEEL_EXIT_CLOSING);
4346
4347           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4348         }
4349
4350         /* player disappears */
4351         DrawLevelField(ExitX, ExitY);
4352       }
4353
4354       for (i = 0; i < MAX_PLAYERS; i++)
4355       {
4356         struct PlayerInfo *player = &stored_player[i];
4357
4358         if (player->present)
4359         {
4360           RemovePlayer(player);
4361
4362           /* player disappears */
4363           DrawLevelField(player->jx, player->jy);
4364         }
4365       }
4366     }
4367
4368     PlaySound(SND_GAME_WINNING);
4369   }
4370
4371   if (game_over_delay_1 > 0)
4372   {
4373     game_over_delay_1--;
4374
4375     return;
4376   }
4377
4378   if (time != time_final)
4379   {
4380     int time_to_go = ABS(time_final - time);
4381     int time_count_dir = (time < time_final ? +1 : -1);
4382     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4383
4384     time  += time_count_steps * time_count_dir;
4385     score += time_count_steps * level.score[SC_TIME_BONUS];
4386
4387     local_player->LevelSolved_CountingTime = time;
4388     local_player->LevelSolved_CountingScore = score;
4389
4390     game_panel_controls[GAME_PANEL_TIME].value = time;
4391     game_panel_controls[GAME_PANEL_SCORE].value = score;
4392
4393     DisplayGameControlValues();
4394
4395     if (time == time_final)
4396       StopSound(SND_GAME_LEVELTIME_BONUS);
4397     else if (setup.sound_loops)
4398       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4399     else
4400       PlaySound(SND_GAME_LEVELTIME_BONUS);
4401
4402     return;
4403   }
4404
4405   local_player->LevelSolved_PanelOff = TRUE;
4406
4407   if (game_over_delay_2 > 0)
4408   {
4409     game_over_delay_2--;
4410
4411     return;
4412   }
4413
4414   GameEnd();
4415 }
4416
4417 void GameEnd()
4418 {
4419   int hi_pos;
4420   boolean raise_level = FALSE;
4421
4422   local_player->LevelSolved_GameEnd = TRUE;
4423
4424   if (!global.use_envelope_request)
4425     CloseDoor(DOOR_CLOSE_1);
4426
4427   if (local_player->LevelSolved_SaveTape)
4428   {
4429     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4430   }
4431
4432   CloseDoor(DOOR_CLOSE_ALL);
4433
4434   if (level_editor_test_game)
4435   {
4436     SetGameStatus(GAME_MODE_MAIN);
4437
4438     DrawMainMenu();
4439
4440     return;
4441   }
4442
4443   if (!local_player->LevelSolved_SaveScore)
4444   {
4445     SetGameStatus(GAME_MODE_MAIN);
4446
4447     DrawMainMenu();
4448
4449     return;
4450   }
4451
4452   if (level_nr == leveldir_current->handicap_level)
4453   {
4454     leveldir_current->handicap_level++;
4455
4456     SaveLevelSetup_SeriesInfo();
4457   }
4458
4459   if (level_nr < leveldir_current->last_level)
4460     raise_level = TRUE;                 /* advance to next level */
4461
4462   if ((hi_pos = NewHiScore()) >= 0) 
4463   {
4464     SetGameStatus(GAME_MODE_SCORES);
4465
4466     DrawHallOfFame(hi_pos);
4467
4468     if (raise_level)
4469     {
4470       level_nr++;
4471       TapeErase();
4472     }
4473   }
4474   else
4475   {
4476     SetGameStatus(GAME_MODE_MAIN);
4477
4478     if (raise_level)
4479     {
4480       level_nr++;
4481       TapeErase();
4482     }
4483
4484     DrawMainMenu();
4485   }
4486 }
4487
4488 int NewHiScore()
4489 {
4490   int k, l;
4491   int position = -1;
4492
4493   LoadScore(level_nr);
4494
4495   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4496       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4497     return -1;
4498
4499   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4500   {
4501     if (local_player->score_final > highscore[k].Score)
4502     {
4503       /* player has made it to the hall of fame */
4504
4505       if (k < MAX_SCORE_ENTRIES - 1)
4506       {
4507         int m = MAX_SCORE_ENTRIES - 1;
4508
4509 #ifdef ONE_PER_NAME
4510         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4511           if (strEqual(setup.player_name, highscore[l].Name))
4512             m = l;
4513         if (m == k)     /* player's new highscore overwrites his old one */
4514           goto put_into_list;
4515 #endif
4516
4517         for (l = m; l > k; l--)
4518         {
4519           strcpy(highscore[l].Name, highscore[l - 1].Name);
4520           highscore[l].Score = highscore[l - 1].Score;
4521         }
4522       }
4523
4524 #ifdef ONE_PER_NAME
4525       put_into_list:
4526 #endif
4527       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4528       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4529       highscore[k].Score = local_player->score_final; 
4530       position = k;
4531       break;
4532     }
4533
4534 #ifdef ONE_PER_NAME
4535     else if (!strncmp(setup.player_name, highscore[k].Name,
4536                       MAX_PLAYER_NAME_LEN))
4537       break;    /* player already there with a higher score */
4538 #endif
4539
4540   }
4541
4542   if (position >= 0) 
4543     SaveScore(level_nr);
4544
4545   return position;
4546 }
4547
4548 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4549 {
4550   int element = Feld[x][y];
4551   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4552   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4553   int horiz_move = (dx != 0);
4554   int sign = (horiz_move ? dx : dy);
4555   int step = sign * element_info[element].move_stepsize;
4556
4557   /* special values for move stepsize for spring and things on conveyor belt */
4558   if (horiz_move)
4559   {
4560     if (CAN_FALL(element) &&
4561         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4562       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4563     else if (element == EL_SPRING)
4564       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4565   }
4566
4567   return step;
4568 }
4569
4570 inline static int getElementMoveStepsize(int x, int y)
4571 {
4572   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4573 }
4574
4575 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4576 {
4577   if (player->GfxAction != action || player->GfxDir != dir)
4578   {
4579     player->GfxAction = action;
4580     player->GfxDir = dir;
4581     player->Frame = 0;
4582     player->StepFrame = 0;
4583   }
4584 }
4585
4586 static void ResetGfxFrame(int x, int y, boolean redraw)
4587 {
4588   int element = Feld[x][y];
4589   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4590   int last_gfx_frame = GfxFrame[x][y];
4591
4592   if (graphic_info[graphic].anim_global_sync)
4593     GfxFrame[x][y] = FrameCounter;
4594   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4595     GfxFrame[x][y] = CustomValue[x][y];
4596   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4597     GfxFrame[x][y] = element_info[element].collect_score;
4598   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4599     GfxFrame[x][y] = ChangeDelay[x][y];
4600
4601   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4602     DrawLevelGraphicAnimation(x, y, graphic);
4603 }
4604
4605 static void ResetGfxAnimation(int x, int y)
4606 {
4607   GfxAction[x][y] = ACTION_DEFAULT;
4608   GfxDir[x][y] = MovDir[x][y];
4609   GfxFrame[x][y] = 0;
4610
4611   ResetGfxFrame(x, y, FALSE);
4612 }
4613
4614 static void ResetRandomAnimationValue(int x, int y)
4615 {
4616   GfxRandom[x][y] = INIT_GFX_RANDOM();
4617 }
4618
4619 void InitMovingField(int x, int y, int direction)
4620 {
4621   int element = Feld[x][y];
4622   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4623   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4624   int newx = x + dx;
4625   int newy = y + dy;
4626   boolean is_moving_before, is_moving_after;
4627
4628   /* check if element was/is moving or being moved before/after mode change */
4629   is_moving_before = (WasJustMoving[x][y] != 0);
4630   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4631
4632   /* reset animation only for moving elements which change direction of moving
4633      or which just started or stopped moving
4634      (else CEs with property "can move" / "not moving" are reset each frame) */
4635   if (is_moving_before != is_moving_after ||
4636       direction != MovDir[x][y])
4637     ResetGfxAnimation(x, y);
4638
4639   MovDir[x][y] = direction;
4640   GfxDir[x][y] = direction;
4641
4642   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4643                      direction == MV_DOWN && CAN_FALL(element) ?
4644                      ACTION_FALLING : ACTION_MOVING);
4645
4646   /* this is needed for CEs with property "can move" / "not moving" */
4647
4648   if (is_moving_after)
4649   {
4650     if (Feld[newx][newy] == EL_EMPTY)
4651       Feld[newx][newy] = EL_BLOCKED;
4652
4653     MovDir[newx][newy] = MovDir[x][y];
4654
4655     CustomValue[newx][newy] = CustomValue[x][y];
4656
4657     GfxFrame[newx][newy] = GfxFrame[x][y];
4658     GfxRandom[newx][newy] = GfxRandom[x][y];
4659     GfxAction[newx][newy] = GfxAction[x][y];
4660     GfxDir[newx][newy] = GfxDir[x][y];
4661   }
4662 }
4663
4664 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4665 {
4666   int direction = MovDir[x][y];
4667   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4668   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4669
4670   *goes_to_x = newx;
4671   *goes_to_y = newy;
4672 }
4673
4674 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4675 {
4676   int oldx = x, oldy = y;
4677   int direction = MovDir[x][y];
4678
4679   if (direction == MV_LEFT)
4680     oldx++;
4681   else if (direction == MV_RIGHT)
4682     oldx--;
4683   else if (direction == MV_UP)
4684     oldy++;
4685   else if (direction == MV_DOWN)
4686     oldy--;
4687
4688   *comes_from_x = oldx;
4689   *comes_from_y = oldy;
4690 }
4691
4692 int MovingOrBlocked2Element(int x, int y)
4693 {
4694   int element = Feld[x][y];
4695
4696   if (element == EL_BLOCKED)
4697   {
4698     int oldx, oldy;
4699
4700     Blocked2Moving(x, y, &oldx, &oldy);
4701     return Feld[oldx][oldy];
4702   }
4703   else
4704     return element;
4705 }
4706
4707 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4708 {
4709   /* like MovingOrBlocked2Element(), but if element is moving
4710      and (x,y) is the field the moving element is just leaving,
4711      return EL_BLOCKED instead of the element value */
4712   int element = Feld[x][y];
4713
4714   if (IS_MOVING(x, y))
4715   {
4716     if (element == EL_BLOCKED)
4717     {
4718       int oldx, oldy;
4719
4720       Blocked2Moving(x, y, &oldx, &oldy);
4721       return Feld[oldx][oldy];
4722     }
4723     else
4724       return EL_BLOCKED;
4725   }
4726   else
4727     return element;
4728 }
4729
4730 static void RemoveField(int x, int y)
4731 {
4732   Feld[x][y] = EL_EMPTY;
4733
4734   MovPos[x][y] = 0;
4735   MovDir[x][y] = 0;
4736   MovDelay[x][y] = 0;
4737
4738   CustomValue[x][y] = 0;
4739
4740   AmoebaNr[x][y] = 0;
4741   ChangeDelay[x][y] = 0;
4742   ChangePage[x][y] = -1;
4743   Pushed[x][y] = FALSE;
4744
4745   GfxElement[x][y] = EL_UNDEFINED;
4746   GfxAction[x][y] = ACTION_DEFAULT;
4747   GfxDir[x][y] = MV_NONE;
4748 }
4749
4750 void RemoveMovingField(int x, int y)
4751 {
4752   int oldx = x, oldy = y, newx = x, newy = y;
4753   int element = Feld[x][y];
4754   int next_element = EL_UNDEFINED;
4755
4756   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4757     return;
4758
4759   if (IS_MOVING(x, y))
4760   {
4761     Moving2Blocked(x, y, &newx, &newy);
4762
4763     if (Feld[newx][newy] != EL_BLOCKED)
4764     {
4765       /* element is moving, but target field is not free (blocked), but
4766          already occupied by something different (example: acid pool);
4767          in this case, only remove the moving field, but not the target */
4768
4769       RemoveField(oldx, oldy);
4770
4771       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4772
4773       TEST_DrawLevelField(oldx, oldy);
4774
4775       return;
4776     }
4777   }
4778   else if (element == EL_BLOCKED)
4779   {
4780     Blocked2Moving(x, y, &oldx, &oldy);
4781     if (!IS_MOVING(oldx, oldy))
4782       return;
4783   }
4784
4785   if (element == EL_BLOCKED &&
4786       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4787        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4788        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4789        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4790        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4791        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4792     next_element = get_next_element(Feld[oldx][oldy]);
4793
4794   RemoveField(oldx, oldy);
4795   RemoveField(newx, newy);
4796
4797   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4798
4799   if (next_element != EL_UNDEFINED)
4800     Feld[oldx][oldy] = next_element;
4801
4802   TEST_DrawLevelField(oldx, oldy);
4803   TEST_DrawLevelField(newx, newy);
4804 }
4805
4806 void DrawDynamite(int x, int y)
4807 {
4808   int sx = SCREENX(x), sy = SCREENY(y);
4809   int graphic = el2img(Feld[x][y]);
4810   int frame;
4811
4812   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4813     return;
4814
4815   if (IS_WALKABLE_INSIDE(Back[x][y]))
4816     return;
4817
4818   if (Back[x][y])
4819     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4820   else if (Store[x][y])
4821     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4822
4823   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4824
4825   if (Back[x][y] || Store[x][y])
4826     DrawGraphicThruMask(sx, sy, graphic, frame);
4827   else
4828     DrawGraphic(sx, sy, graphic, frame);
4829 }
4830
4831 void CheckDynamite(int x, int y)
4832 {
4833   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4834   {
4835     MovDelay[x][y]--;
4836
4837     if (MovDelay[x][y] != 0)
4838     {
4839       DrawDynamite(x, y);
4840       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4841
4842       return;
4843     }
4844   }
4845
4846   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4847
4848   Bang(x, y);
4849 }
4850
4851 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4852 {
4853   boolean num_checked_players = 0;
4854   int i;
4855
4856   for (i = 0; i < MAX_PLAYERS; i++)
4857   {
4858     if (stored_player[i].active)
4859     {
4860       int sx = stored_player[i].jx;
4861       int sy = stored_player[i].jy;
4862
4863       if (num_checked_players == 0)
4864       {
4865         *sx1 = *sx2 = sx;
4866         *sy1 = *sy2 = sy;
4867       }
4868       else
4869       {
4870         *sx1 = MIN(*sx1, sx);
4871         *sy1 = MIN(*sy1, sy);
4872         *sx2 = MAX(*sx2, sx);
4873         *sy2 = MAX(*sy2, sy);
4874       }
4875
4876       num_checked_players++;
4877     }
4878   }
4879 }
4880
4881 static boolean checkIfAllPlayersFitToScreen_RND()
4882 {
4883   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4884
4885   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4886
4887   return (sx2 - sx1 < SCR_FIELDX &&
4888           sy2 - sy1 < SCR_FIELDY);
4889 }
4890
4891 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4892 {
4893   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4894
4895   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4896
4897   *sx = (sx1 + sx2) / 2;
4898   *sy = (sy1 + sy2) / 2;
4899 }
4900
4901 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4902                         boolean center_screen, boolean quick_relocation)
4903 {
4904   unsigned int frame_delay_value_old = GetVideoFrameDelay();
4905   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4906   boolean no_delay = (tape.warp_forward);
4907   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4908   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4909   int new_scroll_x, new_scroll_y;
4910
4911   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4912   {
4913     /* case 1: quick relocation inside visible screen (without scrolling) */
4914
4915     RedrawPlayfield();
4916
4917     return;
4918   }
4919
4920   if (!level.shifted_relocation || center_screen)
4921   {
4922     /* relocation _with_ centering of screen */
4923
4924     new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4925                     x > SBX_Right + MIDPOSX ? SBX_Right :
4926                     x - MIDPOSX);
4927
4928     new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4929                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4930                     y - MIDPOSY);
4931   }
4932   else
4933   {
4934     /* relocation _without_ centering of screen */
4935
4936     int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4937                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
4938                            old_x - MIDPOSX);
4939
4940     int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4941                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4942                            old_y - MIDPOSY);
4943
4944     int offset_x = x + (scroll_x - center_scroll_x);
4945     int offset_y = y + (scroll_y - center_scroll_y);
4946
4947     new_scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4948                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4949                     offset_x - MIDPOSX);
4950
4951     new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4952                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4953                     offset_y - MIDPOSY);
4954   }
4955
4956   if (quick_relocation)
4957   {
4958     /* case 2: quick relocation (redraw without visible scrolling) */
4959
4960     scroll_x = new_scroll_x;
4961     scroll_y = new_scroll_y;
4962
4963     RedrawPlayfield();
4964
4965     return;
4966   }
4967
4968   /* case 3: visible relocation (with scrolling to new position) */
4969
4970   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4971
4972   SetVideoFrameDelay(wait_delay_value);
4973
4974   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4975   {
4976     int dx = 0, dy = 0;
4977     int fx = FX, fy = FY;
4978
4979     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4980     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4981
4982     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4983       break;
4984
4985     scroll_x -= dx;
4986     scroll_y -= dy;
4987
4988     fx += dx * TILEX / 2;
4989     fy += dy * TILEY / 2;
4990
4991     ScrollLevel(dx, dy);
4992     DrawAllPlayers();
4993
4994     /* scroll in two steps of half tile size to make things smoother */
4995     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4996
4997     /* scroll second step to align at full tile size */
4998     BlitScreenToBitmap(window);
4999   }
5000
5001   DrawAllPlayers();
5002   BackToFront();
5003
5004   SetVideoFrameDelay(frame_delay_value_old);
5005 }
5006
5007 void RelocatePlayer(int jx, int jy, int el_player_raw)
5008 {
5009   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5010   int player_nr = GET_PLAYER_NR(el_player);
5011   struct PlayerInfo *player = &stored_player[player_nr];
5012   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5013   boolean no_delay = (tape.warp_forward);
5014   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5015   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5016   int old_jx = player->jx;
5017   int old_jy = player->jy;
5018   int old_element = Feld[old_jx][old_jy];
5019   int element = Feld[jx][jy];
5020   boolean player_relocated = (old_jx != jx || old_jy != jy);
5021
5022   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5023   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5024   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5025   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5026   int leave_side_horiz = move_dir_horiz;
5027   int leave_side_vert  = move_dir_vert;
5028   int enter_side = enter_side_horiz | enter_side_vert;
5029   int leave_side = leave_side_horiz | leave_side_vert;
5030
5031   if (player->GameOver)         /* do not reanimate dead player */
5032     return;
5033
5034   if (!player_relocated)        /* no need to relocate the player */
5035     return;
5036
5037   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5038   {
5039     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5040     DrawLevelField(jx, jy);
5041   }
5042
5043   if (player->present)
5044   {
5045     while (player->MovPos)
5046     {
5047       ScrollPlayer(player, SCROLL_GO_ON);
5048       ScrollScreen(NULL, SCROLL_GO_ON);
5049
5050       AdvanceFrameAndPlayerCounters(player->index_nr);
5051
5052       DrawPlayer(player);
5053
5054       BackToFront_WithFrameDelay(wait_delay_value);
5055     }
5056
5057     DrawPlayer(player);         /* needed here only to cleanup last field */
5058     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5059
5060     player->is_moving = FALSE;
5061   }
5062
5063   if (IS_CUSTOM_ELEMENT(old_element))
5064     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5065                                CE_LEFT_BY_PLAYER,
5066                                player->index_bit, leave_side);
5067
5068   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5069                                       CE_PLAYER_LEAVES_X,
5070                                       player->index_bit, leave_side);
5071
5072   Feld[jx][jy] = el_player;
5073   InitPlayerField(jx, jy, el_player, TRUE);
5074
5075   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5076      possible that the relocation target field did not contain a player element,
5077      but a walkable element, to which the new player was relocated -- in this
5078      case, restore that (already initialized!) element on the player field */
5079   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5080   {
5081     Feld[jx][jy] = element;     /* restore previously existing element */
5082   }
5083
5084   /* only visually relocate centered player */
5085   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5086                      FALSE, level.instant_relocation);
5087
5088   TestIfPlayerTouchesBadThing(jx, jy);
5089   TestIfPlayerTouchesCustomElement(jx, jy);
5090
5091   if (IS_CUSTOM_ELEMENT(element))
5092     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5093                                player->index_bit, enter_side);
5094
5095   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5096                                       player->index_bit, enter_side);
5097
5098   if (player->is_switching)
5099   {
5100     /* ensure that relocation while still switching an element does not cause
5101        a new element to be treated as also switched directly after relocation
5102        (this is important for teleporter switches that teleport the player to
5103        a place where another teleporter switch is in the same direction, which
5104        would then incorrectly be treated as immediately switched before the
5105        direction key that caused the switch was released) */
5106
5107     player->switch_x += jx - old_jx;
5108     player->switch_y += jy - old_jy;
5109   }
5110 }
5111
5112 void Explode(int ex, int ey, int phase, int mode)
5113 {
5114   int x, y;
5115   int last_phase;
5116   int border_element;
5117
5118   /* !!! eliminate this variable !!! */
5119   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5120
5121   if (game.explosions_delayed)
5122   {
5123     ExplodeField[ex][ey] = mode;
5124     return;
5125   }
5126
5127   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5128   {
5129     int center_element = Feld[ex][ey];
5130     int artwork_element, explosion_element;     /* set these values later */
5131
5132     /* remove things displayed in background while burning dynamite */
5133     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5134       Back[ex][ey] = 0;
5135
5136     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5137     {
5138       /* put moving element to center field (and let it explode there) */
5139       center_element = MovingOrBlocked2Element(ex, ey);
5140       RemoveMovingField(ex, ey);
5141       Feld[ex][ey] = center_element;
5142     }
5143
5144     /* now "center_element" is finally determined -- set related values now */
5145     artwork_element = center_element;           /* for custom player artwork */
5146     explosion_element = center_element;         /* for custom player artwork */
5147
5148     if (IS_PLAYER(ex, ey))
5149     {
5150       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5151
5152       artwork_element = stored_player[player_nr].artwork_element;
5153
5154       if (level.use_explosion_element[player_nr])
5155       {
5156         explosion_element = level.explosion_element[player_nr];
5157         artwork_element = explosion_element;
5158       }
5159     }
5160
5161     if (mode == EX_TYPE_NORMAL ||
5162         mode == EX_TYPE_CENTER ||
5163         mode == EX_TYPE_CROSS)
5164       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5165
5166     last_phase = element_info[explosion_element].explosion_delay + 1;
5167
5168     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5169     {
5170       int xx = x - ex + 1;
5171       int yy = y - ey + 1;
5172       int element;
5173
5174       if (!IN_LEV_FIELD(x, y) ||
5175           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5176           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5177         continue;
5178
5179       element = Feld[x][y];
5180
5181       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5182       {
5183         element = MovingOrBlocked2Element(x, y);
5184
5185         if (!IS_EXPLOSION_PROOF(element))
5186           RemoveMovingField(x, y);
5187       }
5188
5189       /* indestructible elements can only explode in center (but not flames) */
5190       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5191                                            mode == EX_TYPE_BORDER)) ||
5192           element == EL_FLAMES)
5193         continue;
5194
5195       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5196          behaviour, for example when touching a yamyam that explodes to rocks
5197          with active deadly shield, a rock is created under the player !!! */
5198       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5199 #if 0
5200       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5201           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5202            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5203 #else
5204       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5205 #endif
5206       {
5207         if (IS_ACTIVE_BOMB(element))
5208         {
5209           /* re-activate things under the bomb like gate or penguin */
5210           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5211           Back[x][y] = 0;
5212         }
5213
5214         continue;
5215       }
5216
5217       /* save walkable background elements while explosion on same tile */
5218       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5219           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5220         Back[x][y] = element;
5221
5222       /* ignite explodable elements reached by other explosion */
5223       if (element == EL_EXPLOSION)
5224         element = Store2[x][y];
5225
5226       if (AmoebaNr[x][y] &&
5227           (element == EL_AMOEBA_FULL ||
5228            element == EL_BD_AMOEBA ||
5229            element == EL_AMOEBA_GROWING))
5230       {
5231         AmoebaCnt[AmoebaNr[x][y]]--;
5232         AmoebaCnt2[AmoebaNr[x][y]]--;
5233       }
5234
5235       RemoveField(x, y);
5236
5237       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5238       {
5239         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5240
5241         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5242
5243         if (PLAYERINFO(ex, ey)->use_murphy)
5244           Store[x][y] = EL_EMPTY;
5245       }
5246
5247       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5248          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5249       else if (ELEM_IS_PLAYER(center_element))
5250         Store[x][y] = EL_EMPTY;
5251       else if (center_element == EL_YAMYAM)
5252         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5253       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5254         Store[x][y] = element_info[center_element].content.e[xx][yy];
5255 #if 1
5256       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5257          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5258          otherwise) -- FIX THIS !!! */
5259       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5260         Store[x][y] = element_info[element].content.e[1][1];
5261 #else
5262       else if (!CAN_EXPLODE(element))
5263         Store[x][y] = element_info[element].content.e[1][1];
5264 #endif
5265       else
5266         Store[x][y] = EL_EMPTY;
5267
5268       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5269           center_element == EL_AMOEBA_TO_DIAMOND)
5270         Store2[x][y] = element;
5271
5272       Feld[x][y] = EL_EXPLOSION;
5273       GfxElement[x][y] = artwork_element;
5274
5275       ExplodePhase[x][y] = 1;
5276       ExplodeDelay[x][y] = last_phase;
5277
5278       Stop[x][y] = TRUE;
5279     }
5280
5281     if (center_element == EL_YAMYAM)
5282       game.yamyam_content_nr =
5283         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5284
5285     return;
5286   }
5287
5288   if (Stop[ex][ey])
5289     return;
5290
5291   x = ex;
5292   y = ey;
5293
5294   if (phase == 1)
5295     GfxFrame[x][y] = 0;         /* restart explosion animation */
5296
5297   last_phase = ExplodeDelay[x][y];
5298
5299   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5300
5301   /* this can happen if the player leaves an explosion just in time */
5302   if (GfxElement[x][y] == EL_UNDEFINED)
5303     GfxElement[x][y] = EL_EMPTY;
5304
5305   border_element = Store2[x][y];
5306   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5307     border_element = StorePlayer[x][y];
5308
5309   if (phase == element_info[border_element].ignition_delay ||
5310       phase == last_phase)
5311   {
5312     boolean border_explosion = FALSE;
5313
5314     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5315         !PLAYER_EXPLOSION_PROTECTED(x, y))
5316     {
5317       KillPlayerUnlessExplosionProtected(x, y);
5318       border_explosion = TRUE;
5319     }
5320     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5321     {
5322       Feld[x][y] = Store2[x][y];
5323       Store2[x][y] = 0;
5324       Bang(x, y);
5325       border_explosion = TRUE;
5326     }
5327     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5328     {
5329       AmoebeUmwandeln(x, y);
5330       Store2[x][y] = 0;
5331       border_explosion = TRUE;
5332     }
5333
5334     /* if an element just explodes due to another explosion (chain-reaction),
5335        do not immediately end the new explosion when it was the last frame of
5336        the explosion (as it would be done in the following "if"-statement!) */
5337     if (border_explosion && phase == last_phase)
5338       return;
5339   }
5340
5341   if (phase == last_phase)
5342   {
5343     int element;
5344
5345     element = Feld[x][y] = Store[x][y];
5346     Store[x][y] = Store2[x][y] = 0;
5347     GfxElement[x][y] = EL_UNDEFINED;
5348
5349     /* player can escape from explosions and might therefore be still alive */
5350     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5351         element <= EL_PLAYER_IS_EXPLODING_4)
5352     {
5353       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5354       int explosion_element = EL_PLAYER_1 + player_nr;
5355       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5356       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5357
5358       if (level.use_explosion_element[player_nr])
5359         explosion_element = level.explosion_element[player_nr];
5360
5361       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5362                     element_info[explosion_element].content.e[xx][yy]);
5363     }
5364
5365     /* restore probably existing indestructible background element */
5366     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5367       element = Feld[x][y] = Back[x][y];
5368     Back[x][y] = 0;
5369
5370     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5371     GfxDir[x][y] = MV_NONE;
5372     ChangeDelay[x][y] = 0;
5373     ChangePage[x][y] = -1;
5374
5375     CustomValue[x][y] = 0;
5376
5377     InitField_WithBug2(x, y, FALSE);
5378
5379     TEST_DrawLevelField(x, y);
5380
5381     TestIfElementTouchesCustomElement(x, y);
5382
5383     if (GFX_CRUMBLED(element))
5384       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5385
5386     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5387       StorePlayer[x][y] = 0;
5388
5389     if (ELEM_IS_PLAYER(element))
5390       RelocatePlayer(x, y, element);
5391   }
5392   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5393   {
5394     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5395     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5396
5397     if (phase == delay)
5398       TEST_DrawLevelFieldCrumbled(x, y);
5399
5400     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5401     {
5402       DrawLevelElement(x, y, Back[x][y]);
5403       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5404     }
5405     else if (IS_WALKABLE_UNDER(Back[x][y]))
5406     {
5407       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5408       DrawLevelElementThruMask(x, y, Back[x][y]);
5409     }
5410     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5411       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5412   }
5413 }
5414
5415 void DynaExplode(int ex, int ey)
5416 {
5417   int i, j;
5418   int dynabomb_element = Feld[ex][ey];
5419   int dynabomb_size = 1;
5420   boolean dynabomb_xl = FALSE;
5421   struct PlayerInfo *player;
5422   static int xy[4][2] =
5423   {
5424     { 0, -1 },
5425     { -1, 0 },
5426     { +1, 0 },
5427     { 0, +1 }
5428   };
5429
5430   if (IS_ACTIVE_BOMB(dynabomb_element))
5431   {
5432     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5433     dynabomb_size = player->dynabomb_size;
5434     dynabomb_xl = player->dynabomb_xl;
5435     player->dynabombs_left++;
5436   }
5437
5438   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5439
5440   for (i = 0; i < NUM_DIRECTIONS; i++)
5441   {
5442     for (j = 1; j <= dynabomb_size; j++)
5443     {
5444       int x = ex + j * xy[i][0];
5445       int y = ey + j * xy[i][1];
5446       int element;
5447
5448       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5449         break;
5450
5451       element = Feld[x][y];
5452
5453       /* do not restart explosions of fields with active bombs */
5454       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5455         continue;
5456
5457       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5458
5459       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5460           !IS_DIGGABLE(element) && !dynabomb_xl)
5461         break;
5462     }
5463   }
5464 }
5465
5466 void Bang(int x, int y)
5467 {
5468   int element = MovingOrBlocked2Element(x, y);
5469   int explosion_type = EX_TYPE_NORMAL;
5470
5471   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5472   {
5473     struct PlayerInfo *player = PLAYERINFO(x, y);
5474
5475     element = Feld[x][y] = player->initial_element;
5476
5477     if (level.use_explosion_element[player->index_nr])
5478     {
5479       int explosion_element = level.explosion_element[player->index_nr];
5480
5481       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5482         explosion_type = EX_TYPE_CROSS;
5483       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5484         explosion_type = EX_TYPE_CENTER;
5485     }
5486   }
5487
5488   switch (element)
5489   {
5490     case EL_BUG:
5491     case EL_SPACESHIP:
5492     case EL_BD_BUTTERFLY:
5493     case EL_BD_FIREFLY:
5494     case EL_YAMYAM:
5495     case EL_DARK_YAMYAM:
5496     case EL_ROBOT:
5497     case EL_PACMAN:
5498     case EL_MOLE:
5499       RaiseScoreElement(element);
5500       break;
5501
5502     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5503     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5504     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5505     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5506     case EL_DYNABOMB_INCREASE_NUMBER:
5507     case EL_DYNABOMB_INCREASE_SIZE:
5508     case EL_DYNABOMB_INCREASE_POWER:
5509       explosion_type = EX_TYPE_DYNA;
5510       break;
5511
5512     case EL_DC_LANDMINE:
5513       explosion_type = EX_TYPE_CENTER;
5514       break;
5515
5516     case EL_PENGUIN:
5517     case EL_LAMP:
5518     case EL_LAMP_ACTIVE:
5519     case EL_AMOEBA_TO_DIAMOND:
5520       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5521         explosion_type = EX_TYPE_CENTER;
5522       break;
5523
5524     default:
5525       if (element_info[element].explosion_type == EXPLODES_CROSS)
5526         explosion_type = EX_TYPE_CROSS;
5527       else if (element_info[element].explosion_type == EXPLODES_1X1)
5528         explosion_type = EX_TYPE_CENTER;
5529       break;
5530   }
5531
5532   if (explosion_type == EX_TYPE_DYNA)
5533     DynaExplode(x, y);
5534   else
5535     Explode(x, y, EX_PHASE_START, explosion_type);
5536
5537   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5538 }
5539
5540 void SplashAcid(int x, int y)
5541 {
5542   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5543       (!IN_LEV_FIELD(x - 1, y - 2) ||
5544        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5545     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5546
5547   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5548       (!IN_LEV_FIELD(x + 1, y - 2) ||
5549        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5550     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5551
5552   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5553 }
5554
5555 static void InitBeltMovement()
5556 {
5557   static int belt_base_element[4] =
5558   {
5559     EL_CONVEYOR_BELT_1_LEFT,
5560     EL_CONVEYOR_BELT_2_LEFT,
5561     EL_CONVEYOR_BELT_3_LEFT,
5562     EL_CONVEYOR_BELT_4_LEFT
5563   };
5564   static int belt_base_active_element[4] =
5565   {
5566     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5567     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5568     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5569     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5570   };
5571
5572   int x, y, i, j;
5573
5574   /* set frame order for belt animation graphic according to belt direction */
5575   for (i = 0; i < NUM_BELTS; i++)
5576   {
5577     int belt_nr = i;
5578
5579     for (j = 0; j < NUM_BELT_PARTS; j++)
5580     {
5581       int element = belt_base_active_element[belt_nr] + j;
5582       int graphic_1 = el2img(element);
5583       int graphic_2 = el2panelimg(element);
5584
5585       if (game.belt_dir[i] == MV_LEFT)
5586       {
5587         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5588         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5589       }
5590       else
5591       {
5592         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5593         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5594       }
5595     }
5596   }
5597
5598   SCAN_PLAYFIELD(x, y)
5599   {
5600     int element = Feld[x][y];
5601
5602     for (i = 0; i < NUM_BELTS; i++)
5603     {
5604       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5605       {
5606         int e_belt_nr = getBeltNrFromBeltElement(element);
5607         int belt_nr = i;
5608
5609         if (e_belt_nr == belt_nr)
5610         {
5611           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5612
5613           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5614         }
5615       }
5616     }
5617   }
5618 }
5619
5620 static void ToggleBeltSwitch(int x, int y)
5621 {
5622   static int belt_base_element[4] =
5623   {
5624     EL_CONVEYOR_BELT_1_LEFT,
5625     EL_CONVEYOR_BELT_2_LEFT,
5626     EL_CONVEYOR_BELT_3_LEFT,
5627     EL_CONVEYOR_BELT_4_LEFT
5628   };
5629   static int belt_base_active_element[4] =
5630   {
5631     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5632     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5633     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5634     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5635   };
5636   static int belt_base_switch_element[4] =
5637   {
5638     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5639     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5640     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5641     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5642   };
5643   static int belt_move_dir[4] =
5644   {
5645     MV_LEFT,
5646     MV_NONE,
5647     MV_RIGHT,
5648     MV_NONE,
5649   };
5650
5651   int element = Feld[x][y];
5652   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5653   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5654   int belt_dir = belt_move_dir[belt_dir_nr];
5655   int xx, yy, i;
5656
5657   if (!IS_BELT_SWITCH(element))
5658     return;
5659
5660   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5661   game.belt_dir[belt_nr] = belt_dir;
5662
5663   if (belt_dir_nr == 3)
5664     belt_dir_nr = 1;
5665
5666   /* set frame order for belt animation graphic according to belt direction */
5667   for (i = 0; i < NUM_BELT_PARTS; i++)
5668   {
5669     int element = belt_base_active_element[belt_nr] + i;
5670     int graphic_1 = el2img(element);
5671     int graphic_2 = el2panelimg(element);
5672
5673     if (belt_dir == MV_LEFT)
5674     {
5675       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5676       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5677     }
5678     else
5679     {
5680       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5681       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5682     }
5683   }
5684
5685   SCAN_PLAYFIELD(xx, yy)
5686   {
5687     int element = Feld[xx][yy];
5688
5689     if (IS_BELT_SWITCH(element))
5690     {
5691       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5692
5693       if (e_belt_nr == belt_nr)
5694       {
5695         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5696         TEST_DrawLevelField(xx, yy);
5697       }
5698     }
5699     else if (IS_BELT(element) && belt_dir != MV_NONE)
5700     {
5701       int e_belt_nr = getBeltNrFromBeltElement(element);
5702
5703       if (e_belt_nr == belt_nr)
5704       {
5705         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5706
5707         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5708         TEST_DrawLevelField(xx, yy);
5709       }
5710     }
5711     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5712     {
5713       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5714
5715       if (e_belt_nr == belt_nr)
5716       {
5717         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5718
5719         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5720         TEST_DrawLevelField(xx, yy);
5721       }
5722     }
5723   }
5724 }
5725
5726 static void ToggleSwitchgateSwitch(int x, int y)
5727 {
5728   int xx, yy;
5729
5730   game.switchgate_pos = !game.switchgate_pos;
5731
5732   SCAN_PLAYFIELD(xx, yy)
5733   {
5734     int element = Feld[xx][yy];
5735
5736     if (element == EL_SWITCHGATE_SWITCH_UP)
5737     {
5738       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5739       TEST_DrawLevelField(xx, yy);
5740     }
5741     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5742     {
5743       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5744       TEST_DrawLevelField(xx, yy);
5745     }
5746     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5747     {
5748       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5749       TEST_DrawLevelField(xx, yy);
5750     }
5751     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5752     {
5753       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5754       TEST_DrawLevelField(xx, yy);
5755     }
5756     else if (element == EL_SWITCHGATE_OPEN ||
5757              element == EL_SWITCHGATE_OPENING)
5758     {
5759       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5760
5761       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5762     }
5763     else if (element == EL_SWITCHGATE_CLOSED ||
5764              element == EL_SWITCHGATE_CLOSING)
5765     {
5766       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5767
5768       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5769     }
5770   }
5771 }
5772
5773 static int getInvisibleActiveFromInvisibleElement(int element)
5774 {
5775   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5776           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5777           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5778           element);
5779 }
5780
5781 static int getInvisibleFromInvisibleActiveElement(int element)
5782 {
5783   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5784           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5785           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5786           element);
5787 }
5788
5789 static void RedrawAllLightSwitchesAndInvisibleElements()
5790 {
5791   int x, y;
5792
5793   SCAN_PLAYFIELD(x, y)
5794   {
5795     int element = Feld[x][y];
5796
5797     if (element == EL_LIGHT_SWITCH &&
5798         game.light_time_left > 0)
5799     {
5800       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5801       TEST_DrawLevelField(x, y);
5802     }
5803     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5804              game.light_time_left == 0)
5805     {
5806       Feld[x][y] = EL_LIGHT_SWITCH;
5807       TEST_DrawLevelField(x, y);
5808     }
5809     else if (element == EL_EMC_DRIPPER &&
5810              game.light_time_left > 0)
5811     {
5812       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5813       TEST_DrawLevelField(x, y);
5814     }
5815     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5816              game.light_time_left == 0)
5817     {
5818       Feld[x][y] = EL_EMC_DRIPPER;
5819       TEST_DrawLevelField(x, y);
5820     }
5821     else if (element == EL_INVISIBLE_STEELWALL ||
5822              element == EL_INVISIBLE_WALL ||
5823              element == EL_INVISIBLE_SAND)
5824     {
5825       if (game.light_time_left > 0)
5826         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5827
5828       TEST_DrawLevelField(x, y);
5829
5830       /* uncrumble neighbour fields, if needed */
5831       if (element == EL_INVISIBLE_SAND)
5832         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5833     }
5834     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5835              element == EL_INVISIBLE_WALL_ACTIVE ||
5836              element == EL_INVISIBLE_SAND_ACTIVE)
5837     {
5838       if (game.light_time_left == 0)
5839         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5840
5841       TEST_DrawLevelField(x, y);
5842
5843       /* re-crumble neighbour fields, if needed */
5844       if (element == EL_INVISIBLE_SAND)
5845         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5846     }
5847   }
5848 }
5849
5850 static void RedrawAllInvisibleElementsForLenses()
5851 {
5852   int x, y;
5853
5854   SCAN_PLAYFIELD(x, y)
5855   {
5856     int element = Feld[x][y];
5857
5858     if (element == EL_EMC_DRIPPER &&
5859         game.lenses_time_left > 0)
5860     {
5861       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5862       TEST_DrawLevelField(x, y);
5863     }
5864     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5865              game.lenses_time_left == 0)
5866     {
5867       Feld[x][y] = EL_EMC_DRIPPER;
5868       TEST_DrawLevelField(x, y);
5869     }
5870     else if (element == EL_INVISIBLE_STEELWALL ||
5871              element == EL_INVISIBLE_WALL ||
5872              element == EL_INVISIBLE_SAND)
5873     {
5874       if (game.lenses_time_left > 0)
5875         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5876
5877       TEST_DrawLevelField(x, y);
5878
5879       /* uncrumble neighbour fields, if needed */
5880       if (element == EL_INVISIBLE_SAND)
5881         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5882     }
5883     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5884              element == EL_INVISIBLE_WALL_ACTIVE ||
5885              element == EL_INVISIBLE_SAND_ACTIVE)
5886     {
5887       if (game.lenses_time_left == 0)
5888         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5889
5890       TEST_DrawLevelField(x, y);
5891
5892       /* re-crumble neighbour fields, if needed */
5893       if (element == EL_INVISIBLE_SAND)
5894         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5895     }
5896   }
5897 }
5898
5899 static void RedrawAllInvisibleElementsForMagnifier()
5900 {
5901   int x, y;
5902
5903   SCAN_PLAYFIELD(x, y)
5904   {
5905     int element = Feld[x][y];
5906
5907     if (element == EL_EMC_FAKE_GRASS &&
5908         game.magnify_time_left > 0)
5909     {
5910       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5911       TEST_DrawLevelField(x, y);
5912     }
5913     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5914              game.magnify_time_left == 0)
5915     {
5916       Feld[x][y] = EL_EMC_FAKE_GRASS;
5917       TEST_DrawLevelField(x, y);
5918     }
5919     else if (IS_GATE_GRAY(element) &&
5920              game.magnify_time_left > 0)
5921     {
5922       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5923                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5924                     IS_EM_GATE_GRAY(element) ?
5925                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5926                     IS_EMC_GATE_GRAY(element) ?
5927                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5928                     IS_DC_GATE_GRAY(element) ?
5929                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5930                     element);
5931       TEST_DrawLevelField(x, y);
5932     }
5933     else if (IS_GATE_GRAY_ACTIVE(element) &&
5934              game.magnify_time_left == 0)
5935     {
5936       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5937                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5938                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5939                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5940                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5941                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5942                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5943                     EL_DC_GATE_WHITE_GRAY :
5944                     element);
5945       TEST_DrawLevelField(x, y);
5946     }
5947   }
5948 }
5949
5950 static void ToggleLightSwitch(int x, int y)
5951 {
5952   int element = Feld[x][y];
5953
5954   game.light_time_left =
5955     (element == EL_LIGHT_SWITCH ?
5956      level.time_light * FRAMES_PER_SECOND : 0);
5957
5958   RedrawAllLightSwitchesAndInvisibleElements();
5959 }
5960
5961 static void ActivateTimegateSwitch(int x, int y)
5962 {
5963   int xx, yy;
5964
5965   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5966
5967   SCAN_PLAYFIELD(xx, yy)
5968   {
5969     int element = Feld[xx][yy];
5970
5971     if (element == EL_TIMEGATE_CLOSED ||
5972         element == EL_TIMEGATE_CLOSING)
5973     {
5974       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5975       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5976     }
5977
5978     /*
5979     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5980     {
5981       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5982       TEST_DrawLevelField(xx, yy);
5983     }
5984     */
5985
5986   }
5987
5988   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5989                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5990 }
5991
5992 void Impact(int x, int y)
5993 {
5994   boolean last_line = (y == lev_fieldy - 1);
5995   boolean object_hit = FALSE;
5996   boolean impact = (last_line || object_hit);
5997   int element = Feld[x][y];
5998   int smashed = EL_STEELWALL;
5999
6000   if (!last_line)       /* check if element below was hit */
6001   {
6002     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6003       return;
6004
6005     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6006                                          MovDir[x][y + 1] != MV_DOWN ||
6007                                          MovPos[x][y + 1] <= TILEY / 2));
6008
6009     /* do not smash moving elements that left the smashed field in time */
6010     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6011         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6012       object_hit = FALSE;
6013
6014 #if USE_QUICKSAND_IMPACT_BUGFIX
6015     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6016     {
6017       RemoveMovingField(x, y + 1);
6018       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6019       Feld[x][y + 2] = EL_ROCK;
6020       TEST_DrawLevelField(x, y + 2);
6021
6022       object_hit = TRUE;
6023     }
6024
6025     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6026     {
6027       RemoveMovingField(x, y + 1);
6028       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6029       Feld[x][y + 2] = EL_ROCK;
6030       TEST_DrawLevelField(x, y + 2);
6031
6032       object_hit = TRUE;
6033     }
6034 #endif
6035
6036     if (object_hit)
6037       smashed = MovingOrBlocked2Element(x, y + 1);
6038
6039     impact = (last_line || object_hit);
6040   }
6041
6042   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6043   {
6044     SplashAcid(x, y + 1);
6045     return;
6046   }
6047
6048   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6049   /* only reset graphic animation if graphic really changes after impact */
6050   if (impact &&
6051       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6052   {
6053     ResetGfxAnimation(x, y);
6054     TEST_DrawLevelField(x, y);
6055   }
6056
6057   if (impact && CAN_EXPLODE_IMPACT(element))
6058   {
6059     Bang(x, y);
6060     return;
6061   }
6062   else if (impact && element == EL_PEARL &&
6063            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6064   {
6065     ResetGfxAnimation(x, y);
6066
6067     Feld[x][y] = EL_PEARL_BREAKING;
6068     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6069     return;
6070   }
6071   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6072   {
6073     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6074
6075     return;
6076   }
6077
6078   if (impact && element == EL_AMOEBA_DROP)
6079   {
6080     if (object_hit && IS_PLAYER(x, y + 1))
6081       KillPlayerUnlessEnemyProtected(x, y + 1);
6082     else if (object_hit && smashed == EL_PENGUIN)
6083       Bang(x, y + 1);
6084     else
6085     {
6086       Feld[x][y] = EL_AMOEBA_GROWING;
6087       Store[x][y] = EL_AMOEBA_WET;
6088
6089       ResetRandomAnimationValue(x, y);
6090     }
6091     return;
6092   }
6093
6094   if (object_hit)               /* check which object was hit */
6095   {
6096     if ((CAN_PASS_MAGIC_WALL(element) && 
6097          (smashed == EL_MAGIC_WALL ||
6098           smashed == EL_BD_MAGIC_WALL)) ||
6099         (CAN_PASS_DC_MAGIC_WALL(element) &&
6100          smashed == EL_DC_MAGIC_WALL))
6101     {
6102       int xx, yy;
6103       int activated_magic_wall =
6104         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6105          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6106          EL_DC_MAGIC_WALL_ACTIVE);
6107
6108       /* activate magic wall / mill */
6109       SCAN_PLAYFIELD(xx, yy)
6110       {
6111         if (Feld[xx][yy] == smashed)
6112           Feld[xx][yy] = activated_magic_wall;
6113       }
6114
6115       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6116       game.magic_wall_active = TRUE;
6117
6118       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6119                             SND_MAGIC_WALL_ACTIVATING :
6120                             smashed == EL_BD_MAGIC_WALL ?
6121                             SND_BD_MAGIC_WALL_ACTIVATING :
6122                             SND_DC_MAGIC_WALL_ACTIVATING));
6123     }
6124
6125     if (IS_PLAYER(x, y + 1))
6126     {
6127       if (CAN_SMASH_PLAYER(element))
6128       {
6129         KillPlayerUnlessEnemyProtected(x, y + 1);
6130         return;
6131       }
6132     }
6133     else if (smashed == EL_PENGUIN)
6134     {
6135       if (CAN_SMASH_PLAYER(element))
6136       {
6137         Bang(x, y + 1);
6138         return;
6139       }
6140     }
6141     else if (element == EL_BD_DIAMOND)
6142     {
6143       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6144       {
6145         Bang(x, y + 1);
6146         return;
6147       }
6148     }
6149     else if (((element == EL_SP_INFOTRON ||
6150                element == EL_SP_ZONK) &&
6151               (smashed == EL_SP_SNIKSNAK ||
6152                smashed == EL_SP_ELECTRON ||
6153                smashed == EL_SP_DISK_ORANGE)) ||
6154              (element == EL_SP_INFOTRON &&
6155               smashed == EL_SP_DISK_YELLOW))
6156     {
6157       Bang(x, y + 1);
6158       return;
6159     }
6160     else if (CAN_SMASH_EVERYTHING(element))
6161     {
6162       if (IS_CLASSIC_ENEMY(smashed) ||
6163           CAN_EXPLODE_SMASHED(smashed))
6164       {
6165         Bang(x, y + 1);
6166         return;
6167       }
6168       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6169       {
6170         if (smashed == EL_LAMP ||
6171             smashed == EL_LAMP_ACTIVE)
6172         {
6173           Bang(x, y + 1);
6174           return;
6175         }
6176         else if (smashed == EL_NUT)
6177         {
6178           Feld[x][y + 1] = EL_NUT_BREAKING;
6179           PlayLevelSound(x, y, SND_NUT_BREAKING);
6180           RaiseScoreElement(EL_NUT);
6181           return;
6182         }
6183         else if (smashed == EL_PEARL)
6184         {
6185           ResetGfxAnimation(x, y);
6186
6187           Feld[x][y + 1] = EL_PEARL_BREAKING;
6188           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6189           return;
6190         }
6191         else if (smashed == EL_DIAMOND)
6192         {
6193           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6194           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6195           return;
6196         }
6197         else if (IS_BELT_SWITCH(smashed))
6198         {
6199           ToggleBeltSwitch(x, y + 1);
6200         }
6201         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6202                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6203                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6204                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6205         {
6206           ToggleSwitchgateSwitch(x, y + 1);
6207         }
6208         else if (smashed == EL_LIGHT_SWITCH ||
6209                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6210         {
6211           ToggleLightSwitch(x, y + 1);
6212         }
6213         else
6214         {
6215           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6216
6217           CheckElementChangeBySide(x, y + 1, smashed, element,
6218                                    CE_SWITCHED, CH_SIDE_TOP);
6219           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6220                                             CH_SIDE_TOP);
6221         }
6222       }
6223       else
6224       {
6225         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6226       }
6227     }
6228   }
6229
6230   /* play sound of magic wall / mill */
6231   if (!last_line &&
6232       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6233        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6234        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6235   {
6236     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6237       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6238     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6239       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6240     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6241       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6242
6243     return;
6244   }
6245
6246   /* play sound of object that hits the ground */
6247   if (last_line || object_hit)
6248     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6249 }
6250
6251 inline static void TurnRoundExt(int x, int y)
6252 {
6253   static struct
6254   {
6255     int dx, dy;
6256   } move_xy[] =
6257   {
6258     {  0,  0 },
6259     { -1,  0 },
6260     { +1,  0 },
6261     {  0,  0 },
6262     {  0, -1 },
6263     {  0,  0 }, { 0, 0 }, { 0, 0 },
6264     {  0, +1 }
6265   };
6266   static struct
6267   {
6268     int left, right, back;
6269   } turn[] =
6270   {
6271     { 0,        0,              0        },
6272     { MV_DOWN,  MV_UP,          MV_RIGHT },
6273     { MV_UP,    MV_DOWN,        MV_LEFT  },
6274     { 0,        0,              0        },
6275     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6276     { 0,        0,              0        },
6277     { 0,        0,              0        },
6278     { 0,        0,              0        },
6279     { MV_RIGHT, MV_LEFT,        MV_UP    }
6280   };
6281
6282   int element = Feld[x][y];
6283   int move_pattern = element_info[element].move_pattern;
6284
6285   int old_move_dir = MovDir[x][y];
6286   int left_dir  = turn[old_move_dir].left;
6287   int right_dir = turn[old_move_dir].right;
6288   int back_dir  = turn[old_move_dir].back;
6289
6290   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6291   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6292   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6293   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6294
6295   int left_x  = x + left_dx,  left_y  = y + left_dy;
6296   int right_x = x + right_dx, right_y = y + right_dy;
6297   int move_x  = x + move_dx,  move_y  = y + move_dy;
6298
6299   int xx, yy;
6300
6301   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6302   {
6303     TestIfBadThingTouchesOtherBadThing(x, y);
6304
6305     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6306       MovDir[x][y] = right_dir;
6307     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6308       MovDir[x][y] = left_dir;
6309
6310     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6311       MovDelay[x][y] = 9;
6312     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6313       MovDelay[x][y] = 1;
6314   }
6315   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6316   {
6317     TestIfBadThingTouchesOtherBadThing(x, y);
6318
6319     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6320       MovDir[x][y] = left_dir;
6321     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6322       MovDir[x][y] = right_dir;
6323
6324     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6325       MovDelay[x][y] = 9;
6326     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6327       MovDelay[x][y] = 1;
6328   }
6329   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6330   {
6331     TestIfBadThingTouchesOtherBadThing(x, y);
6332
6333     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6334       MovDir[x][y] = left_dir;
6335     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6336       MovDir[x][y] = right_dir;
6337
6338     if (MovDir[x][y] != old_move_dir)
6339       MovDelay[x][y] = 9;
6340   }
6341   else if (element == EL_YAMYAM)
6342   {
6343     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6344     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6345
6346     if (can_turn_left && can_turn_right)
6347       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6348     else if (can_turn_left)
6349       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6350     else if (can_turn_right)
6351       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6352     else
6353       MovDir[x][y] = back_dir;
6354
6355     MovDelay[x][y] = 16 + 16 * RND(3);
6356   }
6357   else if (element == EL_DARK_YAMYAM)
6358   {
6359     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6360                                                          left_x, left_y);
6361     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6362                                                          right_x, right_y);
6363
6364     if (can_turn_left && can_turn_right)
6365       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6366     else if (can_turn_left)
6367       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6368     else if (can_turn_right)
6369       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6370     else
6371       MovDir[x][y] = back_dir;
6372
6373     MovDelay[x][y] = 16 + 16 * RND(3);
6374   }
6375   else if (element == EL_PACMAN)
6376   {
6377     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6378     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6379
6380     if (can_turn_left && can_turn_right)
6381       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6382     else if (can_turn_left)
6383       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6384     else if (can_turn_right)
6385       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6386     else
6387       MovDir[x][y] = back_dir;
6388
6389     MovDelay[x][y] = 6 + RND(40);
6390   }
6391   else if (element == EL_PIG)
6392   {
6393     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6394     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6395     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6396     boolean should_turn_left, should_turn_right, should_move_on;
6397     int rnd_value = 24;
6398     int rnd = RND(rnd_value);
6399
6400     should_turn_left = (can_turn_left &&
6401                         (!can_move_on ||
6402                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6403                                                    y + back_dy + left_dy)));
6404     should_turn_right = (can_turn_right &&
6405                          (!can_move_on ||
6406                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6407                                                     y + back_dy + right_dy)));
6408     should_move_on = (can_move_on &&
6409                       (!can_turn_left ||
6410                        !can_turn_right ||
6411                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6412                                                  y + move_dy + left_dy) ||
6413                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6414                                                  y + move_dy + right_dy)));
6415
6416     if (should_turn_left || should_turn_right || should_move_on)
6417     {
6418       if (should_turn_left && should_turn_right && should_move_on)
6419         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6420                         rnd < 2 * rnd_value / 3 ? right_dir :
6421                         old_move_dir);
6422       else if (should_turn_left && should_turn_right)
6423         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6424       else if (should_turn_left && should_move_on)
6425         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6426       else if (should_turn_right && should_move_on)
6427         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6428       else if (should_turn_left)
6429         MovDir[x][y] = left_dir;
6430       else if (should_turn_right)
6431         MovDir[x][y] = right_dir;
6432       else if (should_move_on)
6433         MovDir[x][y] = old_move_dir;
6434     }
6435     else if (can_move_on && rnd > rnd_value / 8)
6436       MovDir[x][y] = old_move_dir;
6437     else if (can_turn_left && can_turn_right)
6438       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6439     else if (can_turn_left && rnd > rnd_value / 8)
6440       MovDir[x][y] = left_dir;
6441     else if (can_turn_right && rnd > rnd_value/8)
6442       MovDir[x][y] = right_dir;
6443     else
6444       MovDir[x][y] = back_dir;
6445
6446     xx = x + move_xy[MovDir[x][y]].dx;
6447     yy = y + move_xy[MovDir[x][y]].dy;
6448
6449     if (!IN_LEV_FIELD(xx, yy) ||
6450         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6451       MovDir[x][y] = old_move_dir;
6452
6453     MovDelay[x][y] = 0;
6454   }
6455   else if (element == EL_DRAGON)
6456   {
6457     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6458     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6459     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6460     int rnd_value = 24;
6461     int rnd = RND(rnd_value);
6462
6463     if (can_move_on && rnd > rnd_value / 8)
6464       MovDir[x][y] = old_move_dir;
6465     else if (can_turn_left && can_turn_right)
6466       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6467     else if (can_turn_left && rnd > rnd_value / 8)
6468       MovDir[x][y] = left_dir;
6469     else if (can_turn_right && rnd > rnd_value / 8)
6470       MovDir[x][y] = right_dir;
6471     else
6472       MovDir[x][y] = back_dir;
6473
6474     xx = x + move_xy[MovDir[x][y]].dx;
6475     yy = y + move_xy[MovDir[x][y]].dy;
6476
6477     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6478       MovDir[x][y] = old_move_dir;
6479
6480     MovDelay[x][y] = 0;
6481   }
6482   else if (element == EL_MOLE)
6483   {
6484     boolean can_move_on =
6485       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6486                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6487                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6488     if (!can_move_on)
6489     {
6490       boolean can_turn_left =
6491         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6492                               IS_AMOEBOID(Feld[left_x][left_y])));
6493
6494       boolean can_turn_right =
6495         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6496                               IS_AMOEBOID(Feld[right_x][right_y])));
6497
6498       if (can_turn_left && can_turn_right)
6499         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6500       else if (can_turn_left)
6501         MovDir[x][y] = left_dir;
6502       else
6503         MovDir[x][y] = right_dir;
6504     }
6505
6506     if (MovDir[x][y] != old_move_dir)
6507       MovDelay[x][y] = 9;
6508   }
6509   else if (element == EL_BALLOON)
6510   {
6511     MovDir[x][y] = game.wind_direction;
6512     MovDelay[x][y] = 0;
6513   }
6514   else if (element == EL_SPRING)
6515   {
6516     if (MovDir[x][y] & MV_HORIZONTAL)
6517     {
6518       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6519           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6520       {
6521         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6522         ResetGfxAnimation(move_x, move_y);
6523         TEST_DrawLevelField(move_x, move_y);
6524
6525         MovDir[x][y] = back_dir;
6526       }
6527       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6528                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6529         MovDir[x][y] = MV_NONE;
6530     }
6531
6532     MovDelay[x][y] = 0;
6533   }
6534   else if (element == EL_ROBOT ||
6535            element == EL_SATELLITE ||
6536            element == EL_PENGUIN ||
6537            element == EL_EMC_ANDROID)
6538   {
6539     int attr_x = -1, attr_y = -1;
6540
6541     if (AllPlayersGone)
6542     {
6543       attr_x = ExitX;
6544       attr_y = ExitY;
6545     }
6546     else
6547     {
6548       int i;
6549
6550       for (i = 0; i < MAX_PLAYERS; i++)
6551       {
6552         struct PlayerInfo *player = &stored_player[i];
6553         int jx = player->jx, jy = player->jy;
6554
6555         if (!player->active)
6556           continue;
6557
6558         if (attr_x == -1 ||
6559             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6560         {
6561           attr_x = jx;
6562           attr_y = jy;
6563         }
6564       }
6565     }
6566
6567     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6568         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6569          game.engine_version < VERSION_IDENT(3,1,0,0)))
6570     {
6571       attr_x = ZX;
6572       attr_y = ZY;
6573     }
6574
6575     if (element == EL_PENGUIN)
6576     {
6577       int i;
6578       static int xy[4][2] =
6579       {
6580         { 0, -1 },
6581         { -1, 0 },
6582         { +1, 0 },
6583         { 0, +1 }
6584       };
6585
6586       for (i = 0; i < NUM_DIRECTIONS; i++)
6587       {
6588         int ex = x + xy[i][0];
6589         int ey = y + xy[i][1];
6590
6591         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6592                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6593                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6594                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6595         {
6596           attr_x = ex;
6597           attr_y = ey;
6598           break;
6599         }
6600       }
6601     }
6602
6603     MovDir[x][y] = MV_NONE;
6604     if (attr_x < x)
6605       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6606     else if (attr_x > x)
6607       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6608     if (attr_y < y)
6609       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6610     else if (attr_y > y)
6611       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6612
6613     if (element == EL_ROBOT)
6614     {
6615       int newx, newy;
6616
6617       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6618         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6619       Moving2Blocked(x, y, &newx, &newy);
6620
6621       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6622         MovDelay[x][y] = 8 + 8 * !RND(3);
6623       else
6624         MovDelay[x][y] = 16;
6625     }
6626     else if (element == EL_PENGUIN)
6627     {
6628       int newx, newy;
6629
6630       MovDelay[x][y] = 1;
6631
6632       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6633       {
6634         boolean first_horiz = RND(2);
6635         int new_move_dir = MovDir[x][y];
6636
6637         MovDir[x][y] =
6638           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6639         Moving2Blocked(x, y, &newx, &newy);
6640
6641         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6642           return;
6643
6644         MovDir[x][y] =
6645           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6646         Moving2Blocked(x, y, &newx, &newy);
6647
6648         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6649           return;
6650
6651         MovDir[x][y] = old_move_dir;
6652         return;
6653       }
6654     }
6655     else if (element == EL_SATELLITE)
6656     {
6657       int newx, newy;
6658
6659       MovDelay[x][y] = 1;
6660
6661       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6662       {
6663         boolean first_horiz = RND(2);
6664         int new_move_dir = MovDir[x][y];
6665
6666         MovDir[x][y] =
6667           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6668         Moving2Blocked(x, y, &newx, &newy);
6669
6670         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6671           return;
6672
6673         MovDir[x][y] =
6674           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6675         Moving2Blocked(x, y, &newx, &newy);
6676
6677         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6678           return;
6679
6680         MovDir[x][y] = old_move_dir;
6681         return;
6682       }
6683     }
6684     else if (element == EL_EMC_ANDROID)
6685     {
6686       static int check_pos[16] =
6687       {
6688         -1,             /*  0 => (invalid)          */
6689         7,              /*  1 => MV_LEFT            */
6690         3,              /*  2 => MV_RIGHT           */
6691         -1,             /*  3 => (invalid)          */
6692         1,              /*  4 =>            MV_UP   */
6693         0,              /*  5 => MV_LEFT  | MV_UP   */
6694         2,              /*  6 => MV_RIGHT | MV_UP   */
6695         -1,             /*  7 => (invalid)          */
6696         5,              /*  8 =>            MV_DOWN */
6697         6,              /*  9 => MV_LEFT  | MV_DOWN */
6698         4,              /* 10 => MV_RIGHT | MV_DOWN */
6699         -1,             /* 11 => (invalid)          */
6700         -1,             /* 12 => (invalid)          */
6701         -1,             /* 13 => (invalid)          */
6702         -1,             /* 14 => (invalid)          */
6703         -1,             /* 15 => (invalid)          */
6704       };
6705       static struct
6706       {
6707         int dx, dy;
6708         int dir;
6709       } check_xy[8] =
6710       {
6711         { -1, -1,       MV_LEFT  | MV_UP   },
6712         {  0, -1,                  MV_UP   },
6713         { +1, -1,       MV_RIGHT | MV_UP   },
6714         { +1,  0,       MV_RIGHT           },
6715         { +1, +1,       MV_RIGHT | MV_DOWN },
6716         {  0, +1,                  MV_DOWN },
6717         { -1, +1,       MV_LEFT  | MV_DOWN },
6718         { -1,  0,       MV_LEFT            },
6719       };
6720       int start_pos, check_order;
6721       boolean can_clone = FALSE;
6722       int i;
6723
6724       /* check if there is any free field around current position */
6725       for (i = 0; i < 8; i++)
6726       {
6727         int newx = x + check_xy[i].dx;
6728         int newy = y + check_xy[i].dy;
6729
6730         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6731         {
6732           can_clone = TRUE;
6733
6734           break;
6735         }
6736       }
6737
6738       if (can_clone)            /* randomly find an element to clone */
6739       {
6740         can_clone = FALSE;
6741
6742         start_pos = check_pos[RND(8)];
6743         check_order = (RND(2) ? -1 : +1);
6744
6745         for (i = 0; i < 8; i++)
6746         {
6747           int pos_raw = start_pos + i * check_order;
6748           int pos = (pos_raw + 8) % 8;
6749           int newx = x + check_xy[pos].dx;
6750           int newy = y + check_xy[pos].dy;
6751
6752           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6753           {
6754             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6755             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6756
6757             Store[x][y] = Feld[newx][newy];
6758
6759             can_clone = TRUE;
6760
6761             break;
6762           }
6763         }
6764       }
6765
6766       if (can_clone)            /* randomly find a direction to move */
6767       {
6768         can_clone = FALSE;
6769
6770         start_pos = check_pos[RND(8)];
6771         check_order = (RND(2) ? -1 : +1);
6772
6773         for (i = 0; i < 8; i++)
6774         {
6775           int pos_raw = start_pos + i * check_order;
6776           int pos = (pos_raw + 8) % 8;
6777           int newx = x + check_xy[pos].dx;
6778           int newy = y + check_xy[pos].dy;
6779           int new_move_dir = check_xy[pos].dir;
6780
6781           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6782           {
6783             MovDir[x][y] = new_move_dir;
6784             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6785
6786             can_clone = TRUE;
6787
6788             break;
6789           }
6790         }
6791       }
6792
6793       if (can_clone)            /* cloning and moving successful */
6794         return;
6795
6796       /* cannot clone -- try to move towards player */
6797
6798       start_pos = check_pos[MovDir[x][y] & 0x0f];
6799       check_order = (RND(2) ? -1 : +1);
6800
6801       for (i = 0; i < 3; i++)
6802       {
6803         /* first check start_pos, then previous/next or (next/previous) pos */
6804         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6805         int pos = (pos_raw + 8) % 8;
6806         int newx = x + check_xy[pos].dx;
6807         int newy = y + check_xy[pos].dy;
6808         int new_move_dir = check_xy[pos].dir;
6809
6810         if (IS_PLAYER(newx, newy))
6811           break;
6812
6813         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6814         {
6815           MovDir[x][y] = new_move_dir;
6816           MovDelay[x][y] = level.android_move_time * 8 + 1;
6817
6818           break;
6819         }
6820       }
6821     }
6822   }
6823   else if (move_pattern == MV_TURNING_LEFT ||
6824            move_pattern == MV_TURNING_RIGHT ||
6825            move_pattern == MV_TURNING_LEFT_RIGHT ||
6826            move_pattern == MV_TURNING_RIGHT_LEFT ||
6827            move_pattern == MV_TURNING_RANDOM ||
6828            move_pattern == MV_ALL_DIRECTIONS)
6829   {
6830     boolean can_turn_left =
6831       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6832     boolean can_turn_right =
6833       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6834
6835     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6836       return;
6837
6838     if (move_pattern == MV_TURNING_LEFT)
6839       MovDir[x][y] = left_dir;
6840     else if (move_pattern == MV_TURNING_RIGHT)
6841       MovDir[x][y] = right_dir;
6842     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6843       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6844     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6845       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6846     else if (move_pattern == MV_TURNING_RANDOM)
6847       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6848                       can_turn_right && !can_turn_left ? right_dir :
6849                       RND(2) ? left_dir : right_dir);
6850     else if (can_turn_left && can_turn_right)
6851       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6852     else if (can_turn_left)
6853       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6854     else if (can_turn_right)
6855       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6856     else
6857       MovDir[x][y] = back_dir;
6858
6859     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6860   }
6861   else if (move_pattern == MV_HORIZONTAL ||
6862            move_pattern == MV_VERTICAL)
6863   {
6864     if (move_pattern & old_move_dir)
6865       MovDir[x][y] = back_dir;
6866     else if (move_pattern == MV_HORIZONTAL)
6867       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6868     else if (move_pattern == MV_VERTICAL)
6869       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6870
6871     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6872   }
6873   else if (move_pattern & MV_ANY_DIRECTION)
6874   {
6875     MovDir[x][y] = move_pattern;
6876     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6877   }
6878   else if (move_pattern & MV_WIND_DIRECTION)
6879   {
6880     MovDir[x][y] = game.wind_direction;
6881     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6882   }
6883   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6884   {
6885     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6886       MovDir[x][y] = left_dir;
6887     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6888       MovDir[x][y] = right_dir;
6889
6890     if (MovDir[x][y] != old_move_dir)
6891       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6892   }
6893   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6894   {
6895     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6896       MovDir[x][y] = right_dir;
6897     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6898       MovDir[x][y] = left_dir;
6899
6900     if (MovDir[x][y] != old_move_dir)
6901       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6902   }
6903   else if (move_pattern == MV_TOWARDS_PLAYER ||
6904            move_pattern == MV_AWAY_FROM_PLAYER)
6905   {
6906     int attr_x = -1, attr_y = -1;
6907     int newx, newy;
6908     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6909
6910     if (AllPlayersGone)
6911     {
6912       attr_x = ExitX;
6913       attr_y = ExitY;
6914     }
6915     else
6916     {
6917       int i;
6918
6919       for (i = 0; i < MAX_PLAYERS; i++)
6920       {
6921         struct PlayerInfo *player = &stored_player[i];
6922         int jx = player->jx, jy = player->jy;
6923
6924         if (!player->active)
6925           continue;
6926
6927         if (attr_x == -1 ||
6928             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6929         {
6930           attr_x = jx;
6931           attr_y = jy;
6932         }
6933       }
6934     }
6935
6936     MovDir[x][y] = MV_NONE;
6937     if (attr_x < x)
6938       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6939     else if (attr_x > x)
6940       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6941     if (attr_y < y)
6942       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6943     else if (attr_y > y)
6944       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6945
6946     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6947
6948     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6949     {
6950       boolean first_horiz = RND(2);
6951       int new_move_dir = MovDir[x][y];
6952
6953       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6954       {
6955         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6956         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6957
6958         return;
6959       }
6960
6961       MovDir[x][y] =
6962         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6963       Moving2Blocked(x, y, &newx, &newy);
6964
6965       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6966         return;
6967
6968       MovDir[x][y] =
6969         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6970       Moving2Blocked(x, y, &newx, &newy);
6971
6972       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6973         return;
6974
6975       MovDir[x][y] = old_move_dir;
6976     }
6977   }
6978   else if (move_pattern == MV_WHEN_PUSHED ||
6979            move_pattern == MV_WHEN_DROPPED)
6980   {
6981     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6982       MovDir[x][y] = MV_NONE;
6983
6984     MovDelay[x][y] = 0;
6985   }
6986   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6987   {
6988     static int test_xy[7][2] =
6989     {
6990       { 0, -1 },
6991       { -1, 0 },
6992       { +1, 0 },
6993       { 0, +1 },
6994       { 0, -1 },
6995       { -1, 0 },
6996       { +1, 0 },
6997     };
6998     static int test_dir[7] =
6999     {
7000       MV_UP,
7001       MV_LEFT,
7002       MV_RIGHT,
7003       MV_DOWN,
7004       MV_UP,
7005       MV_LEFT,
7006       MV_RIGHT,
7007     };
7008     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7009     int move_preference = -1000000;     /* start with very low preference */
7010     int new_move_dir = MV_NONE;
7011     int start_test = RND(4);
7012     int i;
7013
7014     for (i = 0; i < NUM_DIRECTIONS; i++)
7015     {
7016       int move_dir = test_dir[start_test + i];
7017       int move_dir_preference;
7018
7019       xx = x + test_xy[start_test + i][0];
7020       yy = y + test_xy[start_test + i][1];
7021
7022       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7023           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7024       {
7025         new_move_dir = move_dir;
7026
7027         break;
7028       }
7029
7030       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7031         continue;
7032
7033       move_dir_preference = -1 * RunnerVisit[xx][yy];
7034       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7035         move_dir_preference = PlayerVisit[xx][yy];
7036
7037       if (move_dir_preference > move_preference)
7038       {
7039         /* prefer field that has not been visited for the longest time */
7040         move_preference = move_dir_preference;
7041         new_move_dir = move_dir;
7042       }
7043       else if (move_dir_preference == move_preference &&
7044                move_dir == old_move_dir)
7045       {
7046         /* prefer last direction when all directions are preferred equally */
7047         move_preference = move_dir_preference;
7048         new_move_dir = move_dir;
7049       }
7050     }
7051
7052     MovDir[x][y] = new_move_dir;
7053     if (old_move_dir != new_move_dir)
7054       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7055   }
7056 }
7057
7058 static void TurnRound(int x, int y)
7059 {
7060   int direction = MovDir[x][y];
7061
7062   TurnRoundExt(x, y);
7063
7064   GfxDir[x][y] = MovDir[x][y];
7065
7066   if (direction != MovDir[x][y])
7067     GfxFrame[x][y] = 0;
7068
7069   if (MovDelay[x][y])
7070     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7071
7072   ResetGfxFrame(x, y, FALSE);
7073 }
7074
7075 static boolean JustBeingPushed(int x, int y)
7076 {
7077   int i;
7078
7079   for (i = 0; i < MAX_PLAYERS; i++)
7080   {
7081     struct PlayerInfo *player = &stored_player[i];
7082
7083     if (player->active && player->is_pushing && player->MovPos)
7084     {
7085       int next_jx = player->jx + (player->jx - player->last_jx);
7086       int next_jy = player->jy + (player->jy - player->last_jy);
7087
7088       if (x == next_jx && y == next_jy)
7089         return TRUE;
7090     }
7091   }
7092
7093   return FALSE;
7094 }
7095
7096 void StartMoving(int x, int y)
7097 {
7098   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7099   int element = Feld[x][y];
7100
7101   if (Stop[x][y])
7102     return;
7103
7104   if (MovDelay[x][y] == 0)
7105     GfxAction[x][y] = ACTION_DEFAULT;
7106
7107   if (CAN_FALL(element) && y < lev_fieldy - 1)
7108   {
7109     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7110         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7111       if (JustBeingPushed(x, y))
7112         return;
7113
7114     if (element == EL_QUICKSAND_FULL)
7115     {
7116       if (IS_FREE(x, y + 1))
7117       {
7118         InitMovingField(x, y, MV_DOWN);
7119         started_moving = TRUE;
7120
7121         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7122 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7123         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7124           Store[x][y] = EL_ROCK;
7125 #else
7126         Store[x][y] = EL_ROCK;
7127 #endif
7128
7129         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7130       }
7131       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7132       {
7133         if (!MovDelay[x][y])
7134         {
7135           MovDelay[x][y] = TILEY + 1;
7136
7137           ResetGfxAnimation(x, y);
7138           ResetGfxAnimation(x, y + 1);
7139         }
7140
7141         if (MovDelay[x][y])
7142         {
7143           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7144           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7145
7146           MovDelay[x][y]--;
7147           if (MovDelay[x][y])
7148             return;
7149         }
7150
7151         Feld[x][y] = EL_QUICKSAND_EMPTY;
7152         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7153         Store[x][y + 1] = Store[x][y];
7154         Store[x][y] = 0;
7155
7156         PlayLevelSoundAction(x, y, ACTION_FILLING);
7157       }
7158       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7159       {
7160         if (!MovDelay[x][y])
7161         {
7162           MovDelay[x][y] = TILEY + 1;
7163
7164           ResetGfxAnimation(x, y);
7165           ResetGfxAnimation(x, y + 1);
7166         }
7167
7168         if (MovDelay[x][y])
7169         {
7170           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7171           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7172
7173           MovDelay[x][y]--;
7174           if (MovDelay[x][y])
7175             return;
7176         }
7177
7178         Feld[x][y] = EL_QUICKSAND_EMPTY;
7179         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7180         Store[x][y + 1] = Store[x][y];
7181         Store[x][y] = 0;
7182
7183         PlayLevelSoundAction(x, y, ACTION_FILLING);
7184       }
7185     }
7186     else if (element == EL_QUICKSAND_FAST_FULL)
7187     {
7188       if (IS_FREE(x, y + 1))
7189       {
7190         InitMovingField(x, y, MV_DOWN);
7191         started_moving = TRUE;
7192
7193         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7194 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7195         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7196           Store[x][y] = EL_ROCK;
7197 #else
7198         Store[x][y] = EL_ROCK;
7199 #endif
7200
7201         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7202       }
7203       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7204       {
7205         if (!MovDelay[x][y])
7206         {
7207           MovDelay[x][y] = TILEY + 1;
7208
7209           ResetGfxAnimation(x, y);
7210           ResetGfxAnimation(x, y + 1);
7211         }
7212
7213         if (MovDelay[x][y])
7214         {
7215           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7216           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7217
7218           MovDelay[x][y]--;
7219           if (MovDelay[x][y])
7220             return;
7221         }
7222
7223         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7224         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7225         Store[x][y + 1] = Store[x][y];
7226         Store[x][y] = 0;
7227
7228         PlayLevelSoundAction(x, y, ACTION_FILLING);
7229       }
7230       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7231       {
7232         if (!MovDelay[x][y])
7233         {
7234           MovDelay[x][y] = TILEY + 1;
7235
7236           ResetGfxAnimation(x, y);
7237           ResetGfxAnimation(x, y + 1);
7238         }
7239
7240         if (MovDelay[x][y])
7241         {
7242           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7243           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7244
7245           MovDelay[x][y]--;
7246           if (MovDelay[x][y])
7247             return;
7248         }
7249
7250         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7251         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7252         Store[x][y + 1] = Store[x][y];
7253         Store[x][y] = 0;
7254
7255         PlayLevelSoundAction(x, y, ACTION_FILLING);
7256       }
7257     }
7258     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7259              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7260     {
7261       InitMovingField(x, y, MV_DOWN);
7262       started_moving = TRUE;
7263
7264       Feld[x][y] = EL_QUICKSAND_FILLING;
7265       Store[x][y] = element;
7266
7267       PlayLevelSoundAction(x, y, ACTION_FILLING);
7268     }
7269     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7270              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7271     {
7272       InitMovingField(x, y, MV_DOWN);
7273       started_moving = TRUE;
7274
7275       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7276       Store[x][y] = element;
7277
7278       PlayLevelSoundAction(x, y, ACTION_FILLING);
7279     }
7280     else if (element == EL_MAGIC_WALL_FULL)
7281     {
7282       if (IS_FREE(x, y + 1))
7283       {
7284         InitMovingField(x, y, MV_DOWN);
7285         started_moving = TRUE;
7286
7287         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7288         Store[x][y] = EL_CHANGED(Store[x][y]);
7289       }
7290       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7291       {
7292         if (!MovDelay[x][y])
7293           MovDelay[x][y] = TILEY / 4 + 1;
7294
7295         if (MovDelay[x][y])
7296         {
7297           MovDelay[x][y]--;
7298           if (MovDelay[x][y])
7299             return;
7300         }
7301
7302         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7303         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7304         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7305         Store[x][y] = 0;
7306       }
7307     }
7308     else if (element == EL_BD_MAGIC_WALL_FULL)
7309     {
7310       if (IS_FREE(x, y + 1))
7311       {
7312         InitMovingField(x, y, MV_DOWN);
7313         started_moving = TRUE;
7314
7315         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7316         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7317       }
7318       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7319       {
7320         if (!MovDelay[x][y])
7321           MovDelay[x][y] = TILEY / 4 + 1;
7322
7323         if (MovDelay[x][y])
7324         {
7325           MovDelay[x][y]--;
7326           if (MovDelay[x][y])
7327             return;
7328         }
7329
7330         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7331         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7332         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7333         Store[x][y] = 0;
7334       }
7335     }
7336     else if (element == EL_DC_MAGIC_WALL_FULL)
7337     {
7338       if (IS_FREE(x, y + 1))
7339       {
7340         InitMovingField(x, y, MV_DOWN);
7341         started_moving = TRUE;
7342
7343         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7344         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7345       }
7346       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7347       {
7348         if (!MovDelay[x][y])
7349           MovDelay[x][y] = TILEY / 4 + 1;
7350
7351         if (MovDelay[x][y])
7352         {
7353           MovDelay[x][y]--;
7354           if (MovDelay[x][y])
7355             return;
7356         }
7357
7358         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7359         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7360         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7361         Store[x][y] = 0;
7362       }
7363     }
7364     else if ((CAN_PASS_MAGIC_WALL(element) &&
7365               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7366                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7367              (CAN_PASS_DC_MAGIC_WALL(element) &&
7368               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7369
7370     {
7371       InitMovingField(x, y, MV_DOWN);
7372       started_moving = TRUE;
7373
7374       Feld[x][y] =
7375         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7376          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7377          EL_DC_MAGIC_WALL_FILLING);
7378       Store[x][y] = element;
7379     }
7380     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7381     {
7382       SplashAcid(x, y + 1);
7383
7384       InitMovingField(x, y, MV_DOWN);
7385       started_moving = TRUE;
7386
7387       Store[x][y] = EL_ACID;
7388     }
7389     else if (
7390              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7391               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7392              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7393               CAN_FALL(element) && WasJustFalling[x][y] &&
7394               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7395
7396              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7397               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7398               (Feld[x][y + 1] == EL_BLOCKED)))
7399     {
7400       /* this is needed for a special case not covered by calling "Impact()"
7401          from "ContinueMoving()": if an element moves to a tile directly below
7402          another element which was just falling on that tile (which was empty
7403          in the previous frame), the falling element above would just stop
7404          instead of smashing the element below (in previous version, the above
7405          element was just checked for "moving" instead of "falling", resulting
7406          in incorrect smashes caused by horizontal movement of the above
7407          element; also, the case of the player being the element to smash was
7408          simply not covered here... :-/ ) */
7409
7410       CheckCollision[x][y] = 0;
7411       CheckImpact[x][y] = 0;
7412
7413       Impact(x, y);
7414     }
7415     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7416     {
7417       if (MovDir[x][y] == MV_NONE)
7418       {
7419         InitMovingField(x, y, MV_DOWN);
7420         started_moving = TRUE;
7421       }
7422     }
7423     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7424     {
7425       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7426         MovDir[x][y] = MV_DOWN;
7427
7428       InitMovingField(x, y, MV_DOWN);
7429       started_moving = TRUE;
7430     }
7431     else if (element == EL_AMOEBA_DROP)
7432     {
7433       Feld[x][y] = EL_AMOEBA_GROWING;
7434       Store[x][y] = EL_AMOEBA_WET;
7435     }
7436     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7437               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7438              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7439              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7440     {
7441       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7442                                 (IS_FREE(x - 1, y + 1) ||
7443                                  Feld[x - 1][y + 1] == EL_ACID));
7444       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7445                                 (IS_FREE(x + 1, y + 1) ||
7446                                  Feld[x + 1][y + 1] == EL_ACID));
7447       boolean can_fall_any  = (can_fall_left || can_fall_right);
7448       boolean can_fall_both = (can_fall_left && can_fall_right);
7449       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7450
7451       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7452       {
7453         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7454           can_fall_right = FALSE;
7455         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7456           can_fall_left = FALSE;
7457         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7458           can_fall_right = FALSE;
7459         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7460           can_fall_left = FALSE;
7461
7462         can_fall_any  = (can_fall_left || can_fall_right);
7463         can_fall_both = FALSE;
7464       }
7465
7466       if (can_fall_both)
7467       {
7468         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7469           can_fall_right = FALSE;       /* slip down on left side */
7470         else
7471           can_fall_left = !(can_fall_right = RND(2));
7472
7473         can_fall_both = FALSE;
7474       }
7475
7476       if (can_fall_any)
7477       {
7478         /* if not determined otherwise, prefer left side for slipping down */
7479         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7480         started_moving = TRUE;
7481       }
7482     }
7483     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7484     {
7485       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7486       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7487       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7488       int belt_dir = game.belt_dir[belt_nr];
7489
7490       if ((belt_dir == MV_LEFT  && left_is_free) ||
7491           (belt_dir == MV_RIGHT && right_is_free))
7492       {
7493         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7494
7495         InitMovingField(x, y, belt_dir);
7496         started_moving = TRUE;
7497
7498         Pushed[x][y] = TRUE;
7499         Pushed[nextx][y] = TRUE;
7500
7501         GfxAction[x][y] = ACTION_DEFAULT;
7502       }
7503       else
7504       {
7505         MovDir[x][y] = 0;       /* if element was moving, stop it */
7506       }
7507     }
7508   }
7509
7510   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7511   if (CAN_MOVE(element) && !started_moving)
7512   {
7513     int move_pattern = element_info[element].move_pattern;
7514     int newx, newy;
7515
7516     Moving2Blocked(x, y, &newx, &newy);
7517
7518     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7519       return;
7520
7521     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7522         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7523     {
7524       WasJustMoving[x][y] = 0;
7525       CheckCollision[x][y] = 0;
7526
7527       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7528
7529       if (Feld[x][y] != element)        /* element has changed */
7530         return;
7531     }
7532
7533     if (!MovDelay[x][y])        /* start new movement phase */
7534     {
7535       /* all objects that can change their move direction after each step
7536          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7537
7538       if (element != EL_YAMYAM &&
7539           element != EL_DARK_YAMYAM &&
7540           element != EL_PACMAN &&
7541           !(move_pattern & MV_ANY_DIRECTION) &&
7542           move_pattern != MV_TURNING_LEFT &&
7543           move_pattern != MV_TURNING_RIGHT &&
7544           move_pattern != MV_TURNING_LEFT_RIGHT &&
7545           move_pattern != MV_TURNING_RIGHT_LEFT &&
7546           move_pattern != MV_TURNING_RANDOM)
7547       {
7548         TurnRound(x, y);
7549
7550         if (MovDelay[x][y] && (element == EL_BUG ||
7551                                element == EL_SPACESHIP ||
7552                                element == EL_SP_SNIKSNAK ||
7553                                element == EL_SP_ELECTRON ||
7554                                element == EL_MOLE))
7555           TEST_DrawLevelField(x, y);
7556       }
7557     }
7558
7559     if (MovDelay[x][y])         /* wait some time before next movement */
7560     {
7561       MovDelay[x][y]--;
7562
7563       if (element == EL_ROBOT ||
7564           element == EL_YAMYAM ||
7565           element == EL_DARK_YAMYAM)
7566       {
7567         DrawLevelElementAnimationIfNeeded(x, y, element);
7568         PlayLevelSoundAction(x, y, ACTION_WAITING);
7569       }
7570       else if (element == EL_SP_ELECTRON)
7571         DrawLevelElementAnimationIfNeeded(x, y, element);
7572       else if (element == EL_DRAGON)
7573       {
7574         int i;
7575         int dir = MovDir[x][y];
7576         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7577         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7578         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7579                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7580                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7581                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7582         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7583
7584         GfxAction[x][y] = ACTION_ATTACKING;
7585
7586         if (IS_PLAYER(x, y))
7587           DrawPlayerField(x, y);
7588         else
7589           TEST_DrawLevelField(x, y);
7590
7591         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7592
7593         for (i = 1; i <= 3; i++)
7594         {
7595           int xx = x + i * dx;
7596           int yy = y + i * dy;
7597           int sx = SCREENX(xx);
7598           int sy = SCREENY(yy);
7599           int flame_graphic = graphic + (i - 1);
7600
7601           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7602             break;
7603
7604           if (MovDelay[x][y])
7605           {
7606             int flamed = MovingOrBlocked2Element(xx, yy);
7607
7608             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7609               Bang(xx, yy);
7610             else
7611               RemoveMovingField(xx, yy);
7612
7613             ChangeDelay[xx][yy] = 0;
7614
7615             Feld[xx][yy] = EL_FLAMES;
7616
7617             if (IN_SCR_FIELD(sx, sy))
7618             {
7619               TEST_DrawLevelFieldCrumbled(xx, yy);
7620               DrawGraphic(sx, sy, flame_graphic, frame);
7621             }
7622           }
7623           else
7624           {
7625             if (Feld[xx][yy] == EL_FLAMES)
7626               Feld[xx][yy] = EL_EMPTY;
7627             TEST_DrawLevelField(xx, yy);
7628           }
7629         }
7630       }
7631
7632       if (MovDelay[x][y])       /* element still has to wait some time */
7633       {
7634         PlayLevelSoundAction(x, y, ACTION_WAITING);
7635
7636         return;
7637       }
7638     }
7639
7640     /* now make next step */
7641
7642     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7643
7644     if (DONT_COLLIDE_WITH(element) &&
7645         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7646         !PLAYER_ENEMY_PROTECTED(newx, newy))
7647     {
7648       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7649
7650       return;
7651     }
7652
7653     else if (CAN_MOVE_INTO_ACID(element) &&
7654              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7655              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7656              (MovDir[x][y] == MV_DOWN ||
7657               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7658     {
7659       SplashAcid(newx, newy);
7660       Store[x][y] = EL_ACID;
7661     }
7662     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7663     {
7664       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7665           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7666           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7667           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7668       {
7669         RemoveField(x, y);
7670         TEST_DrawLevelField(x, y);
7671
7672         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7673         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7674           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7675
7676         local_player->friends_still_needed--;
7677         if (!local_player->friends_still_needed &&
7678             !local_player->GameOver && AllPlayersGone)
7679           PlayerWins(local_player);
7680
7681         return;
7682       }
7683       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7684       {
7685         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7686           TEST_DrawLevelField(newx, newy);
7687         else
7688           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7689       }
7690       else if (!IS_FREE(newx, newy))
7691       {
7692         GfxAction[x][y] = ACTION_WAITING;
7693
7694         if (IS_PLAYER(x, y))
7695           DrawPlayerField(x, y);
7696         else
7697           TEST_DrawLevelField(x, y);
7698
7699         return;
7700       }
7701     }
7702     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7703     {
7704       if (IS_FOOD_PIG(Feld[newx][newy]))
7705       {
7706         if (IS_MOVING(newx, newy))
7707           RemoveMovingField(newx, newy);
7708         else
7709         {
7710           Feld[newx][newy] = EL_EMPTY;
7711           TEST_DrawLevelField(newx, newy);
7712         }
7713
7714         PlayLevelSound(x, y, SND_PIG_DIGGING);
7715       }
7716       else if (!IS_FREE(newx, newy))
7717       {
7718         if (IS_PLAYER(x, y))
7719           DrawPlayerField(x, y);
7720         else
7721           TEST_DrawLevelField(x, y);
7722
7723         return;
7724       }
7725     }
7726     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7727     {
7728       if (Store[x][y] != EL_EMPTY)
7729       {
7730         boolean can_clone = FALSE;
7731         int xx, yy;
7732
7733         /* check if element to clone is still there */
7734         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7735         {
7736           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7737           {
7738             can_clone = TRUE;
7739
7740             break;
7741           }
7742         }
7743
7744         /* cannot clone or target field not free anymore -- do not clone */
7745         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7746           Store[x][y] = EL_EMPTY;
7747       }
7748
7749       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7750       {
7751         if (IS_MV_DIAGONAL(MovDir[x][y]))
7752         {
7753           int diagonal_move_dir = MovDir[x][y];
7754           int stored = Store[x][y];
7755           int change_delay = 8;
7756           int graphic;
7757
7758           /* android is moving diagonally */
7759
7760           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7761
7762           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7763           GfxElement[x][y] = EL_EMC_ANDROID;
7764           GfxAction[x][y] = ACTION_SHRINKING;
7765           GfxDir[x][y] = diagonal_move_dir;
7766           ChangeDelay[x][y] = change_delay;
7767
7768           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7769                                    GfxDir[x][y]);
7770
7771           DrawLevelGraphicAnimation(x, y, graphic);
7772           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7773
7774           if (Feld[newx][newy] == EL_ACID)
7775           {
7776             SplashAcid(newx, newy);
7777
7778             return;
7779           }
7780
7781           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7782
7783           Store[newx][newy] = EL_EMC_ANDROID;
7784           GfxElement[newx][newy] = EL_EMC_ANDROID;
7785           GfxAction[newx][newy] = ACTION_GROWING;
7786           GfxDir[newx][newy] = diagonal_move_dir;
7787           ChangeDelay[newx][newy] = change_delay;
7788
7789           graphic = el_act_dir2img(GfxElement[newx][newy],
7790                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7791
7792           DrawLevelGraphicAnimation(newx, newy, graphic);
7793           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7794
7795           return;
7796         }
7797         else
7798         {
7799           Feld[newx][newy] = EL_EMPTY;
7800           TEST_DrawLevelField(newx, newy);
7801
7802           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7803         }
7804       }
7805       else if (!IS_FREE(newx, newy))
7806       {
7807         return;
7808       }
7809     }
7810     else if (IS_CUSTOM_ELEMENT(element) &&
7811              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7812     {
7813       if (!DigFieldByCE(newx, newy, element))
7814         return;
7815
7816       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7817       {
7818         RunnerVisit[x][y] = FrameCounter;
7819         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7820       }
7821     }
7822     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7823     {
7824       if (!IS_FREE(newx, newy))
7825       {
7826         if (IS_PLAYER(x, y))
7827           DrawPlayerField(x, y);
7828         else
7829           TEST_DrawLevelField(x, y);
7830
7831         return;
7832       }
7833       else
7834       {
7835         boolean wanna_flame = !RND(10);
7836         int dx = newx - x, dy = newy - y;
7837         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7838         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7839         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7840                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7841         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7842                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7843
7844         if ((wanna_flame ||
7845              IS_CLASSIC_ENEMY(element1) ||
7846              IS_CLASSIC_ENEMY(element2)) &&
7847             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7848             element1 != EL_FLAMES && element2 != EL_FLAMES)
7849         {
7850           ResetGfxAnimation(x, y);
7851           GfxAction[x][y] = ACTION_ATTACKING;
7852
7853           if (IS_PLAYER(x, y))
7854             DrawPlayerField(x, y);
7855           else
7856             TEST_DrawLevelField(x, y);
7857
7858           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7859
7860           MovDelay[x][y] = 50;
7861
7862           Feld[newx][newy] = EL_FLAMES;
7863           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7864             Feld[newx1][newy1] = EL_FLAMES;
7865           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7866             Feld[newx2][newy2] = EL_FLAMES;
7867
7868           return;
7869         }
7870       }
7871     }
7872     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7873              Feld[newx][newy] == EL_DIAMOND)
7874     {
7875       if (IS_MOVING(newx, newy))
7876         RemoveMovingField(newx, newy);
7877       else
7878       {
7879         Feld[newx][newy] = EL_EMPTY;
7880         TEST_DrawLevelField(newx, newy);
7881       }
7882
7883       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7884     }
7885     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7886              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7887     {
7888       if (AmoebaNr[newx][newy])
7889       {
7890         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7891         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7892             Feld[newx][newy] == EL_BD_AMOEBA)
7893           AmoebaCnt[AmoebaNr[newx][newy]]--;
7894       }
7895
7896       if (IS_MOVING(newx, newy))
7897       {
7898         RemoveMovingField(newx, newy);
7899       }
7900       else
7901       {
7902         Feld[newx][newy] = EL_EMPTY;
7903         TEST_DrawLevelField(newx, newy);
7904       }
7905
7906       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7907     }
7908     else if ((element == EL_PACMAN || element == EL_MOLE)
7909              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7910     {
7911       if (AmoebaNr[newx][newy])
7912       {
7913         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7914         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7915             Feld[newx][newy] == EL_BD_AMOEBA)
7916           AmoebaCnt[AmoebaNr[newx][newy]]--;
7917       }
7918
7919       if (element == EL_MOLE)
7920       {
7921         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7922         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7923
7924         ResetGfxAnimation(x, y);
7925         GfxAction[x][y] = ACTION_DIGGING;
7926         TEST_DrawLevelField(x, y);
7927
7928         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7929
7930         return;                         /* wait for shrinking amoeba */
7931       }
7932       else      /* element == EL_PACMAN */
7933       {
7934         Feld[newx][newy] = EL_EMPTY;
7935         TEST_DrawLevelField(newx, newy);
7936         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7937       }
7938     }
7939     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7940              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7941               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7942     {
7943       /* wait for shrinking amoeba to completely disappear */
7944       return;
7945     }
7946     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7947     {
7948       /* object was running against a wall */
7949
7950       TurnRound(x, y);
7951
7952       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7953         DrawLevelElementAnimation(x, y, element);
7954
7955       if (DONT_TOUCH(element))
7956         TestIfBadThingTouchesPlayer(x, y);
7957
7958       return;
7959     }
7960
7961     InitMovingField(x, y, MovDir[x][y]);
7962
7963     PlayLevelSoundAction(x, y, ACTION_MOVING);
7964   }
7965
7966   if (MovDir[x][y])
7967     ContinueMoving(x, y);
7968 }
7969
7970 void ContinueMoving(int x, int y)
7971 {
7972   int element = Feld[x][y];
7973   struct ElementInfo *ei = &element_info[element];
7974   int direction = MovDir[x][y];
7975   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7976   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7977   int newx = x + dx, newy = y + dy;
7978   int stored = Store[x][y];
7979   int stored_new = Store[newx][newy];
7980   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7981   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7982   boolean last_line = (newy == lev_fieldy - 1);
7983
7984   MovPos[x][y] += getElementMoveStepsize(x, y);
7985
7986   if (pushed_by_player) /* special case: moving object pushed by player */
7987     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7988
7989   if (ABS(MovPos[x][y]) < TILEX)
7990   {
7991     TEST_DrawLevelField(x, y);
7992
7993     return;     /* element is still moving */
7994   }
7995
7996   /* element reached destination field */
7997
7998   Feld[x][y] = EL_EMPTY;
7999   Feld[newx][newy] = element;
8000   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8001
8002   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8003   {
8004     element = Feld[newx][newy] = EL_ACID;
8005   }
8006   else if (element == EL_MOLE)
8007   {
8008     Feld[x][y] = EL_SAND;
8009
8010     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8011   }
8012   else if (element == EL_QUICKSAND_FILLING)
8013   {
8014     element = Feld[newx][newy] = get_next_element(element);
8015     Store[newx][newy] = Store[x][y];
8016   }
8017   else if (element == EL_QUICKSAND_EMPTYING)
8018   {
8019     Feld[x][y] = get_next_element(element);
8020     element = Feld[newx][newy] = Store[x][y];
8021   }
8022   else if (element == EL_QUICKSAND_FAST_FILLING)
8023   {
8024     element = Feld[newx][newy] = get_next_element(element);
8025     Store[newx][newy] = Store[x][y];
8026   }
8027   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8028   {
8029     Feld[x][y] = get_next_element(element);
8030     element = Feld[newx][newy] = Store[x][y];
8031   }
8032   else if (element == EL_MAGIC_WALL_FILLING)
8033   {
8034     element = Feld[newx][newy] = get_next_element(element);
8035     if (!game.magic_wall_active)
8036       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8037     Store[newx][newy] = Store[x][y];
8038   }
8039   else if (element == EL_MAGIC_WALL_EMPTYING)
8040   {
8041     Feld[x][y] = get_next_element(element);
8042     if (!game.magic_wall_active)
8043       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8044     element = Feld[newx][newy] = Store[x][y];
8045
8046     InitField(newx, newy, FALSE);
8047   }
8048   else if (element == EL_BD_MAGIC_WALL_FILLING)
8049   {
8050     element = Feld[newx][newy] = get_next_element(element);
8051     if (!game.magic_wall_active)
8052       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8053     Store[newx][newy] = Store[x][y];
8054   }
8055   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8056   {
8057     Feld[x][y] = get_next_element(element);
8058     if (!game.magic_wall_active)
8059       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8060     element = Feld[newx][newy] = Store[x][y];
8061
8062     InitField(newx, newy, FALSE);
8063   }
8064   else if (element == EL_DC_MAGIC_WALL_FILLING)
8065   {
8066     element = Feld[newx][newy] = get_next_element(element);
8067     if (!game.magic_wall_active)
8068       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8069     Store[newx][newy] = Store[x][y];
8070   }
8071   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8072   {
8073     Feld[x][y] = get_next_element(element);
8074     if (!game.magic_wall_active)
8075       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8076     element = Feld[newx][newy] = Store[x][y];
8077
8078     InitField(newx, newy, FALSE);
8079   }
8080   else if (element == EL_AMOEBA_DROPPING)
8081   {
8082     Feld[x][y] = get_next_element(element);
8083     element = Feld[newx][newy] = Store[x][y];
8084   }
8085   else if (element == EL_SOKOBAN_OBJECT)
8086   {
8087     if (Back[x][y])
8088       Feld[x][y] = Back[x][y];
8089
8090     if (Back[newx][newy])
8091       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8092
8093     Back[x][y] = Back[newx][newy] = 0;
8094   }
8095
8096   Store[x][y] = EL_EMPTY;
8097   MovPos[x][y] = 0;
8098   MovDir[x][y] = 0;
8099   MovDelay[x][y] = 0;
8100
8101   MovDelay[newx][newy] = 0;
8102
8103   if (CAN_CHANGE_OR_HAS_ACTION(element))
8104   {
8105     /* copy element change control values to new field */
8106     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8107     ChangePage[newx][newy]  = ChangePage[x][y];
8108     ChangeCount[newx][newy] = ChangeCount[x][y];
8109     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8110   }
8111
8112   CustomValue[newx][newy] = CustomValue[x][y];
8113
8114   ChangeDelay[x][y] = 0;
8115   ChangePage[x][y] = -1;
8116   ChangeCount[x][y] = 0;
8117   ChangeEvent[x][y] = -1;
8118
8119   CustomValue[x][y] = 0;
8120
8121   /* copy animation control values to new field */
8122   GfxFrame[newx][newy]  = GfxFrame[x][y];
8123   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8124   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8125   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8126
8127   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8128
8129   /* some elements can leave other elements behind after moving */
8130   if (ei->move_leave_element != EL_EMPTY &&
8131       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8132       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8133   {
8134     int move_leave_element = ei->move_leave_element;
8135
8136     /* this makes it possible to leave the removed element again */
8137     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8138       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8139
8140     Feld[x][y] = move_leave_element;
8141
8142     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8143       MovDir[x][y] = direction;
8144
8145     InitField(x, y, FALSE);
8146
8147     if (GFX_CRUMBLED(Feld[x][y]))
8148       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8149
8150     if (ELEM_IS_PLAYER(move_leave_element))
8151       RelocatePlayer(x, y, move_leave_element);
8152   }
8153
8154   /* do this after checking for left-behind element */
8155   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8156
8157   if (!CAN_MOVE(element) ||
8158       (CAN_FALL(element) && direction == MV_DOWN &&
8159        (element == EL_SPRING ||
8160         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8161         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8162     GfxDir[x][y] = MovDir[newx][newy] = 0;
8163
8164   TEST_DrawLevelField(x, y);
8165   TEST_DrawLevelField(newx, newy);
8166
8167   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8168
8169   /* prevent pushed element from moving on in pushed direction */
8170   if (pushed_by_player && CAN_MOVE(element) &&
8171       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8172       !(element_info[element].move_pattern & direction))
8173     TurnRound(newx, newy);
8174
8175   /* prevent elements on conveyor belt from moving on in last direction */
8176   if (pushed_by_conveyor && CAN_FALL(element) &&
8177       direction & MV_HORIZONTAL)
8178     MovDir[newx][newy] = 0;
8179
8180   if (!pushed_by_player)
8181   {
8182     int nextx = newx + dx, nexty = newy + dy;
8183     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8184
8185     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8186
8187     if (CAN_FALL(element) && direction == MV_DOWN)
8188       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8189
8190     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8191       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8192
8193     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8194       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8195   }
8196
8197   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8198   {
8199     TestIfBadThingTouchesPlayer(newx, newy);
8200     TestIfBadThingTouchesFriend(newx, newy);
8201
8202     if (!IS_CUSTOM_ELEMENT(element))
8203       TestIfBadThingTouchesOtherBadThing(newx, newy);
8204   }
8205   else if (element == EL_PENGUIN)
8206     TestIfFriendTouchesBadThing(newx, newy);
8207
8208   if (DONT_GET_HIT_BY(element))
8209   {
8210     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8211   }
8212
8213   /* give the player one last chance (one more frame) to move away */
8214   if (CAN_FALL(element) && direction == MV_DOWN &&
8215       (last_line || (!IS_FREE(x, newy + 1) &&
8216                      (!IS_PLAYER(x, newy + 1) ||
8217                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8218     Impact(x, newy);
8219
8220   if (pushed_by_player && !game.use_change_when_pushing_bug)
8221   {
8222     int push_side = MV_DIR_OPPOSITE(direction);
8223     struct PlayerInfo *player = PLAYERINFO(x, y);
8224
8225     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8226                                player->index_bit, push_side);
8227     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8228                                         player->index_bit, push_side);
8229   }
8230
8231   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8232     MovDelay[newx][newy] = 1;
8233
8234   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8235
8236   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8237   TestIfElementHitsCustomElement(newx, newy, direction);
8238   TestIfPlayerTouchesCustomElement(newx, newy);
8239   TestIfElementTouchesCustomElement(newx, newy);
8240
8241   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8242       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8243     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8244                              MV_DIR_OPPOSITE(direction));
8245 }
8246
8247 int AmoebeNachbarNr(int ax, int ay)
8248 {
8249   int i;
8250   int element = Feld[ax][ay];
8251   int group_nr = 0;
8252   static int xy[4][2] =
8253   {
8254     { 0, -1 },
8255     { -1, 0 },
8256     { +1, 0 },
8257     { 0, +1 }
8258   };
8259
8260   for (i = 0; i < NUM_DIRECTIONS; i++)
8261   {
8262     int x = ax + xy[i][0];
8263     int y = ay + xy[i][1];
8264
8265     if (!IN_LEV_FIELD(x, y))
8266       continue;
8267
8268     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8269       group_nr = AmoebaNr[x][y];
8270   }
8271
8272   return group_nr;
8273 }
8274
8275 void AmoebenVereinigen(int ax, int ay)
8276 {
8277   int i, x, y, xx, yy;
8278   int new_group_nr = AmoebaNr[ax][ay];
8279   static int xy[4][2] =
8280   {
8281     { 0, -1 },
8282     { -1, 0 },
8283     { +1, 0 },
8284     { 0, +1 }
8285   };
8286
8287   if (new_group_nr == 0)
8288     return;
8289
8290   for (i = 0; i < NUM_DIRECTIONS; i++)
8291   {
8292     x = ax + xy[i][0];
8293     y = ay + xy[i][1];
8294
8295     if (!IN_LEV_FIELD(x, y))
8296       continue;
8297
8298     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8299          Feld[x][y] == EL_BD_AMOEBA ||
8300          Feld[x][y] == EL_AMOEBA_DEAD) &&
8301         AmoebaNr[x][y] != new_group_nr)
8302     {
8303       int old_group_nr = AmoebaNr[x][y];
8304
8305       if (old_group_nr == 0)
8306         return;
8307
8308       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8309       AmoebaCnt[old_group_nr] = 0;
8310       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8311       AmoebaCnt2[old_group_nr] = 0;
8312
8313       SCAN_PLAYFIELD(xx, yy)
8314       {
8315         if (AmoebaNr[xx][yy] == old_group_nr)
8316           AmoebaNr[xx][yy] = new_group_nr;
8317       }
8318     }
8319   }
8320 }
8321
8322 void AmoebeUmwandeln(int ax, int ay)
8323 {
8324   int i, x, y;
8325
8326   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8327   {
8328     int group_nr = AmoebaNr[ax][ay];
8329
8330 #ifdef DEBUG
8331     if (group_nr == 0)
8332     {
8333       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8334       printf("AmoebeUmwandeln(): This should never happen!\n");
8335       return;
8336     }
8337 #endif
8338
8339     SCAN_PLAYFIELD(x, y)
8340     {
8341       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8342       {
8343         AmoebaNr[x][y] = 0;
8344         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8345       }
8346     }
8347
8348     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8349                             SND_AMOEBA_TURNING_TO_GEM :
8350                             SND_AMOEBA_TURNING_TO_ROCK));
8351     Bang(ax, ay);
8352   }
8353   else
8354   {
8355     static int xy[4][2] =
8356     {
8357       { 0, -1 },
8358       { -1, 0 },
8359       { +1, 0 },
8360       { 0, +1 }
8361     };
8362
8363     for (i = 0; i < NUM_DIRECTIONS; i++)
8364     {
8365       x = ax + xy[i][0];
8366       y = ay + xy[i][1];
8367
8368       if (!IN_LEV_FIELD(x, y))
8369         continue;
8370
8371       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8372       {
8373         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8374                               SND_AMOEBA_TURNING_TO_GEM :
8375                               SND_AMOEBA_TURNING_TO_ROCK));
8376         Bang(x, y);
8377       }
8378     }
8379   }
8380 }
8381
8382 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8383 {
8384   int x, y;
8385   int group_nr = AmoebaNr[ax][ay];
8386   boolean done = FALSE;
8387
8388 #ifdef DEBUG
8389   if (group_nr == 0)
8390   {
8391     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8392     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8393     return;
8394   }
8395 #endif
8396
8397   SCAN_PLAYFIELD(x, y)
8398   {
8399     if (AmoebaNr[x][y] == group_nr &&
8400         (Feld[x][y] == EL_AMOEBA_DEAD ||
8401          Feld[x][y] == EL_BD_AMOEBA ||
8402          Feld[x][y] == EL_AMOEBA_GROWING))
8403     {
8404       AmoebaNr[x][y] = 0;
8405       Feld[x][y] = new_element;
8406       InitField(x, y, FALSE);
8407       TEST_DrawLevelField(x, y);
8408       done = TRUE;
8409     }
8410   }
8411
8412   if (done)
8413     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8414                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8415                             SND_BD_AMOEBA_TURNING_TO_GEM));
8416 }
8417
8418 void AmoebeWaechst(int x, int y)
8419 {
8420   static unsigned int sound_delay = 0;
8421   static unsigned int sound_delay_value = 0;
8422
8423   if (!MovDelay[x][y])          /* start new growing cycle */
8424   {
8425     MovDelay[x][y] = 7;
8426
8427     if (DelayReached(&sound_delay, sound_delay_value))
8428     {
8429       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8430       sound_delay_value = 30;
8431     }
8432   }
8433
8434   if (MovDelay[x][y])           /* wait some time before growing bigger */
8435   {
8436     MovDelay[x][y]--;
8437     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8438     {
8439       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8440                                            6 - MovDelay[x][y]);
8441
8442       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8443     }
8444
8445     if (!MovDelay[x][y])
8446     {
8447       Feld[x][y] = Store[x][y];
8448       Store[x][y] = 0;
8449       TEST_DrawLevelField(x, y);
8450     }
8451   }
8452 }
8453
8454 void AmoebaDisappearing(int x, int y)
8455 {
8456   static unsigned int sound_delay = 0;
8457   static unsigned int sound_delay_value = 0;
8458
8459   if (!MovDelay[x][y])          /* start new shrinking cycle */
8460   {
8461     MovDelay[x][y] = 7;
8462
8463     if (DelayReached(&sound_delay, sound_delay_value))
8464       sound_delay_value = 30;
8465   }
8466
8467   if (MovDelay[x][y])           /* wait some time before shrinking */
8468   {
8469     MovDelay[x][y]--;
8470     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8471     {
8472       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8473                                            6 - MovDelay[x][y]);
8474
8475       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8476     }
8477
8478     if (!MovDelay[x][y])
8479     {
8480       Feld[x][y] = EL_EMPTY;
8481       TEST_DrawLevelField(x, y);
8482
8483       /* don't let mole enter this field in this cycle;
8484          (give priority to objects falling to this field from above) */
8485       Stop[x][y] = TRUE;
8486     }
8487   }
8488 }
8489
8490 void AmoebeAbleger(int ax, int ay)
8491 {
8492   int i;
8493   int element = Feld[ax][ay];
8494   int graphic = el2img(element);
8495   int newax = ax, neway = ay;
8496   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8497   static int xy[4][2] =
8498   {
8499     { 0, -1 },
8500     { -1, 0 },
8501     { +1, 0 },
8502     { 0, +1 }
8503   };
8504
8505   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8506   {
8507     Feld[ax][ay] = EL_AMOEBA_DEAD;
8508     TEST_DrawLevelField(ax, ay);
8509     return;
8510   }
8511
8512   if (IS_ANIMATED(graphic))
8513     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8514
8515   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8516     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8517
8518   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8519   {
8520     MovDelay[ax][ay]--;
8521     if (MovDelay[ax][ay])
8522       return;
8523   }
8524
8525   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8526   {
8527     int start = RND(4);
8528     int x = ax + xy[start][0];
8529     int y = ay + xy[start][1];
8530
8531     if (!IN_LEV_FIELD(x, y))
8532       return;
8533
8534     if (IS_FREE(x, y) ||
8535         CAN_GROW_INTO(Feld[x][y]) ||
8536         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8537         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8538     {
8539       newax = x;
8540       neway = y;
8541     }
8542
8543     if (newax == ax && neway == ay)
8544       return;
8545   }
8546   else                          /* normal or "filled" (BD style) amoeba */
8547   {
8548     int start = RND(4);
8549     boolean waiting_for_player = FALSE;
8550
8551     for (i = 0; i < NUM_DIRECTIONS; i++)
8552     {
8553       int j = (start + i) % 4;
8554       int x = ax + xy[j][0];
8555       int y = ay + xy[j][1];
8556
8557       if (!IN_LEV_FIELD(x, y))
8558         continue;
8559
8560       if (IS_FREE(x, y) ||
8561           CAN_GROW_INTO(Feld[x][y]) ||
8562           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8563           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8564       {
8565         newax = x;
8566         neway = y;
8567         break;
8568       }
8569       else if (IS_PLAYER(x, y))
8570         waiting_for_player = TRUE;
8571     }
8572
8573     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8574     {
8575       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8576       {
8577         Feld[ax][ay] = EL_AMOEBA_DEAD;
8578         TEST_DrawLevelField(ax, ay);
8579         AmoebaCnt[AmoebaNr[ax][ay]]--;
8580
8581         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8582         {
8583           if (element == EL_AMOEBA_FULL)
8584             AmoebeUmwandeln(ax, ay);
8585           else if (element == EL_BD_AMOEBA)
8586             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8587         }
8588       }
8589       return;
8590     }
8591     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8592     {
8593       /* amoeba gets larger by growing in some direction */
8594
8595       int new_group_nr = AmoebaNr[ax][ay];
8596
8597 #ifdef DEBUG
8598   if (new_group_nr == 0)
8599   {
8600     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8601     printf("AmoebeAbleger(): This should never happen!\n");
8602     return;
8603   }
8604 #endif
8605
8606       AmoebaNr[newax][neway] = new_group_nr;
8607       AmoebaCnt[new_group_nr]++;
8608       AmoebaCnt2[new_group_nr]++;
8609
8610       /* if amoeba touches other amoeba(s) after growing, unify them */
8611       AmoebenVereinigen(newax, neway);
8612
8613       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8614       {
8615         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8616         return;
8617       }
8618     }
8619   }
8620
8621   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8622       (neway == lev_fieldy - 1 && newax != ax))
8623   {
8624     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8625     Store[newax][neway] = element;
8626   }
8627   else if (neway == ay || element == EL_EMC_DRIPPER)
8628   {
8629     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8630
8631     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8632   }
8633   else
8634   {
8635     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8636     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8637     Store[ax][ay] = EL_AMOEBA_DROP;
8638     ContinueMoving(ax, ay);
8639     return;
8640   }
8641
8642   TEST_DrawLevelField(newax, neway);
8643 }
8644
8645 void Life(int ax, int ay)
8646 {
8647   int x1, y1, x2, y2;
8648   int life_time = 40;
8649   int element = Feld[ax][ay];
8650   int graphic = el2img(element);
8651   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8652                          level.biomaze);
8653   boolean changed = FALSE;
8654
8655   if (IS_ANIMATED(graphic))
8656     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8657
8658   if (Stop[ax][ay])
8659     return;
8660
8661   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8662     MovDelay[ax][ay] = life_time;
8663
8664   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8665   {
8666     MovDelay[ax][ay]--;
8667     if (MovDelay[ax][ay])
8668       return;
8669   }
8670
8671   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8672   {
8673     int xx = ax+x1, yy = ay+y1;
8674     int nachbarn = 0;
8675
8676     if (!IN_LEV_FIELD(xx, yy))
8677       continue;
8678
8679     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8680     {
8681       int x = xx+x2, y = yy+y2;
8682
8683       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8684         continue;
8685
8686       if (((Feld[x][y] == element ||
8687             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8688            !Stop[x][y]) ||
8689           (IS_FREE(x, y) && Stop[x][y]))
8690         nachbarn++;
8691     }
8692
8693     if (xx == ax && yy == ay)           /* field in the middle */
8694     {
8695       if (nachbarn < life_parameter[0] ||
8696           nachbarn > life_parameter[1])
8697       {
8698         Feld[xx][yy] = EL_EMPTY;
8699         if (!Stop[xx][yy])
8700           TEST_DrawLevelField(xx, yy);
8701         Stop[xx][yy] = TRUE;
8702         changed = TRUE;
8703       }
8704     }
8705     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8706     {                                   /* free border field */
8707       if (nachbarn >= life_parameter[2] &&
8708           nachbarn <= life_parameter[3])
8709       {
8710         Feld[xx][yy] = element;
8711         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8712         if (!Stop[xx][yy])
8713           TEST_DrawLevelField(xx, yy);
8714         Stop[xx][yy] = TRUE;
8715         changed = TRUE;
8716       }
8717     }
8718   }
8719
8720   if (changed)
8721     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8722                    SND_GAME_OF_LIFE_GROWING);
8723 }
8724
8725 static void InitRobotWheel(int x, int y)
8726 {
8727   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8728 }
8729
8730 static void RunRobotWheel(int x, int y)
8731 {
8732   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8733 }
8734
8735 static void StopRobotWheel(int x, int y)
8736 {
8737   if (ZX == x && ZY == y)
8738   {
8739     ZX = ZY = -1;
8740
8741     game.robot_wheel_active = FALSE;
8742   }
8743 }
8744
8745 static void InitTimegateWheel(int x, int y)
8746 {
8747   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8748 }
8749
8750 static void RunTimegateWheel(int x, int y)
8751 {
8752   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8753 }
8754
8755 static void InitMagicBallDelay(int x, int y)
8756 {
8757   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8758 }
8759
8760 static void ActivateMagicBall(int bx, int by)
8761 {
8762   int x, y;
8763
8764   if (level.ball_random)
8765   {
8766     int pos_border = RND(8);    /* select one of the eight border elements */
8767     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8768     int xx = pos_content % 3;
8769     int yy = pos_content / 3;
8770
8771     x = bx - 1 + xx;
8772     y = by - 1 + yy;
8773
8774     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8775       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8776   }
8777   else
8778   {
8779     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8780     {
8781       int xx = x - bx + 1;
8782       int yy = y - by + 1;
8783
8784       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8785         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8786     }
8787   }
8788
8789   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8790 }
8791
8792 void CheckExit(int x, int y)
8793 {
8794   if (local_player->gems_still_needed > 0 ||
8795       local_player->sokobanfields_still_needed > 0 ||
8796       local_player->lights_still_needed > 0)
8797   {
8798     int element = Feld[x][y];
8799     int graphic = el2img(element);
8800
8801     if (IS_ANIMATED(graphic))
8802       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8803
8804     return;
8805   }
8806
8807   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8808     return;
8809
8810   Feld[x][y] = EL_EXIT_OPENING;
8811
8812   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8813 }
8814
8815 void CheckExitEM(int x, int y)
8816 {
8817   if (local_player->gems_still_needed > 0 ||
8818       local_player->sokobanfields_still_needed > 0 ||
8819       local_player->lights_still_needed > 0)
8820   {
8821     int element = Feld[x][y];
8822     int graphic = el2img(element);
8823
8824     if (IS_ANIMATED(graphic))
8825       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8826
8827     return;
8828   }
8829
8830   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8831     return;
8832
8833   Feld[x][y] = EL_EM_EXIT_OPENING;
8834
8835   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8836 }
8837
8838 void CheckExitSteel(int x, int y)
8839 {
8840   if (local_player->gems_still_needed > 0 ||
8841       local_player->sokobanfields_still_needed > 0 ||
8842       local_player->lights_still_needed > 0)
8843   {
8844     int element = Feld[x][y];
8845     int graphic = el2img(element);
8846
8847     if (IS_ANIMATED(graphic))
8848       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8849
8850     return;
8851   }
8852
8853   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8854     return;
8855
8856   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8857
8858   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8859 }
8860
8861 void CheckExitSteelEM(int x, int y)
8862 {
8863   if (local_player->gems_still_needed > 0 ||
8864       local_player->sokobanfields_still_needed > 0 ||
8865       local_player->lights_still_needed > 0)
8866   {
8867     int element = Feld[x][y];
8868     int graphic = el2img(element);
8869
8870     if (IS_ANIMATED(graphic))
8871       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8872
8873     return;
8874   }
8875
8876   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8877     return;
8878
8879   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8880
8881   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8882 }
8883
8884 void CheckExitSP(int x, int y)
8885 {
8886   if (local_player->gems_still_needed > 0)
8887   {
8888     int element = Feld[x][y];
8889     int graphic = el2img(element);
8890
8891     if (IS_ANIMATED(graphic))
8892       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8893
8894     return;
8895   }
8896
8897   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8898     return;
8899
8900   Feld[x][y] = EL_SP_EXIT_OPENING;
8901
8902   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8903 }
8904
8905 static void CloseAllOpenTimegates()
8906 {
8907   int x, y;
8908
8909   SCAN_PLAYFIELD(x, y)
8910   {
8911     int element = Feld[x][y];
8912
8913     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8914     {
8915       Feld[x][y] = EL_TIMEGATE_CLOSING;
8916
8917       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8918     }
8919   }
8920 }
8921
8922 void DrawTwinkleOnField(int x, int y)
8923 {
8924   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8925     return;
8926
8927   if (Feld[x][y] == EL_BD_DIAMOND)
8928     return;
8929
8930   if (MovDelay[x][y] == 0)      /* next animation frame */
8931     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8932
8933   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8934   {
8935     MovDelay[x][y]--;
8936
8937     DrawLevelElementAnimation(x, y, Feld[x][y]);
8938
8939     if (MovDelay[x][y] != 0)
8940     {
8941       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8942                                            10 - MovDelay[x][y]);
8943
8944       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8945     }
8946   }
8947 }
8948
8949 void MauerWaechst(int x, int y)
8950 {
8951   int delay = 6;
8952
8953   if (!MovDelay[x][y])          /* next animation frame */
8954     MovDelay[x][y] = 3 * delay;
8955
8956   if (MovDelay[x][y])           /* wait some time before next frame */
8957   {
8958     MovDelay[x][y]--;
8959
8960     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8961     {
8962       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8963       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8964
8965       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8966     }
8967
8968     if (!MovDelay[x][y])
8969     {
8970       if (MovDir[x][y] == MV_LEFT)
8971       {
8972         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8973           TEST_DrawLevelField(x - 1, y);
8974       }
8975       else if (MovDir[x][y] == MV_RIGHT)
8976       {
8977         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8978           TEST_DrawLevelField(x + 1, y);
8979       }
8980       else if (MovDir[x][y] == MV_UP)
8981       {
8982         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8983           TEST_DrawLevelField(x, y - 1);
8984       }
8985       else
8986       {
8987         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8988           TEST_DrawLevelField(x, y + 1);
8989       }
8990
8991       Feld[x][y] = Store[x][y];
8992       Store[x][y] = 0;
8993       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8994       TEST_DrawLevelField(x, y);
8995     }
8996   }
8997 }
8998
8999 void MauerAbleger(int ax, int ay)
9000 {
9001   int element = Feld[ax][ay];
9002   int graphic = el2img(element);
9003   boolean oben_frei = FALSE, unten_frei = FALSE;
9004   boolean links_frei = FALSE, rechts_frei = FALSE;
9005   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9006   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9007   boolean new_wall = FALSE;
9008
9009   if (IS_ANIMATED(graphic))
9010     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9011
9012   if (!MovDelay[ax][ay])        /* start building new wall */
9013     MovDelay[ax][ay] = 6;
9014
9015   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9016   {
9017     MovDelay[ax][ay]--;
9018     if (MovDelay[ax][ay])
9019       return;
9020   }
9021
9022   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9023     oben_frei = TRUE;
9024   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9025     unten_frei = TRUE;
9026   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9027     links_frei = TRUE;
9028   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9029     rechts_frei = TRUE;
9030
9031   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9032       element == EL_EXPANDABLE_WALL_ANY)
9033   {
9034     if (oben_frei)
9035     {
9036       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9037       Store[ax][ay-1] = element;
9038       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9039       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9040         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9041                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9042       new_wall = TRUE;
9043     }
9044     if (unten_frei)
9045     {
9046       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9047       Store[ax][ay+1] = element;
9048       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9049       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9050         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9051                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9052       new_wall = TRUE;
9053     }
9054   }
9055
9056   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9057       element == EL_EXPANDABLE_WALL_ANY ||
9058       element == EL_EXPANDABLE_WALL ||
9059       element == EL_BD_EXPANDABLE_WALL)
9060   {
9061     if (links_frei)
9062     {
9063       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9064       Store[ax-1][ay] = element;
9065       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9066       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9067         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9068                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9069       new_wall = TRUE;
9070     }
9071
9072     if (rechts_frei)
9073     {
9074       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9075       Store[ax+1][ay] = element;
9076       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9077       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9078         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9079                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9080       new_wall = TRUE;
9081     }
9082   }
9083
9084   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9085     TEST_DrawLevelField(ax, ay);
9086
9087   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9088     oben_massiv = TRUE;
9089   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9090     unten_massiv = TRUE;
9091   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9092     links_massiv = TRUE;
9093   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9094     rechts_massiv = TRUE;
9095
9096   if (((oben_massiv && unten_massiv) ||
9097        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9098        element == EL_EXPANDABLE_WALL) &&
9099       ((links_massiv && rechts_massiv) ||
9100        element == EL_EXPANDABLE_WALL_VERTICAL))
9101     Feld[ax][ay] = EL_WALL;
9102
9103   if (new_wall)
9104     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9105 }
9106
9107 void MauerAblegerStahl(int ax, int ay)
9108 {
9109   int element = Feld[ax][ay];
9110   int graphic = el2img(element);
9111   boolean oben_frei = FALSE, unten_frei = FALSE;
9112   boolean links_frei = FALSE, rechts_frei = FALSE;
9113   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9114   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9115   boolean new_wall = FALSE;
9116
9117   if (IS_ANIMATED(graphic))
9118     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9119
9120   if (!MovDelay[ax][ay])        /* start building new wall */
9121     MovDelay[ax][ay] = 6;
9122
9123   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9124   {
9125     MovDelay[ax][ay]--;
9126     if (MovDelay[ax][ay])
9127       return;
9128   }
9129
9130   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9131     oben_frei = TRUE;
9132   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9133     unten_frei = TRUE;
9134   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9135     links_frei = TRUE;
9136   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9137     rechts_frei = TRUE;
9138
9139   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9140       element == EL_EXPANDABLE_STEELWALL_ANY)
9141   {
9142     if (oben_frei)
9143     {
9144       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9145       Store[ax][ay-1] = element;
9146       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9147       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9148         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9149                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9150       new_wall = TRUE;
9151     }
9152     if (unten_frei)
9153     {
9154       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9155       Store[ax][ay+1] = element;
9156       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9157       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9158         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9159                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9160       new_wall = TRUE;
9161     }
9162   }
9163
9164   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9165       element == EL_EXPANDABLE_STEELWALL_ANY)
9166   {
9167     if (links_frei)
9168     {
9169       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9170       Store[ax-1][ay] = element;
9171       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9172       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9173         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9174                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9175       new_wall = TRUE;
9176     }
9177
9178     if (rechts_frei)
9179     {
9180       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9181       Store[ax+1][ay] = element;
9182       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9183       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9184         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9185                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9186       new_wall = TRUE;
9187     }
9188   }
9189
9190   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9191     oben_massiv = TRUE;
9192   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9193     unten_massiv = TRUE;
9194   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9195     links_massiv = TRUE;
9196   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9197     rechts_massiv = TRUE;
9198
9199   if (((oben_massiv && unten_massiv) ||
9200        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9201       ((links_massiv && rechts_massiv) ||
9202        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9203     Feld[ax][ay] = EL_STEELWALL;
9204
9205   if (new_wall)
9206     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9207 }
9208
9209 void CheckForDragon(int x, int y)
9210 {
9211   int i, j;
9212   boolean dragon_found = FALSE;
9213   static int xy[4][2] =
9214   {
9215     { 0, -1 },
9216     { -1, 0 },
9217     { +1, 0 },
9218     { 0, +1 }
9219   };
9220
9221   for (i = 0; i < NUM_DIRECTIONS; i++)
9222   {
9223     for (j = 0; j < 4; j++)
9224     {
9225       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9226
9227       if (IN_LEV_FIELD(xx, yy) &&
9228           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9229       {
9230         if (Feld[xx][yy] == EL_DRAGON)
9231           dragon_found = TRUE;
9232       }
9233       else
9234         break;
9235     }
9236   }
9237
9238   if (!dragon_found)
9239   {
9240     for (i = 0; i < NUM_DIRECTIONS; i++)
9241     {
9242       for (j = 0; j < 3; j++)
9243       {
9244         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9245   
9246         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9247         {
9248           Feld[xx][yy] = EL_EMPTY;
9249           TEST_DrawLevelField(xx, yy);
9250         }
9251         else
9252           break;
9253       }
9254     }
9255   }
9256 }
9257
9258 static void InitBuggyBase(int x, int y)
9259 {
9260   int element = Feld[x][y];
9261   int activating_delay = FRAMES_PER_SECOND / 4;
9262
9263   ChangeDelay[x][y] =
9264     (element == EL_SP_BUGGY_BASE ?
9265      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9266      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9267      activating_delay :
9268      element == EL_SP_BUGGY_BASE_ACTIVE ?
9269      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9270 }
9271
9272 static void WarnBuggyBase(int x, int y)
9273 {
9274   int i;
9275   static int xy[4][2] =
9276   {
9277     { 0, -1 },
9278     { -1, 0 },
9279     { +1, 0 },
9280     { 0, +1 }
9281   };
9282
9283   for (i = 0; i < NUM_DIRECTIONS; i++)
9284   {
9285     int xx = x + xy[i][0];
9286     int yy = y + xy[i][1];
9287
9288     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9289     {
9290       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9291
9292       break;
9293     }
9294   }
9295 }
9296
9297 static void InitTrap(int x, int y)
9298 {
9299   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9300 }
9301
9302 static void ActivateTrap(int x, int y)
9303 {
9304   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9305 }
9306
9307 static void ChangeActiveTrap(int x, int y)
9308 {
9309   int graphic = IMG_TRAP_ACTIVE;
9310
9311   /* if new animation frame was drawn, correct crumbled sand border */
9312   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9313     TEST_DrawLevelFieldCrumbled(x, y);
9314 }
9315
9316 static int getSpecialActionElement(int element, int number, int base_element)
9317 {
9318   return (element != EL_EMPTY ? element :
9319           number != -1 ? base_element + number - 1 :
9320           EL_EMPTY);
9321 }
9322
9323 static int getModifiedActionNumber(int value_old, int operator, int operand,
9324                                    int value_min, int value_max)
9325 {
9326   int value_new = (operator == CA_MODE_SET      ? operand :
9327                    operator == CA_MODE_ADD      ? value_old + operand :
9328                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9329                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9330                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9331                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9332                    value_old);
9333
9334   return (value_new < value_min ? value_min :
9335           value_new > value_max ? value_max :
9336           value_new);
9337 }
9338
9339 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9340 {
9341   struct ElementInfo *ei = &element_info[element];
9342   struct ElementChangeInfo *change = &ei->change_page[page];
9343   int target_element = change->target_element;
9344   int action_type = change->action_type;
9345   int action_mode = change->action_mode;
9346   int action_arg = change->action_arg;
9347   int action_element = change->action_element;
9348   int i;
9349
9350   if (!change->has_action)
9351     return;
9352
9353   /* ---------- determine action paramater values -------------------------- */
9354
9355   int level_time_value =
9356     (level.time > 0 ? TimeLeft :
9357      TimePlayed);
9358
9359   int action_arg_element_raw =
9360     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9361      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9362      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9363      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9364      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9365      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9366      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9367      EL_EMPTY);
9368   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9369
9370   int action_arg_direction =
9371     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9372      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9373      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9374      change->actual_trigger_side :
9375      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9376      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9377      MV_NONE);
9378
9379   int action_arg_number_min =
9380     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9381      CA_ARG_MIN);
9382
9383   int action_arg_number_max =
9384     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9385      action_type == CA_SET_LEVEL_GEMS ? 999 :
9386      action_type == CA_SET_LEVEL_TIME ? 9999 :
9387      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9388      action_type == CA_SET_CE_VALUE ? 9999 :
9389      action_type == CA_SET_CE_SCORE ? 9999 :
9390      CA_ARG_MAX);
9391
9392   int action_arg_number_reset =
9393     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9394      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9395      action_type == CA_SET_LEVEL_TIME ? level.time :
9396      action_type == CA_SET_LEVEL_SCORE ? 0 :
9397      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9398      action_type == CA_SET_CE_SCORE ? 0 :
9399      0);
9400
9401   int action_arg_number =
9402     (action_arg <= CA_ARG_MAX ? action_arg :
9403      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9404      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9405      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9406      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9407      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9408      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9409      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9410      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9411      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9412      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9413      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9414      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9415      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9416      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9417      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9418      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9419      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9420      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9421      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9422      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9423      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9424      -1);
9425
9426   int action_arg_number_old =
9427     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9428      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9429      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9430      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9431      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9432      0);
9433
9434   int action_arg_number_new =
9435     getModifiedActionNumber(action_arg_number_old,
9436                             action_mode, action_arg_number,
9437                             action_arg_number_min, action_arg_number_max);
9438
9439   int trigger_player_bits =
9440     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9441      change->actual_trigger_player_bits : change->trigger_player);
9442
9443   int action_arg_player_bits =
9444     (action_arg >= CA_ARG_PLAYER_1 &&
9445      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9446      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9447      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9448      PLAYER_BITS_ANY);
9449
9450   /* ---------- execute action  -------------------------------------------- */
9451
9452   switch (action_type)
9453   {
9454     case CA_NO_ACTION:
9455     {
9456       return;
9457     }
9458
9459     /* ---------- level actions  ------------------------------------------- */
9460
9461     case CA_RESTART_LEVEL:
9462     {
9463       game.restart_level = TRUE;
9464
9465       break;
9466     }
9467
9468     case CA_SHOW_ENVELOPE:
9469     {
9470       int element = getSpecialActionElement(action_arg_element,
9471                                             action_arg_number, EL_ENVELOPE_1);
9472
9473       if (IS_ENVELOPE(element))
9474         local_player->show_envelope = element;
9475
9476       break;
9477     }
9478
9479     case CA_SET_LEVEL_TIME:
9480     {
9481       if (level.time > 0)       /* only modify limited time value */
9482       {
9483         TimeLeft = action_arg_number_new;
9484
9485         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9486
9487         DisplayGameControlValues();
9488
9489         if (!TimeLeft && setup.time_limit)
9490           for (i = 0; i < MAX_PLAYERS; i++)
9491             KillPlayer(&stored_player[i]);
9492       }
9493
9494       break;
9495     }
9496
9497     case CA_SET_LEVEL_SCORE:
9498     {
9499       local_player->score = action_arg_number_new;
9500
9501       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9502
9503       DisplayGameControlValues();
9504
9505       break;
9506     }
9507
9508     case CA_SET_LEVEL_GEMS:
9509     {
9510       local_player->gems_still_needed = action_arg_number_new;
9511
9512       game.snapshot.collected_item = TRUE;
9513
9514       game_panel_controls[GAME_PANEL_GEMS].value =
9515         local_player->gems_still_needed;
9516
9517       DisplayGameControlValues();
9518
9519       break;
9520     }
9521
9522     case CA_SET_LEVEL_WIND:
9523     {
9524       game.wind_direction = action_arg_direction;
9525
9526       break;
9527     }
9528
9529     case CA_SET_LEVEL_RANDOM_SEED:
9530     {
9531       /* ensure that setting a new random seed while playing is predictable */
9532       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9533
9534       break;
9535     }
9536
9537     /* ---------- player actions  ------------------------------------------ */
9538
9539     case CA_MOVE_PLAYER:
9540     {
9541       /* automatically move to the next field in specified direction */
9542       for (i = 0; i < MAX_PLAYERS; i++)
9543         if (trigger_player_bits & (1 << i))
9544           stored_player[i].programmed_action = action_arg_direction;
9545
9546       break;
9547     }
9548
9549     case CA_EXIT_PLAYER:
9550     {
9551       for (i = 0; i < MAX_PLAYERS; i++)
9552         if (action_arg_player_bits & (1 << i))
9553           PlayerWins(&stored_player[i]);
9554
9555       break;
9556     }
9557
9558     case CA_KILL_PLAYER:
9559     {
9560       for (i = 0; i < MAX_PLAYERS; i++)
9561         if (action_arg_player_bits & (1 << i))
9562           KillPlayer(&stored_player[i]);
9563
9564       break;
9565     }
9566
9567     case CA_SET_PLAYER_KEYS:
9568     {
9569       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9570       int element = getSpecialActionElement(action_arg_element,
9571                                             action_arg_number, EL_KEY_1);
9572
9573       if (IS_KEY(element))
9574       {
9575         for (i = 0; i < MAX_PLAYERS; i++)
9576         {
9577           if (trigger_player_bits & (1 << i))
9578           {
9579             stored_player[i].key[KEY_NR(element)] = key_state;
9580
9581             DrawGameDoorValues();
9582           }
9583         }
9584       }
9585
9586       break;
9587     }
9588
9589     case CA_SET_PLAYER_SPEED:
9590     {
9591       for (i = 0; i < MAX_PLAYERS; i++)
9592       {
9593         if (trigger_player_bits & (1 << i))
9594         {
9595           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9596
9597           if (action_arg == CA_ARG_SPEED_FASTER &&
9598               stored_player[i].cannot_move)
9599           {
9600             action_arg_number = STEPSIZE_VERY_SLOW;
9601           }
9602           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9603                    action_arg == CA_ARG_SPEED_FASTER)
9604           {
9605             action_arg_number = 2;
9606             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9607                            CA_MODE_MULTIPLY);
9608           }
9609           else if (action_arg == CA_ARG_NUMBER_RESET)
9610           {
9611             action_arg_number = level.initial_player_stepsize[i];
9612           }
9613
9614           move_stepsize =
9615             getModifiedActionNumber(move_stepsize,
9616                                     action_mode,
9617                                     action_arg_number,
9618                                     action_arg_number_min,
9619                                     action_arg_number_max);
9620
9621           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9622         }
9623       }
9624
9625       break;
9626     }
9627
9628     case CA_SET_PLAYER_SHIELD:
9629     {
9630       for (i = 0; i < MAX_PLAYERS; i++)
9631       {
9632         if (trigger_player_bits & (1 << i))
9633         {
9634           if (action_arg == CA_ARG_SHIELD_OFF)
9635           {
9636             stored_player[i].shield_normal_time_left = 0;
9637             stored_player[i].shield_deadly_time_left = 0;
9638           }
9639           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9640           {
9641             stored_player[i].shield_normal_time_left = 999999;
9642           }
9643           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9644           {
9645             stored_player[i].shield_normal_time_left = 999999;
9646             stored_player[i].shield_deadly_time_left = 999999;
9647           }
9648         }
9649       }
9650
9651       break;
9652     }
9653
9654     case CA_SET_PLAYER_GRAVITY:
9655     {
9656       for (i = 0; i < MAX_PLAYERS; i++)
9657       {
9658         if (trigger_player_bits & (1 << i))
9659         {
9660           stored_player[i].gravity =
9661             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9662              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9663              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9664              stored_player[i].gravity);
9665         }
9666       }
9667
9668       break;
9669     }
9670
9671     case CA_SET_PLAYER_ARTWORK:
9672     {
9673       for (i = 0; i < MAX_PLAYERS; i++)
9674       {
9675         if (trigger_player_bits & (1 << i))
9676         {
9677           int artwork_element = action_arg_element;
9678
9679           if (action_arg == CA_ARG_ELEMENT_RESET)
9680             artwork_element =
9681               (level.use_artwork_element[i] ? level.artwork_element[i] :
9682                stored_player[i].element_nr);
9683
9684           if (stored_player[i].artwork_element != artwork_element)
9685             stored_player[i].Frame = 0;
9686
9687           stored_player[i].artwork_element = artwork_element;
9688
9689           SetPlayerWaiting(&stored_player[i], FALSE);
9690
9691           /* set number of special actions for bored and sleeping animation */
9692           stored_player[i].num_special_action_bored =
9693             get_num_special_action(artwork_element,
9694                                    ACTION_BORING_1, ACTION_BORING_LAST);
9695           stored_player[i].num_special_action_sleeping =
9696             get_num_special_action(artwork_element,
9697                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9698         }
9699       }
9700
9701       break;
9702     }
9703
9704     case CA_SET_PLAYER_INVENTORY:
9705     {
9706       for (i = 0; i < MAX_PLAYERS; i++)
9707       {
9708         struct PlayerInfo *player = &stored_player[i];
9709         int j, k;
9710
9711         if (trigger_player_bits & (1 << i))
9712         {
9713           int inventory_element = action_arg_element;
9714
9715           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9716               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9717               action_arg == CA_ARG_ELEMENT_ACTION)
9718           {
9719             int element = inventory_element;
9720             int collect_count = element_info[element].collect_count_initial;
9721
9722             if (!IS_CUSTOM_ELEMENT(element))
9723               collect_count = 1;
9724
9725             if (collect_count == 0)
9726               player->inventory_infinite_element = element;
9727             else
9728               for (k = 0; k < collect_count; k++)
9729                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9730                   player->inventory_element[player->inventory_size++] =
9731                     element;
9732           }
9733           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9734                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9735                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9736           {
9737             if (player->inventory_infinite_element != EL_UNDEFINED &&
9738                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9739                                      action_arg_element_raw))
9740               player->inventory_infinite_element = EL_UNDEFINED;
9741
9742             for (k = 0, j = 0; j < player->inventory_size; j++)
9743             {
9744               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9745                                         action_arg_element_raw))
9746                 player->inventory_element[k++] = player->inventory_element[j];
9747             }
9748
9749             player->inventory_size = k;
9750           }
9751           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9752           {
9753             if (player->inventory_size > 0)
9754             {
9755               for (j = 0; j < player->inventory_size - 1; j++)
9756                 player->inventory_element[j] = player->inventory_element[j + 1];
9757
9758               player->inventory_size--;
9759             }
9760           }
9761           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9762           {
9763             if (player->inventory_size > 0)
9764               player->inventory_size--;
9765           }
9766           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9767           {
9768             player->inventory_infinite_element = EL_UNDEFINED;
9769             player->inventory_size = 0;
9770           }
9771           else if (action_arg == CA_ARG_INVENTORY_RESET)
9772           {
9773             player->inventory_infinite_element = EL_UNDEFINED;
9774             player->inventory_size = 0;
9775
9776             if (level.use_initial_inventory[i])
9777             {
9778               for (j = 0; j < level.initial_inventory_size[i]; j++)
9779               {
9780                 int element = level.initial_inventory_content[i][j];
9781                 int collect_count = element_info[element].collect_count_initial;
9782
9783                 if (!IS_CUSTOM_ELEMENT(element))
9784                   collect_count = 1;
9785
9786                 if (collect_count == 0)
9787                   player->inventory_infinite_element = element;
9788                 else
9789                   for (k = 0; k < collect_count; k++)
9790                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9791                       player->inventory_element[player->inventory_size++] =
9792                         element;
9793               }
9794             }
9795           }
9796         }
9797       }
9798
9799       break;
9800     }
9801
9802     /* ---------- CE actions  ---------------------------------------------- */
9803
9804     case CA_SET_CE_VALUE:
9805     {
9806       int last_ce_value = CustomValue[x][y];
9807
9808       CustomValue[x][y] = action_arg_number_new;
9809
9810       if (CustomValue[x][y] != last_ce_value)
9811       {
9812         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9813         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9814
9815         if (CustomValue[x][y] == 0)
9816         {
9817           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9818           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9819         }
9820       }
9821
9822       break;
9823     }
9824
9825     case CA_SET_CE_SCORE:
9826     {
9827       int last_ce_score = ei->collect_score;
9828
9829       ei->collect_score = action_arg_number_new;
9830
9831       if (ei->collect_score != last_ce_score)
9832       {
9833         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9834         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9835
9836         if (ei->collect_score == 0)
9837         {
9838           int xx, yy;
9839
9840           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9841           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9842
9843           /*
9844             This is a very special case that seems to be a mixture between
9845             CheckElementChange() and CheckTriggeredElementChange(): while
9846             the first one only affects single elements that are triggered
9847             directly, the second one affects multiple elements in the playfield
9848             that are triggered indirectly by another element. This is a third
9849             case: Changing the CE score always affects multiple identical CEs,
9850             so every affected CE must be checked, not only the single CE for
9851             which the CE score was changed in the first place (as every instance
9852             of that CE shares the same CE score, and therefore also can change)!
9853           */
9854           SCAN_PLAYFIELD(xx, yy)
9855           {
9856             if (Feld[xx][yy] == element)
9857               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9858                                  CE_SCORE_GETS_ZERO);
9859           }
9860         }
9861       }
9862
9863       break;
9864     }
9865
9866     case CA_SET_CE_ARTWORK:
9867     {
9868       int artwork_element = action_arg_element;
9869       boolean reset_frame = FALSE;
9870       int xx, yy;
9871
9872       if (action_arg == CA_ARG_ELEMENT_RESET)
9873         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9874                            element);
9875
9876       if (ei->gfx_element != artwork_element)
9877         reset_frame = TRUE;
9878
9879       ei->gfx_element = artwork_element;
9880
9881       SCAN_PLAYFIELD(xx, yy)
9882       {
9883         if (Feld[xx][yy] == element)
9884         {
9885           if (reset_frame)
9886           {
9887             ResetGfxAnimation(xx, yy);
9888             ResetRandomAnimationValue(xx, yy);
9889           }
9890
9891           TEST_DrawLevelField(xx, yy);
9892         }
9893       }
9894
9895       break;
9896     }
9897
9898     /* ---------- engine actions  ------------------------------------------ */
9899
9900     case CA_SET_ENGINE_SCAN_MODE:
9901     {
9902       InitPlayfieldScanMode(action_arg);
9903
9904       break;
9905     }
9906
9907     default:
9908       break;
9909   }
9910 }
9911
9912 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9913 {
9914   int old_element = Feld[x][y];
9915   int new_element = GetElementFromGroupElement(element);
9916   int previous_move_direction = MovDir[x][y];
9917   int last_ce_value = CustomValue[x][y];
9918   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9919   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9920   boolean add_player_onto_element = (new_element_is_player &&
9921                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9922                                      IS_WALKABLE(old_element));
9923
9924   if (!add_player_onto_element)
9925   {
9926     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9927       RemoveMovingField(x, y);
9928     else
9929       RemoveField(x, y);
9930
9931     Feld[x][y] = new_element;
9932
9933     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9934       MovDir[x][y] = previous_move_direction;
9935
9936     if (element_info[new_element].use_last_ce_value)
9937       CustomValue[x][y] = last_ce_value;
9938
9939     InitField_WithBug1(x, y, FALSE);
9940
9941     new_element = Feld[x][y];   /* element may have changed */
9942
9943     ResetGfxAnimation(x, y);
9944     ResetRandomAnimationValue(x, y);
9945
9946     TEST_DrawLevelField(x, y);
9947
9948     if (GFX_CRUMBLED(new_element))
9949       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9950   }
9951
9952   /* check if element under the player changes from accessible to unaccessible
9953      (needed for special case of dropping element which then changes) */
9954   /* (must be checked after creating new element for walkable group elements) */
9955   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9956       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9957   {
9958     Bang(x, y);
9959
9960     return;
9961   }
9962
9963   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9964   if (new_element_is_player)
9965     RelocatePlayer(x, y, new_element);
9966
9967   if (is_change)
9968     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9969
9970   TestIfBadThingTouchesPlayer(x, y);
9971   TestIfPlayerTouchesCustomElement(x, y);
9972   TestIfElementTouchesCustomElement(x, y);
9973 }
9974
9975 static void CreateField(int x, int y, int element)
9976 {
9977   CreateFieldExt(x, y, element, FALSE);
9978 }
9979
9980 static void CreateElementFromChange(int x, int y, int element)
9981 {
9982   element = GET_VALID_RUNTIME_ELEMENT(element);
9983
9984   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9985   {
9986     int old_element = Feld[x][y];
9987
9988     /* prevent changed element from moving in same engine frame
9989        unless both old and new element can either fall or move */
9990     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9991         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9992       Stop[x][y] = TRUE;
9993   }
9994
9995   CreateFieldExt(x, y, element, TRUE);
9996 }
9997
9998 static boolean ChangeElement(int x, int y, int element, int page)
9999 {
10000   struct ElementInfo *ei = &element_info[element];
10001   struct ElementChangeInfo *change = &ei->change_page[page];
10002   int ce_value = CustomValue[x][y];
10003   int ce_score = ei->collect_score;
10004   int target_element;
10005   int old_element = Feld[x][y];
10006
10007   /* always use default change event to prevent running into a loop */
10008   if (ChangeEvent[x][y] == -1)
10009     ChangeEvent[x][y] = CE_DELAY;
10010
10011   if (ChangeEvent[x][y] == CE_DELAY)
10012   {
10013     /* reset actual trigger element, trigger player and action element */
10014     change->actual_trigger_element = EL_EMPTY;
10015     change->actual_trigger_player = EL_EMPTY;
10016     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10017     change->actual_trigger_side = CH_SIDE_NONE;
10018     change->actual_trigger_ce_value = 0;
10019     change->actual_trigger_ce_score = 0;
10020   }
10021
10022   /* do not change elements more than a specified maximum number of changes */
10023   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10024     return FALSE;
10025
10026   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10027
10028   if (change->explode)
10029   {
10030     Bang(x, y);
10031
10032     return TRUE;
10033   }
10034
10035   if (change->use_target_content)
10036   {
10037     boolean complete_replace = TRUE;
10038     boolean can_replace[3][3];
10039     int xx, yy;
10040
10041     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10042     {
10043       boolean is_empty;
10044       boolean is_walkable;
10045       boolean is_diggable;
10046       boolean is_collectible;
10047       boolean is_removable;
10048       boolean is_destructible;
10049       int ex = x + xx - 1;
10050       int ey = y + yy - 1;
10051       int content_element = change->target_content.e[xx][yy];
10052       int e;
10053
10054       can_replace[xx][yy] = TRUE;
10055
10056       if (ex == x && ey == y)   /* do not check changing element itself */
10057         continue;
10058
10059       if (content_element == EL_EMPTY_SPACE)
10060       {
10061         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10062
10063         continue;
10064       }
10065
10066       if (!IN_LEV_FIELD(ex, ey))
10067       {
10068         can_replace[xx][yy] = FALSE;
10069         complete_replace = FALSE;
10070
10071         continue;
10072       }
10073
10074       e = Feld[ex][ey];
10075
10076       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10077         e = MovingOrBlocked2Element(ex, ey);
10078
10079       is_empty = (IS_FREE(ex, ey) ||
10080                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10081
10082       is_walkable     = (is_empty || IS_WALKABLE(e));
10083       is_diggable     = (is_empty || IS_DIGGABLE(e));
10084       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10085       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10086       is_removable    = (is_diggable || is_collectible);
10087
10088       can_replace[xx][yy] =
10089         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10090           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10091           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10092           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10093           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10094           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10095          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10096
10097       if (!can_replace[xx][yy])
10098         complete_replace = FALSE;
10099     }
10100
10101     if (!change->only_if_complete || complete_replace)
10102     {
10103       boolean something_has_changed = FALSE;
10104
10105       if (change->only_if_complete && change->use_random_replace &&
10106           RND(100) < change->random_percentage)
10107         return FALSE;
10108
10109       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10110       {
10111         int ex = x + xx - 1;
10112         int ey = y + yy - 1;
10113         int content_element;
10114
10115         if (can_replace[xx][yy] && (!change->use_random_replace ||
10116                                     RND(100) < change->random_percentage))
10117         {
10118           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10119             RemoveMovingField(ex, ey);
10120
10121           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10122
10123           content_element = change->target_content.e[xx][yy];
10124           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10125                                               ce_value, ce_score);
10126
10127           CreateElementFromChange(ex, ey, target_element);
10128
10129           something_has_changed = TRUE;
10130
10131           /* for symmetry reasons, freeze newly created border elements */
10132           if (ex != x || ey != y)
10133             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10134         }
10135       }
10136
10137       if (something_has_changed)
10138       {
10139         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10140         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10141       }
10142     }
10143   }
10144   else
10145   {
10146     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10147                                         ce_value, ce_score);
10148
10149     if (element == EL_DIAGONAL_GROWING ||
10150         element == EL_DIAGONAL_SHRINKING)
10151     {
10152       target_element = Store[x][y];
10153
10154       Store[x][y] = EL_EMPTY;
10155     }
10156
10157     CreateElementFromChange(x, y, target_element);
10158
10159     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10160     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10161   }
10162
10163   /* this uses direct change before indirect change */
10164   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10165
10166   return TRUE;
10167 }
10168
10169 static void HandleElementChange(int x, int y, int page)
10170 {
10171   int element = MovingOrBlocked2Element(x, y);
10172   struct ElementInfo *ei = &element_info[element];
10173   struct ElementChangeInfo *change = &ei->change_page[page];
10174   boolean handle_action_before_change = FALSE;
10175
10176 #ifdef DEBUG
10177   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10178       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10179   {
10180     printf("\n\n");
10181     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10182            x, y, element, element_info[element].token_name);
10183     printf("HandleElementChange(): This should never happen!\n");
10184     printf("\n\n");
10185   }
10186 #endif
10187
10188   /* this can happen with classic bombs on walkable, changing elements */
10189   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10190   {
10191     return;
10192   }
10193
10194   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10195   {
10196     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10197
10198     if (change->can_change)
10199     {
10200       /* !!! not clear why graphic animation should be reset at all here !!! */
10201       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10202       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10203
10204       /*
10205         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10206
10207         When using an animation frame delay of 1 (this only happens with
10208         "sp_zonk.moving.left/right" in the classic graphics), the default
10209         (non-moving) animation shows wrong animation frames (while the
10210         moving animation, like "sp_zonk.moving.left/right", is correct,
10211         so this graphical bug never shows up with the classic graphics).
10212         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10213         be drawn instead of the correct frames 0,1,2,3. This is caused by
10214         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10215         an element change: First when the change delay ("ChangeDelay[][]")
10216         counter has reached zero after decrementing, then a second time in
10217         the next frame (after "GfxFrame[][]" was already incremented) when
10218         "ChangeDelay[][]" is reset to the initial delay value again.
10219
10220         This causes frame 0 to be drawn twice, while the last frame won't
10221         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10222
10223         As some animations may already be cleverly designed around this bug
10224         (at least the "Snake Bite" snake tail animation does this), it cannot
10225         simply be fixed here without breaking such existing animations.
10226         Unfortunately, it cannot easily be detected if a graphics set was
10227         designed "before" or "after" the bug was fixed. As a workaround,
10228         a new graphics set option "game.graphics_engine_version" was added
10229         to be able to specify the game's major release version for which the
10230         graphics set was designed, which can then be used to decide if the
10231         bugfix should be used (version 4 and above) or not (version 3 or
10232         below, or if no version was specified at all, as with old sets).
10233
10234         (The wrong/fixed animation frames can be tested with the test level set
10235         "test_gfxframe" and level "000", which contains a specially prepared
10236         custom element at level position (x/y) == (11/9) which uses the zonk
10237         animation mentioned above. Using "game.graphics_engine_version: 4"
10238         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10239         This can also be seen from the debug output for this test element.)
10240       */
10241
10242       /* when a custom element is about to change (for example by change delay),
10243          do not reset graphic animation when the custom element is moving */
10244       if (game.graphics_engine_version < 4 &&
10245           !IS_MOVING(x, y))
10246       {
10247         ResetGfxAnimation(x, y);
10248         ResetRandomAnimationValue(x, y);
10249       }
10250
10251       if (change->pre_change_function)
10252         change->pre_change_function(x, y);
10253     }
10254   }
10255
10256   ChangeDelay[x][y]--;
10257
10258   if (ChangeDelay[x][y] != 0)           /* continue element change */
10259   {
10260     if (change->can_change)
10261     {
10262       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10263
10264       if (IS_ANIMATED(graphic))
10265         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10266
10267       if (change->change_function)
10268         change->change_function(x, y);
10269     }
10270   }
10271   else                                  /* finish element change */
10272   {
10273     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10274     {
10275       page = ChangePage[x][y];
10276       ChangePage[x][y] = -1;
10277
10278       change = &ei->change_page[page];
10279     }
10280
10281     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10282     {
10283       ChangeDelay[x][y] = 1;            /* try change after next move step */
10284       ChangePage[x][y] = page;          /* remember page to use for change */
10285
10286       return;
10287     }
10288
10289     /* special case: set new level random seed before changing element */
10290     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10291       handle_action_before_change = TRUE;
10292
10293     if (change->has_action && handle_action_before_change)
10294       ExecuteCustomElementAction(x, y, element, page);
10295
10296     if (change->can_change)
10297     {
10298       if (ChangeElement(x, y, element, page))
10299       {
10300         if (change->post_change_function)
10301           change->post_change_function(x, y);
10302       }
10303     }
10304
10305     if (change->has_action && !handle_action_before_change)
10306       ExecuteCustomElementAction(x, y, element, page);
10307   }
10308 }
10309
10310 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10311                                               int trigger_element,
10312                                               int trigger_event,
10313                                               int trigger_player,
10314                                               int trigger_side,
10315                                               int trigger_page)
10316 {
10317   boolean change_done_any = FALSE;
10318   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10319   int i;
10320
10321   if (!(trigger_events[trigger_element][trigger_event]))
10322     return FALSE;
10323
10324   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10325
10326   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10327   {
10328     int element = EL_CUSTOM_START + i;
10329     boolean change_done = FALSE;
10330     int p;
10331
10332     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10333         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10334       continue;
10335
10336     for (p = 0; p < element_info[element].num_change_pages; p++)
10337     {
10338       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10339
10340       if (change->can_change_or_has_action &&
10341           change->has_event[trigger_event] &&
10342           change->trigger_side & trigger_side &&
10343           change->trigger_player & trigger_player &&
10344           change->trigger_page & trigger_page_bits &&
10345           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10346       {
10347         change->actual_trigger_element = trigger_element;
10348         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10349         change->actual_trigger_player_bits = trigger_player;
10350         change->actual_trigger_side = trigger_side;
10351         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10352         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10353
10354         if ((change->can_change && !change_done) || change->has_action)
10355         {
10356           int x, y;
10357
10358           SCAN_PLAYFIELD(x, y)
10359           {
10360             if (Feld[x][y] == element)
10361             {
10362               if (change->can_change && !change_done)
10363               {
10364                 /* if element already changed in this frame, not only prevent
10365                    another element change (checked in ChangeElement()), but
10366                    also prevent additional element actions for this element */
10367
10368                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10369                     !level.use_action_after_change_bug)
10370                   continue;
10371
10372                 ChangeDelay[x][y] = 1;
10373                 ChangeEvent[x][y] = trigger_event;
10374
10375                 HandleElementChange(x, y, p);
10376               }
10377               else if (change->has_action)
10378               {
10379                 /* if element already changed in this frame, not only prevent
10380                    another element change (checked in ChangeElement()), but
10381                    also prevent additional element actions for this element */
10382
10383                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10384                     !level.use_action_after_change_bug)
10385                   continue;
10386
10387                 ExecuteCustomElementAction(x, y, element, p);
10388                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10389               }
10390             }
10391           }
10392
10393           if (change->can_change)
10394           {
10395             change_done = TRUE;
10396             change_done_any = TRUE;
10397           }
10398         }
10399       }
10400     }
10401   }
10402
10403   RECURSION_LOOP_DETECTION_END();
10404
10405   return change_done_any;
10406 }
10407
10408 static boolean CheckElementChangeExt(int x, int y,
10409                                      int element,
10410                                      int trigger_element,
10411                                      int trigger_event,
10412                                      int trigger_player,
10413                                      int trigger_side)
10414 {
10415   boolean change_done = FALSE;
10416   int p;
10417
10418   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10419       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10420     return FALSE;
10421
10422   if (Feld[x][y] == EL_BLOCKED)
10423   {
10424     Blocked2Moving(x, y, &x, &y);
10425     element = Feld[x][y];
10426   }
10427
10428   /* check if element has already changed or is about to change after moving */
10429   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10430        Feld[x][y] != element) ||
10431
10432       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10433        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10434         ChangePage[x][y] != -1)))
10435     return FALSE;
10436
10437   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10438
10439   for (p = 0; p < element_info[element].num_change_pages; p++)
10440   {
10441     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10442
10443     /* check trigger element for all events where the element that is checked
10444        for changing interacts with a directly adjacent element -- this is
10445        different to element changes that affect other elements to change on the
10446        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10447     boolean check_trigger_element =
10448       (trigger_event == CE_TOUCHING_X ||
10449        trigger_event == CE_HITTING_X ||
10450        trigger_event == CE_HIT_BY_X ||
10451        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10452
10453     if (change->can_change_or_has_action &&
10454         change->has_event[trigger_event] &&
10455         change->trigger_side & trigger_side &&
10456         change->trigger_player & trigger_player &&
10457         (!check_trigger_element ||
10458          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10459     {
10460       change->actual_trigger_element = trigger_element;
10461       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10462       change->actual_trigger_player_bits = trigger_player;
10463       change->actual_trigger_side = trigger_side;
10464       change->actual_trigger_ce_value = CustomValue[x][y];
10465       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10466
10467       /* special case: trigger element not at (x,y) position for some events */
10468       if (check_trigger_element)
10469       {
10470         static struct
10471         {
10472           int dx, dy;
10473         } move_xy[] =
10474           {
10475             {  0,  0 },
10476             { -1,  0 },
10477             { +1,  0 },
10478             {  0,  0 },
10479             {  0, -1 },
10480             {  0,  0 }, { 0, 0 }, { 0, 0 },
10481             {  0, +1 }
10482           };
10483
10484         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10485         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10486
10487         change->actual_trigger_ce_value = CustomValue[xx][yy];
10488         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10489       }
10490
10491       if (change->can_change && !change_done)
10492       {
10493         ChangeDelay[x][y] = 1;
10494         ChangeEvent[x][y] = trigger_event;
10495
10496         HandleElementChange(x, y, p);
10497
10498         change_done = TRUE;
10499       }
10500       else if (change->has_action)
10501       {
10502         ExecuteCustomElementAction(x, y, element, p);
10503         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10504       }
10505     }
10506   }
10507
10508   RECURSION_LOOP_DETECTION_END();
10509
10510   return change_done;
10511 }
10512
10513 static void PlayPlayerSound(struct PlayerInfo *player)
10514 {
10515   int jx = player->jx, jy = player->jy;
10516   int sound_element = player->artwork_element;
10517   int last_action = player->last_action_waiting;
10518   int action = player->action_waiting;
10519
10520   if (player->is_waiting)
10521   {
10522     if (action != last_action)
10523       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10524     else
10525       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10526   }
10527   else
10528   {
10529     if (action != last_action)
10530       StopSound(element_info[sound_element].sound[last_action]);
10531
10532     if (last_action == ACTION_SLEEPING)
10533       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10534   }
10535 }
10536
10537 static void PlayAllPlayersSound()
10538 {
10539   int i;
10540
10541   for (i = 0; i < MAX_PLAYERS; i++)
10542     if (stored_player[i].active)
10543       PlayPlayerSound(&stored_player[i]);
10544 }
10545
10546 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10547 {
10548   boolean last_waiting = player->is_waiting;
10549   int move_dir = player->MovDir;
10550
10551   player->dir_waiting = move_dir;
10552   player->last_action_waiting = player->action_waiting;
10553
10554   if (is_waiting)
10555   {
10556     if (!last_waiting)          /* not waiting -> waiting */
10557     {
10558       player->is_waiting = TRUE;
10559
10560       player->frame_counter_bored =
10561         FrameCounter +
10562         game.player_boring_delay_fixed +
10563         GetSimpleRandom(game.player_boring_delay_random);
10564       player->frame_counter_sleeping =
10565         FrameCounter +
10566         game.player_sleeping_delay_fixed +
10567         GetSimpleRandom(game.player_sleeping_delay_random);
10568
10569       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10570     }
10571
10572     if (game.player_sleeping_delay_fixed +
10573         game.player_sleeping_delay_random > 0 &&
10574         player->anim_delay_counter == 0 &&
10575         player->post_delay_counter == 0 &&
10576         FrameCounter >= player->frame_counter_sleeping)
10577       player->is_sleeping = TRUE;
10578     else if (game.player_boring_delay_fixed +
10579              game.player_boring_delay_random > 0 &&
10580              FrameCounter >= player->frame_counter_bored)
10581       player->is_bored = TRUE;
10582
10583     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10584                               player->is_bored ? ACTION_BORING :
10585                               ACTION_WAITING);
10586
10587     if (player->is_sleeping && player->use_murphy)
10588     {
10589       /* special case for sleeping Murphy when leaning against non-free tile */
10590
10591       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10592           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10593            !IS_MOVING(player->jx - 1, player->jy)))
10594         move_dir = MV_LEFT;
10595       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10596                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10597                 !IS_MOVING(player->jx + 1, player->jy)))
10598         move_dir = MV_RIGHT;
10599       else
10600         player->is_sleeping = FALSE;
10601
10602       player->dir_waiting = move_dir;
10603     }
10604
10605     if (player->is_sleeping)
10606     {
10607       if (player->num_special_action_sleeping > 0)
10608       {
10609         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10610         {
10611           int last_special_action = player->special_action_sleeping;
10612           int num_special_action = player->num_special_action_sleeping;
10613           int special_action =
10614             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10615              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10616              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10617              last_special_action + 1 : ACTION_SLEEPING);
10618           int special_graphic =
10619             el_act_dir2img(player->artwork_element, special_action, move_dir);
10620
10621           player->anim_delay_counter =
10622             graphic_info[special_graphic].anim_delay_fixed +
10623             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10624           player->post_delay_counter =
10625             graphic_info[special_graphic].post_delay_fixed +
10626             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10627
10628           player->special_action_sleeping = special_action;
10629         }
10630
10631         if (player->anim_delay_counter > 0)
10632         {
10633           player->action_waiting = player->special_action_sleeping;
10634           player->anim_delay_counter--;
10635         }
10636         else if (player->post_delay_counter > 0)
10637         {
10638           player->post_delay_counter--;
10639         }
10640       }
10641     }
10642     else if (player->is_bored)
10643     {
10644       if (player->num_special_action_bored > 0)
10645       {
10646         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10647         {
10648           int special_action =
10649             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10650           int special_graphic =
10651             el_act_dir2img(player->artwork_element, special_action, move_dir);
10652
10653           player->anim_delay_counter =
10654             graphic_info[special_graphic].anim_delay_fixed +
10655             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10656           player->post_delay_counter =
10657             graphic_info[special_graphic].post_delay_fixed +
10658             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10659
10660           player->special_action_bored = special_action;
10661         }
10662
10663         if (player->anim_delay_counter > 0)
10664         {
10665           player->action_waiting = player->special_action_bored;
10666           player->anim_delay_counter--;
10667         }
10668         else if (player->post_delay_counter > 0)
10669         {
10670           player->post_delay_counter--;
10671         }
10672       }
10673     }
10674   }
10675   else if (last_waiting)        /* waiting -> not waiting */
10676   {
10677     player->is_waiting = FALSE;
10678     player->is_bored = FALSE;
10679     player->is_sleeping = FALSE;
10680
10681     player->frame_counter_bored = -1;
10682     player->frame_counter_sleeping = -1;
10683
10684     player->anim_delay_counter = 0;
10685     player->post_delay_counter = 0;
10686
10687     player->dir_waiting = player->MovDir;
10688     player->action_waiting = ACTION_DEFAULT;
10689
10690     player->special_action_bored = ACTION_DEFAULT;
10691     player->special_action_sleeping = ACTION_DEFAULT;
10692   }
10693 }
10694
10695 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10696 {
10697   if ((!player->is_moving  && player->was_moving) ||
10698       (player->MovPos == 0 && player->was_moving) ||
10699       (player->is_snapping && !player->was_snapping) ||
10700       (player->is_dropping && !player->was_dropping))
10701   {
10702     if (!CheckSaveEngineSnapshotToList())
10703       return;
10704
10705     player->was_moving = FALSE;
10706     player->was_snapping = TRUE;
10707     player->was_dropping = TRUE;
10708   }
10709   else
10710   {
10711     if (player->is_moving)
10712       player->was_moving = TRUE;
10713
10714     if (!player->is_snapping)
10715       player->was_snapping = FALSE;
10716
10717     if (!player->is_dropping)
10718       player->was_dropping = FALSE;
10719   }
10720 }
10721
10722 static void CheckSingleStepMode(struct PlayerInfo *player)
10723 {
10724   if (tape.single_step && tape.recording && !tape.pausing)
10725   {
10726     /* as it is called "single step mode", just return to pause mode when the
10727        player stopped moving after one tile (or never starts moving at all) */
10728     if (!player->is_moving && !player->is_pushing)
10729     {
10730       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10731       SnapField(player, 0, 0);                  /* stop snapping */
10732     }
10733   }
10734
10735   CheckSaveEngineSnapshot(player);
10736 }
10737
10738 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10739 {
10740   int left      = player_action & JOY_LEFT;
10741   int right     = player_action & JOY_RIGHT;
10742   int up        = player_action & JOY_UP;
10743   int down      = player_action & JOY_DOWN;
10744   int button1   = player_action & JOY_BUTTON_1;
10745   int button2   = player_action & JOY_BUTTON_2;
10746   int dx        = (left ? -1 : right ? 1 : 0);
10747   int dy        = (up   ? -1 : down  ? 1 : 0);
10748
10749   if (!player->active || tape.pausing)
10750     return 0;
10751
10752   if (player_action)
10753   {
10754     if (button1)
10755       SnapField(player, dx, dy);
10756     else
10757     {
10758       if (button2)
10759         DropElement(player);
10760
10761       MovePlayer(player, dx, dy);
10762     }
10763
10764     CheckSingleStepMode(player);
10765
10766     SetPlayerWaiting(player, FALSE);
10767
10768     return player_action;
10769   }
10770   else
10771   {
10772     /* no actions for this player (no input at player's configured device) */
10773
10774     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10775     SnapField(player, 0, 0);
10776     CheckGravityMovementWhenNotMoving(player);
10777
10778     if (player->MovPos == 0)
10779       SetPlayerWaiting(player, TRUE);
10780
10781     if (player->MovPos == 0)    /* needed for tape.playing */
10782       player->is_moving = FALSE;
10783
10784     player->is_dropping = FALSE;
10785     player->is_dropping_pressed = FALSE;
10786     player->drop_pressed_delay = 0;
10787
10788     CheckSingleStepMode(player);
10789
10790     return 0;
10791   }
10792 }
10793
10794 static void CheckLevelTime()
10795 {
10796   int i;
10797
10798   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10799   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10800   {
10801     if (level.native_em_level->lev->home == 0)  /* all players at home */
10802     {
10803       PlayerWins(local_player);
10804
10805       AllPlayersGone = TRUE;
10806
10807       level.native_em_level->lev->home = -1;
10808     }
10809
10810     if (level.native_em_level->ply[0]->alive == 0 &&
10811         level.native_em_level->ply[1]->alive == 0 &&
10812         level.native_em_level->ply[2]->alive == 0 &&
10813         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10814       AllPlayersGone = TRUE;
10815   }
10816   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10817   {
10818     if (game_sp.LevelSolved &&
10819         !game_sp.GameOver)                              /* game won */
10820     {
10821       PlayerWins(local_player);
10822
10823       game_sp.GameOver = TRUE;
10824
10825       AllPlayersGone = TRUE;
10826     }
10827
10828     if (game_sp.GameOver)                               /* game lost */
10829       AllPlayersGone = TRUE;
10830   }
10831
10832   if (TimeFrames >= FRAMES_PER_SECOND)
10833   {
10834     TimeFrames = 0;
10835     TapeTime++;
10836
10837     for (i = 0; i < MAX_PLAYERS; i++)
10838     {
10839       struct PlayerInfo *player = &stored_player[i];
10840
10841       if (SHIELD_ON(player))
10842       {
10843         player->shield_normal_time_left--;
10844
10845         if (player->shield_deadly_time_left > 0)
10846           player->shield_deadly_time_left--;
10847       }
10848     }
10849
10850     if (!local_player->LevelSolved && !level.use_step_counter)
10851     {
10852       TimePlayed++;
10853
10854       if (TimeLeft > 0)
10855       {
10856         TimeLeft--;
10857
10858         if (TimeLeft <= 10 && setup.time_limit)
10859           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10860
10861         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10862            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10863
10864         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10865
10866         if (!TimeLeft && setup.time_limit)
10867         {
10868           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10869             level.native_em_level->lev->killed_out_of_time = TRUE;
10870           else
10871             for (i = 0; i < MAX_PLAYERS; i++)
10872               KillPlayer(&stored_player[i]);
10873         }
10874       }
10875       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10876       {
10877         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10878       }
10879
10880       level.native_em_level->lev->time =
10881         (game.no_time_limit ? TimePlayed : TimeLeft);
10882     }
10883
10884     if (tape.recording || tape.playing)
10885       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10886   }
10887
10888   if (tape.recording || tape.playing)
10889     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10890
10891   UpdateAndDisplayGameControlValues();
10892 }
10893
10894 void AdvanceFrameAndPlayerCounters(int player_nr)
10895 {
10896   int i;
10897
10898   /* advance frame counters (global frame counter and time frame counter) */
10899   FrameCounter++;
10900   TimeFrames++;
10901
10902   /* advance player counters (counters for move delay, move animation etc.) */
10903   for (i = 0; i < MAX_PLAYERS; i++)
10904   {
10905     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10906     int move_delay_value = stored_player[i].move_delay_value;
10907     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10908
10909     if (!advance_player_counters)       /* not all players may be affected */
10910       continue;
10911
10912     if (move_frames == 0)       /* less than one move per game frame */
10913     {
10914       int stepsize = TILEX / move_delay_value;
10915       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10916       int count = (stored_player[i].is_moving ?
10917                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10918
10919       if (count % delay == 0)
10920         move_frames = 1;
10921     }
10922
10923     stored_player[i].Frame += move_frames;
10924
10925     if (stored_player[i].MovPos != 0)
10926       stored_player[i].StepFrame += move_frames;
10927
10928     if (stored_player[i].move_delay > 0)
10929       stored_player[i].move_delay--;
10930
10931     /* due to bugs in previous versions, counter must count up, not down */
10932     if (stored_player[i].push_delay != -1)
10933       stored_player[i].push_delay++;
10934
10935     if (stored_player[i].drop_delay > 0)
10936       stored_player[i].drop_delay--;
10937
10938     if (stored_player[i].is_dropping_pressed)
10939       stored_player[i].drop_pressed_delay++;
10940   }
10941 }
10942
10943 void StartGameActions(boolean init_network_game, boolean record_tape,
10944                       int random_seed)
10945 {
10946   unsigned int new_random_seed = InitRND(random_seed);
10947
10948   if (record_tape)
10949     TapeStartRecording(new_random_seed);
10950
10951 #if defined(NETWORK_AVALIABLE)
10952   if (init_network_game)
10953   {
10954     SendToServer_StartPlaying();
10955
10956     return;
10957   }
10958 #endif
10959
10960   InitGame();
10961 }
10962
10963 void GameActionsExt()
10964 {
10965 #if 0
10966   static unsigned int game_frame_delay = 0;
10967 #endif
10968   unsigned int game_frame_delay_value;
10969   byte *recorded_player_action;
10970   byte summarized_player_action = 0;
10971   byte tape_action[MAX_PLAYERS];
10972   int i;
10973
10974   /* detect endless loops, caused by custom element programming */
10975   if (recursion_loop_detected && recursion_loop_depth == 0)
10976   {
10977     char *message = getStringCat3("Internal Error! Element ",
10978                                   EL_NAME(recursion_loop_element),
10979                                   " caused endless loop! Quit the game?");
10980
10981     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10982           EL_NAME(recursion_loop_element));
10983
10984     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10985
10986     recursion_loop_detected = FALSE;    /* if game should be continued */
10987
10988     free(message);
10989
10990     return;
10991   }
10992
10993   if (game.restart_level)
10994     StartGameActions(options.network, setup.autorecord, level.random_seed);
10995
10996   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10997   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10998   {
10999     if (level.native_em_level->lev->home == 0)  /* all players at home */
11000     {
11001       PlayerWins(local_player);
11002
11003       AllPlayersGone = TRUE;
11004
11005       level.native_em_level->lev->home = -1;
11006     }
11007
11008     if (level.native_em_level->ply[0]->alive == 0 &&
11009         level.native_em_level->ply[1]->alive == 0 &&
11010         level.native_em_level->ply[2]->alive == 0 &&
11011         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11012       AllPlayersGone = TRUE;
11013   }
11014   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11015   {
11016     if (game_sp.LevelSolved &&
11017         !game_sp.GameOver)                              /* game won */
11018     {
11019       PlayerWins(local_player);
11020
11021       game_sp.GameOver = TRUE;
11022
11023       AllPlayersGone = TRUE;
11024     }
11025
11026     if (game_sp.GameOver)                               /* game lost */
11027       AllPlayersGone = TRUE;
11028   }
11029
11030   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11031     GameWon();
11032
11033   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11034     TapeStop();
11035
11036   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11037     return;
11038
11039   game_frame_delay_value =
11040     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11041
11042   if (tape.playing && tape.warp_forward && !tape.pausing)
11043     game_frame_delay_value = 0;
11044
11045   SetVideoFrameDelay(game_frame_delay_value);
11046
11047 #if 0
11048 #if 0
11049   /* ---------- main game synchronization point ---------- */
11050
11051   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11052
11053   printf("::: skip == %d\n", skip);
11054
11055 #else
11056   /* ---------- main game synchronization point ---------- */
11057
11058   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11059 #endif
11060 #endif
11061
11062   if (network_playing && !network_player_action_received)
11063   {
11064     /* try to get network player actions in time */
11065
11066 #if defined(NETWORK_AVALIABLE)
11067     /* last chance to get network player actions without main loop delay */
11068     HandleNetworking();
11069 #endif
11070
11071     /* game was quit by network peer */
11072     if (game_status != GAME_MODE_PLAYING)
11073       return;
11074
11075     if (!network_player_action_received)
11076       return;           /* failed to get network player actions in time */
11077
11078     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11079   }
11080
11081   if (tape.pausing)
11082     return;
11083
11084   /* at this point we know that we really continue executing the game */
11085
11086   network_player_action_received = FALSE;
11087
11088   /* when playing tape, read previously recorded player input from tape data */
11089   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11090
11091   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11092   if (tape.pausing)
11093     return;
11094
11095   if (tape.set_centered_player)
11096   {
11097     game.centered_player_nr_next = tape.centered_player_nr_next;
11098     game.set_centered_player = TRUE;
11099   }
11100
11101   for (i = 0; i < MAX_PLAYERS; i++)
11102   {
11103     summarized_player_action |= stored_player[i].action;
11104
11105     if (!network_playing && (game.team_mode || tape.playing))
11106       stored_player[i].effective_action = stored_player[i].action;
11107   }
11108
11109 #if defined(NETWORK_AVALIABLE)
11110   if (network_playing)
11111     SendToServer_MovePlayer(summarized_player_action);
11112 #endif
11113
11114   // summarize all actions at local players mapped input device position
11115   // (this allows using different input devices in single player mode)
11116   if (!options.network && !game.team_mode)
11117     stored_player[map_player_action[local_player->index_nr]].effective_action =
11118       summarized_player_action;
11119
11120   if (tape.recording &&
11121       setup.team_mode &&
11122       setup.input_on_focus &&
11123       game.centered_player_nr != -1)
11124   {
11125     for (i = 0; i < MAX_PLAYERS; i++)
11126       stored_player[i].effective_action =
11127         (i == game.centered_player_nr ? summarized_player_action : 0);
11128   }
11129
11130   if (recorded_player_action != NULL)
11131     for (i = 0; i < MAX_PLAYERS; i++)
11132       stored_player[i].effective_action = recorded_player_action[i];
11133
11134   for (i = 0; i < MAX_PLAYERS; i++)
11135   {
11136     tape_action[i] = stored_player[i].effective_action;
11137
11138     /* (this may happen in the RND game engine if a player was not present on
11139        the playfield on level start, but appeared later from a custom element */
11140     if (setup.team_mode &&
11141         tape.recording &&
11142         tape_action[i] &&
11143         !tape.player_participates[i])
11144       tape.player_participates[i] = TRUE;
11145   }
11146
11147   /* only record actions from input devices, but not programmed actions */
11148   if (tape.recording)
11149     TapeRecordAction(tape_action);
11150
11151 #if USE_NEW_PLAYER_ASSIGNMENTS
11152   // !!! also map player actions in single player mode !!!
11153   // if (game.team_mode)
11154   if (1)
11155   {
11156     byte mapped_action[MAX_PLAYERS];
11157
11158 #if DEBUG_PLAYER_ACTIONS
11159     printf(":::");
11160     for (i = 0; i < MAX_PLAYERS; i++)
11161       printf(" %d, ", stored_player[i].effective_action);
11162 #endif
11163
11164     for (i = 0; i < MAX_PLAYERS; i++)
11165       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11166
11167     for (i = 0; i < MAX_PLAYERS; i++)
11168       stored_player[i].effective_action = mapped_action[i];
11169
11170 #if DEBUG_PLAYER_ACTIONS
11171     printf(" =>");
11172     for (i = 0; i < MAX_PLAYERS; i++)
11173       printf(" %d, ", stored_player[i].effective_action);
11174     printf("\n");
11175 #endif
11176   }
11177 #if DEBUG_PLAYER_ACTIONS
11178   else
11179   {
11180     printf(":::");
11181     for (i = 0; i < MAX_PLAYERS; i++)
11182       printf(" %d, ", stored_player[i].effective_action);
11183     printf("\n");
11184   }
11185 #endif
11186 #endif
11187
11188   for (i = 0; i < MAX_PLAYERS; i++)
11189   {
11190     // allow engine snapshot in case of changed movement attempt
11191     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11192         (stored_player[i].effective_action & KEY_MOTION))
11193       game.snapshot.changed_action = TRUE;
11194
11195     // allow engine snapshot in case of snapping/dropping attempt
11196     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11197         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11198       game.snapshot.changed_action = TRUE;
11199
11200     game.snapshot.last_action[i] = stored_player[i].effective_action;
11201   }
11202
11203   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11204   {
11205     GameActions_EM_Main();
11206   }
11207   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11208   {
11209     GameActions_SP_Main();
11210   }
11211   else
11212   {
11213     GameActions_RND_Main();
11214   }
11215
11216   BlitScreenToBitmap(backbuffer);
11217
11218   CheckLevelTime();
11219
11220   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11221
11222   if (options.debug)                    /* calculate frames per second */
11223   {
11224     static unsigned int fps_counter = 0;
11225     static int fps_frames = 0;
11226     unsigned int fps_delay_ms = Counter() - fps_counter;
11227
11228     fps_frames++;
11229
11230     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11231     {
11232       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11233
11234       fps_frames = 0;
11235       fps_counter = Counter();
11236     }
11237
11238     redraw_mask |= REDRAW_FPS;
11239   }
11240 }
11241
11242 static void GameActions_CheckSaveEngineSnapshot()
11243 {
11244   if (!game.snapshot.save_snapshot)
11245     return;
11246
11247   // clear flag for saving snapshot _before_ saving snapshot
11248   game.snapshot.save_snapshot = FALSE;
11249
11250   SaveEngineSnapshotToList();
11251 }
11252
11253 void GameActions()
11254 {
11255   GameActionsExt();
11256
11257   GameActions_CheckSaveEngineSnapshot();
11258 }
11259
11260 void GameActions_EM_Main()
11261 {
11262   byte effective_action[MAX_PLAYERS];
11263   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11264   int i;
11265
11266   for (i = 0; i < MAX_PLAYERS; i++)
11267     effective_action[i] = stored_player[i].effective_action;
11268
11269   GameActions_EM(effective_action, warp_mode);
11270 }
11271
11272 void GameActions_SP_Main()
11273 {
11274   byte effective_action[MAX_PLAYERS];
11275   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11276   int i;
11277
11278   for (i = 0; i < MAX_PLAYERS; i++)
11279     effective_action[i] = stored_player[i].effective_action;
11280
11281   GameActions_SP(effective_action, warp_mode);
11282 }
11283
11284 void GameActions_RND_Main()
11285 {
11286   GameActions_RND();
11287 }
11288
11289 void GameActions_RND()
11290 {
11291   int magic_wall_x = 0, magic_wall_y = 0;
11292   int i, x, y, element, graphic;
11293
11294   InitPlayfieldScanModeVars();
11295
11296   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11297   {
11298     SCAN_PLAYFIELD(x, y)
11299     {
11300       ChangeCount[x][y] = 0;
11301       ChangeEvent[x][y] = -1;
11302     }
11303   }
11304
11305   if (game.set_centered_player)
11306   {
11307     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11308
11309     /* switching to "all players" only possible if all players fit to screen */
11310     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11311     {
11312       game.centered_player_nr_next = game.centered_player_nr;
11313       game.set_centered_player = FALSE;
11314     }
11315
11316     /* do not switch focus to non-existing (or non-active) player */
11317     if (game.centered_player_nr_next >= 0 &&
11318         !stored_player[game.centered_player_nr_next].active)
11319     {
11320       game.centered_player_nr_next = game.centered_player_nr;
11321       game.set_centered_player = FALSE;
11322     }
11323   }
11324
11325   if (game.set_centered_player &&
11326       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11327   {
11328     int sx, sy;
11329
11330     if (game.centered_player_nr_next == -1)
11331     {
11332       setScreenCenteredToAllPlayers(&sx, &sy);
11333     }
11334     else
11335     {
11336       sx = stored_player[game.centered_player_nr_next].jx;
11337       sy = stored_player[game.centered_player_nr_next].jy;
11338     }
11339
11340     game.centered_player_nr = game.centered_player_nr_next;
11341     game.set_centered_player = FALSE;
11342
11343     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11344     DrawGameDoorValues();
11345   }
11346
11347   for (i = 0; i < MAX_PLAYERS; i++)
11348   {
11349     int actual_player_action = stored_player[i].effective_action;
11350
11351 #if 1
11352     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11353        - rnd_equinox_tetrachloride 048
11354        - rnd_equinox_tetrachloride_ii 096
11355        - rnd_emanuel_schmieg 002
11356        - doctor_sloan_ww 001, 020
11357     */
11358     if (stored_player[i].MovPos == 0)
11359       CheckGravityMovement(&stored_player[i]);
11360 #endif
11361
11362     /* overwrite programmed action with tape action */
11363     if (stored_player[i].programmed_action)
11364       actual_player_action = stored_player[i].programmed_action;
11365
11366     PlayerActions(&stored_player[i], actual_player_action);
11367
11368     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11369   }
11370
11371   ScrollScreen(NULL, SCROLL_GO_ON);
11372
11373   /* for backwards compatibility, the following code emulates a fixed bug that
11374      occured when pushing elements (causing elements that just made their last
11375      pushing step to already (if possible) make their first falling step in the
11376      same game frame, which is bad); this code is also needed to use the famous
11377      "spring push bug" which is used in older levels and might be wanted to be
11378      used also in newer levels, but in this case the buggy pushing code is only
11379      affecting the "spring" element and no other elements */
11380
11381   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11382   {
11383     for (i = 0; i < MAX_PLAYERS; i++)
11384     {
11385       struct PlayerInfo *player = &stored_player[i];
11386       int x = player->jx;
11387       int y = player->jy;
11388
11389       if (player->active && player->is_pushing && player->is_moving &&
11390           IS_MOVING(x, y) &&
11391           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11392            Feld[x][y] == EL_SPRING))
11393       {
11394         ContinueMoving(x, y);
11395
11396         /* continue moving after pushing (this is actually a bug) */
11397         if (!IS_MOVING(x, y))
11398           Stop[x][y] = FALSE;
11399       }
11400     }
11401   }
11402
11403   SCAN_PLAYFIELD(x, y)
11404   {
11405     ChangeCount[x][y] = 0;
11406     ChangeEvent[x][y] = -1;
11407
11408     /* this must be handled before main playfield loop */
11409     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11410     {
11411       MovDelay[x][y]--;
11412       if (MovDelay[x][y] <= 0)
11413         RemoveField(x, y);
11414     }
11415
11416     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11417     {
11418       MovDelay[x][y]--;
11419       if (MovDelay[x][y] <= 0)
11420       {
11421         RemoveField(x, y);
11422         TEST_DrawLevelField(x, y);
11423
11424         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11425       }
11426     }
11427
11428 #if DEBUG
11429     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11430     {
11431       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11432       printf("GameActions(): This should never happen!\n");
11433
11434       ChangePage[x][y] = -1;
11435     }
11436 #endif
11437
11438     Stop[x][y] = FALSE;
11439     if (WasJustMoving[x][y] > 0)
11440       WasJustMoving[x][y]--;
11441     if (WasJustFalling[x][y] > 0)
11442       WasJustFalling[x][y]--;
11443     if (CheckCollision[x][y] > 0)
11444       CheckCollision[x][y]--;
11445     if (CheckImpact[x][y] > 0)
11446       CheckImpact[x][y]--;
11447
11448     GfxFrame[x][y]++;
11449
11450     /* reset finished pushing action (not done in ContinueMoving() to allow
11451        continuous pushing animation for elements with zero push delay) */
11452     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11453     {
11454       ResetGfxAnimation(x, y);
11455       TEST_DrawLevelField(x, y);
11456     }
11457
11458 #if DEBUG
11459     if (IS_BLOCKED(x, y))
11460     {
11461       int oldx, oldy;
11462
11463       Blocked2Moving(x, y, &oldx, &oldy);
11464       if (!IS_MOVING(oldx, oldy))
11465       {
11466         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11467         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11468         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11469         printf("GameActions(): This should never happen!\n");
11470       }
11471     }
11472 #endif
11473   }
11474
11475   SCAN_PLAYFIELD(x, y)
11476   {
11477     element = Feld[x][y];
11478     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11479
11480     ResetGfxFrame(x, y, TRUE);
11481
11482     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11483         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11484       ResetRandomAnimationValue(x, y);
11485
11486     SetRandomAnimationValue(x, y);
11487
11488     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11489
11490     if (IS_INACTIVE(element))
11491     {
11492       if (IS_ANIMATED(graphic))
11493         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11494
11495       continue;
11496     }
11497
11498     /* this may take place after moving, so 'element' may have changed */
11499     if (IS_CHANGING(x, y) &&
11500         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11501     {
11502       int page = element_info[element].event_page_nr[CE_DELAY];
11503
11504       HandleElementChange(x, y, page);
11505
11506       element = Feld[x][y];
11507       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11508     }
11509
11510     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11511     {
11512       StartMoving(x, y);
11513
11514       element = Feld[x][y];
11515       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11516
11517       if (IS_ANIMATED(graphic) &&
11518           !IS_MOVING(x, y) &&
11519           !Stop[x][y])
11520         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11521
11522       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11523         TEST_DrawTwinkleOnField(x, y);
11524     }
11525     else if ((element == EL_ACID ||
11526               element == EL_EXIT_OPEN ||
11527               element == EL_EM_EXIT_OPEN ||
11528               element == EL_SP_EXIT_OPEN ||
11529               element == EL_STEEL_EXIT_OPEN ||
11530               element == EL_EM_STEEL_EXIT_OPEN ||
11531               element == EL_SP_TERMINAL ||
11532               element == EL_SP_TERMINAL_ACTIVE ||
11533               element == EL_EXTRA_TIME ||
11534               element == EL_SHIELD_NORMAL ||
11535               element == EL_SHIELD_DEADLY) &&
11536              IS_ANIMATED(graphic))
11537       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11538     else if (IS_MOVING(x, y))
11539       ContinueMoving(x, y);
11540     else if (IS_ACTIVE_BOMB(element))
11541       CheckDynamite(x, y);
11542     else if (element == EL_AMOEBA_GROWING)
11543       AmoebeWaechst(x, y);
11544     else if (element == EL_AMOEBA_SHRINKING)
11545       AmoebaDisappearing(x, y);
11546
11547 #if !USE_NEW_AMOEBA_CODE
11548     else if (IS_AMOEBALIVE(element))
11549       AmoebeAbleger(x, y);
11550 #endif
11551
11552     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11553       Life(x, y);
11554     else if (element == EL_EXIT_CLOSED)
11555       CheckExit(x, y);
11556     else if (element == EL_EM_EXIT_CLOSED)
11557       CheckExitEM(x, y);
11558     else if (element == EL_STEEL_EXIT_CLOSED)
11559       CheckExitSteel(x, y);
11560     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11561       CheckExitSteelEM(x, y);
11562     else if (element == EL_SP_EXIT_CLOSED)
11563       CheckExitSP(x, y);
11564     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11565              element == EL_EXPANDABLE_STEELWALL_GROWING)
11566       MauerWaechst(x, y);
11567     else if (element == EL_EXPANDABLE_WALL ||
11568              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11569              element == EL_EXPANDABLE_WALL_VERTICAL ||
11570              element == EL_EXPANDABLE_WALL_ANY ||
11571              element == EL_BD_EXPANDABLE_WALL)
11572       MauerAbleger(x, y);
11573     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11574              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11575              element == EL_EXPANDABLE_STEELWALL_ANY)
11576       MauerAblegerStahl(x, y);
11577     else if (element == EL_FLAMES)
11578       CheckForDragon(x, y);
11579     else if (element == EL_EXPLOSION)
11580       ; /* drawing of correct explosion animation is handled separately */
11581     else if (element == EL_ELEMENT_SNAPPING ||
11582              element == EL_DIAGONAL_SHRINKING ||
11583              element == EL_DIAGONAL_GROWING)
11584     {
11585       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11586
11587       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11588     }
11589     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11590       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11591
11592     if (IS_BELT_ACTIVE(element))
11593       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11594
11595     if (game.magic_wall_active)
11596     {
11597       int jx = local_player->jx, jy = local_player->jy;
11598
11599       /* play the element sound at the position nearest to the player */
11600       if ((element == EL_MAGIC_WALL_FULL ||
11601            element == EL_MAGIC_WALL_ACTIVE ||
11602            element == EL_MAGIC_WALL_EMPTYING ||
11603            element == EL_BD_MAGIC_WALL_FULL ||
11604            element == EL_BD_MAGIC_WALL_ACTIVE ||
11605            element == EL_BD_MAGIC_WALL_EMPTYING ||
11606            element == EL_DC_MAGIC_WALL_FULL ||
11607            element == EL_DC_MAGIC_WALL_ACTIVE ||
11608            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11609           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11610       {
11611         magic_wall_x = x;
11612         magic_wall_y = y;
11613       }
11614     }
11615   }
11616
11617 #if USE_NEW_AMOEBA_CODE
11618   /* new experimental amoeba growth stuff */
11619   if (!(FrameCounter % 8))
11620   {
11621     static unsigned int random = 1684108901;
11622
11623     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11624     {
11625       x = RND(lev_fieldx);
11626       y = RND(lev_fieldy);
11627       element = Feld[x][y];
11628
11629       if (!IS_PLAYER(x,y) &&
11630           (element == EL_EMPTY ||
11631            CAN_GROW_INTO(element) ||
11632            element == EL_QUICKSAND_EMPTY ||
11633            element == EL_QUICKSAND_FAST_EMPTY ||
11634            element == EL_ACID_SPLASH_LEFT ||
11635            element == EL_ACID_SPLASH_RIGHT))
11636       {
11637         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11638             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11639             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11640             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11641           Feld[x][y] = EL_AMOEBA_DROP;
11642       }
11643
11644       random = random * 129 + 1;
11645     }
11646   }
11647 #endif
11648
11649   game.explosions_delayed = FALSE;
11650
11651   SCAN_PLAYFIELD(x, y)
11652   {
11653     element = Feld[x][y];
11654
11655     if (ExplodeField[x][y])
11656       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11657     else if (element == EL_EXPLOSION)
11658       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11659
11660     ExplodeField[x][y] = EX_TYPE_NONE;
11661   }
11662
11663   game.explosions_delayed = TRUE;
11664
11665   if (game.magic_wall_active)
11666   {
11667     if (!(game.magic_wall_time_left % 4))
11668     {
11669       int element = Feld[magic_wall_x][magic_wall_y];
11670
11671       if (element == EL_BD_MAGIC_WALL_FULL ||
11672           element == EL_BD_MAGIC_WALL_ACTIVE ||
11673           element == EL_BD_MAGIC_WALL_EMPTYING)
11674         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11675       else if (element == EL_DC_MAGIC_WALL_FULL ||
11676                element == EL_DC_MAGIC_WALL_ACTIVE ||
11677                element == EL_DC_MAGIC_WALL_EMPTYING)
11678         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11679       else
11680         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11681     }
11682
11683     if (game.magic_wall_time_left > 0)
11684     {
11685       game.magic_wall_time_left--;
11686
11687       if (!game.magic_wall_time_left)
11688       {
11689         SCAN_PLAYFIELD(x, y)
11690         {
11691           element = Feld[x][y];
11692
11693           if (element == EL_MAGIC_WALL_ACTIVE ||
11694               element == EL_MAGIC_WALL_FULL)
11695           {
11696             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11697             TEST_DrawLevelField(x, y);
11698           }
11699           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11700                    element == EL_BD_MAGIC_WALL_FULL)
11701           {
11702             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11703             TEST_DrawLevelField(x, y);
11704           }
11705           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11706                    element == EL_DC_MAGIC_WALL_FULL)
11707           {
11708             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11709             TEST_DrawLevelField(x, y);
11710           }
11711         }
11712
11713         game.magic_wall_active = FALSE;
11714       }
11715     }
11716   }
11717
11718   if (game.light_time_left > 0)
11719   {
11720     game.light_time_left--;
11721
11722     if (game.light_time_left == 0)
11723       RedrawAllLightSwitchesAndInvisibleElements();
11724   }
11725
11726   if (game.timegate_time_left > 0)
11727   {
11728     game.timegate_time_left--;
11729
11730     if (game.timegate_time_left == 0)
11731       CloseAllOpenTimegates();
11732   }
11733
11734   if (game.lenses_time_left > 0)
11735   {
11736     game.lenses_time_left--;
11737
11738     if (game.lenses_time_left == 0)
11739       RedrawAllInvisibleElementsForLenses();
11740   }
11741
11742   if (game.magnify_time_left > 0)
11743   {
11744     game.magnify_time_left--;
11745
11746     if (game.magnify_time_left == 0)
11747       RedrawAllInvisibleElementsForMagnifier();
11748   }
11749
11750   for (i = 0; i < MAX_PLAYERS; i++)
11751   {
11752     struct PlayerInfo *player = &stored_player[i];
11753
11754     if (SHIELD_ON(player))
11755     {
11756       if (player->shield_deadly_time_left)
11757         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11758       else if (player->shield_normal_time_left)
11759         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11760     }
11761   }
11762
11763 #if USE_DELAYED_GFX_REDRAW
11764   SCAN_PLAYFIELD(x, y)
11765   {
11766     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11767     {
11768       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11769          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11770
11771       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11772         DrawLevelField(x, y);
11773
11774       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11775         DrawLevelFieldCrumbled(x, y);
11776
11777       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11778         DrawLevelFieldCrumbledNeighbours(x, y);
11779
11780       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11781         DrawTwinkleOnField(x, y);
11782     }
11783
11784     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11785   }
11786 #endif
11787
11788   DrawAllPlayers();
11789   PlayAllPlayersSound();
11790
11791   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11792   {
11793     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11794
11795     local_player->show_envelope = 0;
11796   }
11797
11798   /* use random number generator in every frame to make it less predictable */
11799   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11800     RND(1);
11801 }
11802
11803 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11804 {
11805   int min_x = x, min_y = y, max_x = x, max_y = y;
11806   int i;
11807
11808   for (i = 0; i < MAX_PLAYERS; i++)
11809   {
11810     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11811
11812     if (!stored_player[i].active || &stored_player[i] == player)
11813       continue;
11814
11815     min_x = MIN(min_x, jx);
11816     min_y = MIN(min_y, jy);
11817     max_x = MAX(max_x, jx);
11818     max_y = MAX(max_y, jy);
11819   }
11820
11821   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11822 }
11823
11824 static boolean AllPlayersInVisibleScreen()
11825 {
11826   int i;
11827
11828   for (i = 0; i < MAX_PLAYERS; i++)
11829   {
11830     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11831
11832     if (!stored_player[i].active)
11833       continue;
11834
11835     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11836       return FALSE;
11837   }
11838
11839   return TRUE;
11840 }
11841
11842 void ScrollLevel(int dx, int dy)
11843 {
11844   int scroll_offset = 2 * TILEX_VAR;
11845   int x, y;
11846
11847   BlitBitmap(drawto_field, drawto_field,
11848              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11849              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11850              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11851              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11852              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11853              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11854
11855   if (dx != 0)
11856   {
11857     x = (dx == 1 ? BX1 : BX2);
11858     for (y = BY1; y <= BY2; y++)
11859       DrawScreenField(x, y);
11860   }
11861
11862   if (dy != 0)
11863   {
11864     y = (dy == 1 ? BY1 : BY2);
11865     for (x = BX1; x <= BX2; x++)
11866       DrawScreenField(x, y);
11867   }
11868
11869   redraw_mask |= REDRAW_FIELD;
11870 }
11871
11872 static boolean canFallDown(struct PlayerInfo *player)
11873 {
11874   int jx = player->jx, jy = player->jy;
11875
11876   return (IN_LEV_FIELD(jx, jy + 1) &&
11877           (IS_FREE(jx, jy + 1) ||
11878            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11879           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11880           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11881 }
11882
11883 static boolean canPassField(int x, int y, int move_dir)
11884 {
11885   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11886   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11887   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11888   int nextx = x + dx;
11889   int nexty = y + dy;
11890   int element = Feld[x][y];
11891
11892   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11893           !CAN_MOVE(element) &&
11894           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11895           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11896           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11897 }
11898
11899 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11900 {
11901   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11902   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11903   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11904   int newx = x + dx;
11905   int newy = y + dy;
11906
11907   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11908           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11909           (IS_DIGGABLE(Feld[newx][newy]) ||
11910            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11911            canPassField(newx, newy, move_dir)));
11912 }
11913
11914 static void CheckGravityMovement(struct PlayerInfo *player)
11915 {
11916   if (player->gravity && !player->programmed_action)
11917   {
11918     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11919     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11920     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11921     int jx = player->jx, jy = player->jy;
11922     boolean player_is_moving_to_valid_field =
11923       (!player_is_snapping &&
11924        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11925         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11926     boolean player_can_fall_down = canFallDown(player);
11927
11928     if (player_can_fall_down &&
11929         !player_is_moving_to_valid_field)
11930       player->programmed_action = MV_DOWN;
11931   }
11932 }
11933
11934 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11935 {
11936   return CheckGravityMovement(player);
11937
11938   if (player->gravity && !player->programmed_action)
11939   {
11940     int jx = player->jx, jy = player->jy;
11941     boolean field_under_player_is_free =
11942       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11943     boolean player_is_standing_on_valid_field =
11944       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11945        (IS_WALKABLE(Feld[jx][jy]) &&
11946         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11947
11948     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11949       player->programmed_action = MV_DOWN;
11950   }
11951 }
11952
11953 /*
11954   MovePlayerOneStep()
11955   -----------------------------------------------------------------------------
11956   dx, dy:               direction (non-diagonal) to try to move the player to
11957   real_dx, real_dy:     direction as read from input device (can be diagonal)
11958 */
11959
11960 boolean MovePlayerOneStep(struct PlayerInfo *player,
11961                           int dx, int dy, int real_dx, int real_dy)
11962 {
11963   int jx = player->jx, jy = player->jy;
11964   int new_jx = jx + dx, new_jy = jy + dy;
11965   int can_move;
11966   boolean player_can_move = !player->cannot_move;
11967
11968   if (!player->active || (!dx && !dy))
11969     return MP_NO_ACTION;
11970
11971   player->MovDir = (dx < 0 ? MV_LEFT :
11972                     dx > 0 ? MV_RIGHT :
11973                     dy < 0 ? MV_UP :
11974                     dy > 0 ? MV_DOWN :  MV_NONE);
11975
11976   if (!IN_LEV_FIELD(new_jx, new_jy))
11977     return MP_NO_ACTION;
11978
11979   if (!player_can_move)
11980   {
11981     if (player->MovPos == 0)
11982     {
11983       player->is_moving = FALSE;
11984       player->is_digging = FALSE;
11985       player->is_collecting = FALSE;
11986       player->is_snapping = FALSE;
11987       player->is_pushing = FALSE;
11988     }
11989   }
11990
11991   if (!options.network && game.centered_player_nr == -1 &&
11992       !AllPlayersInSight(player, new_jx, new_jy))
11993     return MP_NO_ACTION;
11994
11995   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11996   if (can_move != MP_MOVING)
11997     return can_move;
11998
11999   /* check if DigField() has caused relocation of the player */
12000   if (player->jx != jx || player->jy != jy)
12001     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12002
12003   StorePlayer[jx][jy] = 0;
12004   player->last_jx = jx;
12005   player->last_jy = jy;
12006   player->jx = new_jx;
12007   player->jy = new_jy;
12008   StorePlayer[new_jx][new_jy] = player->element_nr;
12009
12010   if (player->move_delay_value_next != -1)
12011   {
12012     player->move_delay_value = player->move_delay_value_next;
12013     player->move_delay_value_next = -1;
12014   }
12015
12016   player->MovPos =
12017     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12018
12019   player->step_counter++;
12020
12021   PlayerVisit[jx][jy] = FrameCounter;
12022
12023   player->is_moving = TRUE;
12024
12025 #if 1
12026   /* should better be called in MovePlayer(), but this breaks some tapes */
12027   ScrollPlayer(player, SCROLL_INIT);
12028 #endif
12029
12030   return MP_MOVING;
12031 }
12032
12033 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12034 {
12035   int jx = player->jx, jy = player->jy;
12036   int old_jx = jx, old_jy = jy;
12037   int moved = MP_NO_ACTION;
12038
12039   if (!player->active)
12040     return FALSE;
12041
12042   if (!dx && !dy)
12043   {
12044     if (player->MovPos == 0)
12045     {
12046       player->is_moving = FALSE;
12047       player->is_digging = FALSE;
12048       player->is_collecting = FALSE;
12049       player->is_snapping = FALSE;
12050       player->is_pushing = FALSE;
12051     }
12052
12053     return FALSE;
12054   }
12055
12056   if (player->move_delay > 0)
12057     return FALSE;
12058
12059   player->move_delay = -1;              /* set to "uninitialized" value */
12060
12061   /* store if player is automatically moved to next field */
12062   player->is_auto_moving = (player->programmed_action != MV_NONE);
12063
12064   /* remove the last programmed player action */
12065   player->programmed_action = 0;
12066
12067   if (player->MovPos)
12068   {
12069     /* should only happen if pre-1.2 tape recordings are played */
12070     /* this is only for backward compatibility */
12071
12072     int original_move_delay_value = player->move_delay_value;
12073
12074 #if DEBUG
12075     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12076            tape.counter);
12077 #endif
12078
12079     /* scroll remaining steps with finest movement resolution */
12080     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12081
12082     while (player->MovPos)
12083     {
12084       ScrollPlayer(player, SCROLL_GO_ON);
12085       ScrollScreen(NULL, SCROLL_GO_ON);
12086
12087       AdvanceFrameAndPlayerCounters(player->index_nr);
12088
12089       DrawAllPlayers();
12090       BackToFront_WithFrameDelay(0);
12091     }
12092
12093     player->move_delay_value = original_move_delay_value;
12094   }
12095
12096   player->is_active = FALSE;
12097
12098   if (player->last_move_dir & MV_HORIZONTAL)
12099   {
12100     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12101       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12102   }
12103   else
12104   {
12105     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12106       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12107   }
12108
12109   if (!moved && !player->is_active)
12110   {
12111     player->is_moving = FALSE;
12112     player->is_digging = FALSE;
12113     player->is_collecting = FALSE;
12114     player->is_snapping = FALSE;
12115     player->is_pushing = FALSE;
12116   }
12117
12118   jx = player->jx;
12119   jy = player->jy;
12120
12121   if (moved & MP_MOVING && !ScreenMovPos &&
12122       (player->index_nr == game.centered_player_nr ||
12123        game.centered_player_nr == -1))
12124   {
12125     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12126     int offset = game.scroll_delay_value;
12127
12128     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12129     {
12130       /* actual player has left the screen -- scroll in that direction */
12131       if (jx != old_jx)         /* player has moved horizontally */
12132         scroll_x += (jx - old_jx);
12133       else                      /* player has moved vertically */
12134         scroll_y += (jy - old_jy);
12135     }
12136     else
12137     {
12138       if (jx != old_jx)         /* player has moved horizontally */
12139       {
12140         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12141             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12142           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12143
12144         /* don't scroll over playfield boundaries */
12145         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12146           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12147
12148         /* don't scroll more than one field at a time */
12149         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12150
12151         /* don't scroll against the player's moving direction */
12152         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12153             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12154           scroll_x = old_scroll_x;
12155       }
12156       else                      /* player has moved vertically */
12157       {
12158         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12159             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12160           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12161
12162         /* don't scroll over playfield boundaries */
12163         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12164           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12165
12166         /* don't scroll more than one field at a time */
12167         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12168
12169         /* don't scroll against the player's moving direction */
12170         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12171             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12172           scroll_y = old_scroll_y;
12173       }
12174     }
12175
12176     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12177     {
12178       if (!options.network && game.centered_player_nr == -1 &&
12179           !AllPlayersInVisibleScreen())
12180       {
12181         scroll_x = old_scroll_x;
12182         scroll_y = old_scroll_y;
12183       }
12184       else
12185       {
12186         ScrollScreen(player, SCROLL_INIT);
12187         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12188       }
12189     }
12190   }
12191
12192   player->StepFrame = 0;
12193
12194   if (moved & MP_MOVING)
12195   {
12196     if (old_jx != jx && old_jy == jy)
12197       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12198     else if (old_jx == jx && old_jy != jy)
12199       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12200
12201     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12202
12203     player->last_move_dir = player->MovDir;
12204     player->is_moving = TRUE;
12205     player->is_snapping = FALSE;
12206     player->is_switching = FALSE;
12207     player->is_dropping = FALSE;
12208     player->is_dropping_pressed = FALSE;
12209     player->drop_pressed_delay = 0;
12210
12211 #if 0
12212     /* should better be called here than above, but this breaks some tapes */
12213     ScrollPlayer(player, SCROLL_INIT);
12214 #endif
12215   }
12216   else
12217   {
12218     CheckGravityMovementWhenNotMoving(player);
12219
12220     player->is_moving = FALSE;
12221
12222     /* at this point, the player is allowed to move, but cannot move right now
12223        (e.g. because of something blocking the way) -- ensure that the player
12224        is also allowed to move in the next frame (in old versions before 3.1.1,
12225        the player was forced to wait again for eight frames before next try) */
12226
12227     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12228       player->move_delay = 0;   /* allow direct movement in the next frame */
12229   }
12230
12231   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12232     player->move_delay = player->move_delay_value;
12233
12234   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12235   {
12236     TestIfPlayerTouchesBadThing(jx, jy);
12237     TestIfPlayerTouchesCustomElement(jx, jy);
12238   }
12239
12240   if (!player->active)
12241     RemovePlayer(player);
12242
12243   return moved;
12244 }
12245
12246 void ScrollPlayer(struct PlayerInfo *player, int mode)
12247 {
12248   int jx = player->jx, jy = player->jy;
12249   int last_jx = player->last_jx, last_jy = player->last_jy;
12250   int move_stepsize = TILEX / player->move_delay_value;
12251
12252   if (!player->active)
12253     return;
12254
12255   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12256     return;
12257
12258   if (mode == SCROLL_INIT)
12259   {
12260     player->actual_frame_counter = FrameCounter;
12261     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12262
12263     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12264         Feld[last_jx][last_jy] == EL_EMPTY)
12265     {
12266       int last_field_block_delay = 0;   /* start with no blocking at all */
12267       int block_delay_adjustment = player->block_delay_adjustment;
12268
12269       /* if player blocks last field, add delay for exactly one move */
12270       if (player->block_last_field)
12271       {
12272         last_field_block_delay += player->move_delay_value;
12273
12274         /* when blocking enabled, prevent moving up despite gravity */
12275         if (player->gravity && player->MovDir == MV_UP)
12276           block_delay_adjustment = -1;
12277       }
12278
12279       /* add block delay adjustment (also possible when not blocking) */
12280       last_field_block_delay += block_delay_adjustment;
12281
12282       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12283       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12284     }
12285
12286     if (player->MovPos != 0)    /* player has not yet reached destination */
12287       return;
12288   }
12289   else if (!FrameReached(&player->actual_frame_counter, 1))
12290     return;
12291
12292   if (player->MovPos != 0)
12293   {
12294     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12295     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12296
12297     /* before DrawPlayer() to draw correct player graphic for this case */
12298     if (player->MovPos == 0)
12299       CheckGravityMovement(player);
12300   }
12301
12302   if (player->MovPos == 0)      /* player reached destination field */
12303   {
12304     if (player->move_delay_reset_counter > 0)
12305     {
12306       player->move_delay_reset_counter--;
12307
12308       if (player->move_delay_reset_counter == 0)
12309       {
12310         /* continue with normal speed after quickly moving through gate */
12311         HALVE_PLAYER_SPEED(player);
12312
12313         /* be able to make the next move without delay */
12314         player->move_delay = 0;
12315       }
12316     }
12317
12318     player->last_jx = jx;
12319     player->last_jy = jy;
12320
12321     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12322         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12323         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12324         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12325         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12326         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12327         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12328         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12329     {
12330       DrawPlayer(player);       /* needed here only to cleanup last field */
12331       RemovePlayer(player);
12332
12333       if (local_player->friends_still_needed == 0 ||
12334           IS_SP_ELEMENT(Feld[jx][jy]))
12335         PlayerWins(player);
12336     }
12337
12338     /* this breaks one level: "machine", level 000 */
12339     {
12340       int move_direction = player->MovDir;
12341       int enter_side = MV_DIR_OPPOSITE(move_direction);
12342       int leave_side = move_direction;
12343       int old_jx = last_jx;
12344       int old_jy = last_jy;
12345       int old_element = Feld[old_jx][old_jy];
12346       int new_element = Feld[jx][jy];
12347
12348       if (IS_CUSTOM_ELEMENT(old_element))
12349         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12350                                    CE_LEFT_BY_PLAYER,
12351                                    player->index_bit, leave_side);
12352
12353       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12354                                           CE_PLAYER_LEAVES_X,
12355                                           player->index_bit, leave_side);
12356
12357       if (IS_CUSTOM_ELEMENT(new_element))
12358         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12359                                    player->index_bit, enter_side);
12360
12361       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12362                                           CE_PLAYER_ENTERS_X,
12363                                           player->index_bit, enter_side);
12364
12365       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12366                                         CE_MOVE_OF_X, move_direction);
12367     }
12368
12369     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12370     {
12371       TestIfPlayerTouchesBadThing(jx, jy);
12372       TestIfPlayerTouchesCustomElement(jx, jy);
12373
12374       /* needed because pushed element has not yet reached its destination,
12375          so it would trigger a change event at its previous field location */
12376       if (!player->is_pushing)
12377         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12378
12379       if (!player->active)
12380         RemovePlayer(player);
12381     }
12382
12383     if (!local_player->LevelSolved && level.use_step_counter)
12384     {
12385       int i;
12386
12387       TimePlayed++;
12388
12389       if (TimeLeft > 0)
12390       {
12391         TimeLeft--;
12392
12393         if (TimeLeft <= 10 && setup.time_limit)
12394           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12395
12396         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12397
12398         DisplayGameControlValues();
12399
12400         if (!TimeLeft && setup.time_limit)
12401           for (i = 0; i < MAX_PLAYERS; i++)
12402             KillPlayer(&stored_player[i]);
12403       }
12404       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12405       {
12406         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12407
12408         DisplayGameControlValues();
12409       }
12410     }
12411
12412     if (tape.single_step && tape.recording && !tape.pausing &&
12413         !player->programmed_action)
12414       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12415
12416     if (!player->programmed_action)
12417       CheckSaveEngineSnapshot(player);
12418   }
12419 }
12420
12421 void ScrollScreen(struct PlayerInfo *player, int mode)
12422 {
12423   static unsigned int screen_frame_counter = 0;
12424
12425   if (mode == SCROLL_INIT)
12426   {
12427     /* set scrolling step size according to actual player's moving speed */
12428     ScrollStepSize = TILEX / player->move_delay_value;
12429
12430     screen_frame_counter = FrameCounter;
12431     ScreenMovDir = player->MovDir;
12432     ScreenMovPos = player->MovPos;
12433     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12434     return;
12435   }
12436   else if (!FrameReached(&screen_frame_counter, 1))
12437     return;
12438
12439   if (ScreenMovPos)
12440   {
12441     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12442     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12443     redraw_mask |= REDRAW_FIELD;
12444   }
12445   else
12446     ScreenMovDir = MV_NONE;
12447 }
12448
12449 void TestIfPlayerTouchesCustomElement(int x, int y)
12450 {
12451   static int xy[4][2] =
12452   {
12453     { 0, -1 },
12454     { -1, 0 },
12455     { +1, 0 },
12456     { 0, +1 }
12457   };
12458   static int trigger_sides[4][2] =
12459   {
12460     /* center side       border side */
12461     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12462     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12463     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12464     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12465   };
12466   static int touch_dir[4] =
12467   {
12468     MV_LEFT | MV_RIGHT,
12469     MV_UP   | MV_DOWN,
12470     MV_UP   | MV_DOWN,
12471     MV_LEFT | MV_RIGHT
12472   };
12473   int center_element = Feld[x][y];      /* should always be non-moving! */
12474   int i;
12475
12476   for (i = 0; i < NUM_DIRECTIONS; i++)
12477   {
12478     int xx = x + xy[i][0];
12479     int yy = y + xy[i][1];
12480     int center_side = trigger_sides[i][0];
12481     int border_side = trigger_sides[i][1];
12482     int border_element;
12483
12484     if (!IN_LEV_FIELD(xx, yy))
12485       continue;
12486
12487     if (IS_PLAYER(x, y))                /* player found at center element */
12488     {
12489       struct PlayerInfo *player = PLAYERINFO(x, y);
12490
12491       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12492         border_element = Feld[xx][yy];          /* may be moving! */
12493       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12494         border_element = Feld[xx][yy];
12495       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12496         border_element = MovingOrBlocked2Element(xx, yy);
12497       else
12498         continue;               /* center and border element do not touch */
12499
12500       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12501                                  player->index_bit, border_side);
12502       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12503                                           CE_PLAYER_TOUCHES_X,
12504                                           player->index_bit, border_side);
12505
12506       {
12507         /* use player element that is initially defined in the level playfield,
12508            not the player element that corresponds to the runtime player number
12509            (example: a level that contains EL_PLAYER_3 as the only player would
12510            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12511         int player_element = PLAYERINFO(x, y)->initial_element;
12512
12513         CheckElementChangeBySide(xx, yy, border_element, player_element,
12514                                  CE_TOUCHING_X, border_side);
12515       }
12516     }
12517     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12518     {
12519       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12520
12521       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12522       {
12523         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12524           continue;             /* center and border element do not touch */
12525       }
12526
12527       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12528                                  player->index_bit, center_side);
12529       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12530                                           CE_PLAYER_TOUCHES_X,
12531                                           player->index_bit, center_side);
12532
12533       {
12534         /* use player element that is initially defined in the level playfield,
12535            not the player element that corresponds to the runtime player number
12536            (example: a level that contains EL_PLAYER_3 as the only player would
12537            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12538         int player_element = PLAYERINFO(xx, yy)->initial_element;
12539
12540         CheckElementChangeBySide(x, y, center_element, player_element,
12541                                  CE_TOUCHING_X, center_side);
12542       }
12543
12544       break;
12545     }
12546   }
12547 }
12548
12549 void TestIfElementTouchesCustomElement(int x, int y)
12550 {
12551   static int xy[4][2] =
12552   {
12553     { 0, -1 },
12554     { -1, 0 },
12555     { +1, 0 },
12556     { 0, +1 }
12557   };
12558   static int trigger_sides[4][2] =
12559   {
12560     /* center side      border side */
12561     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12562     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12563     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12564     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12565   };
12566   static int touch_dir[4] =
12567   {
12568     MV_LEFT | MV_RIGHT,
12569     MV_UP   | MV_DOWN,
12570     MV_UP   | MV_DOWN,
12571     MV_LEFT | MV_RIGHT
12572   };
12573   boolean change_center_element = FALSE;
12574   int center_element = Feld[x][y];      /* should always be non-moving! */
12575   int border_element_old[NUM_DIRECTIONS];
12576   int i;
12577
12578   for (i = 0; i < NUM_DIRECTIONS; i++)
12579   {
12580     int xx = x + xy[i][0];
12581     int yy = y + xy[i][1];
12582     int border_element;
12583
12584     border_element_old[i] = -1;
12585
12586     if (!IN_LEV_FIELD(xx, yy))
12587       continue;
12588
12589     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12590       border_element = Feld[xx][yy];    /* may be moving! */
12591     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12592       border_element = Feld[xx][yy];
12593     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12594       border_element = MovingOrBlocked2Element(xx, yy);
12595     else
12596       continue;                 /* center and border element do not touch */
12597
12598     border_element_old[i] = border_element;
12599   }
12600
12601   for (i = 0; i < NUM_DIRECTIONS; i++)
12602   {
12603     int xx = x + xy[i][0];
12604     int yy = y + xy[i][1];
12605     int center_side = trigger_sides[i][0];
12606     int border_element = border_element_old[i];
12607
12608     if (border_element == -1)
12609       continue;
12610
12611     /* check for change of border element */
12612     CheckElementChangeBySide(xx, yy, border_element, center_element,
12613                              CE_TOUCHING_X, center_side);
12614
12615     /* (center element cannot be player, so we dont have to check this here) */
12616   }
12617
12618   for (i = 0; i < NUM_DIRECTIONS; i++)
12619   {
12620     int xx = x + xy[i][0];
12621     int yy = y + xy[i][1];
12622     int border_side = trigger_sides[i][1];
12623     int border_element = border_element_old[i];
12624
12625     if (border_element == -1)
12626       continue;
12627
12628     /* check for change of center element (but change it only once) */
12629     if (!change_center_element)
12630       change_center_element =
12631         CheckElementChangeBySide(x, y, center_element, border_element,
12632                                  CE_TOUCHING_X, border_side);
12633
12634     if (IS_PLAYER(xx, yy))
12635     {
12636       /* use player element that is initially defined in the level playfield,
12637          not the player element that corresponds to the runtime player number
12638          (example: a level that contains EL_PLAYER_3 as the only player would
12639          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12640       int player_element = PLAYERINFO(xx, yy)->initial_element;
12641
12642       CheckElementChangeBySide(x, y, center_element, player_element,
12643                                CE_TOUCHING_X, border_side);
12644     }
12645   }
12646 }
12647
12648 void TestIfElementHitsCustomElement(int x, int y, int direction)
12649 {
12650   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12651   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12652   int hitx = x + dx, hity = y + dy;
12653   int hitting_element = Feld[x][y];
12654   int touched_element;
12655
12656   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12657     return;
12658
12659   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12660                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12661
12662   if (IN_LEV_FIELD(hitx, hity))
12663   {
12664     int opposite_direction = MV_DIR_OPPOSITE(direction);
12665     int hitting_side = direction;
12666     int touched_side = opposite_direction;
12667     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12668                           MovDir[hitx][hity] != direction ||
12669                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12670
12671     object_hit = TRUE;
12672
12673     if (object_hit)
12674     {
12675       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12676                                CE_HITTING_X, touched_side);
12677
12678       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12679                                CE_HIT_BY_X, hitting_side);
12680
12681       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12682                                CE_HIT_BY_SOMETHING, opposite_direction);
12683
12684       if (IS_PLAYER(hitx, hity))
12685       {
12686         /* use player element that is initially defined in the level playfield,
12687            not the player element that corresponds to the runtime player number
12688            (example: a level that contains EL_PLAYER_3 as the only player would
12689            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12690         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12691
12692         CheckElementChangeBySide(x, y, hitting_element, player_element,
12693                                  CE_HITTING_X, touched_side);
12694       }
12695     }
12696   }
12697
12698   /* "hitting something" is also true when hitting the playfield border */
12699   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12700                            CE_HITTING_SOMETHING, direction);
12701 }
12702
12703 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12704 {
12705   int i, kill_x = -1, kill_y = -1;
12706
12707   int bad_element = -1;
12708   static int test_xy[4][2] =
12709   {
12710     { 0, -1 },
12711     { -1, 0 },
12712     { +1, 0 },
12713     { 0, +1 }
12714   };
12715   static int test_dir[4] =
12716   {
12717     MV_UP,
12718     MV_LEFT,
12719     MV_RIGHT,
12720     MV_DOWN
12721   };
12722
12723   for (i = 0; i < NUM_DIRECTIONS; i++)
12724   {
12725     int test_x, test_y, test_move_dir, test_element;
12726
12727     test_x = good_x + test_xy[i][0];
12728     test_y = good_y + test_xy[i][1];
12729
12730     if (!IN_LEV_FIELD(test_x, test_y))
12731       continue;
12732
12733     test_move_dir =
12734       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12735
12736     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12737
12738     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12739        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12740     */
12741     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12742         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12743     {
12744       kill_x = test_x;
12745       kill_y = test_y;
12746       bad_element = test_element;
12747
12748       break;
12749     }
12750   }
12751
12752   if (kill_x != -1 || kill_y != -1)
12753   {
12754     if (IS_PLAYER(good_x, good_y))
12755     {
12756       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12757
12758       if (player->shield_deadly_time_left > 0 &&
12759           !IS_INDESTRUCTIBLE(bad_element))
12760         Bang(kill_x, kill_y);
12761       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12762         KillPlayer(player);
12763     }
12764     else
12765       Bang(good_x, good_y);
12766   }
12767 }
12768
12769 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12770 {
12771   int i, kill_x = -1, kill_y = -1;
12772   int bad_element = Feld[bad_x][bad_y];
12773   static int test_xy[4][2] =
12774   {
12775     { 0, -1 },
12776     { -1, 0 },
12777     { +1, 0 },
12778     { 0, +1 }
12779   };
12780   static int touch_dir[4] =
12781   {
12782     MV_LEFT | MV_RIGHT,
12783     MV_UP   | MV_DOWN,
12784     MV_UP   | MV_DOWN,
12785     MV_LEFT | MV_RIGHT
12786   };
12787   static int test_dir[4] =
12788   {
12789     MV_UP,
12790     MV_LEFT,
12791     MV_RIGHT,
12792     MV_DOWN
12793   };
12794
12795   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12796     return;
12797
12798   for (i = 0; i < NUM_DIRECTIONS; i++)
12799   {
12800     int test_x, test_y, test_move_dir, test_element;
12801
12802     test_x = bad_x + test_xy[i][0];
12803     test_y = bad_y + test_xy[i][1];
12804
12805     if (!IN_LEV_FIELD(test_x, test_y))
12806       continue;
12807
12808     test_move_dir =
12809       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12810
12811     test_element = Feld[test_x][test_y];
12812
12813     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12814        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12815     */
12816     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12817         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12818     {
12819       /* good thing is player or penguin that does not move away */
12820       if (IS_PLAYER(test_x, test_y))
12821       {
12822         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12823
12824         if (bad_element == EL_ROBOT && player->is_moving)
12825           continue;     /* robot does not kill player if he is moving */
12826
12827         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12828         {
12829           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12830             continue;           /* center and border element do not touch */
12831         }
12832
12833         kill_x = test_x;
12834         kill_y = test_y;
12835
12836         break;
12837       }
12838       else if (test_element == EL_PENGUIN)
12839       {
12840         kill_x = test_x;
12841         kill_y = test_y;
12842
12843         break;
12844       }
12845     }
12846   }
12847
12848   if (kill_x != -1 || kill_y != -1)
12849   {
12850     if (IS_PLAYER(kill_x, kill_y))
12851     {
12852       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12853
12854       if (player->shield_deadly_time_left > 0 &&
12855           !IS_INDESTRUCTIBLE(bad_element))
12856         Bang(bad_x, bad_y);
12857       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12858         KillPlayer(player);
12859     }
12860     else
12861       Bang(kill_x, kill_y);
12862   }
12863 }
12864
12865 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12866 {
12867   int bad_element = Feld[bad_x][bad_y];
12868   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12869   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12870   int test_x = bad_x + dx, test_y = bad_y + dy;
12871   int test_move_dir, test_element;
12872   int kill_x = -1, kill_y = -1;
12873
12874   if (!IN_LEV_FIELD(test_x, test_y))
12875     return;
12876
12877   test_move_dir =
12878     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12879
12880   test_element = Feld[test_x][test_y];
12881
12882   if (test_move_dir != bad_move_dir)
12883   {
12884     /* good thing can be player or penguin that does not move away */
12885     if (IS_PLAYER(test_x, test_y))
12886     {
12887       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12888
12889       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12890          player as being hit when he is moving towards the bad thing, because
12891          the "get hit by" condition would be lost after the player stops) */
12892       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12893         return;         /* player moves away from bad thing */
12894
12895       kill_x = test_x;
12896       kill_y = test_y;
12897     }
12898     else if (test_element == EL_PENGUIN)
12899     {
12900       kill_x = test_x;
12901       kill_y = test_y;
12902     }
12903   }
12904
12905   if (kill_x != -1 || kill_y != -1)
12906   {
12907     if (IS_PLAYER(kill_x, kill_y))
12908     {
12909       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12910
12911       if (player->shield_deadly_time_left > 0 &&
12912           !IS_INDESTRUCTIBLE(bad_element))
12913         Bang(bad_x, bad_y);
12914       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12915         KillPlayer(player);
12916     }
12917     else
12918       Bang(kill_x, kill_y);
12919   }
12920 }
12921
12922 void TestIfPlayerTouchesBadThing(int x, int y)
12923 {
12924   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12925 }
12926
12927 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12928 {
12929   TestIfGoodThingHitsBadThing(x, y, move_dir);
12930 }
12931
12932 void TestIfBadThingTouchesPlayer(int x, int y)
12933 {
12934   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12935 }
12936
12937 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12938 {
12939   TestIfBadThingHitsGoodThing(x, y, move_dir);
12940 }
12941
12942 void TestIfFriendTouchesBadThing(int x, int y)
12943 {
12944   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12945 }
12946
12947 void TestIfBadThingTouchesFriend(int x, int y)
12948 {
12949   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12950 }
12951
12952 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12953 {
12954   int i, kill_x = bad_x, kill_y = bad_y;
12955   static int xy[4][2] =
12956   {
12957     { 0, -1 },
12958     { -1, 0 },
12959     { +1, 0 },
12960     { 0, +1 }
12961   };
12962
12963   for (i = 0; i < NUM_DIRECTIONS; i++)
12964   {
12965     int x, y, element;
12966
12967     x = bad_x + xy[i][0];
12968     y = bad_y + xy[i][1];
12969     if (!IN_LEV_FIELD(x, y))
12970       continue;
12971
12972     element = Feld[x][y];
12973     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12974         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12975     {
12976       kill_x = x;
12977       kill_y = y;
12978       break;
12979     }
12980   }
12981
12982   if (kill_x != bad_x || kill_y != bad_y)
12983     Bang(bad_x, bad_y);
12984 }
12985
12986 void KillPlayer(struct PlayerInfo *player)
12987 {
12988   int jx = player->jx, jy = player->jy;
12989
12990   if (!player->active)
12991     return;
12992
12993 #if 0
12994   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12995          player->killed, player->active, player->reanimated);
12996 #endif
12997
12998   /* the following code was introduced to prevent an infinite loop when calling
12999      -> Bang()
13000      -> CheckTriggeredElementChangeExt()
13001      -> ExecuteCustomElementAction()
13002      -> KillPlayer()
13003      -> (infinitely repeating the above sequence of function calls)
13004      which occurs when killing the player while having a CE with the setting
13005      "kill player X when explosion of <player X>"; the solution using a new
13006      field "player->killed" was chosen for backwards compatibility, although
13007      clever use of the fields "player->active" etc. would probably also work */
13008 #if 1
13009   if (player->killed)
13010     return;
13011 #endif
13012
13013   player->killed = TRUE;
13014
13015   /* remove accessible field at the player's position */
13016   Feld[jx][jy] = EL_EMPTY;
13017
13018   /* deactivate shield (else Bang()/Explode() would not work right) */
13019   player->shield_normal_time_left = 0;
13020   player->shield_deadly_time_left = 0;
13021
13022 #if 0
13023   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13024          player->killed, player->active, player->reanimated);
13025 #endif
13026
13027   Bang(jx, jy);
13028
13029 #if 0
13030   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13031          player->killed, player->active, player->reanimated);
13032 #endif
13033
13034   if (player->reanimated)       /* killed player may have been reanimated */
13035     player->killed = player->reanimated = FALSE;
13036   else
13037     BuryPlayer(player);
13038 }
13039
13040 static void KillPlayerUnlessEnemyProtected(int x, int y)
13041 {
13042   if (!PLAYER_ENEMY_PROTECTED(x, y))
13043     KillPlayer(PLAYERINFO(x, y));
13044 }
13045
13046 static void KillPlayerUnlessExplosionProtected(int x, int y)
13047 {
13048   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13049     KillPlayer(PLAYERINFO(x, y));
13050 }
13051
13052 void BuryPlayer(struct PlayerInfo *player)
13053 {
13054   int jx = player->jx, jy = player->jy;
13055
13056   if (!player->active)
13057     return;
13058
13059   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13060   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13061
13062   player->GameOver = TRUE;
13063   RemovePlayer(player);
13064 }
13065
13066 void RemovePlayer(struct PlayerInfo *player)
13067 {
13068   int jx = player->jx, jy = player->jy;
13069   int i, found = FALSE;
13070
13071   player->present = FALSE;
13072   player->active = FALSE;
13073
13074   if (!ExplodeField[jx][jy])
13075     StorePlayer[jx][jy] = 0;
13076
13077   if (player->is_moving)
13078     TEST_DrawLevelField(player->last_jx, player->last_jy);
13079
13080   for (i = 0; i < MAX_PLAYERS; i++)
13081     if (stored_player[i].active)
13082       found = TRUE;
13083
13084   if (!found)
13085     AllPlayersGone = TRUE;
13086
13087   ExitX = ZX = jx;
13088   ExitY = ZY = jy;
13089 }
13090
13091 static void setFieldForSnapping(int x, int y, int element, int direction)
13092 {
13093   struct ElementInfo *ei = &element_info[element];
13094   int direction_bit = MV_DIR_TO_BIT(direction);
13095   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13096   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13097                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13098
13099   Feld[x][y] = EL_ELEMENT_SNAPPING;
13100   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13101
13102   ResetGfxAnimation(x, y);
13103
13104   GfxElement[x][y] = element;
13105   GfxAction[x][y] = action;
13106   GfxDir[x][y] = direction;
13107   GfxFrame[x][y] = -1;
13108 }
13109
13110 /*
13111   =============================================================================
13112   checkDiagonalPushing()
13113   -----------------------------------------------------------------------------
13114   check if diagonal input device direction results in pushing of object
13115   (by checking if the alternative direction is walkable, diggable, ...)
13116   =============================================================================
13117 */
13118
13119 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13120                                     int x, int y, int real_dx, int real_dy)
13121 {
13122   int jx, jy, dx, dy, xx, yy;
13123
13124   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13125     return TRUE;
13126
13127   /* diagonal direction: check alternative direction */
13128   jx = player->jx;
13129   jy = player->jy;
13130   dx = x - jx;
13131   dy = y - jy;
13132   xx = jx + (dx == 0 ? real_dx : 0);
13133   yy = jy + (dy == 0 ? real_dy : 0);
13134
13135   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13136 }
13137
13138 /*
13139   =============================================================================
13140   DigField()
13141   -----------------------------------------------------------------------------
13142   x, y:                 field next to player (non-diagonal) to try to dig to
13143   real_dx, real_dy:     direction as read from input device (can be diagonal)
13144   =============================================================================
13145 */
13146
13147 static int DigField(struct PlayerInfo *player,
13148                     int oldx, int oldy, int x, int y,
13149                     int real_dx, int real_dy, int mode)
13150 {
13151   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13152   boolean player_was_pushing = player->is_pushing;
13153   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13154   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13155   int jx = oldx, jy = oldy;
13156   int dx = x - jx, dy = y - jy;
13157   int nextx = x + dx, nexty = y + dy;
13158   int move_direction = (dx == -1 ? MV_LEFT  :
13159                         dx == +1 ? MV_RIGHT :
13160                         dy == -1 ? MV_UP    :
13161                         dy == +1 ? MV_DOWN  : MV_NONE);
13162   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13163   int dig_side = MV_DIR_OPPOSITE(move_direction);
13164   int old_element = Feld[jx][jy];
13165   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13166   int collect_count;
13167
13168   if (is_player)                /* function can also be called by EL_PENGUIN */
13169   {
13170     if (player->MovPos == 0)
13171     {
13172       player->is_digging = FALSE;
13173       player->is_collecting = FALSE;
13174     }
13175
13176     if (player->MovPos == 0)    /* last pushing move finished */
13177       player->is_pushing = FALSE;
13178
13179     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13180     {
13181       player->is_switching = FALSE;
13182       player->push_delay = -1;
13183
13184       return MP_NO_ACTION;
13185     }
13186   }
13187
13188   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13189     old_element = Back[jx][jy];
13190
13191   /* in case of element dropped at player position, check background */
13192   else if (Back[jx][jy] != EL_EMPTY &&
13193            game.engine_version >= VERSION_IDENT(2,2,0,0))
13194     old_element = Back[jx][jy];
13195
13196   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13197     return MP_NO_ACTION;        /* field has no opening in this direction */
13198
13199   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13200     return MP_NO_ACTION;        /* field has no opening in this direction */
13201
13202   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13203   {
13204     SplashAcid(x, y);
13205
13206     Feld[jx][jy] = player->artwork_element;
13207     InitMovingField(jx, jy, MV_DOWN);
13208     Store[jx][jy] = EL_ACID;
13209     ContinueMoving(jx, jy);
13210     BuryPlayer(player);
13211
13212     return MP_DONT_RUN_INTO;
13213   }
13214
13215   if (player_can_move && DONT_RUN_INTO(element))
13216   {
13217     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13218
13219     return MP_DONT_RUN_INTO;
13220   }
13221
13222   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13223     return MP_NO_ACTION;
13224
13225   collect_count = element_info[element].collect_count_initial;
13226
13227   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13228     return MP_NO_ACTION;
13229
13230   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13231     player_can_move = player_can_move_or_snap;
13232
13233   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13234       game.engine_version >= VERSION_IDENT(2,2,0,0))
13235   {
13236     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13237                                player->index_bit, dig_side);
13238     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13239                                         player->index_bit, dig_side);
13240
13241     if (element == EL_DC_LANDMINE)
13242       Bang(x, y);
13243
13244     if (Feld[x][y] != element)          /* field changed by snapping */
13245       return MP_ACTION;
13246
13247     return MP_NO_ACTION;
13248   }
13249
13250   if (player->gravity && is_player && !player->is_auto_moving &&
13251       canFallDown(player) && move_direction != MV_DOWN &&
13252       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13253     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13254
13255   if (player_can_move &&
13256       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13257   {
13258     int sound_element = SND_ELEMENT(element);
13259     int sound_action = ACTION_WALKING;
13260
13261     if (IS_RND_GATE(element))
13262     {
13263       if (!player->key[RND_GATE_NR(element)])
13264         return MP_NO_ACTION;
13265     }
13266     else if (IS_RND_GATE_GRAY(element))
13267     {
13268       if (!player->key[RND_GATE_GRAY_NR(element)])
13269         return MP_NO_ACTION;
13270     }
13271     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13272     {
13273       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13274         return MP_NO_ACTION;
13275     }
13276     else if (element == EL_EXIT_OPEN ||
13277              element == EL_EM_EXIT_OPEN ||
13278              element == EL_EM_EXIT_OPENING ||
13279              element == EL_STEEL_EXIT_OPEN ||
13280              element == EL_EM_STEEL_EXIT_OPEN ||
13281              element == EL_EM_STEEL_EXIT_OPENING ||
13282              element == EL_SP_EXIT_OPEN ||
13283              element == EL_SP_EXIT_OPENING)
13284     {
13285       sound_action = ACTION_PASSING;    /* player is passing exit */
13286     }
13287     else if (element == EL_EMPTY)
13288     {
13289       sound_action = ACTION_MOVING;             /* nothing to walk on */
13290     }
13291
13292     /* play sound from background or player, whatever is available */
13293     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13294       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13295     else
13296       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13297   }
13298   else if (player_can_move &&
13299            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13300   {
13301     if (!ACCESS_FROM(element, opposite_direction))
13302       return MP_NO_ACTION;      /* field not accessible from this direction */
13303
13304     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13305       return MP_NO_ACTION;
13306
13307     if (IS_EM_GATE(element))
13308     {
13309       if (!player->key[EM_GATE_NR(element)])
13310         return MP_NO_ACTION;
13311     }
13312     else if (IS_EM_GATE_GRAY(element))
13313     {
13314       if (!player->key[EM_GATE_GRAY_NR(element)])
13315         return MP_NO_ACTION;
13316     }
13317     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13318     {
13319       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13320         return MP_NO_ACTION;
13321     }
13322     else if (IS_EMC_GATE(element))
13323     {
13324       if (!player->key[EMC_GATE_NR(element)])
13325         return MP_NO_ACTION;
13326     }
13327     else if (IS_EMC_GATE_GRAY(element))
13328     {
13329       if (!player->key[EMC_GATE_GRAY_NR(element)])
13330         return MP_NO_ACTION;
13331     }
13332     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13333     {
13334       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13335         return MP_NO_ACTION;
13336     }
13337     else if (element == EL_DC_GATE_WHITE ||
13338              element == EL_DC_GATE_WHITE_GRAY ||
13339              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13340     {
13341       if (player->num_white_keys == 0)
13342         return MP_NO_ACTION;
13343
13344       player->num_white_keys--;
13345     }
13346     else if (IS_SP_PORT(element))
13347     {
13348       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13349           element == EL_SP_GRAVITY_PORT_RIGHT ||
13350           element == EL_SP_GRAVITY_PORT_UP ||
13351           element == EL_SP_GRAVITY_PORT_DOWN)
13352         player->gravity = !player->gravity;
13353       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13354                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13355                element == EL_SP_GRAVITY_ON_PORT_UP ||
13356                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13357         player->gravity = TRUE;
13358       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13359                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13360                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13361                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13362         player->gravity = FALSE;
13363     }
13364
13365     /* automatically move to the next field with double speed */
13366     player->programmed_action = move_direction;
13367
13368     if (player->move_delay_reset_counter == 0)
13369     {
13370       player->move_delay_reset_counter = 2;     /* two double speed steps */
13371
13372       DOUBLE_PLAYER_SPEED(player);
13373     }
13374
13375     PlayLevelSoundAction(x, y, ACTION_PASSING);
13376   }
13377   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13378   {
13379     RemoveField(x, y);
13380
13381     if (mode != DF_SNAP)
13382     {
13383       GfxElement[x][y] = GFX_ELEMENT(element);
13384       player->is_digging = TRUE;
13385     }
13386
13387     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13388
13389     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13390                                         player->index_bit, dig_side);
13391
13392     if (mode == DF_SNAP)
13393     {
13394       if (level.block_snap_field)
13395         setFieldForSnapping(x, y, element, move_direction);
13396       else
13397         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13398
13399       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13400                                           player->index_bit, dig_side);
13401     }
13402   }
13403   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13404   {
13405     RemoveField(x, y);
13406
13407     if (is_player && mode != DF_SNAP)
13408     {
13409       GfxElement[x][y] = element;
13410       player->is_collecting = TRUE;
13411     }
13412
13413     if (element == EL_SPEED_PILL)
13414     {
13415       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13416     }
13417     else if (element == EL_EXTRA_TIME && level.time > 0)
13418     {
13419       TimeLeft += level.extra_time;
13420
13421       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13422
13423       DisplayGameControlValues();
13424     }
13425     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13426     {
13427       player->shield_normal_time_left += level.shield_normal_time;
13428       if (element == EL_SHIELD_DEADLY)
13429         player->shield_deadly_time_left += level.shield_deadly_time;
13430     }
13431     else if (element == EL_DYNAMITE ||
13432              element == EL_EM_DYNAMITE ||
13433              element == EL_SP_DISK_RED)
13434     {
13435       if (player->inventory_size < MAX_INVENTORY_SIZE)
13436         player->inventory_element[player->inventory_size++] = element;
13437
13438       DrawGameDoorValues();
13439     }
13440     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13441     {
13442       player->dynabomb_count++;
13443       player->dynabombs_left++;
13444     }
13445     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13446     {
13447       player->dynabomb_size++;
13448     }
13449     else if (element == EL_DYNABOMB_INCREASE_POWER)
13450     {
13451       player->dynabomb_xl = TRUE;
13452     }
13453     else if (IS_KEY(element))
13454     {
13455       player->key[KEY_NR(element)] = TRUE;
13456
13457       DrawGameDoorValues();
13458     }
13459     else if (element == EL_DC_KEY_WHITE)
13460     {
13461       player->num_white_keys++;
13462
13463       /* display white keys? */
13464       /* DrawGameDoorValues(); */
13465     }
13466     else if (IS_ENVELOPE(element))
13467     {
13468       player->show_envelope = element;
13469     }
13470     else if (element == EL_EMC_LENSES)
13471     {
13472       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13473
13474       RedrawAllInvisibleElementsForLenses();
13475     }
13476     else if (element == EL_EMC_MAGNIFIER)
13477     {
13478       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13479
13480       RedrawAllInvisibleElementsForMagnifier();
13481     }
13482     else if (IS_DROPPABLE(element) ||
13483              IS_THROWABLE(element))     /* can be collected and dropped */
13484     {
13485       int i;
13486
13487       if (collect_count == 0)
13488         player->inventory_infinite_element = element;
13489       else
13490         for (i = 0; i < collect_count; i++)
13491           if (player->inventory_size < MAX_INVENTORY_SIZE)
13492             player->inventory_element[player->inventory_size++] = element;
13493
13494       DrawGameDoorValues();
13495     }
13496     else if (collect_count > 0)
13497     {
13498       local_player->gems_still_needed -= collect_count;
13499       if (local_player->gems_still_needed < 0)
13500         local_player->gems_still_needed = 0;
13501
13502       game.snapshot.collected_item = TRUE;
13503
13504       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13505
13506       DisplayGameControlValues();
13507     }
13508
13509     RaiseScoreElement(element);
13510     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13511
13512     if (is_player)
13513       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13514                                           player->index_bit, dig_side);
13515
13516     if (mode == DF_SNAP)
13517     {
13518       if (level.block_snap_field)
13519         setFieldForSnapping(x, y, element, move_direction);
13520       else
13521         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13522
13523       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13524                                           player->index_bit, dig_side);
13525     }
13526   }
13527   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13528   {
13529     if (mode == DF_SNAP && element != EL_BD_ROCK)
13530       return MP_NO_ACTION;
13531
13532     if (CAN_FALL(element) && dy)
13533       return MP_NO_ACTION;
13534
13535     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13536         !(element == EL_SPRING && level.use_spring_bug))
13537       return MP_NO_ACTION;
13538
13539     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13540         ((move_direction & MV_VERTICAL &&
13541           ((element_info[element].move_pattern & MV_LEFT &&
13542             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13543            (element_info[element].move_pattern & MV_RIGHT &&
13544             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13545          (move_direction & MV_HORIZONTAL &&
13546           ((element_info[element].move_pattern & MV_UP &&
13547             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13548            (element_info[element].move_pattern & MV_DOWN &&
13549             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13550       return MP_NO_ACTION;
13551
13552     /* do not push elements already moving away faster than player */
13553     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13554         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13555       return MP_NO_ACTION;
13556
13557     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13558     {
13559       if (player->push_delay_value == -1 || !player_was_pushing)
13560         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13561     }
13562     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13563     {
13564       if (player->push_delay_value == -1)
13565         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13566     }
13567     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13568     {
13569       if (!player->is_pushing)
13570         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13571     }
13572
13573     player->is_pushing = TRUE;
13574     player->is_active = TRUE;
13575
13576     if (!(IN_LEV_FIELD(nextx, nexty) &&
13577           (IS_FREE(nextx, nexty) ||
13578            (IS_SB_ELEMENT(element) &&
13579             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13580            (IS_CUSTOM_ELEMENT(element) &&
13581             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13582       return MP_NO_ACTION;
13583
13584     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13585       return MP_NO_ACTION;
13586
13587     if (player->push_delay == -1)       /* new pushing; restart delay */
13588       player->push_delay = 0;
13589
13590     if (player->push_delay < player->push_delay_value &&
13591         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13592         element != EL_SPRING && element != EL_BALLOON)
13593     {
13594       /* make sure that there is no move delay before next try to push */
13595       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13596         player->move_delay = 0;
13597
13598       return MP_NO_ACTION;
13599     }
13600
13601     if (IS_CUSTOM_ELEMENT(element) &&
13602         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13603     {
13604       if (!DigFieldByCE(nextx, nexty, element))
13605         return MP_NO_ACTION;
13606     }
13607
13608     if (IS_SB_ELEMENT(element))
13609     {
13610       if (element == EL_SOKOBAN_FIELD_FULL)
13611       {
13612         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13613         local_player->sokobanfields_still_needed++;
13614       }
13615
13616       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13617       {
13618         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13619         local_player->sokobanfields_still_needed--;
13620       }
13621
13622       Feld[x][y] = EL_SOKOBAN_OBJECT;
13623
13624       if (Back[x][y] == Back[nextx][nexty])
13625         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13626       else if (Back[x][y] != 0)
13627         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13628                                     ACTION_EMPTYING);
13629       else
13630         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13631                                     ACTION_FILLING);
13632
13633       if (local_player->sokobanfields_still_needed == 0 &&
13634           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13635       {
13636         PlayerWins(player);
13637
13638         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13639       }
13640     }
13641     else
13642       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13643
13644     InitMovingField(x, y, move_direction);
13645     GfxAction[x][y] = ACTION_PUSHING;
13646
13647     if (mode == DF_SNAP)
13648       ContinueMoving(x, y);
13649     else
13650       MovPos[x][y] = (dx != 0 ? dx : dy);
13651
13652     Pushed[x][y] = TRUE;
13653     Pushed[nextx][nexty] = TRUE;
13654
13655     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13656       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13657     else
13658       player->push_delay_value = -1;    /* get new value later */
13659
13660     /* check for element change _after_ element has been pushed */
13661     if (game.use_change_when_pushing_bug)
13662     {
13663       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13664                                  player->index_bit, dig_side);
13665       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13666                                           player->index_bit, dig_side);
13667     }
13668   }
13669   else if (IS_SWITCHABLE(element))
13670   {
13671     if (PLAYER_SWITCHING(player, x, y))
13672     {
13673       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13674                                           player->index_bit, dig_side);
13675
13676       return MP_ACTION;
13677     }
13678
13679     player->is_switching = TRUE;
13680     player->switch_x = x;
13681     player->switch_y = y;
13682
13683     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13684
13685     if (element == EL_ROBOT_WHEEL)
13686     {
13687       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13688       ZX = x;
13689       ZY = y;
13690
13691       game.robot_wheel_active = TRUE;
13692
13693       TEST_DrawLevelField(x, y);
13694     }
13695     else if (element == EL_SP_TERMINAL)
13696     {
13697       int xx, yy;
13698
13699       SCAN_PLAYFIELD(xx, yy)
13700       {
13701         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13702         {
13703           Bang(xx, yy);
13704         }
13705         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13706         {
13707           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13708
13709           ResetGfxAnimation(xx, yy);
13710           TEST_DrawLevelField(xx, yy);
13711         }
13712       }
13713     }
13714     else if (IS_BELT_SWITCH(element))
13715     {
13716       ToggleBeltSwitch(x, y);
13717     }
13718     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13719              element == EL_SWITCHGATE_SWITCH_DOWN ||
13720              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13721              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13722     {
13723       ToggleSwitchgateSwitch(x, y);
13724     }
13725     else if (element == EL_LIGHT_SWITCH ||
13726              element == EL_LIGHT_SWITCH_ACTIVE)
13727     {
13728       ToggleLightSwitch(x, y);
13729     }
13730     else if (element == EL_TIMEGATE_SWITCH ||
13731              element == EL_DC_TIMEGATE_SWITCH)
13732     {
13733       ActivateTimegateSwitch(x, y);
13734     }
13735     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13736              element == EL_BALLOON_SWITCH_RIGHT ||
13737              element == EL_BALLOON_SWITCH_UP    ||
13738              element == EL_BALLOON_SWITCH_DOWN  ||
13739              element == EL_BALLOON_SWITCH_NONE  ||
13740              element == EL_BALLOON_SWITCH_ANY)
13741     {
13742       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13743                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13744                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13745                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13746                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13747                              move_direction);
13748     }
13749     else if (element == EL_LAMP)
13750     {
13751       Feld[x][y] = EL_LAMP_ACTIVE;
13752       local_player->lights_still_needed--;
13753
13754       ResetGfxAnimation(x, y);
13755       TEST_DrawLevelField(x, y);
13756     }
13757     else if (element == EL_TIME_ORB_FULL)
13758     {
13759       Feld[x][y] = EL_TIME_ORB_EMPTY;
13760
13761       if (level.time > 0 || level.use_time_orb_bug)
13762       {
13763         TimeLeft += level.time_orb_time;
13764         game.no_time_limit = FALSE;
13765
13766         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13767
13768         DisplayGameControlValues();
13769       }
13770
13771       ResetGfxAnimation(x, y);
13772       TEST_DrawLevelField(x, y);
13773     }
13774     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13775              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13776     {
13777       int xx, yy;
13778
13779       game.ball_state = !game.ball_state;
13780
13781       SCAN_PLAYFIELD(xx, yy)
13782       {
13783         int e = Feld[xx][yy];
13784
13785         if (game.ball_state)
13786         {
13787           if (e == EL_EMC_MAGIC_BALL)
13788             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13789           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13790             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13791         }
13792         else
13793         {
13794           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13795             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13796           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13797             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13798         }
13799       }
13800     }
13801
13802     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13803                                         player->index_bit, dig_side);
13804
13805     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13806                                         player->index_bit, dig_side);
13807
13808     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13809                                         player->index_bit, dig_side);
13810
13811     return MP_ACTION;
13812   }
13813   else
13814   {
13815     if (!PLAYER_SWITCHING(player, x, y))
13816     {
13817       player->is_switching = TRUE;
13818       player->switch_x = x;
13819       player->switch_y = y;
13820
13821       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13822                                  player->index_bit, dig_side);
13823       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13824                                           player->index_bit, dig_side);
13825
13826       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13827                                  player->index_bit, dig_side);
13828       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13829                                           player->index_bit, dig_side);
13830     }
13831
13832     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13833                                player->index_bit, dig_side);
13834     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13835                                         player->index_bit, dig_side);
13836
13837     return MP_NO_ACTION;
13838   }
13839
13840   player->push_delay = -1;
13841
13842   if (is_player)                /* function can also be called by EL_PENGUIN */
13843   {
13844     if (Feld[x][y] != element)          /* really digged/collected something */
13845     {
13846       player->is_collecting = !player->is_digging;
13847       player->is_active = TRUE;
13848     }
13849   }
13850
13851   return MP_MOVING;
13852 }
13853
13854 static boolean DigFieldByCE(int x, int y, int digging_element)
13855 {
13856   int element = Feld[x][y];
13857
13858   if (!IS_FREE(x, y))
13859   {
13860     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13861                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13862                   ACTION_BREAKING);
13863
13864     /* no element can dig solid indestructible elements */
13865     if (IS_INDESTRUCTIBLE(element) &&
13866         !IS_DIGGABLE(element) &&
13867         !IS_COLLECTIBLE(element))
13868       return FALSE;
13869
13870     if (AmoebaNr[x][y] &&
13871         (element == EL_AMOEBA_FULL ||
13872          element == EL_BD_AMOEBA ||
13873          element == EL_AMOEBA_GROWING))
13874     {
13875       AmoebaCnt[AmoebaNr[x][y]]--;
13876       AmoebaCnt2[AmoebaNr[x][y]]--;
13877     }
13878
13879     if (IS_MOVING(x, y))
13880       RemoveMovingField(x, y);
13881     else
13882     {
13883       RemoveField(x, y);
13884       TEST_DrawLevelField(x, y);
13885     }
13886
13887     /* if digged element was about to explode, prevent the explosion */
13888     ExplodeField[x][y] = EX_TYPE_NONE;
13889
13890     PlayLevelSoundAction(x, y, action);
13891   }
13892
13893   Store[x][y] = EL_EMPTY;
13894
13895   /* this makes it possible to leave the removed element again */
13896   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13897     Store[x][y] = element;
13898
13899   return TRUE;
13900 }
13901
13902 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13903 {
13904   int jx = player->jx, jy = player->jy;
13905   int x = jx + dx, y = jy + dy;
13906   int snap_direction = (dx == -1 ? MV_LEFT  :
13907                         dx == +1 ? MV_RIGHT :
13908                         dy == -1 ? MV_UP    :
13909                         dy == +1 ? MV_DOWN  : MV_NONE);
13910   boolean can_continue_snapping = (level.continuous_snapping &&
13911                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13912
13913   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13914     return FALSE;
13915
13916   if (!player->active || !IN_LEV_FIELD(x, y))
13917     return FALSE;
13918
13919   if (dx && dy)
13920     return FALSE;
13921
13922   if (!dx && !dy)
13923   {
13924     if (player->MovPos == 0)
13925       player->is_pushing = FALSE;
13926
13927     player->is_snapping = FALSE;
13928
13929     if (player->MovPos == 0)
13930     {
13931       player->is_moving = FALSE;
13932       player->is_digging = FALSE;
13933       player->is_collecting = FALSE;
13934     }
13935
13936     return FALSE;
13937   }
13938
13939   /* prevent snapping with already pressed snap key when not allowed */
13940   if (player->is_snapping && !can_continue_snapping)
13941     return FALSE;
13942
13943   player->MovDir = snap_direction;
13944
13945   if (player->MovPos == 0)
13946   {
13947     player->is_moving = FALSE;
13948     player->is_digging = FALSE;
13949     player->is_collecting = FALSE;
13950   }
13951
13952   player->is_dropping = FALSE;
13953   player->is_dropping_pressed = FALSE;
13954   player->drop_pressed_delay = 0;
13955
13956   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13957     return FALSE;
13958
13959   player->is_snapping = TRUE;
13960   player->is_active = TRUE;
13961
13962   if (player->MovPos == 0)
13963   {
13964     player->is_moving = FALSE;
13965     player->is_digging = FALSE;
13966     player->is_collecting = FALSE;
13967   }
13968
13969   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13970     TEST_DrawLevelField(player->last_jx, player->last_jy);
13971
13972   TEST_DrawLevelField(x, y);
13973
13974   return TRUE;
13975 }
13976
13977 static boolean DropElement(struct PlayerInfo *player)
13978 {
13979   int old_element, new_element;
13980   int dropx = player->jx, dropy = player->jy;
13981   int drop_direction = player->MovDir;
13982   int drop_side = drop_direction;
13983   int drop_element = get_next_dropped_element(player);
13984
13985   player->is_dropping_pressed = TRUE;
13986
13987   /* do not drop an element on top of another element; when holding drop key
13988      pressed without moving, dropped element must move away before the next
13989      element can be dropped (this is especially important if the next element
13990      is dynamite, which can be placed on background for historical reasons) */
13991   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13992     return MP_ACTION;
13993
13994   if (IS_THROWABLE(drop_element))
13995   {
13996     dropx += GET_DX_FROM_DIR(drop_direction);
13997     dropy += GET_DY_FROM_DIR(drop_direction);
13998
13999     if (!IN_LEV_FIELD(dropx, dropy))
14000       return FALSE;
14001   }
14002
14003   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14004   new_element = drop_element;           /* default: no change when dropping */
14005
14006   /* check if player is active, not moving and ready to drop */
14007   if (!player->active || player->MovPos || player->drop_delay > 0)
14008     return FALSE;
14009
14010   /* check if player has anything that can be dropped */
14011   if (new_element == EL_UNDEFINED)
14012     return FALSE;
14013
14014   /* check if drop key was pressed long enough for EM style dynamite */
14015   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14016     return FALSE;
14017
14018   /* check if anything can be dropped at the current position */
14019   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14020     return FALSE;
14021
14022   /* collected custom elements can only be dropped on empty fields */
14023   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14024     return FALSE;
14025
14026   if (old_element != EL_EMPTY)
14027     Back[dropx][dropy] = old_element;   /* store old element on this field */
14028
14029   ResetGfxAnimation(dropx, dropy);
14030   ResetRandomAnimationValue(dropx, dropy);
14031
14032   if (player->inventory_size > 0 ||
14033       player->inventory_infinite_element != EL_UNDEFINED)
14034   {
14035     if (player->inventory_size > 0)
14036     {
14037       player->inventory_size--;
14038
14039       DrawGameDoorValues();
14040
14041       if (new_element == EL_DYNAMITE)
14042         new_element = EL_DYNAMITE_ACTIVE;
14043       else if (new_element == EL_EM_DYNAMITE)
14044         new_element = EL_EM_DYNAMITE_ACTIVE;
14045       else if (new_element == EL_SP_DISK_RED)
14046         new_element = EL_SP_DISK_RED_ACTIVE;
14047     }
14048
14049     Feld[dropx][dropy] = new_element;
14050
14051     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14052       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14053                           el2img(Feld[dropx][dropy]), 0);
14054
14055     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14056
14057     /* needed if previous element just changed to "empty" in the last frame */
14058     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14059
14060     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14061                                player->index_bit, drop_side);
14062     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14063                                         CE_PLAYER_DROPS_X,
14064                                         player->index_bit, drop_side);
14065
14066     TestIfElementTouchesCustomElement(dropx, dropy);
14067   }
14068   else          /* player is dropping a dyna bomb */
14069   {
14070     player->dynabombs_left--;
14071
14072     Feld[dropx][dropy] = new_element;
14073
14074     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14075       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14076                           el2img(Feld[dropx][dropy]), 0);
14077
14078     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14079   }
14080
14081   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14082     InitField_WithBug1(dropx, dropy, FALSE);
14083
14084   new_element = Feld[dropx][dropy];     /* element might have changed */
14085
14086   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14087       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14088   {
14089     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14090       MovDir[dropx][dropy] = drop_direction;
14091
14092     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14093
14094     /* do not cause impact style collision by dropping elements that can fall */
14095     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14096   }
14097
14098   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14099   player->is_dropping = TRUE;
14100
14101   player->drop_pressed_delay = 0;
14102   player->is_dropping_pressed = FALSE;
14103
14104   player->drop_x = dropx;
14105   player->drop_y = dropy;
14106
14107   return TRUE;
14108 }
14109
14110 /* ------------------------------------------------------------------------- */
14111 /* game sound playing functions                                              */
14112 /* ------------------------------------------------------------------------- */
14113
14114 static int *loop_sound_frame = NULL;
14115 static int *loop_sound_volume = NULL;
14116
14117 void InitPlayLevelSound()
14118 {
14119   int num_sounds = getSoundListSize();
14120
14121   checked_free(loop_sound_frame);
14122   checked_free(loop_sound_volume);
14123
14124   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14125   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14126 }
14127
14128 static void PlayLevelSound(int x, int y, int nr)
14129 {
14130   int sx = SCREENX(x), sy = SCREENY(y);
14131   int volume, stereo_position;
14132   int max_distance = 8;
14133   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14134
14135   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14136       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14137     return;
14138
14139   if (!IN_LEV_FIELD(x, y) ||
14140       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14141       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14142     return;
14143
14144   volume = SOUND_MAX_VOLUME;
14145
14146   if (!IN_SCR_FIELD(sx, sy))
14147   {
14148     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14149     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14150
14151     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14152   }
14153
14154   stereo_position = (SOUND_MAX_LEFT +
14155                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14156                      (SCR_FIELDX + 2 * max_distance));
14157
14158   if (IS_LOOP_SOUND(nr))
14159   {
14160     /* This assures that quieter loop sounds do not overwrite louder ones,
14161        while restarting sound volume comparison with each new game frame. */
14162
14163     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14164       return;
14165
14166     loop_sound_volume[nr] = volume;
14167     loop_sound_frame[nr] = FrameCounter;
14168   }
14169
14170   PlaySoundExt(nr, volume, stereo_position, type);
14171 }
14172
14173 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14174 {
14175   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14176                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14177                  y < LEVELY(BY1) ? LEVELY(BY1) :
14178                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14179                  sound_action);
14180 }
14181
14182 static void PlayLevelSoundAction(int x, int y, int action)
14183 {
14184   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14185 }
14186
14187 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14188 {
14189   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14190
14191   if (sound_effect != SND_UNDEFINED)
14192     PlayLevelSound(x, y, sound_effect);
14193 }
14194
14195 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14196                                               int action)
14197 {
14198   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14199
14200   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14201     PlayLevelSound(x, y, sound_effect);
14202 }
14203
14204 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14205 {
14206   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14207
14208   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14209     PlayLevelSound(x, y, sound_effect);
14210 }
14211
14212 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14213 {
14214   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14215
14216   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14217     StopSound(sound_effect);
14218 }
14219
14220 static void PlayLevelMusic()
14221 {
14222   if (levelset.music[level_nr] != MUS_UNDEFINED)
14223     PlayMusic(levelset.music[level_nr]);        /* from config file */
14224   else
14225     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14226 }
14227
14228 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14229 {
14230   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14231   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14232   int x = xx - 1 - offset;
14233   int y = yy - 1 - offset;
14234
14235   switch (sample)
14236   {
14237     case SAMPLE_blank:
14238       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14239       break;
14240
14241     case SAMPLE_roll:
14242       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14243       break;
14244
14245     case SAMPLE_stone:
14246       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14247       break;
14248
14249     case SAMPLE_nut:
14250       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14251       break;
14252
14253     case SAMPLE_crack:
14254       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14255       break;
14256
14257     case SAMPLE_bug:
14258       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14259       break;
14260
14261     case SAMPLE_tank:
14262       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14263       break;
14264
14265     case SAMPLE_android_clone:
14266       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14267       break;
14268
14269     case SAMPLE_android_move:
14270       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14271       break;
14272
14273     case SAMPLE_spring:
14274       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14275       break;
14276
14277     case SAMPLE_slurp:
14278       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14279       break;
14280
14281     case SAMPLE_eater:
14282       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14283       break;
14284
14285     case SAMPLE_eater_eat:
14286       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14287       break;
14288
14289     case SAMPLE_alien:
14290       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14291       break;
14292
14293     case SAMPLE_collect:
14294       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14295       break;
14296
14297     case SAMPLE_diamond:
14298       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14299       break;
14300
14301     case SAMPLE_squash:
14302       /* !!! CHECK THIS !!! */
14303 #if 1
14304       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14305 #else
14306       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14307 #endif
14308       break;
14309
14310     case SAMPLE_wonderfall:
14311       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14312       break;
14313
14314     case SAMPLE_drip:
14315       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14316       break;
14317
14318     case SAMPLE_push:
14319       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14320       break;
14321
14322     case SAMPLE_dirt:
14323       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14324       break;
14325
14326     case SAMPLE_acid:
14327       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14328       break;
14329
14330     case SAMPLE_ball:
14331       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14332       break;
14333
14334     case SAMPLE_grow:
14335       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14336       break;
14337
14338     case SAMPLE_wonder:
14339       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14340       break;
14341
14342     case SAMPLE_door:
14343       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14344       break;
14345
14346     case SAMPLE_exit_open:
14347       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14348       break;
14349
14350     case SAMPLE_exit_leave:
14351       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14352       break;
14353
14354     case SAMPLE_dynamite:
14355       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14356       break;
14357
14358     case SAMPLE_tick:
14359       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14360       break;
14361
14362     case SAMPLE_press:
14363       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14364       break;
14365
14366     case SAMPLE_wheel:
14367       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14368       break;
14369
14370     case SAMPLE_boom:
14371       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14372       break;
14373
14374     case SAMPLE_die:
14375       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14376       break;
14377
14378     case SAMPLE_time:
14379       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14380       break;
14381
14382     default:
14383       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14384       break;
14385   }
14386 }
14387
14388 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14389 {
14390   int element = map_element_SP_to_RND(element_sp);
14391   int action = map_action_SP_to_RND(action_sp);
14392   int offset = (setup.sp_show_border_elements ? 0 : 1);
14393   int x = xx - offset;
14394   int y = yy - offset;
14395
14396   PlayLevelSoundElementAction(x, y, element, action);
14397 }
14398
14399 void RaiseScore(int value)
14400 {
14401   local_player->score += value;
14402
14403   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14404
14405   DisplayGameControlValues();
14406 }
14407
14408 void RaiseScoreElement(int element)
14409 {
14410   switch (element)
14411   {
14412     case EL_EMERALD:
14413     case EL_BD_DIAMOND:
14414     case EL_EMERALD_YELLOW:
14415     case EL_EMERALD_RED:
14416     case EL_EMERALD_PURPLE:
14417     case EL_SP_INFOTRON:
14418       RaiseScore(level.score[SC_EMERALD]);
14419       break;
14420     case EL_DIAMOND:
14421       RaiseScore(level.score[SC_DIAMOND]);
14422       break;
14423     case EL_CRYSTAL:
14424       RaiseScore(level.score[SC_CRYSTAL]);
14425       break;
14426     case EL_PEARL:
14427       RaiseScore(level.score[SC_PEARL]);
14428       break;
14429     case EL_BUG:
14430     case EL_BD_BUTTERFLY:
14431     case EL_SP_ELECTRON:
14432       RaiseScore(level.score[SC_BUG]);
14433       break;
14434     case EL_SPACESHIP:
14435     case EL_BD_FIREFLY:
14436     case EL_SP_SNIKSNAK:
14437       RaiseScore(level.score[SC_SPACESHIP]);
14438       break;
14439     case EL_YAMYAM:
14440     case EL_DARK_YAMYAM:
14441       RaiseScore(level.score[SC_YAMYAM]);
14442       break;
14443     case EL_ROBOT:
14444       RaiseScore(level.score[SC_ROBOT]);
14445       break;
14446     case EL_PACMAN:
14447       RaiseScore(level.score[SC_PACMAN]);
14448       break;
14449     case EL_NUT:
14450       RaiseScore(level.score[SC_NUT]);
14451       break;
14452     case EL_DYNAMITE:
14453     case EL_EM_DYNAMITE:
14454     case EL_SP_DISK_RED:
14455     case EL_DYNABOMB_INCREASE_NUMBER:
14456     case EL_DYNABOMB_INCREASE_SIZE:
14457     case EL_DYNABOMB_INCREASE_POWER:
14458       RaiseScore(level.score[SC_DYNAMITE]);
14459       break;
14460     case EL_SHIELD_NORMAL:
14461     case EL_SHIELD_DEADLY:
14462       RaiseScore(level.score[SC_SHIELD]);
14463       break;
14464     case EL_EXTRA_TIME:
14465       RaiseScore(level.extra_time_score);
14466       break;
14467     case EL_KEY_1:
14468     case EL_KEY_2:
14469     case EL_KEY_3:
14470     case EL_KEY_4:
14471     case EL_EM_KEY_1:
14472     case EL_EM_KEY_2:
14473     case EL_EM_KEY_3:
14474     case EL_EM_KEY_4:
14475     case EL_EMC_KEY_5:
14476     case EL_EMC_KEY_6:
14477     case EL_EMC_KEY_7:
14478     case EL_EMC_KEY_8:
14479     case EL_DC_KEY_WHITE:
14480       RaiseScore(level.score[SC_KEY]);
14481       break;
14482     default:
14483       RaiseScore(element_info[element].collect_score);
14484       break;
14485   }
14486 }
14487
14488 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14489 {
14490   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14491   {
14492     /* closing door required in case of envelope style request dialogs */
14493     if (!skip_request)
14494       CloseDoor(DOOR_CLOSE_1);
14495
14496 #if defined(NETWORK_AVALIABLE)
14497     if (options.network)
14498       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14499     else
14500 #endif
14501     {
14502       if (quick_quit)
14503         FadeSkipNextFadeIn();
14504
14505       SetGameStatus(GAME_MODE_MAIN);
14506
14507       DrawMainMenu();
14508     }
14509   }
14510   else          /* continue playing the game */
14511   {
14512     if (tape.playing && tape.deactivate_display)
14513       TapeDeactivateDisplayOff(TRUE);
14514
14515     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14516
14517     if (tape.playing && tape.deactivate_display)
14518       TapeDeactivateDisplayOn();
14519   }
14520 }
14521
14522 void RequestQuitGame(boolean ask_if_really_quit)
14523 {
14524   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14525   boolean skip_request = AllPlayersGone || quick_quit;
14526
14527   RequestQuitGameExt(skip_request, quick_quit,
14528                      "Do you really want to quit the game?");
14529 }
14530
14531
14532 /* ------------------------------------------------------------------------- */
14533 /* random generator functions                                                */
14534 /* ------------------------------------------------------------------------- */
14535
14536 unsigned int InitEngineRandom_RND(int seed)
14537 {
14538   game.num_random_calls = 0;
14539
14540   return InitEngineRandom(seed);
14541 }
14542
14543 unsigned int RND(int max)
14544 {
14545   if (max > 0)
14546   {
14547     game.num_random_calls++;
14548
14549     return GetEngineRandom(max);
14550   }
14551
14552   return 0;
14553 }
14554
14555
14556 /* ------------------------------------------------------------------------- */
14557 /* game engine snapshot handling functions                                   */
14558 /* ------------------------------------------------------------------------- */
14559
14560 struct EngineSnapshotInfo
14561 {
14562   /* runtime values for custom element collect score */
14563   int collect_score[NUM_CUSTOM_ELEMENTS];
14564
14565   /* runtime values for group element choice position */
14566   int choice_pos[NUM_GROUP_ELEMENTS];
14567
14568   /* runtime values for belt position animations */
14569   int belt_graphic[4][NUM_BELT_PARTS];
14570   int belt_anim_mode[4][NUM_BELT_PARTS];
14571 };
14572
14573 static struct EngineSnapshotInfo engine_snapshot_rnd;
14574 static char *snapshot_level_identifier = NULL;
14575 static int snapshot_level_nr = -1;
14576
14577 static void SaveEngineSnapshotValues_RND()
14578 {
14579   static int belt_base_active_element[4] =
14580   {
14581     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14582     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14583     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14584     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14585   };
14586   int i, j;
14587
14588   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14589   {
14590     int element = EL_CUSTOM_START + i;
14591
14592     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14593   }
14594
14595   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14596   {
14597     int element = EL_GROUP_START + i;
14598
14599     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14600   }
14601
14602   for (i = 0; i < 4; i++)
14603   {
14604     for (j = 0; j < NUM_BELT_PARTS; j++)
14605     {
14606       int element = belt_base_active_element[i] + j;
14607       int graphic = el2img(element);
14608       int anim_mode = graphic_info[graphic].anim_mode;
14609
14610       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14611       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14612     }
14613   }
14614 }
14615
14616 static void LoadEngineSnapshotValues_RND()
14617 {
14618   unsigned int num_random_calls = game.num_random_calls;
14619   int i, j;
14620
14621   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14622   {
14623     int element = EL_CUSTOM_START + i;
14624
14625     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14626   }
14627
14628   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14629   {
14630     int element = EL_GROUP_START + i;
14631
14632     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14633   }
14634
14635   for (i = 0; i < 4; i++)
14636   {
14637     for (j = 0; j < NUM_BELT_PARTS; j++)
14638     {
14639       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14640       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14641
14642       graphic_info[graphic].anim_mode = anim_mode;
14643     }
14644   }
14645
14646   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14647   {
14648     InitRND(tape.random_seed);
14649     for (i = 0; i < num_random_calls; i++)
14650       RND(1);
14651   }
14652
14653   if (game.num_random_calls != num_random_calls)
14654   {
14655     Error(ERR_INFO, "number of random calls out of sync");
14656     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14657     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14658     Error(ERR_EXIT, "this should not happen -- please debug");
14659   }
14660 }
14661
14662 void FreeEngineSnapshotSingle()
14663 {
14664   FreeSnapshotSingle();
14665
14666   setString(&snapshot_level_identifier, NULL);
14667   snapshot_level_nr = -1;
14668 }
14669
14670 void FreeEngineSnapshotList()
14671 {
14672   FreeSnapshotList();
14673 }
14674
14675 ListNode *SaveEngineSnapshotBuffers()
14676 {
14677   ListNode *buffers = NULL;
14678
14679   /* copy some special values to a structure better suited for the snapshot */
14680
14681   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14682     SaveEngineSnapshotValues_RND();
14683   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14684     SaveEngineSnapshotValues_EM();
14685   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14686     SaveEngineSnapshotValues_SP(&buffers);
14687
14688   /* save values stored in special snapshot structure */
14689
14690   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14691     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14692   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14693     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14694   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14695     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14696
14697   /* save further RND engine values */
14698
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14702
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14707
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14713
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14717
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14719
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14721
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14724
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14743
14744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14746
14747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14750
14751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14753
14754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14759
14760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14762
14763 #if 0
14764   ListNode *node = engine_snapshot_list_rnd;
14765   int num_bytes = 0;
14766
14767   while (node != NULL)
14768   {
14769     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14770
14771     node = node->next;
14772   }
14773
14774   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14775 #endif
14776
14777   return buffers;
14778 }
14779
14780 void SaveEngineSnapshotSingle()
14781 {
14782   ListNode *buffers = SaveEngineSnapshotBuffers();
14783
14784   /* finally save all snapshot buffers to single snapshot */
14785   SaveSnapshotSingle(buffers);
14786
14787   /* save level identification information */
14788   setString(&snapshot_level_identifier, leveldir_current->identifier);
14789   snapshot_level_nr = level_nr;
14790 }
14791
14792 boolean CheckSaveEngineSnapshotToList()
14793 {
14794   boolean save_snapshot =
14795     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14796      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14797       game.snapshot.changed_action) ||
14798      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14799       game.snapshot.collected_item));
14800
14801   game.snapshot.changed_action = FALSE;
14802   game.snapshot.collected_item = FALSE;
14803   game.snapshot.save_snapshot = save_snapshot;
14804
14805   return save_snapshot;
14806 }
14807
14808 void SaveEngineSnapshotToList()
14809 {
14810   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14811       tape.quick_resume)
14812     return;
14813
14814   ListNode *buffers = SaveEngineSnapshotBuffers();
14815
14816   /* finally save all snapshot buffers to snapshot list */
14817   SaveSnapshotToList(buffers);
14818 }
14819
14820 void SaveEngineSnapshotToListInitial()
14821 {
14822   FreeEngineSnapshotList();
14823
14824   SaveEngineSnapshotToList();
14825 }
14826
14827 void LoadEngineSnapshotValues()
14828 {
14829   /* restore special values from snapshot structure */
14830
14831   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14832     LoadEngineSnapshotValues_RND();
14833   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14834     LoadEngineSnapshotValues_EM();
14835   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14836     LoadEngineSnapshotValues_SP();
14837 }
14838
14839 void LoadEngineSnapshotSingle()
14840 {
14841   LoadSnapshotSingle();
14842
14843   LoadEngineSnapshotValues();
14844 }
14845
14846 void LoadEngineSnapshot_Undo(int steps)
14847 {
14848   LoadSnapshotFromList_Older(steps);
14849
14850   LoadEngineSnapshotValues();
14851 }
14852
14853 void LoadEngineSnapshot_Redo(int steps)
14854 {
14855   LoadSnapshotFromList_Newer(steps);
14856
14857   LoadEngineSnapshotValues();
14858 }
14859
14860 boolean CheckEngineSnapshotSingle()
14861 {
14862   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14863           snapshot_level_nr == level_nr);
14864 }
14865
14866 boolean CheckEngineSnapshotList()
14867 {
14868   return CheckSnapshotList();
14869 }
14870
14871
14872 /* ---------- new game button stuff ---------------------------------------- */
14873
14874 static struct
14875 {
14876   int graphic;
14877   struct XY *pos;
14878   int gadget_id;
14879   char *infotext;
14880 } gamebutton_info[NUM_GAME_BUTTONS] =
14881 {
14882   {
14883     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14884     GAME_CTRL_ID_STOP,                  "stop game"
14885   },
14886   {
14887     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14888     GAME_CTRL_ID_PAUSE,                 "pause game"
14889   },
14890   {
14891     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14892     GAME_CTRL_ID_PLAY,                  "play game"
14893   },
14894   {
14895     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14896     GAME_CTRL_ID_UNDO,                  "undo step"
14897   },
14898   {
14899     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14900     GAME_CTRL_ID_REDO,                  "redo step"
14901   },
14902   {
14903     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14904     GAME_CTRL_ID_SAVE,                  "save game"
14905   },
14906   {
14907     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14908     GAME_CTRL_ID_PAUSE2,                "pause game"
14909   },
14910   {
14911     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14912     GAME_CTRL_ID_LOAD,                  "load game"
14913   },
14914   {
14915     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14916     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14917   },
14918   {
14919     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14920     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14921   },
14922   {
14923     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14924     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14925   }
14926 };
14927
14928 void CreateGameButtons()
14929 {
14930   int i;
14931
14932   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14933   {
14934     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14935     struct XY *pos = gamebutton_info[i].pos;
14936     struct GadgetInfo *gi;
14937     int button_type;
14938     boolean checked;
14939     unsigned int event_mask;
14940     int base_x = (tape.show_game_buttons ? VX : DX);
14941     int base_y = (tape.show_game_buttons ? VY : DY);
14942     int gd_x   = gfx->src_x;
14943     int gd_y   = gfx->src_y;
14944     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14945     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14946     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14947     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14948     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14949     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14950     int id = i;
14951
14952     if (gfx->bitmap == NULL)
14953     {
14954       game_gadget[id] = NULL;
14955
14956       continue;
14957     }
14958
14959     if (id == GAME_CTRL_ID_STOP ||
14960         id == GAME_CTRL_ID_PLAY ||
14961         id == GAME_CTRL_ID_SAVE ||
14962         id == GAME_CTRL_ID_LOAD)
14963     {
14964       button_type = GD_TYPE_NORMAL_BUTTON;
14965       checked = FALSE;
14966       event_mask = GD_EVENT_RELEASED;
14967     }
14968     else if (id == GAME_CTRL_ID_UNDO ||
14969              id == GAME_CTRL_ID_REDO)
14970     {
14971       button_type = GD_TYPE_NORMAL_BUTTON;
14972       checked = FALSE;
14973       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14974     }
14975     else
14976     {
14977       button_type = GD_TYPE_CHECK_BUTTON;
14978       checked =
14979         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14980          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14981          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14982       event_mask = GD_EVENT_PRESSED;
14983     }
14984
14985     gi = CreateGadget(GDI_CUSTOM_ID, id,
14986                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14987                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14988                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14989                       GDI_WIDTH, gfx->width,
14990                       GDI_HEIGHT, gfx->height,
14991                       GDI_TYPE, button_type,
14992                       GDI_STATE, GD_BUTTON_UNPRESSED,
14993                       GDI_CHECKED, checked,
14994                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14995                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14996                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14997                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14998                       GDI_DIRECT_DRAW, FALSE,
14999                       GDI_EVENT_MASK, event_mask,
15000                       GDI_CALLBACK_ACTION, HandleGameButtons,
15001                       GDI_END);
15002
15003     if (gi == NULL)
15004       Error(ERR_EXIT, "cannot create gadget");
15005
15006     game_gadget[id] = gi;
15007   }
15008 }
15009
15010 void FreeGameButtons()
15011 {
15012   int i;
15013
15014   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15015     FreeGadget(game_gadget[i]);
15016 }
15017
15018 static void UnmapGameButtonsAtSamePosition(int id)
15019 {
15020   int i;
15021
15022   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15023     if (i != id &&
15024         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15025         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15026       UnmapGadget(game_gadget[i]);
15027 }
15028
15029 static void UnmapGameButtonsAtSamePosition_All()
15030 {
15031   if (setup.show_snapshot_buttons)
15032   {
15033     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15034     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15035     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15036   }
15037   else
15038   {
15039     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15040     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15041     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15042   }
15043 }
15044
15045 static void MapGameButtonsAtSamePosition(int id)
15046 {
15047   int i;
15048
15049   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15050     if (i != id &&
15051         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15052         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15053       MapGadget(game_gadget[i]);
15054
15055   UnmapGameButtonsAtSamePosition_All();
15056 }
15057
15058 void MapUndoRedoButtons()
15059 {
15060   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15061   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15062
15063   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15064   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15065
15066   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15067 }
15068
15069 void UnmapUndoRedoButtons()
15070 {
15071   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15072   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15073
15074   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15075   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15076
15077   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15078 }
15079
15080 void MapGameButtons()
15081 {
15082   int i;
15083
15084   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15085     if (i != GAME_CTRL_ID_UNDO &&
15086         i != GAME_CTRL_ID_REDO)
15087       MapGadget(game_gadget[i]);
15088
15089   UnmapGameButtonsAtSamePosition_All();
15090
15091   RedrawGameButtons();
15092 }
15093
15094 void UnmapGameButtons()
15095 {
15096   int i;
15097
15098   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15099     UnmapGadget(game_gadget[i]);
15100 }
15101
15102 void RedrawGameButtons()
15103 {
15104   int i;
15105
15106   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15107     RedrawGadget(game_gadget[i]);
15108
15109   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15110   redraw_mask &= ~REDRAW_ALL;
15111 }
15112
15113 void GameUndoRedoExt()
15114 {
15115   ClearPlayerAction();
15116
15117   tape.pausing = TRUE;
15118
15119   RedrawPlayfield();
15120   UpdateAndDisplayGameControlValues();
15121
15122   DrawCompleteVideoDisplay();
15123   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15124   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15125   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15126
15127   BackToFront();
15128 }
15129
15130 void GameUndo(int steps)
15131 {
15132   if (!CheckEngineSnapshotList())
15133     return;
15134
15135   LoadEngineSnapshot_Undo(steps);
15136
15137   GameUndoRedoExt();
15138 }
15139
15140 void GameRedo(int steps)
15141 {
15142   if (!CheckEngineSnapshotList())
15143     return;
15144
15145   LoadEngineSnapshot_Redo(steps);
15146
15147   GameUndoRedoExt();
15148 }
15149
15150 static void HandleGameButtonsExt(int id, int button)
15151 {
15152   static boolean game_undo_executed = FALSE;
15153   int steps = BUTTON_STEPSIZE(button);
15154   boolean handle_game_buttons =
15155     (game_status == GAME_MODE_PLAYING ||
15156      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15157
15158   if (!handle_game_buttons)
15159     return;
15160
15161   switch (id)
15162   {
15163     case GAME_CTRL_ID_STOP:
15164       if (game_status == GAME_MODE_MAIN)
15165         break;
15166
15167       if (tape.playing)
15168         TapeStop();
15169       else
15170         RequestQuitGame(TRUE);
15171
15172       break;
15173
15174     case GAME_CTRL_ID_PAUSE:
15175     case GAME_CTRL_ID_PAUSE2:
15176       if (options.network && game_status == GAME_MODE_PLAYING)
15177       {
15178 #if defined(NETWORK_AVALIABLE)
15179         if (tape.pausing)
15180           SendToServer_ContinuePlaying();
15181         else
15182           SendToServer_PausePlaying();
15183 #endif
15184       }
15185       else
15186         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15187
15188       game_undo_executed = FALSE;
15189
15190       break;
15191
15192     case GAME_CTRL_ID_PLAY:
15193       if (game_status == GAME_MODE_MAIN)
15194       {
15195         StartGameActions(options.network, setup.autorecord, level.random_seed);
15196       }
15197       else if (tape.pausing)
15198       {
15199 #if defined(NETWORK_AVALIABLE)
15200         if (options.network)
15201           SendToServer_ContinuePlaying();
15202         else
15203 #endif
15204           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15205       }
15206       break;
15207
15208     case GAME_CTRL_ID_UNDO:
15209       // Important: When using "save snapshot when collecting an item" mode,
15210       // load last (current) snapshot for first "undo" after pressing "pause"
15211       // (else the last-but-one snapshot would be loaded, because the snapshot
15212       // pointer already points to the last snapshot when pressing "pause",
15213       // which is fine for "every step/move" mode, but not for "every collect")
15214       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15215           !game_undo_executed)
15216         steps--;
15217
15218       game_undo_executed = TRUE;
15219
15220       GameUndo(steps);
15221       break;
15222
15223     case GAME_CTRL_ID_REDO:
15224       GameRedo(steps);
15225       break;
15226
15227     case GAME_CTRL_ID_SAVE:
15228       TapeQuickSave();
15229       break;
15230
15231     case GAME_CTRL_ID_LOAD:
15232       TapeQuickLoad();
15233       break;
15234
15235     case SOUND_CTRL_ID_MUSIC:
15236       if (setup.sound_music)
15237       { 
15238         setup.sound_music = FALSE;
15239
15240         FadeMusic();
15241       }
15242       else if (audio.music_available)
15243       { 
15244         setup.sound = setup.sound_music = TRUE;
15245
15246         SetAudioMode(setup.sound);
15247
15248         PlayLevelMusic();
15249       }
15250       break;
15251
15252     case SOUND_CTRL_ID_LOOPS:
15253       if (setup.sound_loops)
15254         setup.sound_loops = FALSE;
15255       else if (audio.loops_available)
15256       {
15257         setup.sound = setup.sound_loops = TRUE;
15258
15259         SetAudioMode(setup.sound);
15260       }
15261       break;
15262
15263     case SOUND_CTRL_ID_SIMPLE:
15264       if (setup.sound_simple)
15265         setup.sound_simple = FALSE;
15266       else if (audio.sound_available)
15267       {
15268         setup.sound = setup.sound_simple = TRUE;
15269
15270         SetAudioMode(setup.sound);
15271       }
15272       break;
15273
15274     default:
15275       break;
15276   }
15277 }
15278
15279 static void HandleGameButtons(struct GadgetInfo *gi)
15280 {
15281   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15282 }
15283
15284 void HandleSoundButtonKeys(Key key)
15285 {
15286
15287   if (key == setup.shortcut.sound_simple)
15288     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15289   else if (key == setup.shortcut.sound_loops)
15290     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15291   else if (key == setup.shortcut.sound_music)
15292     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15293 }