fixed broken tapes caused by a bugfix (with TAS snap keys) in EM engine
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_FRAME                        35
126 #define GAME_PANEL_SHIELD_NORMAL                36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
128 #define GAME_PANEL_SHIELD_DEADLY                38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
130 #define GAME_PANEL_EXIT                         40
131 #define GAME_PANEL_EMC_MAGIC_BALL               41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
133 #define GAME_PANEL_LIGHT_SWITCH                 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
135 #define GAME_PANEL_TIMEGATE_SWITCH              45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
137 #define GAME_PANEL_SWITCHGATE_SWITCH            47
138 #define GAME_PANEL_EMC_LENSES                   48
139 #define GAME_PANEL_EMC_LENSES_TIME              49
140 #define GAME_PANEL_EMC_MAGNIFIER                50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
142 #define GAME_PANEL_BALLOON_SWITCH               52
143 #define GAME_PANEL_DYNABOMB_NUMBER              53
144 #define GAME_PANEL_DYNABOMB_SIZE                54
145 #define GAME_PANEL_DYNABOMB_POWER               55
146 #define GAME_PANEL_PENGUINS                     56
147 #define GAME_PANEL_SOKOBAN_OBJECTS              57
148 #define GAME_PANEL_SOKOBAN_FIELDS               58
149 #define GAME_PANEL_ROBOT_WHEEL                  59
150 #define GAME_PANEL_CONVEYOR_BELT_1              60
151 #define GAME_PANEL_CONVEYOR_BELT_2              61
152 #define GAME_PANEL_CONVEYOR_BELT_3              62
153 #define GAME_PANEL_CONVEYOR_BELT_4              63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
158 #define GAME_PANEL_MAGIC_WALL                   68
159 #define GAME_PANEL_MAGIC_WALL_TIME              69
160 #define GAME_PANEL_GRAVITY_STATE                70
161 #define GAME_PANEL_GRAPHIC_1                    71
162 #define GAME_PANEL_GRAPHIC_2                    72
163 #define GAME_PANEL_GRAPHIC_3                    73
164 #define GAME_PANEL_GRAPHIC_4                    74
165 #define GAME_PANEL_GRAPHIC_5                    75
166 #define GAME_PANEL_GRAPHIC_6                    76
167 #define GAME_PANEL_GRAPHIC_7                    77
168 #define GAME_PANEL_GRAPHIC_8                    78
169 #define GAME_PANEL_ELEMENT_1                    79
170 #define GAME_PANEL_ELEMENT_2                    80
171 #define GAME_PANEL_ELEMENT_3                    81
172 #define GAME_PANEL_ELEMENT_4                    82
173 #define GAME_PANEL_ELEMENT_5                    83
174 #define GAME_PANEL_ELEMENT_6                    84
175 #define GAME_PANEL_ELEMENT_7                    85
176 #define GAME_PANEL_ELEMENT_8                    86
177 #define GAME_PANEL_ELEMENT_COUNT_1              87
178 #define GAME_PANEL_ELEMENT_COUNT_2              88
179 #define GAME_PANEL_ELEMENT_COUNT_3              89
180 #define GAME_PANEL_ELEMENT_COUNT_4              90
181 #define GAME_PANEL_ELEMENT_COUNT_5              91
182 #define GAME_PANEL_ELEMENT_COUNT_6              92
183 #define GAME_PANEL_ELEMENT_COUNT_7              93
184 #define GAME_PANEL_ELEMENT_COUNT_8              94
185 #define GAME_PANEL_CE_SCORE_1                   95
186 #define GAME_PANEL_CE_SCORE_2                   96
187 #define GAME_PANEL_CE_SCORE_3                   97
188 #define GAME_PANEL_CE_SCORE_4                   98
189 #define GAME_PANEL_CE_SCORE_5                   99
190 #define GAME_PANEL_CE_SCORE_6                   100
191 #define GAME_PANEL_CE_SCORE_7                   101
192 #define GAME_PANEL_CE_SCORE_8                   102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
201 #define GAME_PANEL_PLAYER_NAME                  111
202 #define GAME_PANEL_LEVEL_NAME                   112
203 #define GAME_PANEL_LEVEL_AUTHOR                 113
204
205 #define NUM_GAME_PANEL_CONTROLS                 114
206
207 struct GamePanelOrderInfo
208 {
209   int nr;
210   int sort_priority;
211 };
212
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
214
215 struct GamePanelControlInfo
216 {
217   int nr;
218
219   struct TextPosInfo *pos;
220   int type;
221
222   int value, last_value;
223   int frame, last_frame;
224   int gfx_frame;
225   int gfx_random;
226 };
227
228 static struct GamePanelControlInfo game_panel_controls[] =
229 {
230   {
231     GAME_PANEL_LEVEL_NUMBER,
232     &game.panel.level_number,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_PANEL_GEMS,
237     &game.panel.gems,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_INVENTORY_COUNT,
242     &game.panel.inventory_count,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_FIRST_1,
247     &game.panel.inventory_first[0],
248     TYPE_ELEMENT,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_2,
252     &game.panel.inventory_first[1],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_3,
257     &game.panel.inventory_first[2],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_4,
262     &game.panel.inventory_first[3],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_5,
267     &game.panel.inventory_first[4],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_6,
272     &game.panel.inventory_first[5],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_7,
277     &game.panel.inventory_first[6],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_8,
282     &game.panel.inventory_first[7],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_LAST_1,
287     &game.panel.inventory_last[0],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_2,
292     &game.panel.inventory_last[1],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_3,
297     &game.panel.inventory_last[2],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_4,
302     &game.panel.inventory_last[3],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_5,
307     &game.panel.inventory_last[4],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_6,
312     &game.panel.inventory_last[5],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_7,
317     &game.panel.inventory_last[6],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_8,
322     &game.panel.inventory_last[7],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_KEY_1,
327     &game.panel.key[0],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_2,
332     &game.panel.key[1],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_3,
337     &game.panel.key[2],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_4,
342     &game.panel.key[3],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_5,
347     &game.panel.key[4],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_6,
352     &game.panel.key[5],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_7,
357     &game.panel.key[6],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_8,
362     &game.panel.key[7],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_WHITE,
367     &game.panel.key_white,
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE_COUNT,
372     &game.panel.key_white_count,
373     TYPE_INTEGER,
374   },
375   {
376     GAME_PANEL_SCORE,
377     &game.panel.score,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_HIGHSCORE,
382     &game.panel.highscore,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_TIME,
387     &game.panel.time,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME_HH,
392     &game.panel.time_hh,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_MM,
397     &game.panel.time_mm,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_SS,
402     &game.panel.time_ss,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_FRAME,
407     &game.panel.frame,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_SHIELD_NORMAL,
412     &game.panel.shield_normal,
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_PANEL_SHIELD_NORMAL_TIME,
417     &game.panel.shield_normal_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_PANEL_SHIELD_DEADLY,
422     &game.panel.shield_deadly,
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_PANEL_SHIELD_DEADLY_TIME,
427     &game.panel.shield_deadly_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_PANEL_EXIT,
432     &game.panel.exit,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_PANEL_EMC_MAGIC_BALL,
437     &game.panel.emc_magic_ball,
438     TYPE_ELEMENT,
439   },
440   {
441     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442     &game.panel.emc_magic_ball_switch,
443     TYPE_ELEMENT,
444   },
445   {
446     GAME_PANEL_LIGHT_SWITCH,
447     &game.panel.light_switch,
448     TYPE_ELEMENT,
449   },
450   {
451     GAME_PANEL_LIGHT_SWITCH_TIME,
452     &game.panel.light_switch_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_PANEL_TIMEGATE_SWITCH,
457     &game.panel.timegate_switch,
458     TYPE_ELEMENT,
459   },
460   {
461     GAME_PANEL_TIMEGATE_SWITCH_TIME,
462     &game.panel.timegate_switch_time,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_PANEL_SWITCHGATE_SWITCH,
467     &game.panel.switchgate_switch,
468     TYPE_ELEMENT,
469   },
470   {
471     GAME_PANEL_EMC_LENSES,
472     &game.panel.emc_lenses,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_PANEL_EMC_LENSES_TIME,
477     &game.panel.emc_lenses_time,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_PANEL_EMC_MAGNIFIER,
482     &game.panel.emc_magnifier,
483     TYPE_ELEMENT,
484   },
485   {
486     GAME_PANEL_EMC_MAGNIFIER_TIME,
487     &game.panel.emc_magnifier_time,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_PANEL_BALLOON_SWITCH,
492     &game.panel.balloon_switch,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_PANEL_DYNABOMB_NUMBER,
497     &game.panel.dynabomb_number,
498     TYPE_INTEGER,
499   },
500   {
501     GAME_PANEL_DYNABOMB_SIZE,
502     &game.panel.dynabomb_size,
503     TYPE_INTEGER,
504   },
505   {
506     GAME_PANEL_DYNABOMB_POWER,
507     &game.panel.dynabomb_power,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_PANEL_PENGUINS,
512     &game.panel.penguins,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_PANEL_SOKOBAN_OBJECTS,
517     &game.panel.sokoban_objects,
518     TYPE_INTEGER,
519   },
520   {
521     GAME_PANEL_SOKOBAN_FIELDS,
522     &game.panel.sokoban_fields,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_PANEL_ROBOT_WHEEL,
527     &game.panel.robot_wheel,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_PANEL_CONVEYOR_BELT_1,
532     &game.panel.conveyor_belt[0],
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_PANEL_CONVEYOR_BELT_2,
537     &game.panel.conveyor_belt[1],
538     TYPE_ELEMENT,
539   },
540   {
541     GAME_PANEL_CONVEYOR_BELT_3,
542     &game.panel.conveyor_belt[2],
543     TYPE_ELEMENT,
544   },
545   {
546     GAME_PANEL_CONVEYOR_BELT_4,
547     &game.panel.conveyor_belt[3],
548     TYPE_ELEMENT,
549   },
550   {
551     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552     &game.panel.conveyor_belt_switch[0],
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557     &game.panel.conveyor_belt_switch[1],
558     TYPE_ELEMENT,
559   },
560   {
561     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562     &game.panel.conveyor_belt_switch[2],
563     TYPE_ELEMENT,
564   },
565   {
566     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567     &game.panel.conveyor_belt_switch[3],
568     TYPE_ELEMENT,
569   },
570   {
571     GAME_PANEL_MAGIC_WALL,
572     &game.panel.magic_wall,
573     TYPE_ELEMENT,
574   },
575   {
576     GAME_PANEL_MAGIC_WALL_TIME,
577     &game.panel.magic_wall_time,
578     TYPE_INTEGER,
579   },
580   {
581     GAME_PANEL_GRAVITY_STATE,
582     &game.panel.gravity_state,
583     TYPE_STRING,
584   },
585   {
586     GAME_PANEL_GRAPHIC_1,
587     &game.panel.graphic[0],
588     TYPE_ELEMENT,
589   },
590   {
591     GAME_PANEL_GRAPHIC_2,
592     &game.panel.graphic[1],
593     TYPE_ELEMENT,
594   },
595   {
596     GAME_PANEL_GRAPHIC_3,
597     &game.panel.graphic[2],
598     TYPE_ELEMENT,
599   },
600   {
601     GAME_PANEL_GRAPHIC_4,
602     &game.panel.graphic[3],
603     TYPE_ELEMENT,
604   },
605   {
606     GAME_PANEL_GRAPHIC_5,
607     &game.panel.graphic[4],
608     TYPE_ELEMENT,
609   },
610   {
611     GAME_PANEL_GRAPHIC_6,
612     &game.panel.graphic[5],
613     TYPE_ELEMENT,
614   },
615   {
616     GAME_PANEL_GRAPHIC_7,
617     &game.panel.graphic[6],
618     TYPE_ELEMENT,
619   },
620   {
621     GAME_PANEL_GRAPHIC_8,
622     &game.panel.graphic[7],
623     TYPE_ELEMENT,
624   },
625   {
626     GAME_PANEL_ELEMENT_1,
627     &game.panel.element[0],
628     TYPE_ELEMENT,
629   },
630   {
631     GAME_PANEL_ELEMENT_2,
632     &game.panel.element[1],
633     TYPE_ELEMENT,
634   },
635   {
636     GAME_PANEL_ELEMENT_3,
637     &game.panel.element[2],
638     TYPE_ELEMENT,
639   },
640   {
641     GAME_PANEL_ELEMENT_4,
642     &game.panel.element[3],
643     TYPE_ELEMENT,
644   },
645   {
646     GAME_PANEL_ELEMENT_5,
647     &game.panel.element[4],
648     TYPE_ELEMENT,
649   },
650   {
651     GAME_PANEL_ELEMENT_6,
652     &game.panel.element[5],
653     TYPE_ELEMENT,
654   },
655   {
656     GAME_PANEL_ELEMENT_7,
657     &game.panel.element[6],
658     TYPE_ELEMENT,
659   },
660   {
661     GAME_PANEL_ELEMENT_8,
662     &game.panel.element[7],
663     TYPE_ELEMENT,
664   },
665   {
666     GAME_PANEL_ELEMENT_COUNT_1,
667     &game.panel.element_count[0],
668     TYPE_INTEGER,
669   },
670   {
671     GAME_PANEL_ELEMENT_COUNT_2,
672     &game.panel.element_count[1],
673     TYPE_INTEGER,
674   },
675   {
676     GAME_PANEL_ELEMENT_COUNT_3,
677     &game.panel.element_count[2],
678     TYPE_INTEGER,
679   },
680   {
681     GAME_PANEL_ELEMENT_COUNT_4,
682     &game.panel.element_count[3],
683     TYPE_INTEGER,
684   },
685   {
686     GAME_PANEL_ELEMENT_COUNT_5,
687     &game.panel.element_count[4],
688     TYPE_INTEGER,
689   },
690   {
691     GAME_PANEL_ELEMENT_COUNT_6,
692     &game.panel.element_count[5],
693     TYPE_INTEGER,
694   },
695   {
696     GAME_PANEL_ELEMENT_COUNT_7,
697     &game.panel.element_count[6],
698     TYPE_INTEGER,
699   },
700   {
701     GAME_PANEL_ELEMENT_COUNT_8,
702     &game.panel.element_count[7],
703     TYPE_INTEGER,
704   },
705   {
706     GAME_PANEL_CE_SCORE_1,
707     &game.panel.ce_score[0],
708     TYPE_INTEGER,
709   },
710   {
711     GAME_PANEL_CE_SCORE_2,
712     &game.panel.ce_score[1],
713     TYPE_INTEGER,
714   },
715   {
716     GAME_PANEL_CE_SCORE_3,
717     &game.panel.ce_score[2],
718     TYPE_INTEGER,
719   },
720   {
721     GAME_PANEL_CE_SCORE_4,
722     &game.panel.ce_score[3],
723     TYPE_INTEGER,
724   },
725   {
726     GAME_PANEL_CE_SCORE_5,
727     &game.panel.ce_score[4],
728     TYPE_INTEGER,
729   },
730   {
731     GAME_PANEL_CE_SCORE_6,
732     &game.panel.ce_score[5],
733     TYPE_INTEGER,
734   },
735   {
736     GAME_PANEL_CE_SCORE_7,
737     &game.panel.ce_score[6],
738     TYPE_INTEGER,
739   },
740   {
741     GAME_PANEL_CE_SCORE_8,
742     &game.panel.ce_score[7],
743     TYPE_INTEGER,
744   },
745   {
746     GAME_PANEL_CE_SCORE_1_ELEMENT,
747     &game.panel.ce_score_element[0],
748     TYPE_ELEMENT,
749   },
750   {
751     GAME_PANEL_CE_SCORE_2_ELEMENT,
752     &game.panel.ce_score_element[1],
753     TYPE_ELEMENT,
754   },
755   {
756     GAME_PANEL_CE_SCORE_3_ELEMENT,
757     &game.panel.ce_score_element[2],
758     TYPE_ELEMENT,
759   },
760   {
761     GAME_PANEL_CE_SCORE_4_ELEMENT,
762     &game.panel.ce_score_element[3],
763     TYPE_ELEMENT,
764   },
765   {
766     GAME_PANEL_CE_SCORE_5_ELEMENT,
767     &game.panel.ce_score_element[4],
768     TYPE_ELEMENT,
769   },
770   {
771     GAME_PANEL_CE_SCORE_6_ELEMENT,
772     &game.panel.ce_score_element[5],
773     TYPE_ELEMENT,
774   },
775   {
776     GAME_PANEL_CE_SCORE_7_ELEMENT,
777     &game.panel.ce_score_element[6],
778     TYPE_ELEMENT,
779   },
780   {
781     GAME_PANEL_CE_SCORE_8_ELEMENT,
782     &game.panel.ce_score_element[7],
783     TYPE_ELEMENT,
784   },
785   {
786     GAME_PANEL_PLAYER_NAME,
787     &game.panel.player_name,
788     TYPE_STRING,
789   },
790   {
791     GAME_PANEL_LEVEL_NAME,
792     &game.panel.level_name,
793     TYPE_STRING,
794   },
795   {
796     GAME_PANEL_LEVEL_AUTHOR,
797     &game.panel.level_author,
798     TYPE_STRING,
799   },
800
801   {
802     -1,
803     NULL,
804     -1,
805   }
806 };
807
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING      3
810 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION   2
812 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
813
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF  -1
816 #define INITIAL_MOVE_DELAY_ON   0
817
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED    32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED   4
822 #define MOVE_DELAY_MAX_SPEED    1
823
824 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
826
827 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
829
830 /* values for scroll positions */
831 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
832                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
833                                  (x) - MIDPOSX)
834 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
835                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
836                                  (y) - MIDPOSY)
837
838 /* values for other actions */
839 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
840 #define MOVE_STEPSIZE_MIN       (1)
841 #define MOVE_STEPSIZE_MAX       (TILEX)
842
843 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
844 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
845
846 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
847
848 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
849                                  RND(element_info[e].push_delay_random))
850 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
851                                  RND(element_info[e].drop_delay_random))
852 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
853                                  RND(element_info[e].move_delay_random))
854 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
855                                     (element_info[e].move_delay_random))
856 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
857                                  RND(element_info[e].ce_value_random_initial))
858 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
859 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
860                                  RND((c)->delay_random * (c)->delay_frames))
861 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
862                                  RND((c)->delay_random))
863
864
865 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
866          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
867
868 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
869         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
870          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
871          (be) + (e) - EL_SELF)
872
873 #define GET_PLAYER_FROM_BITS(p)                                         \
874         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
875
876 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
877         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
878          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
879          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
880          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
881          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
882          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
883          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
884          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
885          (e))
886
887 #define CAN_GROW_INTO(e)                                                \
888         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
889
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
891                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
892                                         (condition)))
893
894 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
895                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
896                                         (CAN_MOVE_INTO_ACID(e) &&       \
897                                          Feld[x][y] == EL_ACID) ||      \
898                                         (condition)))
899
900 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
901                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
902                                         (CAN_MOVE_INTO_ACID(e) &&       \
903                                          Feld[x][y] == EL_ACID) ||      \
904                                         (condition)))
905
906 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
907                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
908                                         (condition) ||                  \
909                                         (CAN_MOVE_INTO_ACID(e) &&       \
910                                          Feld[x][y] == EL_ACID) ||      \
911                                         (DONT_COLLIDE_WITH(e) &&        \
912                                          IS_PLAYER(x, y) &&             \
913                                          !PLAYER_ENEMY_PROTECTED(x, y))))
914
915 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
916         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
917
918 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
919         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
920
921 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
922         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
923
924 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
925         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
926                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
927
928 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
929         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
930
931 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
932         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
933
934 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
935         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
936
937 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
938         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
939
940 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
942
943 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
945                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
946                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
947                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
948                                                  IS_FOOD_PENGUIN(Feld[x][y])))
949 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
950         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
951
952 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
953         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
954
955 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
956         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957
958 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
959         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
960                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
961
962 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
963
964 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
965                 (!IS_PLAYER(x, y) &&                                    \
966                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
967
968 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
969         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
970
971 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
972 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
973
974 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
975 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
976 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
977 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
978
979 /* game button identifiers */
980 #define GAME_CTRL_ID_STOP               0
981 #define GAME_CTRL_ID_PAUSE              1
982 #define GAME_CTRL_ID_PLAY               2
983 #define GAME_CTRL_ID_UNDO               3
984 #define GAME_CTRL_ID_REDO               4
985 #define GAME_CTRL_ID_SAVE               5
986 #define GAME_CTRL_ID_PAUSE2             6
987 #define GAME_CTRL_ID_LOAD               7
988 #define SOUND_CTRL_ID_MUSIC             8
989 #define SOUND_CTRL_ID_LOOPS             9
990 #define SOUND_CTRL_ID_SIMPLE            10
991
992 #define NUM_GAME_BUTTONS                11
993
994
995 /* forward declaration for internal use */
996
997 static void CreateField(int, int, int);
998
999 static void ResetGfxAnimation(int, int);
1000
1001 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1002 static void AdvanceFrameAndPlayerCounters(int);
1003
1004 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1005 static boolean MovePlayer(struct PlayerInfo *, int, int);
1006 static void ScrollPlayer(struct PlayerInfo *, int);
1007 static void ScrollScreen(struct PlayerInfo *, int);
1008
1009 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1010 static boolean DigFieldByCE(int, int, int);
1011 static boolean SnapField(struct PlayerInfo *, int, int);
1012 static boolean DropElement(struct PlayerInfo *);
1013
1014 static void InitBeltMovement(void);
1015 static void CloseAllOpenTimegates(void);
1016 static void CheckGravityMovement(struct PlayerInfo *);
1017 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1018 static void KillPlayerUnlessEnemyProtected(int, int);
1019 static void KillPlayerUnlessExplosionProtected(int, int);
1020
1021 static void TestIfPlayerTouchesCustomElement(int, int);
1022 static void TestIfElementTouchesCustomElement(int, int);
1023 static void TestIfElementHitsCustomElement(int, int, int);
1024
1025 static void HandleElementChange(int, int, int);
1026 static void ExecuteCustomElementAction(int, int, int, int);
1027 static boolean ChangeElement(int, int, int, int);
1028
1029 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1030 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1031         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1032 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1033         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1034 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1035         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1036 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1037         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1038
1039 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1040 #define CheckElementChange(x, y, e, te, ev)                             \
1041         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1042 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1043         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1044 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1045         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1046
1047 static void PlayLevelSound(int, int, int);
1048 static void PlayLevelSoundNearest(int, int, int);
1049 static void PlayLevelSoundAction(int, int, int);
1050 static void PlayLevelSoundElementAction(int, int, int, int);
1051 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1052 static void PlayLevelSoundActionIfLoop(int, int, int);
1053 static void StopLevelSoundActionIfLoop(int, int, int);
1054 static void PlayLevelMusic();
1055 static void FadeLevelSoundsAndMusic();
1056
1057 static void HandleGameButtons(struct GadgetInfo *);
1058
1059 int AmoebeNachbarNr(int, int);
1060 void AmoebeUmwandeln(int, int);
1061 void ContinueMoving(int, int);
1062 void Bang(int, int);
1063 void InitMovDir(int, int);
1064 void InitAmoebaNr(int, int);
1065 int NewHiScore(void);
1066
1067 void TestIfGoodThingHitsBadThing(int, int, int);
1068 void TestIfBadThingHitsGoodThing(int, int, int);
1069 void TestIfPlayerTouchesBadThing(int, int);
1070 void TestIfPlayerRunsIntoBadThing(int, int, int);
1071 void TestIfBadThingTouchesPlayer(int, int);
1072 void TestIfBadThingRunsIntoPlayer(int, int, int);
1073 void TestIfFriendTouchesBadThing(int, int);
1074 void TestIfBadThingTouchesFriend(int, int);
1075 void TestIfBadThingTouchesOtherBadThing(int, int);
1076 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1077
1078 void KillPlayer(struct PlayerInfo *);
1079 void BuryPlayer(struct PlayerInfo *);
1080 void RemovePlayer(struct PlayerInfo *);
1081
1082 static int getInvisibleActiveFromInvisibleElement(int);
1083 static int getInvisibleFromInvisibleActiveElement(int);
1084
1085 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1086
1087 /* for detection of endless loops, caused by custom element programming */
1088 /* (using maximal playfield width x 10 is just a rough approximation) */
1089 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1090
1091 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1092 {                                                                       \
1093   if (recursion_loop_detected)                                          \
1094     return (rc);                                                        \
1095                                                                         \
1096   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1097   {                                                                     \
1098     recursion_loop_detected = TRUE;                                     \
1099     recursion_loop_element = (e);                                       \
1100   }                                                                     \
1101                                                                         \
1102   recursion_loop_depth++;                                               \
1103 }
1104
1105 #define RECURSION_LOOP_DETECTION_END()                                  \
1106 {                                                                       \
1107   recursion_loop_depth--;                                               \
1108 }
1109
1110 static int recursion_loop_depth;
1111 static boolean recursion_loop_detected;
1112 static boolean recursion_loop_element;
1113
1114 static int map_player_action[MAX_PLAYERS];
1115
1116
1117 /* ------------------------------------------------------------------------- */
1118 /* definition of elements that automatically change to other elements after  */
1119 /* a specified time, eventually calling a function when changing             */
1120 /* ------------------------------------------------------------------------- */
1121
1122 /* forward declaration for changer functions */
1123 static void InitBuggyBase(int, int);
1124 static void WarnBuggyBase(int, int);
1125
1126 static void InitTrap(int, int);
1127 static void ActivateTrap(int, int);
1128 static void ChangeActiveTrap(int, int);
1129
1130 static void InitRobotWheel(int, int);
1131 static void RunRobotWheel(int, int);
1132 static void StopRobotWheel(int, int);
1133
1134 static void InitTimegateWheel(int, int);
1135 static void RunTimegateWheel(int, int);
1136
1137 static void InitMagicBallDelay(int, int);
1138 static void ActivateMagicBall(int, int);
1139
1140 struct ChangingElementInfo
1141 {
1142   int element;
1143   int target_element;
1144   int change_delay;
1145   void (*pre_change_function)(int x, int y);
1146   void (*change_function)(int x, int y);
1147   void (*post_change_function)(int x, int y);
1148 };
1149
1150 static struct ChangingElementInfo change_delay_list[] =
1151 {
1152   {
1153     EL_NUT_BREAKING,
1154     EL_EMERALD,
1155     6,
1156     NULL,
1157     NULL,
1158     NULL
1159   },
1160   {
1161     EL_PEARL_BREAKING,
1162     EL_EMPTY,
1163     8,
1164     NULL,
1165     NULL,
1166     NULL
1167   },
1168   {
1169     EL_EXIT_OPENING,
1170     EL_EXIT_OPEN,
1171     29,
1172     NULL,
1173     NULL,
1174     NULL
1175   },
1176   {
1177     EL_EXIT_CLOSING,
1178     EL_EXIT_CLOSED,
1179     29,
1180     NULL,
1181     NULL,
1182     NULL
1183   },
1184   {
1185     EL_STEEL_EXIT_OPENING,
1186     EL_STEEL_EXIT_OPEN,
1187     29,
1188     NULL,
1189     NULL,
1190     NULL
1191   },
1192   {
1193     EL_STEEL_EXIT_CLOSING,
1194     EL_STEEL_EXIT_CLOSED,
1195     29,
1196     NULL,
1197     NULL,
1198     NULL
1199   },
1200   {
1201     EL_EM_EXIT_OPENING,
1202     EL_EM_EXIT_OPEN,
1203     29,
1204     NULL,
1205     NULL,
1206     NULL
1207   },
1208   {
1209     EL_EM_EXIT_CLOSING,
1210     EL_EMPTY,
1211     29,
1212     NULL,
1213     NULL,
1214     NULL
1215   },
1216   {
1217     EL_EM_STEEL_EXIT_OPENING,
1218     EL_EM_STEEL_EXIT_OPEN,
1219     29,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_EM_STEEL_EXIT_CLOSING,
1226     EL_STEELWALL,
1227     29,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_SP_EXIT_OPENING,
1234     EL_SP_EXIT_OPEN,
1235     29,
1236     NULL,
1237     NULL,
1238     NULL
1239   },
1240   {
1241     EL_SP_EXIT_CLOSING,
1242     EL_SP_EXIT_CLOSED,
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_SWITCHGATE_OPENING,
1250     EL_SWITCHGATE_OPEN,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_SWITCHGATE_CLOSING,
1258     EL_SWITCHGATE_CLOSED,
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_TIMEGATE_OPENING,
1266     EL_TIMEGATE_OPEN,
1267     29,
1268     NULL,
1269     NULL,
1270     NULL
1271   },
1272   {
1273     EL_TIMEGATE_CLOSING,
1274     EL_TIMEGATE_CLOSED,
1275     29,
1276     NULL,
1277     NULL,
1278     NULL
1279   },
1280
1281   {
1282     EL_ACID_SPLASH_LEFT,
1283     EL_EMPTY,
1284     8,
1285     NULL,
1286     NULL,
1287     NULL
1288   },
1289   {
1290     EL_ACID_SPLASH_RIGHT,
1291     EL_EMPTY,
1292     8,
1293     NULL,
1294     NULL,
1295     NULL
1296   },
1297   {
1298     EL_SP_BUGGY_BASE,
1299     EL_SP_BUGGY_BASE_ACTIVATING,
1300     0,
1301     InitBuggyBase,
1302     NULL,
1303     NULL
1304   },
1305   {
1306     EL_SP_BUGGY_BASE_ACTIVATING,
1307     EL_SP_BUGGY_BASE_ACTIVE,
1308     0,
1309     InitBuggyBase,
1310     NULL,
1311     NULL
1312   },
1313   {
1314     EL_SP_BUGGY_BASE_ACTIVE,
1315     EL_SP_BUGGY_BASE,
1316     0,
1317     InitBuggyBase,
1318     WarnBuggyBase,
1319     NULL
1320   },
1321   {
1322     EL_TRAP,
1323     EL_TRAP_ACTIVE,
1324     0,
1325     InitTrap,
1326     NULL,
1327     ActivateTrap
1328   },
1329   {
1330     EL_TRAP_ACTIVE,
1331     EL_TRAP,
1332     31,
1333     NULL,
1334     ChangeActiveTrap,
1335     NULL
1336   },
1337   {
1338     EL_ROBOT_WHEEL_ACTIVE,
1339     EL_ROBOT_WHEEL,
1340     0,
1341     InitRobotWheel,
1342     RunRobotWheel,
1343     StopRobotWheel
1344   },
1345   {
1346     EL_TIMEGATE_SWITCH_ACTIVE,
1347     EL_TIMEGATE_SWITCH,
1348     0,
1349     InitTimegateWheel,
1350     RunTimegateWheel,
1351     NULL
1352   },
1353   {
1354     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1355     EL_DC_TIMEGATE_SWITCH,
1356     0,
1357     InitTimegateWheel,
1358     RunTimegateWheel,
1359     NULL
1360   },
1361   {
1362     EL_EMC_MAGIC_BALL_ACTIVE,
1363     EL_EMC_MAGIC_BALL_ACTIVE,
1364     0,
1365     InitMagicBallDelay,
1366     NULL,
1367     ActivateMagicBall
1368   },
1369   {
1370     EL_EMC_SPRING_BUMPER_ACTIVE,
1371     EL_EMC_SPRING_BUMPER,
1372     8,
1373     NULL,
1374     NULL,
1375     NULL
1376   },
1377   {
1378     EL_DIAGONAL_SHRINKING,
1379     EL_UNDEFINED,
1380     0,
1381     NULL,
1382     NULL,
1383     NULL
1384   },
1385   {
1386     EL_DIAGONAL_GROWING,
1387     EL_UNDEFINED,
1388     0,
1389     NULL,
1390     NULL,
1391     NULL,
1392   },
1393
1394   {
1395     EL_UNDEFINED,
1396     EL_UNDEFINED,
1397     -1,
1398     NULL,
1399     NULL,
1400     NULL
1401   }
1402 };
1403
1404 struct
1405 {
1406   int element;
1407   int push_delay_fixed, push_delay_random;
1408 }
1409 push_delay_list[] =
1410 {
1411   { EL_SPRING,                  0, 0 },
1412   { EL_BALLOON,                 0, 0 },
1413
1414   { EL_SOKOBAN_OBJECT,          2, 0 },
1415   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1416   { EL_SATELLITE,               2, 0 },
1417   { EL_SP_DISK_YELLOW,          2, 0 },
1418
1419   { EL_UNDEFINED,               0, 0 },
1420 };
1421
1422 struct
1423 {
1424   int element;
1425   int move_stepsize;
1426 }
1427 move_stepsize_list[] =
1428 {
1429   { EL_AMOEBA_DROP,             2 },
1430   { EL_AMOEBA_DROPPING,         2 },
1431   { EL_QUICKSAND_FILLING,       1 },
1432   { EL_QUICKSAND_EMPTYING,      1 },
1433   { EL_QUICKSAND_FAST_FILLING,  2 },
1434   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1435   { EL_MAGIC_WALL_FILLING,      2 },
1436   { EL_MAGIC_WALL_EMPTYING,     2 },
1437   { EL_BD_MAGIC_WALL_FILLING,   2 },
1438   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1439   { EL_DC_MAGIC_WALL_FILLING,   2 },
1440   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1441
1442   { EL_UNDEFINED,               0 },
1443 };
1444
1445 struct
1446 {
1447   int element;
1448   int count;
1449 }
1450 collect_count_list[] =
1451 {
1452   { EL_EMERALD,                 1 },
1453   { EL_BD_DIAMOND,              1 },
1454   { EL_EMERALD_YELLOW,          1 },
1455   { EL_EMERALD_RED,             1 },
1456   { EL_EMERALD_PURPLE,          1 },
1457   { EL_DIAMOND,                 3 },
1458   { EL_SP_INFOTRON,             1 },
1459   { EL_PEARL,                   5 },
1460   { EL_CRYSTAL,                 8 },
1461
1462   { EL_UNDEFINED,               0 },
1463 };
1464
1465 struct
1466 {
1467   int element;
1468   int direction;
1469 }
1470 access_direction_list[] =
1471 {
1472   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1473   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1474   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1475   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1476   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1477   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1478   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1479   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1480   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1481   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1482   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1483
1484   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1485   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1486   { EL_SP_PORT_UP,                                                   MV_DOWN },
1487   { EL_SP_PORT_DOWN,                                         MV_UP           },
1488   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1489   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1490   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1491   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1492   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1493   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1494   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1495   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1496   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1497   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1498   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1499   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1500   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1501   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1502   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1503
1504   { EL_UNDEFINED,                       MV_NONE                              }
1505 };
1506
1507 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1508
1509 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1510 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1511 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1512                                  IS_JUST_CHANGING(x, y))
1513
1514 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1515
1516 /* static variables for playfield scan mode (scanning forward or backward) */
1517 static int playfield_scan_start_x = 0;
1518 static int playfield_scan_start_y = 0;
1519 static int playfield_scan_delta_x = 1;
1520 static int playfield_scan_delta_y = 1;
1521
1522 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1523                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1524                                      (y) += playfield_scan_delta_y)     \
1525                                 for ((x) = playfield_scan_start_x;      \
1526                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1527                                      (x) += playfield_scan_delta_x)
1528
1529 #ifdef DEBUG
1530 void DEBUG_SetMaximumDynamite()
1531 {
1532   int i;
1533
1534   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1535     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1536       local_player->inventory_element[local_player->inventory_size++] =
1537         EL_DYNAMITE;
1538 }
1539 #endif
1540
1541 static void InitPlayfieldScanModeVars()
1542 {
1543   if (game.use_reverse_scan_direction)
1544   {
1545     playfield_scan_start_x = lev_fieldx - 1;
1546     playfield_scan_start_y = lev_fieldy - 1;
1547
1548     playfield_scan_delta_x = -1;
1549     playfield_scan_delta_y = -1;
1550   }
1551   else
1552   {
1553     playfield_scan_start_x = 0;
1554     playfield_scan_start_y = 0;
1555
1556     playfield_scan_delta_x = 1;
1557     playfield_scan_delta_y = 1;
1558   }
1559 }
1560
1561 static void InitPlayfieldScanMode(int mode)
1562 {
1563   game.use_reverse_scan_direction =
1564     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1565
1566   InitPlayfieldScanModeVars();
1567 }
1568
1569 static int get_move_delay_from_stepsize(int move_stepsize)
1570 {
1571   move_stepsize =
1572     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1573
1574   /* make sure that stepsize value is always a power of 2 */
1575   move_stepsize = (1 << log_2(move_stepsize));
1576
1577   return TILEX / move_stepsize;
1578 }
1579
1580 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1581                                boolean init_game)
1582 {
1583   int player_nr = player->index_nr;
1584   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1585   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1586
1587   /* do no immediately change move delay -- the player might just be moving */
1588   player->move_delay_value_next = move_delay;
1589
1590   /* information if player can move must be set separately */
1591   player->cannot_move = cannot_move;
1592
1593   if (init_game)
1594   {
1595     player->move_delay       = game.initial_move_delay[player_nr];
1596     player->move_delay_value = game.initial_move_delay_value[player_nr];
1597
1598     player->move_delay_value_next = -1;
1599
1600     player->move_delay_reset_counter = 0;
1601   }
1602 }
1603
1604 void GetPlayerConfig()
1605 {
1606   GameFrameDelay = setup.game_frame_delay;
1607
1608   if (!audio.sound_available)
1609     setup.sound_simple = FALSE;
1610
1611   if (!audio.loops_available)
1612     setup.sound_loops = FALSE;
1613
1614   if (!audio.music_available)
1615     setup.sound_music = FALSE;
1616
1617   if (!video.fullscreen_available)
1618     setup.fullscreen = FALSE;
1619
1620   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1621
1622   SetAudioMode(setup.sound);
1623 }
1624
1625 int GetElementFromGroupElement(int element)
1626 {
1627   if (IS_GROUP_ELEMENT(element))
1628   {
1629     struct ElementGroupInfo *group = element_info[element].group;
1630     int last_anim_random_frame = gfx.anim_random_frame;
1631     int element_pos;
1632
1633     if (group->choice_mode == ANIM_RANDOM)
1634       gfx.anim_random_frame = RND(group->num_elements_resolved);
1635
1636     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1637                                     group->choice_mode, 0,
1638                                     group->choice_pos);
1639
1640     if (group->choice_mode == ANIM_RANDOM)
1641       gfx.anim_random_frame = last_anim_random_frame;
1642
1643     group->choice_pos++;
1644
1645     element = group->element_resolved[element_pos];
1646   }
1647
1648   return element;
1649 }
1650
1651 static void InitPlayerField(int x, int y, int element, boolean init_game)
1652 {
1653   if (element == EL_SP_MURPHY)
1654   {
1655     if (init_game)
1656     {
1657       if (stored_player[0].present)
1658       {
1659         Feld[x][y] = EL_SP_MURPHY_CLONE;
1660
1661         return;
1662       }
1663       else
1664       {
1665         stored_player[0].initial_element = element;
1666         stored_player[0].use_murphy = TRUE;
1667
1668         if (!level.use_artwork_element[0])
1669           stored_player[0].artwork_element = EL_SP_MURPHY;
1670       }
1671
1672       Feld[x][y] = EL_PLAYER_1;
1673     }
1674   }
1675
1676   if (init_game)
1677   {
1678     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1679     int jx = player->jx, jy = player->jy;
1680
1681     player->present = TRUE;
1682
1683     player->block_last_field = (element == EL_SP_MURPHY ?
1684                                 level.sp_block_last_field :
1685                                 level.block_last_field);
1686
1687     /* ---------- initialize player's last field block delay --------------- */
1688
1689     /* always start with reliable default value (no adjustment needed) */
1690     player->block_delay_adjustment = 0;
1691
1692     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1693     if (player->block_last_field && element == EL_SP_MURPHY)
1694       player->block_delay_adjustment = 1;
1695
1696     /* special case 2: in game engines before 3.1.1, blocking was different */
1697     if (game.use_block_last_field_bug)
1698       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1699
1700     if (!options.network || player->connected)
1701     {
1702       player->active = TRUE;
1703
1704       /* remove potentially duplicate players */
1705       if (StorePlayer[jx][jy] == Feld[x][y])
1706         StorePlayer[jx][jy] = 0;
1707
1708       StorePlayer[x][y] = Feld[x][y];
1709
1710 #if DEBUG_INIT_PLAYER
1711       if (options.debug)
1712       {
1713         printf("- player element %d activated", player->element_nr);
1714         printf(" (local player is %d and currently %s)\n",
1715                local_player->element_nr,
1716                local_player->active ? "active" : "not active");
1717       }
1718     }
1719 #endif
1720
1721     Feld[x][y] = EL_EMPTY;
1722
1723     player->jx = player->last_jx = x;
1724     player->jy = player->last_jy = y;
1725   }
1726
1727   if (!init_game)
1728   {
1729     int player_nr = GET_PLAYER_NR(element);
1730     struct PlayerInfo *player = &stored_player[player_nr];
1731
1732     if (player->active && player->killed)
1733       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1734   }
1735 }
1736
1737 static void InitField(int x, int y, boolean init_game)
1738 {
1739   int element = Feld[x][y];
1740
1741   switch (element)
1742   {
1743     case EL_SP_MURPHY:
1744     case EL_PLAYER_1:
1745     case EL_PLAYER_2:
1746     case EL_PLAYER_3:
1747     case EL_PLAYER_4:
1748       InitPlayerField(x, y, element, init_game);
1749       break;
1750
1751     case EL_SOKOBAN_FIELD_PLAYER:
1752       element = Feld[x][y] = EL_PLAYER_1;
1753       InitField(x, y, init_game);
1754
1755       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1756       InitField(x, y, init_game);
1757       break;
1758
1759     case EL_SOKOBAN_FIELD_EMPTY:
1760       local_player->sokobanfields_still_needed++;
1761       break;
1762
1763     case EL_STONEBLOCK:
1764       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1765         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1766       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1767         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1768       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1769         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1770       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1771         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1772       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1773         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1774       break;
1775
1776     case EL_BUG:
1777     case EL_BUG_RIGHT:
1778     case EL_BUG_UP:
1779     case EL_BUG_LEFT:
1780     case EL_BUG_DOWN:
1781     case EL_SPACESHIP:
1782     case EL_SPACESHIP_RIGHT:
1783     case EL_SPACESHIP_UP:
1784     case EL_SPACESHIP_LEFT:
1785     case EL_SPACESHIP_DOWN:
1786     case EL_BD_BUTTERFLY:
1787     case EL_BD_BUTTERFLY_RIGHT:
1788     case EL_BD_BUTTERFLY_UP:
1789     case EL_BD_BUTTERFLY_LEFT:
1790     case EL_BD_BUTTERFLY_DOWN:
1791     case EL_BD_FIREFLY:
1792     case EL_BD_FIREFLY_RIGHT:
1793     case EL_BD_FIREFLY_UP:
1794     case EL_BD_FIREFLY_LEFT:
1795     case EL_BD_FIREFLY_DOWN:
1796     case EL_PACMAN_RIGHT:
1797     case EL_PACMAN_UP:
1798     case EL_PACMAN_LEFT:
1799     case EL_PACMAN_DOWN:
1800     case EL_YAMYAM:
1801     case EL_YAMYAM_LEFT:
1802     case EL_YAMYAM_RIGHT:
1803     case EL_YAMYAM_UP:
1804     case EL_YAMYAM_DOWN:
1805     case EL_DARK_YAMYAM:
1806     case EL_ROBOT:
1807     case EL_PACMAN:
1808     case EL_SP_SNIKSNAK:
1809     case EL_SP_ELECTRON:
1810     case EL_MOLE:
1811     case EL_MOLE_LEFT:
1812     case EL_MOLE_RIGHT:
1813     case EL_MOLE_UP:
1814     case EL_MOLE_DOWN:
1815       InitMovDir(x, y);
1816       break;
1817
1818     case EL_AMOEBA_FULL:
1819     case EL_BD_AMOEBA:
1820       InitAmoebaNr(x, y);
1821       break;
1822
1823     case EL_AMOEBA_DROP:
1824       if (y == lev_fieldy - 1)
1825       {
1826         Feld[x][y] = EL_AMOEBA_GROWING;
1827         Store[x][y] = EL_AMOEBA_WET;
1828       }
1829       break;
1830
1831     case EL_DYNAMITE_ACTIVE:
1832     case EL_SP_DISK_RED_ACTIVE:
1833     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1834     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1835     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1836     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1837       MovDelay[x][y] = 96;
1838       break;
1839
1840     case EL_EM_DYNAMITE_ACTIVE:
1841       MovDelay[x][y] = 32;
1842       break;
1843
1844     case EL_LAMP:
1845       local_player->lights_still_needed++;
1846       break;
1847
1848     case EL_PENGUIN:
1849       local_player->friends_still_needed++;
1850       break;
1851
1852     case EL_PIG:
1853     case EL_DRAGON:
1854       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1855       break;
1856
1857     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1860     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1861     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1862     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1863     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1864     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1865     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1866     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1867     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1868     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1869       if (init_game)
1870       {
1871         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1872         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1873         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1874
1875         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1876         {
1877           game.belt_dir[belt_nr] = belt_dir;
1878           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1879         }
1880         else    /* more than one switch -- set it like the first switch */
1881         {
1882           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1883         }
1884       }
1885       break;
1886
1887     case EL_LIGHT_SWITCH_ACTIVE:
1888       if (init_game)
1889         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1890       break;
1891
1892     case EL_INVISIBLE_STEELWALL:
1893     case EL_INVISIBLE_WALL:
1894     case EL_INVISIBLE_SAND:
1895       if (game.light_time_left > 0 ||
1896           game.lenses_time_left > 0)
1897         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1898       break;
1899
1900     case EL_EMC_MAGIC_BALL:
1901       if (game.ball_state)
1902         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1903       break;
1904
1905     case EL_EMC_MAGIC_BALL_SWITCH:
1906       if (game.ball_state)
1907         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1908       break;
1909
1910     case EL_TRIGGER_PLAYER:
1911     case EL_TRIGGER_ELEMENT:
1912     case EL_TRIGGER_CE_VALUE:
1913     case EL_TRIGGER_CE_SCORE:
1914     case EL_SELF:
1915     case EL_ANY_ELEMENT:
1916     case EL_CURRENT_CE_VALUE:
1917     case EL_CURRENT_CE_SCORE:
1918     case EL_PREV_CE_1:
1919     case EL_PREV_CE_2:
1920     case EL_PREV_CE_3:
1921     case EL_PREV_CE_4:
1922     case EL_PREV_CE_5:
1923     case EL_PREV_CE_6:
1924     case EL_PREV_CE_7:
1925     case EL_PREV_CE_8:
1926     case EL_NEXT_CE_1:
1927     case EL_NEXT_CE_2:
1928     case EL_NEXT_CE_3:
1929     case EL_NEXT_CE_4:
1930     case EL_NEXT_CE_5:
1931     case EL_NEXT_CE_6:
1932     case EL_NEXT_CE_7:
1933     case EL_NEXT_CE_8:
1934       /* reference elements should not be used on the playfield */
1935       Feld[x][y] = EL_EMPTY;
1936       break;
1937
1938     default:
1939       if (IS_CUSTOM_ELEMENT(element))
1940       {
1941         if (CAN_MOVE(element))
1942           InitMovDir(x, y);
1943
1944         if (!element_info[element].use_last_ce_value || init_game)
1945           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1946       }
1947       else if (IS_GROUP_ELEMENT(element))
1948       {
1949         Feld[x][y] = GetElementFromGroupElement(element);
1950
1951         InitField(x, y, init_game);
1952       }
1953
1954       break;
1955   }
1956
1957   if (!init_game)
1958     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1959 }
1960
1961 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1962 {
1963   InitField(x, y, init_game);
1964
1965   /* not needed to call InitMovDir() -- already done by InitField()! */
1966   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1967       CAN_MOVE(Feld[x][y]))
1968     InitMovDir(x, y);
1969 }
1970
1971 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1972 {
1973   int old_element = Feld[x][y];
1974
1975   InitField(x, y, init_game);
1976
1977   /* not needed to call InitMovDir() -- already done by InitField()! */
1978   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1979       CAN_MOVE(old_element) &&
1980       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1981     InitMovDir(x, y);
1982
1983   /* this case is in fact a combination of not less than three bugs:
1984      first, it calls InitMovDir() for elements that can move, although this is
1985      already done by InitField(); then, it checks the element that was at this
1986      field _before_ the call to InitField() (which can change it); lastly, it
1987      was not called for "mole with direction" elements, which were treated as
1988      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1989   */
1990 }
1991
1992 static int get_key_element_from_nr(int key_nr)
1993 {
1994   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1995                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1996                           EL_EM_KEY_1 : EL_KEY_1);
1997
1998   return key_base_element + key_nr;
1999 }
2000
2001 static int get_next_dropped_element(struct PlayerInfo *player)
2002 {
2003   return (player->inventory_size > 0 ?
2004           player->inventory_element[player->inventory_size - 1] :
2005           player->inventory_infinite_element != EL_UNDEFINED ?
2006           player->inventory_infinite_element :
2007           player->dynabombs_left > 0 ?
2008           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2009           EL_UNDEFINED);
2010 }
2011
2012 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2013 {
2014   /* pos >= 0: get element from bottom of the stack;
2015      pos <  0: get element from top of the stack */
2016
2017   if (pos < 0)
2018   {
2019     int min_inventory_size = -pos;
2020     int inventory_pos = player->inventory_size - min_inventory_size;
2021     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2022
2023     return (player->inventory_size >= min_inventory_size ?
2024             player->inventory_element[inventory_pos] :
2025             player->inventory_infinite_element != EL_UNDEFINED ?
2026             player->inventory_infinite_element :
2027             player->dynabombs_left >= min_dynabombs_left ?
2028             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2029             EL_UNDEFINED);
2030   }
2031   else
2032   {
2033     int min_dynabombs_left = pos + 1;
2034     int min_inventory_size = pos + 1 - player->dynabombs_left;
2035     int inventory_pos = pos - player->dynabombs_left;
2036
2037     return (player->inventory_infinite_element != EL_UNDEFINED ?
2038             player->inventory_infinite_element :
2039             player->dynabombs_left >= min_dynabombs_left ?
2040             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2041             player->inventory_size >= min_inventory_size ?
2042             player->inventory_element[inventory_pos] :
2043             EL_UNDEFINED);
2044   }
2045 }
2046
2047 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2048 {
2049   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2050   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2051   int compare_result;
2052
2053   if (gpo1->sort_priority != gpo2->sort_priority)
2054     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2055   else
2056     compare_result = gpo1->nr - gpo2->nr;
2057
2058   return compare_result;
2059 }
2060
2061 int getPlayerInventorySize(int player_nr)
2062 {
2063   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2064     return level.native_em_level->ply[player_nr]->dynamite;
2065   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2066     return level.native_sp_level->game_sp->red_disk_count;
2067   else
2068     return stored_player[player_nr].inventory_size;
2069 }
2070
2071 void InitGameControlValues()
2072 {
2073   int i;
2074
2075   for (i = 0; game_panel_controls[i].nr != -1; i++)
2076   {
2077     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2078     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2079     struct TextPosInfo *pos = gpc->pos;
2080     int nr = gpc->nr;
2081     int type = gpc->type;
2082
2083     if (nr != i)
2084     {
2085       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2086       Error(ERR_EXIT, "this should not happen -- please debug");
2087     }
2088
2089     /* force update of game controls after initialization */
2090     gpc->value = gpc->last_value = -1;
2091     gpc->frame = gpc->last_frame = -1;
2092     gpc->gfx_frame = -1;
2093
2094     /* determine panel value width for later calculation of alignment */
2095     if (type == TYPE_INTEGER || type == TYPE_STRING)
2096     {
2097       pos->width = pos->size * getFontWidth(pos->font);
2098       pos->height = getFontHeight(pos->font);
2099     }
2100     else if (type == TYPE_ELEMENT)
2101     {
2102       pos->width = pos->size;
2103       pos->height = pos->size;
2104     }
2105
2106     /* fill structure for game panel draw order */
2107     gpo->nr = gpc->nr;
2108     gpo->sort_priority = pos->sort_priority;
2109   }
2110
2111   /* sort game panel controls according to sort_priority and control number */
2112   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2113         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2114 }
2115
2116 void UpdatePlayfieldElementCount()
2117 {
2118   boolean use_element_count = FALSE;
2119   int i, j, x, y;
2120
2121   /* first check if it is needed at all to calculate playfield element count */
2122   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2123     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2124       use_element_count = TRUE;
2125
2126   if (!use_element_count)
2127     return;
2128
2129   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2130     element_info[i].element_count = 0;
2131
2132   SCAN_PLAYFIELD(x, y)
2133   {
2134     element_info[Feld[x][y]].element_count++;
2135   }
2136
2137   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2138     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2139       if (IS_IN_GROUP(j, i))
2140         element_info[EL_GROUP_START + i].element_count +=
2141           element_info[j].element_count;
2142 }
2143
2144 void UpdateGameControlValues()
2145 {
2146   int i, k;
2147   int time = (local_player->LevelSolved ?
2148               local_player->LevelSolved_CountingTime :
2149               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2150               level.native_em_level->lev->time :
2151               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2152               level.native_sp_level->game_sp->time_played :
2153               game.no_time_limit ? TimePlayed : TimeLeft);
2154   int score = (local_player->LevelSolved ?
2155                local_player->LevelSolved_CountingScore :
2156                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157                level.native_em_level->lev->score :
2158                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2159                level.native_sp_level->game_sp->score :
2160                local_player->score);
2161   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2162               level.native_em_level->lev->required :
2163               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2164               level.native_sp_level->game_sp->infotrons_still_needed :
2165               local_player->gems_still_needed);
2166   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2167                      level.native_em_level->lev->required > 0 :
2168                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2169                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2170                      local_player->gems_still_needed > 0 ||
2171                      local_player->sokobanfields_still_needed > 0 ||
2172                      local_player->lights_still_needed > 0);
2173
2174   UpdatePlayfieldElementCount();
2175
2176   /* update game panel control values */
2177
2178   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2179   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2180
2181   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2182   for (i = 0; i < MAX_NUM_KEYS; i++)
2183     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2184   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2185   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2186
2187   if (game.centered_player_nr == -1)
2188   {
2189     for (i = 0; i < MAX_PLAYERS; i++)
2190     {
2191       /* only one player in Supaplex game engine */
2192       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2193         break;
2194
2195       for (k = 0; k < MAX_NUM_KEYS; k++)
2196       {
2197         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2198         {
2199           if (level.native_em_level->ply[i]->keys & (1 << k))
2200             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2201               get_key_element_from_nr(k);
2202         }
2203         else if (stored_player[i].key[k])
2204           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2205             get_key_element_from_nr(k);
2206       }
2207
2208       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2209         getPlayerInventorySize(i);
2210
2211       if (stored_player[i].num_white_keys > 0)
2212         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2213           EL_DC_KEY_WHITE;
2214
2215       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2216         stored_player[i].num_white_keys;
2217     }
2218   }
2219   else
2220   {
2221     int player_nr = game.centered_player_nr;
2222
2223     for (k = 0; k < MAX_NUM_KEYS; k++)
2224     {
2225       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226       {
2227         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2228           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2229             get_key_element_from_nr(k);
2230       }
2231       else if (stored_player[player_nr].key[k])
2232         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2233           get_key_element_from_nr(k);
2234     }
2235
2236     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2237       getPlayerInventorySize(player_nr);
2238
2239     if (stored_player[player_nr].num_white_keys > 0)
2240       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2241
2242     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2243       stored_player[player_nr].num_white_keys;
2244   }
2245
2246   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2247   {
2248     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2249       get_inventory_element_from_pos(local_player, i);
2250     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2251       get_inventory_element_from_pos(local_player, -i - 1);
2252   }
2253
2254   game_panel_controls[GAME_PANEL_SCORE].value = score;
2255   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2256
2257   game_panel_controls[GAME_PANEL_TIME].value = time;
2258
2259   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2260   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2261   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2262
2263   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2264
2265   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2266     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2269     local_player->shield_normal_time_left;
2270   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2271     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2272      EL_EMPTY);
2273   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2274     local_player->shield_deadly_time_left;
2275
2276   game_panel_controls[GAME_PANEL_EXIT].value =
2277     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2278
2279   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2280     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2281   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2282     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2283      EL_EMC_MAGIC_BALL_SWITCH);
2284
2285   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2286     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2287   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2288     game.light_time_left;
2289
2290   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2291     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2292   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2293     game.timegate_time_left;
2294
2295   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2296     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2297
2298   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2299     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2301     game.lenses_time_left;
2302
2303   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2304     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2305   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2306     game.magnify_time_left;
2307
2308   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2309     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2310      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2311      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2312      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2313      EL_BALLOON_SWITCH_NONE);
2314
2315   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2316     local_player->dynabomb_count;
2317   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2318     local_player->dynabomb_size;
2319   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2320     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2321
2322   game_panel_controls[GAME_PANEL_PENGUINS].value =
2323     local_player->friends_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2326     local_player->sokobanfields_still_needed;
2327   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2328     local_player->sokobanfields_still_needed;
2329
2330   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2331     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2332
2333   for (i = 0; i < NUM_BELTS; i++)
2334   {
2335     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2336       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2337        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2338     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2339       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2340   }
2341
2342   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2343     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2344   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2345     game.magic_wall_time_left;
2346
2347   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2348     local_player->gravity;
2349
2350   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2351     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2355       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2356        game.panel.element[i].id : EL_UNDEFINED);
2357
2358   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2359     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2360       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2361        element_info[game.panel.element_count[i].id].element_count : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2366        element_info[game.panel.ce_score[i].id].collect_score : 0);
2367
2368   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2369     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2370       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2371        element_info[game.panel.ce_score_element[i].id].collect_score :
2372        EL_UNDEFINED);
2373
2374   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2375   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2376   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2377
2378   /* update game panel control frames */
2379
2380   for (i = 0; game_panel_controls[i].nr != -1; i++)
2381   {
2382     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2383
2384     if (gpc->type == TYPE_ELEMENT)
2385     {
2386       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2387       {
2388         int last_anim_random_frame = gfx.anim_random_frame;
2389         int element = gpc->value;
2390         int graphic = el2panelimg(element);
2391
2392         if (gpc->value != gpc->last_value)
2393         {
2394           gpc->gfx_frame = 0;
2395           gpc->gfx_random = INIT_GFX_RANDOM();
2396         }
2397         else
2398         {
2399           gpc->gfx_frame++;
2400
2401           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2402               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2403             gpc->gfx_random = INIT_GFX_RANDOM();
2404         }
2405
2406         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407           gfx.anim_random_frame = gpc->gfx_random;
2408
2409         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2410           gpc->gfx_frame = element_info[element].collect_score;
2411
2412         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2413                                               gpc->gfx_frame);
2414
2415         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2416           gfx.anim_random_frame = last_anim_random_frame;
2417       }
2418     }
2419   }
2420 }
2421
2422 void DisplayGameControlValues()
2423 {
2424   boolean redraw_panel = FALSE;
2425   int i;
2426
2427   for (i = 0; game_panel_controls[i].nr != -1; i++)
2428   {
2429     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2430
2431     if (PANEL_DEACTIVATED(gpc->pos))
2432       continue;
2433
2434     if (gpc->value == gpc->last_value &&
2435         gpc->frame == gpc->last_frame)
2436       continue;
2437
2438     redraw_panel = TRUE;
2439   }
2440
2441   if (!redraw_panel)
2442     return;
2443
2444   /* copy default game door content to main double buffer */
2445
2446   /* !!! CHECK AGAIN !!! */
2447   SetPanelBackground();
2448   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2449   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2450
2451   /* redraw game control buttons */
2452   RedrawGameButtons();
2453
2454   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2455
2456   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2457   {
2458     int nr = game_panel_order[i].nr;
2459     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2460     struct TextPosInfo *pos = gpc->pos;
2461     int type = gpc->type;
2462     int value = gpc->value;
2463     int frame = gpc->frame;
2464     int size = pos->size;
2465     int font = pos->font;
2466     boolean draw_masked = pos->draw_masked;
2467     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2468
2469     if (PANEL_DEACTIVATED(pos))
2470       continue;
2471
2472     gpc->last_value = value;
2473     gpc->last_frame = frame;
2474
2475     if (type == TYPE_INTEGER)
2476     {
2477       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2478           nr == GAME_PANEL_TIME)
2479       {
2480         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2481
2482         if (use_dynamic_size)           /* use dynamic number of digits */
2483         {
2484           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2485           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2486           int size2 = size1 + 1;
2487           int font1 = pos->font;
2488           int font2 = pos->font_alt;
2489
2490           size = (value < value_change ? size1 : size2);
2491           font = (value < value_change ? font1 : font2);
2492         }
2493       }
2494
2495       /* correct text size if "digits" is zero or less */
2496       if (size <= 0)
2497         size = strlen(int2str(value, size));
2498
2499       /* dynamically correct text alignment */
2500       pos->width = size * getFontWidth(font);
2501
2502       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2503                   int2str(value, size), font, mask_mode);
2504     }
2505     else if (type == TYPE_ELEMENT)
2506     {
2507       int element, graphic;
2508       Bitmap *src_bitmap;
2509       int src_x, src_y;
2510       int width, height;
2511       int dst_x = PANEL_XPOS(pos);
2512       int dst_y = PANEL_YPOS(pos);
2513
2514       if (value != EL_UNDEFINED && value != EL_EMPTY)
2515       {
2516         element = value;
2517         graphic = el2panelimg(value);
2518
2519         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2520
2521         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2522           size = TILESIZE;
2523
2524         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2525                               &src_x, &src_y);
2526
2527         width  = graphic_info[graphic].width  * size / TILESIZE;
2528         height = graphic_info[graphic].height * size / TILESIZE;
2529
2530         if (draw_masked)
2531           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2532                            dst_x, dst_y);
2533         else
2534           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2535                      dst_x, dst_y);
2536       }
2537     }
2538     else if (type == TYPE_STRING)
2539     {
2540       boolean active = (value != 0);
2541       char *state_normal = "off";
2542       char *state_active = "on";
2543       char *state = (active ? state_active : state_normal);
2544       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2545                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2546                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2547                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2548
2549       if (nr == GAME_PANEL_GRAVITY_STATE)
2550       {
2551         int font1 = pos->font;          /* (used for normal state) */
2552         int font2 = pos->font_alt;      /* (used for active state) */
2553
2554         font = (active ? font2 : font1);
2555       }
2556
2557       if (s != NULL)
2558       {
2559         char *s_cut;
2560
2561         if (size <= 0)
2562         {
2563           /* don't truncate output if "chars" is zero or less */
2564           size = strlen(s);
2565
2566           /* dynamically correct text alignment */
2567           pos->width = size * getFontWidth(font);
2568         }
2569
2570         s_cut = getStringCopyN(s, size);
2571
2572         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2573                     s_cut, font, mask_mode);
2574
2575         free(s_cut);
2576       }
2577     }
2578
2579     redraw_mask |= REDRAW_DOOR_1;
2580   }
2581
2582   SetGameStatus(GAME_MODE_PLAYING);
2583 }
2584
2585 void UpdateAndDisplayGameControlValues()
2586 {
2587   if (tape.deactivate_display)
2588     return;
2589
2590   UpdateGameControlValues();
2591   DisplayGameControlValues();
2592 }
2593
2594 void UpdateGameDoorValues()
2595 {
2596   UpdateGameControlValues();
2597 }
2598
2599 void DrawGameDoorValues()
2600 {
2601   DisplayGameControlValues();
2602 }
2603
2604
2605 /*
2606   =============================================================================
2607   InitGameEngine()
2608   -----------------------------------------------------------------------------
2609   initialize game engine due to level / tape version number
2610   =============================================================================
2611 */
2612
2613 static void InitGameEngine()
2614 {
2615   int i, j, k, l, x, y;
2616
2617   /* set game engine from tape file when re-playing, else from level file */
2618   game.engine_version = (tape.playing ? tape.engine_version :
2619                          level.game_version);
2620
2621   /* set single or multi-player game mode (needed for re-playing tapes) */
2622   game.team_mode = setup.team_mode;
2623
2624   if (tape.playing)
2625   {
2626     int num_players = 0;
2627
2628     for (i = 0; i < MAX_PLAYERS; i++)
2629       if (tape.player_participates[i])
2630         num_players++;
2631
2632     /* multi-player tapes contain input data for more than one player */
2633     game.team_mode = (num_players > 1);
2634   }
2635
2636   /* ---------------------------------------------------------------------- */
2637   /* set flags for bugs and changes according to active game engine version */
2638   /* ---------------------------------------------------------------------- */
2639
2640   /*
2641     Summary of bugfix/change:
2642     Fixed handling for custom elements that change when pushed by the player.
2643
2644     Fixed/changed in version:
2645     3.1.0
2646
2647     Description:
2648     Before 3.1.0, custom elements that "change when pushing" changed directly
2649     after the player started pushing them (until then handled in "DigField()").
2650     Since 3.1.0, these custom elements are not changed until the "pushing"
2651     move of the element is finished (now handled in "ContinueMoving()").
2652
2653     Affected levels/tapes:
2654     The first condition is generally needed for all levels/tapes before version
2655     3.1.0, which might use the old behaviour before it was changed; known tapes
2656     that are affected are some tapes from the level set "Walpurgis Gardens" by
2657     Jamie Cullen.
2658     The second condition is an exception from the above case and is needed for
2659     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2660     above (including some development versions of 3.1.0), but before it was
2661     known that this change would break tapes like the above and was fixed in
2662     3.1.1, so that the changed behaviour was active although the engine version
2663     while recording maybe was before 3.1.0. There is at least one tape that is
2664     affected by this exception, which is the tape for the one-level set "Bug
2665     Machine" by Juergen Bonhagen.
2666   */
2667
2668   game.use_change_when_pushing_bug =
2669     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2670      !(tape.playing &&
2671        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2672        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2673
2674   /*
2675     Summary of bugfix/change:
2676     Fixed handling for blocking the field the player leaves when moving.
2677
2678     Fixed/changed in version:
2679     3.1.1
2680
2681     Description:
2682     Before 3.1.1, when "block last field when moving" was enabled, the field
2683     the player is leaving when moving was blocked for the time of the move,
2684     and was directly unblocked afterwards. This resulted in the last field
2685     being blocked for exactly one less than the number of frames of one player
2686     move. Additionally, even when blocking was disabled, the last field was
2687     blocked for exactly one frame.
2688     Since 3.1.1, due to changes in player movement handling, the last field
2689     is not blocked at all when blocking is disabled. When blocking is enabled,
2690     the last field is blocked for exactly the number of frames of one player
2691     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2692     last field is blocked for exactly one more than the number of frames of
2693     one player move.
2694
2695     Affected levels/tapes:
2696     (!!! yet to be determined -- probably many !!!)
2697   */
2698
2699   game.use_block_last_field_bug =
2700     (game.engine_version < VERSION_IDENT(3,1,1,0));
2701
2702   game_em.use_single_button =
2703     (game.engine_version > VERSION_IDENT(4,0,0,2));
2704
2705   game_em.use_snap_key_bug =
2706     (game.engine_version < VERSION_IDENT(4,0,1,0));
2707
2708   /* ---------------------------------------------------------------------- */
2709
2710   /* set maximal allowed number of custom element changes per game frame */
2711   game.max_num_changes_per_frame = 1;
2712
2713   /* default scan direction: scan playfield from top/left to bottom/right */
2714   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2715
2716   /* dynamically adjust element properties according to game engine version */
2717   InitElementPropertiesEngine(game.engine_version);
2718
2719 #if 0
2720   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2721   printf("          tape version == %06d [%s] [file: %06d]\n",
2722          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2723          tape.file_version);
2724   printf("       => game.engine_version == %06d\n", game.engine_version);
2725 #endif
2726
2727   /* ---------- initialize player's initial move delay --------------------- */
2728
2729   /* dynamically adjust player properties according to level information */
2730   for (i = 0; i < MAX_PLAYERS; i++)
2731     game.initial_move_delay_value[i] =
2732       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2733
2734   /* dynamically adjust player properties according to game engine version */
2735   for (i = 0; i < MAX_PLAYERS; i++)
2736     game.initial_move_delay[i] =
2737       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2738        game.initial_move_delay_value[i] : 0);
2739
2740   /* ---------- initialize player's initial push delay --------------------- */
2741
2742   /* dynamically adjust player properties according to game engine version */
2743   game.initial_push_delay_value =
2744     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2745
2746   /* ---------- initialize changing elements ------------------------------- */
2747
2748   /* initialize changing elements information */
2749   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2750   {
2751     struct ElementInfo *ei = &element_info[i];
2752
2753     /* this pointer might have been changed in the level editor */
2754     ei->change = &ei->change_page[0];
2755
2756     if (!IS_CUSTOM_ELEMENT(i))
2757     {
2758       ei->change->target_element = EL_EMPTY_SPACE;
2759       ei->change->delay_fixed = 0;
2760       ei->change->delay_random = 0;
2761       ei->change->delay_frames = 1;
2762     }
2763
2764     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2765     {
2766       ei->has_change_event[j] = FALSE;
2767
2768       ei->event_page_nr[j] = 0;
2769       ei->event_page[j] = &ei->change_page[0];
2770     }
2771   }
2772
2773   /* add changing elements from pre-defined list */
2774   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2775   {
2776     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2777     struct ElementInfo *ei = &element_info[ch_delay->element];
2778
2779     ei->change->target_element       = ch_delay->target_element;
2780     ei->change->delay_fixed          = ch_delay->change_delay;
2781
2782     ei->change->pre_change_function  = ch_delay->pre_change_function;
2783     ei->change->change_function      = ch_delay->change_function;
2784     ei->change->post_change_function = ch_delay->post_change_function;
2785
2786     ei->change->can_change = TRUE;
2787     ei->change->can_change_or_has_action = TRUE;
2788
2789     ei->has_change_event[CE_DELAY] = TRUE;
2790
2791     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2792     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2793   }
2794
2795   /* ---------- initialize internal run-time variables --------------------- */
2796
2797   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2798   {
2799     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2800
2801     for (j = 0; j < ei->num_change_pages; j++)
2802     {
2803       ei->change_page[j].can_change_or_has_action =
2804         (ei->change_page[j].can_change |
2805          ei->change_page[j].has_action);
2806     }
2807   }
2808
2809   /* add change events from custom element configuration */
2810   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2811   {
2812     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2813
2814     for (j = 0; j < ei->num_change_pages; j++)
2815     {
2816       if (!ei->change_page[j].can_change_or_has_action)
2817         continue;
2818
2819       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2820       {
2821         /* only add event page for the first page found with this event */
2822         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2823         {
2824           ei->has_change_event[k] = TRUE;
2825
2826           ei->event_page_nr[k] = j;
2827           ei->event_page[k] = &ei->change_page[j];
2828         }
2829       }
2830     }
2831   }
2832
2833   /* ---------- initialize reference elements in change conditions --------- */
2834
2835   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2836   {
2837     int element = EL_CUSTOM_START + i;
2838     struct ElementInfo *ei = &element_info[element];
2839
2840     for (j = 0; j < ei->num_change_pages; j++)
2841     {
2842       int trigger_element = ei->change_page[j].initial_trigger_element;
2843
2844       if (trigger_element >= EL_PREV_CE_8 &&
2845           trigger_element <= EL_NEXT_CE_8)
2846         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2847
2848       ei->change_page[j].trigger_element = trigger_element;
2849     }
2850   }
2851
2852   /* ---------- initialize run-time trigger player and element ------------- */
2853
2854   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2855   {
2856     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2857
2858     for (j = 0; j < ei->num_change_pages; j++)
2859     {
2860       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2861       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2862       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2863       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2864       ei->change_page[j].actual_trigger_ce_value = 0;
2865       ei->change_page[j].actual_trigger_ce_score = 0;
2866     }
2867   }
2868
2869   /* ---------- initialize trigger events ---------------------------------- */
2870
2871   /* initialize trigger events information */
2872   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2873     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2874       trigger_events[i][j] = FALSE;
2875
2876   /* add trigger events from element change event properties */
2877   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2878   {
2879     struct ElementInfo *ei = &element_info[i];
2880
2881     for (j = 0; j < ei->num_change_pages; j++)
2882     {
2883       if (!ei->change_page[j].can_change_or_has_action)
2884         continue;
2885
2886       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2887       {
2888         int trigger_element = ei->change_page[j].trigger_element;
2889
2890         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2891         {
2892           if (ei->change_page[j].has_event[k])
2893           {
2894             if (IS_GROUP_ELEMENT(trigger_element))
2895             {
2896               struct ElementGroupInfo *group =
2897                 element_info[trigger_element].group;
2898
2899               for (l = 0; l < group->num_elements_resolved; l++)
2900                 trigger_events[group->element_resolved[l]][k] = TRUE;
2901             }
2902             else if (trigger_element == EL_ANY_ELEMENT)
2903               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2904                 trigger_events[l][k] = TRUE;
2905             else
2906               trigger_events[trigger_element][k] = TRUE;
2907           }
2908         }
2909       }
2910     }
2911   }
2912
2913   /* ---------- initialize push delay -------------------------------------- */
2914
2915   /* initialize push delay values to default */
2916   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2917   {
2918     if (!IS_CUSTOM_ELEMENT(i))
2919     {
2920       /* set default push delay values (corrected since version 3.0.7-1) */
2921       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2922       {
2923         element_info[i].push_delay_fixed = 2;
2924         element_info[i].push_delay_random = 8;
2925       }
2926       else
2927       {
2928         element_info[i].push_delay_fixed = 8;
2929         element_info[i].push_delay_random = 8;
2930       }
2931     }
2932   }
2933
2934   /* set push delay value for certain elements from pre-defined list */
2935   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2936   {
2937     int e = push_delay_list[i].element;
2938
2939     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2940     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2941   }
2942
2943   /* set push delay value for Supaplex elements for newer engine versions */
2944   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2945   {
2946     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2947     {
2948       if (IS_SP_ELEMENT(i))
2949       {
2950         /* set SP push delay to just enough to push under a falling zonk */
2951         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2952
2953         element_info[i].push_delay_fixed  = delay;
2954         element_info[i].push_delay_random = 0;
2955       }
2956     }
2957   }
2958
2959   /* ---------- initialize move stepsize ----------------------------------- */
2960
2961   /* initialize move stepsize values to default */
2962   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2963     if (!IS_CUSTOM_ELEMENT(i))
2964       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2965
2966   /* set move stepsize value for certain elements from pre-defined list */
2967   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2968   {
2969     int e = move_stepsize_list[i].element;
2970
2971     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2972   }
2973
2974   /* ---------- initialize collect score ----------------------------------- */
2975
2976   /* initialize collect score values for custom elements from initial value */
2977   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2978     if (IS_CUSTOM_ELEMENT(i))
2979       element_info[i].collect_score = element_info[i].collect_score_initial;
2980
2981   /* ---------- initialize collect count ----------------------------------- */
2982
2983   /* initialize collect count values for non-custom elements */
2984   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2985     if (!IS_CUSTOM_ELEMENT(i))
2986       element_info[i].collect_count_initial = 0;
2987
2988   /* add collect count values for all elements from pre-defined list */
2989   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2990     element_info[collect_count_list[i].element].collect_count_initial =
2991       collect_count_list[i].count;
2992
2993   /* ---------- initialize access direction -------------------------------- */
2994
2995   /* initialize access direction values to default (access from every side) */
2996   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2997     if (!IS_CUSTOM_ELEMENT(i))
2998       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2999
3000   /* set access direction value for certain elements from pre-defined list */
3001   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3002     element_info[access_direction_list[i].element].access_direction =
3003       access_direction_list[i].direction;
3004
3005   /* ---------- initialize explosion content ------------------------------- */
3006   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3007   {
3008     if (IS_CUSTOM_ELEMENT(i))
3009       continue;
3010
3011     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3012     {
3013       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3014
3015       element_info[i].content.e[x][y] =
3016         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3017          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3018          i == EL_PLAYER_3 ? EL_EMERALD :
3019          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3020          i == EL_MOLE ? EL_EMERALD_RED :
3021          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3022          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3023          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3024          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3025          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3026          i == EL_WALL_EMERALD ? EL_EMERALD :
3027          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3028          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3029          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3030          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3031          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3032          i == EL_WALL_PEARL ? EL_PEARL :
3033          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3034          EL_EMPTY);
3035     }
3036   }
3037
3038   /* ---------- initialize recursion detection ------------------------------ */
3039   recursion_loop_depth = 0;
3040   recursion_loop_detected = FALSE;
3041   recursion_loop_element = EL_UNDEFINED;
3042
3043   /* ---------- initialize graphics engine ---------------------------------- */
3044   game.scroll_delay_value =
3045     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3046      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3047   game.scroll_delay_value =
3048     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3049
3050   /* ---------- initialize game engine snapshots ---------------------------- */
3051   for (i = 0; i < MAX_PLAYERS; i++)
3052     game.snapshot.last_action[i] = 0;
3053   game.snapshot.changed_action = FALSE;
3054   game.snapshot.collected_item = FALSE;
3055   game.snapshot.mode =
3056     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3057      SNAPSHOT_MODE_EVERY_STEP :
3058      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3059      SNAPSHOT_MODE_EVERY_MOVE :
3060      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3061      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3062   game.snapshot.save_snapshot = FALSE;
3063 }
3064
3065 int get_num_special_action(int element, int action_first, int action_last)
3066 {
3067   int num_special_action = 0;
3068   int i, j;
3069
3070   for (i = action_first; i <= action_last; i++)
3071   {
3072     boolean found = FALSE;
3073
3074     for (j = 0; j < NUM_DIRECTIONS; j++)
3075       if (el_act_dir2img(element, i, j) !=
3076           el_act_dir2img(element, ACTION_DEFAULT, j))
3077         found = TRUE;
3078
3079     if (found)
3080       num_special_action++;
3081     else
3082       break;
3083   }
3084
3085   return num_special_action;
3086 }
3087
3088
3089 /*
3090   =============================================================================
3091   InitGame()
3092   -----------------------------------------------------------------------------
3093   initialize and start new game
3094   =============================================================================
3095 */
3096
3097 void InitGame()
3098 {
3099   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3100   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3101   int fade_mask = REDRAW_FIELD;
3102
3103   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3104   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3105   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3106   int initial_move_dir = MV_DOWN;
3107   int i, j, x, y;
3108
3109   // required here to update video display before fading (FIX THIS)
3110   DrawMaskedBorder(REDRAW_DOOR_2);
3111
3112   if (!game.restart_level)
3113     CloseDoor(DOOR_CLOSE_1);
3114
3115   SetGameStatus(GAME_MODE_PLAYING);
3116
3117   if (level_editor_test_game)
3118     FadeSkipNextFadeIn();
3119   else
3120     FadeSetEnterScreen();
3121
3122   if (CheckIfGlobalBorderHasChanged())
3123     fade_mask = REDRAW_ALL;
3124
3125   FadeLevelSoundsAndMusic();
3126
3127   ExpireSoundLoops(TRUE);
3128
3129   FadeOut(fade_mask);
3130
3131   /* needed if different viewport properties defined for playing */
3132   ChangeViewportPropertiesIfNeeded();
3133
3134   ClearField();
3135
3136   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3137
3138   DrawCompleteVideoDisplay();
3139
3140   InitGameEngine();
3141   InitGameControlValues();
3142
3143   /* don't play tapes over network */
3144   network_playing = (options.network && !tape.playing);
3145
3146   for (i = 0; i < MAX_PLAYERS; i++)
3147   {
3148     struct PlayerInfo *player = &stored_player[i];
3149
3150     player->index_nr = i;
3151     player->index_bit = (1 << i);
3152     player->element_nr = EL_PLAYER_1 + i;
3153
3154     player->present = FALSE;
3155     player->active = FALSE;
3156     player->mapped = FALSE;
3157
3158     player->killed = FALSE;
3159     player->reanimated = FALSE;
3160
3161     player->action = 0;
3162     player->effective_action = 0;
3163     player->programmed_action = 0;
3164
3165     player->score = 0;
3166     player->score_final = 0;
3167
3168     player->gems_still_needed = level.gems_needed;
3169     player->sokobanfields_still_needed = 0;
3170     player->lights_still_needed = 0;
3171     player->friends_still_needed = 0;
3172
3173     for (j = 0; j < MAX_NUM_KEYS; j++)
3174       player->key[j] = FALSE;
3175
3176     player->num_white_keys = 0;
3177
3178     player->dynabomb_count = 0;
3179     player->dynabomb_size = 1;
3180     player->dynabombs_left = 0;
3181     player->dynabomb_xl = FALSE;
3182
3183     player->MovDir = initial_move_dir;
3184     player->MovPos = 0;
3185     player->GfxPos = 0;
3186     player->GfxDir = initial_move_dir;
3187     player->GfxAction = ACTION_DEFAULT;
3188     player->Frame = 0;
3189     player->StepFrame = 0;
3190
3191     player->initial_element = player->element_nr;
3192     player->artwork_element =
3193       (level.use_artwork_element[i] ? level.artwork_element[i] :
3194        player->element_nr);
3195     player->use_murphy = FALSE;
3196
3197     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3198     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3199
3200     player->gravity = level.initial_player_gravity[i];
3201
3202     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3203
3204     player->actual_frame_counter = 0;
3205
3206     player->step_counter = 0;
3207
3208     player->last_move_dir = initial_move_dir;
3209
3210     player->is_active = FALSE;
3211
3212     player->is_waiting = FALSE;
3213     player->is_moving = FALSE;
3214     player->is_auto_moving = FALSE;
3215     player->is_digging = FALSE;
3216     player->is_snapping = FALSE;
3217     player->is_collecting = FALSE;
3218     player->is_pushing = FALSE;
3219     player->is_switching = FALSE;
3220     player->is_dropping = FALSE;
3221     player->is_dropping_pressed = FALSE;
3222
3223     player->is_bored = FALSE;
3224     player->is_sleeping = FALSE;
3225
3226     player->was_waiting = TRUE;
3227     player->was_moving = FALSE;
3228     player->was_snapping = FALSE;
3229     player->was_dropping = FALSE;
3230
3231     player->force_dropping = FALSE;
3232
3233     player->frame_counter_bored = -1;
3234     player->frame_counter_sleeping = -1;
3235
3236     player->anim_delay_counter = 0;
3237     player->post_delay_counter = 0;
3238
3239     player->dir_waiting = initial_move_dir;
3240     player->action_waiting = ACTION_DEFAULT;
3241     player->last_action_waiting = ACTION_DEFAULT;
3242     player->special_action_bored = ACTION_DEFAULT;
3243     player->special_action_sleeping = ACTION_DEFAULT;
3244
3245     player->switch_x = -1;
3246     player->switch_y = -1;
3247
3248     player->drop_x = -1;
3249     player->drop_y = -1;
3250
3251     player->show_envelope = 0;
3252
3253     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3254
3255     player->push_delay       = -1;      /* initialized when pushing starts */
3256     player->push_delay_value = game.initial_push_delay_value;
3257
3258     player->drop_delay = 0;
3259     player->drop_pressed_delay = 0;
3260
3261     player->last_jx = -1;
3262     player->last_jy = -1;
3263     player->jx = -1;
3264     player->jy = -1;
3265
3266     player->shield_normal_time_left = 0;
3267     player->shield_deadly_time_left = 0;
3268
3269     player->inventory_infinite_element = EL_UNDEFINED;
3270     player->inventory_size = 0;
3271
3272     if (level.use_initial_inventory[i])
3273     {
3274       for (j = 0; j < level.initial_inventory_size[i]; j++)
3275       {
3276         int element = level.initial_inventory_content[i][j];
3277         int collect_count = element_info[element].collect_count_initial;
3278         int k;
3279
3280         if (!IS_CUSTOM_ELEMENT(element))
3281           collect_count = 1;
3282
3283         if (collect_count == 0)
3284           player->inventory_infinite_element = element;
3285         else
3286           for (k = 0; k < collect_count; k++)
3287             if (player->inventory_size < MAX_INVENTORY_SIZE)
3288               player->inventory_element[player->inventory_size++] = element;
3289       }
3290     }
3291
3292     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3293     SnapField(player, 0, 0);
3294
3295     player->LevelSolved = FALSE;
3296     player->GameOver = FALSE;
3297
3298     player->LevelSolved_GameWon = FALSE;
3299     player->LevelSolved_GameEnd = FALSE;
3300     player->LevelSolved_PanelOff = FALSE;
3301     player->LevelSolved_SaveTape = FALSE;
3302     player->LevelSolved_SaveScore = FALSE;
3303     player->LevelSolved_CountingTime = 0;
3304     player->LevelSolved_CountingScore = 0;
3305
3306     map_player_action[i] = i;
3307   }
3308
3309   network_player_action_received = FALSE;
3310
3311 #if defined(NETWORK_AVALIABLE)
3312   /* initial null action */
3313   if (network_playing)
3314     SendToServer_MovePlayer(MV_NONE);
3315 #endif
3316
3317   ZX = ZY = -1;
3318   ExitX = ExitY = -1;
3319
3320   FrameCounter = 0;
3321   TimeFrames = 0;
3322   TimePlayed = 0;
3323   TimeLeft = level.time;
3324   TapeTime = 0;
3325
3326   ScreenMovDir = MV_NONE;
3327   ScreenMovPos = 0;
3328   ScreenGfxPos = 0;
3329
3330   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3331
3332   AllPlayersGone = FALSE;
3333
3334   game.no_time_limit = (level.time == 0);
3335
3336   game.yamyam_content_nr = 0;
3337   game.robot_wheel_active = FALSE;
3338   game.magic_wall_active = FALSE;
3339   game.magic_wall_time_left = 0;
3340   game.light_time_left = 0;
3341   game.timegate_time_left = 0;
3342   game.switchgate_pos = 0;
3343   game.wind_direction = level.wind_direction_initial;
3344
3345   game.lenses_time_left = 0;
3346   game.magnify_time_left = 0;
3347
3348   game.ball_state = level.ball_state_initial;
3349   game.ball_content_nr = 0;
3350
3351   game.envelope_active = FALSE;
3352
3353   /* set focus to local player for network games, else to all players */
3354   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3355   game.centered_player_nr_next = game.centered_player_nr;
3356   game.set_centered_player = FALSE;
3357
3358   if (network_playing && tape.recording)
3359   {
3360     /* store client dependent player focus when recording network games */
3361     tape.centered_player_nr_next = game.centered_player_nr_next;
3362     tape.set_centered_player = TRUE;
3363   }
3364
3365   for (i = 0; i < NUM_BELTS; i++)
3366   {
3367     game.belt_dir[i] = MV_NONE;
3368     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3369   }
3370
3371   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3372     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3373
3374 #if DEBUG_INIT_PLAYER
3375   if (options.debug)
3376   {
3377     printf("Player status at level initialization:\n");
3378   }
3379 #endif
3380
3381   SCAN_PLAYFIELD(x, y)
3382   {
3383     Feld[x][y] = level.field[x][y];
3384     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3385     ChangeDelay[x][y] = 0;
3386     ChangePage[x][y] = -1;
3387     CustomValue[x][y] = 0;              /* initialized in InitField() */
3388     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3389     AmoebaNr[x][y] = 0;
3390     WasJustMoving[x][y] = 0;
3391     WasJustFalling[x][y] = 0;
3392     CheckCollision[x][y] = 0;
3393     CheckImpact[x][y] = 0;
3394     Stop[x][y] = FALSE;
3395     Pushed[x][y] = FALSE;
3396
3397     ChangeCount[x][y] = 0;
3398     ChangeEvent[x][y] = -1;
3399
3400     ExplodePhase[x][y] = 0;
3401     ExplodeDelay[x][y] = 0;
3402     ExplodeField[x][y] = EX_TYPE_NONE;
3403
3404     RunnerVisit[x][y] = 0;
3405     PlayerVisit[x][y] = 0;
3406
3407     GfxFrame[x][y] = 0;
3408     GfxRandom[x][y] = INIT_GFX_RANDOM();
3409     GfxElement[x][y] = EL_UNDEFINED;
3410     GfxAction[x][y] = ACTION_DEFAULT;
3411     GfxDir[x][y] = MV_NONE;
3412     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3413   }
3414
3415   SCAN_PLAYFIELD(x, y)
3416   {
3417     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3418       emulate_bd = FALSE;
3419     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3420       emulate_sb = FALSE;
3421     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3422       emulate_sp = FALSE;
3423
3424     InitField(x, y, TRUE);
3425
3426     ResetGfxAnimation(x, y);
3427   }
3428
3429   InitBeltMovement();
3430
3431   for (i = 0; i < MAX_PLAYERS; i++)
3432   {
3433     struct PlayerInfo *player = &stored_player[i];
3434
3435     /* set number of special actions for bored and sleeping animation */
3436     player->num_special_action_bored =
3437       get_num_special_action(player->artwork_element,
3438                              ACTION_BORING_1, ACTION_BORING_LAST);
3439     player->num_special_action_sleeping =
3440       get_num_special_action(player->artwork_element,
3441                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3442   }
3443
3444   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3445                     emulate_sb ? EMU_SOKOBAN :
3446                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3447
3448   /* initialize type of slippery elements */
3449   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3450   {
3451     if (!IS_CUSTOM_ELEMENT(i))
3452     {
3453       /* default: elements slip down either to the left or right randomly */
3454       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3455
3456       /* SP style elements prefer to slip down on the left side */
3457       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3458         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3459
3460       /* BD style elements prefer to slip down on the left side */
3461       if (game.emulation == EMU_BOULDERDASH)
3462         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3463     }
3464   }
3465
3466   /* initialize explosion and ignition delay */
3467   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3468   {
3469     if (!IS_CUSTOM_ELEMENT(i))
3470     {
3471       int num_phase = 8;
3472       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3473                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3474                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3475       int last_phase = (num_phase + 1) * delay;
3476       int half_phase = (num_phase / 2) * delay;
3477
3478       element_info[i].explosion_delay = last_phase - 1;
3479       element_info[i].ignition_delay = half_phase;
3480
3481       if (i == EL_BLACK_ORB)
3482         element_info[i].ignition_delay = 1;
3483     }
3484   }
3485
3486   /* correct non-moving belts to start moving left */
3487   for (i = 0; i < NUM_BELTS; i++)
3488     if (game.belt_dir[i] == MV_NONE)
3489       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3490
3491 #if USE_NEW_PLAYER_ASSIGNMENTS
3492   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3493   /* choose default local player */
3494   local_player = &stored_player[0];
3495
3496   for (i = 0; i < MAX_PLAYERS; i++)
3497     stored_player[i].connected = FALSE;
3498
3499   local_player->connected = TRUE;
3500   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3501
3502   if (tape.playing)
3503   {
3504     for (i = 0; i < MAX_PLAYERS; i++)
3505       stored_player[i].connected = tape.player_participates[i];
3506   }
3507   else if (game.team_mode && !options.network)
3508   {
3509     /* try to guess locally connected team mode players (needed for correct
3510        assignment of player figures from level to locally playing players) */
3511
3512     for (i = 0; i < MAX_PLAYERS; i++)
3513       if (setup.input[i].use_joystick ||
3514           setup.input[i].key.left != KSYM_UNDEFINED)
3515         stored_player[i].connected = TRUE;
3516   }
3517
3518 #if DEBUG_INIT_PLAYER
3519   if (options.debug)
3520   {
3521     printf("Player status after level initialization:\n");
3522
3523     for (i = 0; i < MAX_PLAYERS; i++)
3524     {
3525       struct PlayerInfo *player = &stored_player[i];
3526
3527       printf("- player %d: present == %d, connected == %d, active == %d",
3528              i + 1,
3529              player->present,
3530              player->connected,
3531              player->active);
3532
3533       if (local_player == player)
3534         printf(" (local player)");
3535
3536       printf("\n");
3537     }
3538   }
3539 #endif
3540
3541 #if DEBUG_INIT_PLAYER
3542   if (options.debug)
3543     printf("Reassigning players ...\n");
3544 #endif
3545
3546   /* check if any connected player was not found in playfield */
3547   for (i = 0; i < MAX_PLAYERS; i++)
3548   {
3549     struct PlayerInfo *player = &stored_player[i];
3550
3551     if (player->connected && !player->present)
3552     {
3553       struct PlayerInfo *field_player = NULL;
3554
3555 #if DEBUG_INIT_PLAYER
3556       if (options.debug)
3557         printf("- looking for field player for player %d ...\n", i + 1);
3558 #endif
3559
3560       /* assign first free player found that is present in the playfield */
3561
3562       /* first try: look for unmapped playfield player that is not connected */
3563       for (j = 0; j < MAX_PLAYERS; j++)
3564         if (field_player == NULL &&
3565             stored_player[j].present &&
3566             !stored_player[j].mapped &&
3567             !stored_player[j].connected)
3568           field_player = &stored_player[j];
3569
3570       /* second try: look for *any* unmapped playfield player */
3571       for (j = 0; j < MAX_PLAYERS; j++)
3572         if (field_player == NULL &&
3573             stored_player[j].present &&
3574             !stored_player[j].mapped)
3575           field_player = &stored_player[j];
3576
3577       if (field_player != NULL)
3578       {
3579         int jx = field_player->jx, jy = field_player->jy;
3580
3581 #if DEBUG_INIT_PLAYER
3582         if (options.debug)
3583           printf("- found player %d\n", field_player->index_nr + 1);
3584 #endif
3585
3586         player->present = FALSE;
3587         player->active = FALSE;
3588
3589         field_player->present = TRUE;
3590         field_player->active = TRUE;
3591
3592         /*
3593         player->initial_element = field_player->initial_element;
3594         player->artwork_element = field_player->artwork_element;
3595
3596         player->block_last_field       = field_player->block_last_field;
3597         player->block_delay_adjustment = field_player->block_delay_adjustment;
3598         */
3599
3600         StorePlayer[jx][jy] = field_player->element_nr;
3601
3602         field_player->jx = field_player->last_jx = jx;
3603         field_player->jy = field_player->last_jy = jy;
3604
3605         if (local_player == player)
3606           local_player = field_player;
3607
3608         map_player_action[field_player->index_nr] = i;
3609
3610         field_player->mapped = TRUE;
3611
3612 #if DEBUG_INIT_PLAYER
3613         if (options.debug)
3614           printf("- map_player_action[%d] == %d\n",
3615                  field_player->index_nr + 1, i + 1);
3616 #endif
3617       }
3618     }
3619
3620     if (player->connected && player->present)
3621       player->mapped = TRUE;
3622   }
3623
3624 #if DEBUG_INIT_PLAYER
3625   if (options.debug)
3626   {
3627     printf("Player status after player assignment (first stage):\n");
3628
3629     for (i = 0; i < MAX_PLAYERS; i++)
3630     {
3631       struct PlayerInfo *player = &stored_player[i];
3632
3633       printf("- player %d: present == %d, connected == %d, active == %d",
3634              i + 1,
3635              player->present,
3636              player->connected,
3637              player->active);
3638
3639       if (local_player == player)
3640         printf(" (local player)");
3641
3642       printf("\n");
3643     }
3644   }
3645 #endif
3646
3647 #else
3648
3649   /* check if any connected player was not found in playfield */
3650   for (i = 0; i < MAX_PLAYERS; i++)
3651   {
3652     struct PlayerInfo *player = &stored_player[i];
3653
3654     if (player->connected && !player->present)
3655     {
3656       for (j = 0; j < MAX_PLAYERS; j++)
3657       {
3658         struct PlayerInfo *field_player = &stored_player[j];
3659         int jx = field_player->jx, jy = field_player->jy;
3660
3661         /* assign first free player found that is present in the playfield */
3662         if (field_player->present && !field_player->connected)
3663         {
3664           player->present = TRUE;
3665           player->active = TRUE;
3666
3667           field_player->present = FALSE;
3668           field_player->active = FALSE;
3669
3670           player->initial_element = field_player->initial_element;
3671           player->artwork_element = field_player->artwork_element;
3672
3673           player->block_last_field       = field_player->block_last_field;
3674           player->block_delay_adjustment = field_player->block_delay_adjustment;
3675
3676           StorePlayer[jx][jy] = player->element_nr;
3677
3678           player->jx = player->last_jx = jx;
3679           player->jy = player->last_jy = jy;
3680
3681           break;
3682         }
3683       }
3684     }
3685   }
3686 #endif
3687
3688 #if 0
3689   printf("::: local_player->present == %d\n", local_player->present);
3690 #endif
3691
3692   if (tape.playing)
3693   {
3694     /* when playing a tape, eliminate all players who do not participate */
3695
3696 #if USE_NEW_PLAYER_ASSIGNMENTS
3697
3698     if (!game.team_mode)
3699     {
3700       for (i = 0; i < MAX_PLAYERS; i++)
3701       {
3702         if (stored_player[i].active &&
3703             !tape.player_participates[map_player_action[i]])
3704         {
3705           struct PlayerInfo *player = &stored_player[i];
3706           int jx = player->jx, jy = player->jy;
3707
3708 #if DEBUG_INIT_PLAYER
3709           if (options.debug)
3710             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3711 #endif
3712
3713           player->active = FALSE;
3714           StorePlayer[jx][jy] = 0;
3715           Feld[jx][jy] = EL_EMPTY;
3716         }
3717       }
3718     }
3719
3720 #else
3721
3722     for (i = 0; i < MAX_PLAYERS; i++)
3723     {
3724       if (stored_player[i].active &&
3725           !tape.player_participates[i])
3726       {
3727         struct PlayerInfo *player = &stored_player[i];
3728         int jx = player->jx, jy = player->jy;
3729
3730         player->active = FALSE;
3731         StorePlayer[jx][jy] = 0;
3732         Feld[jx][jy] = EL_EMPTY;
3733       }
3734     }
3735 #endif
3736   }
3737   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3738   {
3739     /* when in single player mode, eliminate all but the first active player */
3740
3741     for (i = 0; i < MAX_PLAYERS; i++)
3742     {
3743       if (stored_player[i].active)
3744       {
3745         for (j = i + 1; j < MAX_PLAYERS; j++)
3746         {
3747           if (stored_player[j].active)
3748           {
3749             struct PlayerInfo *player = &stored_player[j];
3750             int jx = player->jx, jy = player->jy;
3751
3752             player->active = FALSE;
3753             player->present = FALSE;
3754
3755             StorePlayer[jx][jy] = 0;
3756             Feld[jx][jy] = EL_EMPTY;
3757           }
3758         }
3759       }
3760     }
3761   }
3762
3763   /* when recording the game, store which players take part in the game */
3764   if (tape.recording)
3765   {
3766 #if USE_NEW_PLAYER_ASSIGNMENTS
3767     for (i = 0; i < MAX_PLAYERS; i++)
3768       if (stored_player[i].connected)
3769         tape.player_participates[i] = TRUE;
3770 #else
3771     for (i = 0; i < MAX_PLAYERS; i++)
3772       if (stored_player[i].active)
3773         tape.player_participates[i] = TRUE;
3774 #endif
3775   }
3776
3777 #if DEBUG_INIT_PLAYER
3778   if (options.debug)
3779   {
3780     printf("Player status after player assignment (final stage):\n");
3781
3782     for (i = 0; i < MAX_PLAYERS; i++)
3783     {
3784       struct PlayerInfo *player = &stored_player[i];
3785
3786       printf("- player %d: present == %d, connected == %d, active == %d",
3787              i + 1,
3788              player->present,
3789              player->connected,
3790              player->active);
3791
3792       if (local_player == player)
3793         printf(" (local player)");
3794
3795       printf("\n");
3796     }
3797   }
3798 #endif
3799
3800   if (BorderElement == EL_EMPTY)
3801   {
3802     SBX_Left = 0;
3803     SBX_Right = lev_fieldx - SCR_FIELDX;
3804     SBY_Upper = 0;
3805     SBY_Lower = lev_fieldy - SCR_FIELDY;
3806   }
3807   else
3808   {
3809     SBX_Left = -1;
3810     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3811     SBY_Upper = -1;
3812     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3813   }
3814
3815   if (full_lev_fieldx <= SCR_FIELDX)
3816     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3817   if (full_lev_fieldy <= SCR_FIELDY)
3818     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3819
3820   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3821     SBX_Left--;
3822   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3823     SBY_Upper--;
3824
3825   /* if local player not found, look for custom element that might create
3826      the player (make some assumptions about the right custom element) */
3827   if (!local_player->present)
3828   {
3829     int start_x = 0, start_y = 0;
3830     int found_rating = 0;
3831     int found_element = EL_UNDEFINED;
3832     int player_nr = local_player->index_nr;
3833
3834     SCAN_PLAYFIELD(x, y)
3835     {
3836       int element = Feld[x][y];
3837       int content;
3838       int xx, yy;
3839       boolean is_player;
3840
3841       if (level.use_start_element[player_nr] &&
3842           level.start_element[player_nr] == element &&
3843           found_rating < 4)
3844       {
3845         start_x = x;
3846         start_y = y;
3847
3848         found_rating = 4;
3849         found_element = element;
3850       }
3851
3852       if (!IS_CUSTOM_ELEMENT(element))
3853         continue;
3854
3855       if (CAN_CHANGE(element))
3856       {
3857         for (i = 0; i < element_info[element].num_change_pages; i++)
3858         {
3859           /* check for player created from custom element as single target */
3860           content = element_info[element].change_page[i].target_element;
3861           is_player = ELEM_IS_PLAYER(content);
3862
3863           if (is_player && (found_rating < 3 ||
3864                             (found_rating == 3 && element < found_element)))
3865           {
3866             start_x = x;
3867             start_y = y;
3868
3869             found_rating = 3;
3870             found_element = element;
3871           }
3872         }
3873       }
3874
3875       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3876       {
3877         /* check for player created from custom element as explosion content */
3878         content = element_info[element].content.e[xx][yy];
3879         is_player = ELEM_IS_PLAYER(content);
3880
3881         if (is_player && (found_rating < 2 ||
3882                           (found_rating == 2 && element < found_element)))
3883         {
3884           start_x = x + xx - 1;
3885           start_y = y + yy - 1;
3886
3887           found_rating = 2;
3888           found_element = element;
3889         }
3890
3891         if (!CAN_CHANGE(element))
3892           continue;
3893
3894         for (i = 0; i < element_info[element].num_change_pages; i++)
3895         {
3896           /* check for player created from custom element as extended target */
3897           content =
3898             element_info[element].change_page[i].target_content.e[xx][yy];
3899
3900           is_player = ELEM_IS_PLAYER(content);
3901
3902           if (is_player && (found_rating < 1 ||
3903                             (found_rating == 1 && element < found_element)))
3904           {
3905             start_x = x + xx - 1;
3906             start_y = y + yy - 1;
3907
3908             found_rating = 1;
3909             found_element = element;
3910           }
3911         }
3912       }
3913     }
3914
3915     scroll_x = SCROLL_POSITION_X(start_x);
3916     scroll_y = SCROLL_POSITION_Y(start_y);
3917   }
3918   else
3919   {
3920     scroll_x = SCROLL_POSITION_X(local_player->jx);
3921     scroll_y = SCROLL_POSITION_Y(local_player->jy);
3922   }
3923
3924   /* !!! FIX THIS (START) !!! */
3925   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3926   {
3927     InitGameEngine_EM();
3928   }
3929   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3930   {
3931     InitGameEngine_SP();
3932   }
3933   else
3934   {
3935     DrawLevel(REDRAW_FIELD);
3936     DrawAllPlayers();
3937
3938     /* after drawing the level, correct some elements */
3939     if (game.timegate_time_left == 0)
3940       CloseAllOpenTimegates();
3941   }
3942
3943   /* blit playfield from scroll buffer to normal back buffer for fading in */
3944   BlitScreenToBitmap(backbuffer);
3945   /* !!! FIX THIS (END) !!! */
3946
3947   DrawMaskedBorder(fade_mask);
3948
3949   FadeIn(fade_mask);
3950
3951 #if 1
3952   // full screen redraw is required at this point in the following cases:
3953   // - special editor door undrawn when game was started from level editor
3954   // - drawing area (playfield) was changed and has to be removed completely
3955   redraw_mask = REDRAW_ALL;
3956   BackToFront();
3957 #endif
3958
3959   if (!game.restart_level)
3960   {
3961     /* copy default game door content to main double buffer */
3962
3963     /* !!! CHECK AGAIN !!! */
3964     SetPanelBackground();
3965     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3966     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3967   }
3968
3969   SetPanelBackground();
3970   SetDrawBackgroundMask(REDRAW_DOOR_1);
3971
3972   UpdateAndDisplayGameControlValues();
3973
3974   if (!game.restart_level)
3975   {
3976     UnmapGameButtons();
3977     UnmapTapeButtons();
3978
3979     FreeGameButtons();
3980     CreateGameButtons();
3981
3982     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3983     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3984     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3985
3986     MapGameButtons();
3987     MapTapeButtons();
3988
3989     /* copy actual game door content to door double buffer for OpenDoor() */
3990     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3991
3992     OpenDoor(DOOR_OPEN_ALL);
3993
3994     PlaySound(SND_GAME_STARTING);
3995
3996     if (setup.sound_music)
3997       PlayLevelMusic();
3998
3999     KeyboardAutoRepeatOffUnlessAutoplay();
4000
4001 #if DEBUG_INIT_PLAYER
4002     if (options.debug)
4003     {
4004       printf("Player status (final):\n");
4005
4006       for (i = 0; i < MAX_PLAYERS; i++)
4007       {
4008         struct PlayerInfo *player = &stored_player[i];
4009
4010         printf("- player %d: present == %d, connected == %d, active == %d",
4011                i + 1,
4012                player->present,
4013                player->connected,
4014                player->active);
4015
4016         if (local_player == player)
4017           printf(" (local player)");
4018
4019         printf("\n");
4020       }
4021     }
4022 #endif
4023   }
4024
4025   UnmapAllGadgets();
4026
4027   MapGameButtons();
4028   MapTapeButtons();
4029
4030   if (!game.restart_level && !tape.playing)
4031   {
4032     LevelStats_incPlayed(level_nr);
4033
4034     SaveLevelSetup_SeriesInfo();
4035   }
4036
4037   game.restart_level = FALSE;
4038
4039   SaveEngineSnapshotToListInitial();
4040 }
4041
4042 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4043                         int actual_player_x, int actual_player_y)
4044 {
4045   /* this is used for non-R'n'D game engines to update certain engine values */
4046
4047   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4048   {
4049     actual_player_x = correctLevelPosX_EM(actual_player_x);
4050     actual_player_y = correctLevelPosY_EM(actual_player_y);
4051   }
4052
4053   /* needed to determine if sounds are played within the visible screen area */
4054   scroll_x = actual_scroll_x;
4055   scroll_y = actual_scroll_y;
4056
4057   /* needed to get player position for "follow finger" playing input method */
4058   local_player->jx = actual_player_x;
4059   local_player->jy = actual_player_y;
4060 }
4061
4062 void InitMovDir(int x, int y)
4063 {
4064   int i, element = Feld[x][y];
4065   static int xy[4][2] =
4066   {
4067     {  0, +1 },
4068     { +1,  0 },
4069     {  0, -1 },
4070     { -1,  0 }
4071   };
4072   static int direction[3][4] =
4073   {
4074     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4075     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4076     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4077   };
4078
4079   switch (element)
4080   {
4081     case EL_BUG_RIGHT:
4082     case EL_BUG_UP:
4083     case EL_BUG_LEFT:
4084     case EL_BUG_DOWN:
4085       Feld[x][y] = EL_BUG;
4086       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4087       break;
4088
4089     case EL_SPACESHIP_RIGHT:
4090     case EL_SPACESHIP_UP:
4091     case EL_SPACESHIP_LEFT:
4092     case EL_SPACESHIP_DOWN:
4093       Feld[x][y] = EL_SPACESHIP;
4094       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4095       break;
4096
4097     case EL_BD_BUTTERFLY_RIGHT:
4098     case EL_BD_BUTTERFLY_UP:
4099     case EL_BD_BUTTERFLY_LEFT:
4100     case EL_BD_BUTTERFLY_DOWN:
4101       Feld[x][y] = EL_BD_BUTTERFLY;
4102       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4103       break;
4104
4105     case EL_BD_FIREFLY_RIGHT:
4106     case EL_BD_FIREFLY_UP:
4107     case EL_BD_FIREFLY_LEFT:
4108     case EL_BD_FIREFLY_DOWN:
4109       Feld[x][y] = EL_BD_FIREFLY;
4110       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4111       break;
4112
4113     case EL_PACMAN_RIGHT:
4114     case EL_PACMAN_UP:
4115     case EL_PACMAN_LEFT:
4116     case EL_PACMAN_DOWN:
4117       Feld[x][y] = EL_PACMAN;
4118       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4119       break;
4120
4121     case EL_YAMYAM_LEFT:
4122     case EL_YAMYAM_RIGHT:
4123     case EL_YAMYAM_UP:
4124     case EL_YAMYAM_DOWN:
4125       Feld[x][y] = EL_YAMYAM;
4126       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4127       break;
4128
4129     case EL_SP_SNIKSNAK:
4130       MovDir[x][y] = MV_UP;
4131       break;
4132
4133     case EL_SP_ELECTRON:
4134       MovDir[x][y] = MV_LEFT;
4135       break;
4136
4137     case EL_MOLE_LEFT:
4138     case EL_MOLE_RIGHT:
4139     case EL_MOLE_UP:
4140     case EL_MOLE_DOWN:
4141       Feld[x][y] = EL_MOLE;
4142       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4143       break;
4144
4145     default:
4146       if (IS_CUSTOM_ELEMENT(element))
4147       {
4148         struct ElementInfo *ei = &element_info[element];
4149         int move_direction_initial = ei->move_direction_initial;
4150         int move_pattern = ei->move_pattern;
4151
4152         if (move_direction_initial == MV_START_PREVIOUS)
4153         {
4154           if (MovDir[x][y] != MV_NONE)
4155             return;
4156
4157           move_direction_initial = MV_START_AUTOMATIC;
4158         }
4159
4160         if (move_direction_initial == MV_START_RANDOM)
4161           MovDir[x][y] = 1 << RND(4);
4162         else if (move_direction_initial & MV_ANY_DIRECTION)
4163           MovDir[x][y] = move_direction_initial;
4164         else if (move_pattern == MV_ALL_DIRECTIONS ||
4165                  move_pattern == MV_TURNING_LEFT ||
4166                  move_pattern == MV_TURNING_RIGHT ||
4167                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4168                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4169                  move_pattern == MV_TURNING_RANDOM)
4170           MovDir[x][y] = 1 << RND(4);
4171         else if (move_pattern == MV_HORIZONTAL)
4172           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4173         else if (move_pattern == MV_VERTICAL)
4174           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4175         else if (move_pattern & MV_ANY_DIRECTION)
4176           MovDir[x][y] = element_info[element].move_pattern;
4177         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4178                  move_pattern == MV_ALONG_RIGHT_SIDE)
4179         {
4180           /* use random direction as default start direction */
4181           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4182             MovDir[x][y] = 1 << RND(4);
4183
4184           for (i = 0; i < NUM_DIRECTIONS; i++)
4185           {
4186             int x1 = x + xy[i][0];
4187             int y1 = y + xy[i][1];
4188
4189             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4190             {
4191               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4192                 MovDir[x][y] = direction[0][i];
4193               else
4194                 MovDir[x][y] = direction[1][i];
4195
4196               break;
4197             }
4198           }
4199         }                
4200       }
4201       else
4202       {
4203         MovDir[x][y] = 1 << RND(4);
4204
4205         if (element != EL_BUG &&
4206             element != EL_SPACESHIP &&
4207             element != EL_BD_BUTTERFLY &&
4208             element != EL_BD_FIREFLY)
4209           break;
4210
4211         for (i = 0; i < NUM_DIRECTIONS; i++)
4212         {
4213           int x1 = x + xy[i][0];
4214           int y1 = y + xy[i][1];
4215
4216           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4217           {
4218             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4219             {
4220               MovDir[x][y] = direction[0][i];
4221               break;
4222             }
4223             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4224                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4225             {
4226               MovDir[x][y] = direction[1][i];
4227               break;
4228             }
4229           }
4230         }
4231       }
4232       break;
4233   }
4234
4235   GfxDir[x][y] = MovDir[x][y];
4236 }
4237
4238 void InitAmoebaNr(int x, int y)
4239 {
4240   int i;
4241   int group_nr = AmoebeNachbarNr(x, y);
4242
4243   if (group_nr == 0)
4244   {
4245     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4246     {
4247       if (AmoebaCnt[i] == 0)
4248       {
4249         group_nr = i;
4250         break;
4251       }
4252     }
4253   }
4254
4255   AmoebaNr[x][y] = group_nr;
4256   AmoebaCnt[group_nr]++;
4257   AmoebaCnt2[group_nr]++;
4258 }
4259
4260 static void PlayerWins(struct PlayerInfo *player)
4261 {
4262   player->LevelSolved = TRUE;
4263   player->GameOver = TRUE;
4264
4265   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4266                          level.native_em_level->lev->score : player->score);
4267
4268   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4269                                       TimeLeft);
4270   player->LevelSolved_CountingScore = player->score_final;
4271 }
4272
4273 void GameWon()
4274 {
4275   static int time, time_final;
4276   static int score, score_final;
4277   static int game_over_delay_1 = 0;
4278   static int game_over_delay_2 = 0;
4279   int game_over_delay_value_1 = 50;
4280   int game_over_delay_value_2 = 50;
4281
4282   if (!local_player->LevelSolved_GameWon)
4283   {
4284     int i;
4285
4286     /* do not start end game actions before the player stops moving (to exit) */
4287     if (local_player->MovPos)
4288       return;
4289
4290     local_player->LevelSolved_GameWon = TRUE;
4291     local_player->LevelSolved_SaveTape = tape.recording;
4292     local_player->LevelSolved_SaveScore = !tape.playing;
4293
4294     if (!tape.playing)
4295     {
4296       LevelStats_incSolved(level_nr);
4297
4298       SaveLevelSetup_SeriesInfo();
4299     }
4300
4301     if (tape.auto_play)         /* tape might already be stopped here */
4302       tape.auto_play_level_solved = TRUE;
4303
4304     TapeStop();
4305
4306     game_over_delay_1 = game_over_delay_value_1;
4307     game_over_delay_2 = game_over_delay_value_2;
4308
4309     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4310     score = score_final = local_player->score_final;
4311
4312     if (TimeLeft > 0)
4313     {
4314       time_final = 0;
4315       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4316     }
4317     else if (game.no_time_limit && TimePlayed < 999)
4318     {
4319       time_final = 999;
4320       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4321     }
4322
4323     local_player->score_final = score_final;
4324
4325     if (level_editor_test_game)
4326     {
4327       time = time_final;
4328       score = score_final;
4329
4330       local_player->LevelSolved_CountingTime = time;
4331       local_player->LevelSolved_CountingScore = score;
4332
4333       game_panel_controls[GAME_PANEL_TIME].value = time;
4334       game_panel_controls[GAME_PANEL_SCORE].value = score;
4335
4336       DisplayGameControlValues();
4337     }
4338
4339     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4340     {
4341       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4342       {
4343         /* close exit door after last player */
4344         if ((AllPlayersGone &&
4345              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4346               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4347               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4348             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4349             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4350         {
4351           int element = Feld[ExitX][ExitY];
4352
4353           Feld[ExitX][ExitY] =
4354             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4355              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4356              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4357              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4358              EL_EM_STEEL_EXIT_CLOSING);
4359
4360           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4361         }
4362
4363         /* player disappears */
4364         DrawLevelField(ExitX, ExitY);
4365       }
4366
4367       for (i = 0; i < MAX_PLAYERS; i++)
4368       {
4369         struct PlayerInfo *player = &stored_player[i];
4370
4371         if (player->present)
4372         {
4373           RemovePlayer(player);
4374
4375           /* player disappears */
4376           DrawLevelField(player->jx, player->jy);
4377         }
4378       }
4379     }
4380
4381     PlaySound(SND_GAME_WINNING);
4382   }
4383
4384   if (game_over_delay_1 > 0)
4385   {
4386     game_over_delay_1--;
4387
4388     return;
4389   }
4390
4391   if (time != time_final)
4392   {
4393     int time_to_go = ABS(time_final - time);
4394     int time_count_dir = (time < time_final ? +1 : -1);
4395     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4396
4397     time  += time_count_steps * time_count_dir;
4398     score += time_count_steps * level.score[SC_TIME_BONUS];
4399
4400     local_player->LevelSolved_CountingTime = time;
4401     local_player->LevelSolved_CountingScore = score;
4402
4403     game_panel_controls[GAME_PANEL_TIME].value = time;
4404     game_panel_controls[GAME_PANEL_SCORE].value = score;
4405
4406     DisplayGameControlValues();
4407
4408     if (time == time_final)
4409       StopSound(SND_GAME_LEVELTIME_BONUS);
4410     else if (setup.sound_loops)
4411       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4412     else
4413       PlaySound(SND_GAME_LEVELTIME_BONUS);
4414
4415     return;
4416   }
4417
4418   local_player->LevelSolved_PanelOff = TRUE;
4419
4420   if (game_over_delay_2 > 0)
4421   {
4422     game_over_delay_2--;
4423
4424     return;
4425   }
4426
4427   GameEnd();
4428 }
4429
4430 void GameEnd()
4431 {
4432   int hi_pos;
4433   boolean raise_level = FALSE;
4434
4435   local_player->LevelSolved_GameEnd = TRUE;
4436
4437   if (!global.use_envelope_request)
4438     CloseDoor(DOOR_CLOSE_1);
4439
4440   if (local_player->LevelSolved_SaveTape)
4441   {
4442     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4443   }
4444
4445   CloseDoor(DOOR_CLOSE_ALL);
4446
4447   if (level_editor_test_game)
4448   {
4449     SetGameStatus(GAME_MODE_MAIN);
4450
4451     DrawMainMenu();
4452
4453     return;
4454   }
4455
4456   if (!local_player->LevelSolved_SaveScore)
4457   {
4458     SetGameStatus(GAME_MODE_MAIN);
4459
4460     DrawMainMenu();
4461
4462     return;
4463   }
4464
4465   if (level_nr == leveldir_current->handicap_level)
4466   {
4467     leveldir_current->handicap_level++;
4468
4469     SaveLevelSetup_SeriesInfo();
4470   }
4471
4472   if (setup.increment_levels &&
4473       level_nr < leveldir_current->last_level)
4474     raise_level = TRUE;                 /* advance to next level */
4475
4476   if ((hi_pos = NewHiScore()) >= 0) 
4477   {
4478     SetGameStatus(GAME_MODE_SCORES);
4479
4480     DrawHallOfFame(hi_pos);
4481
4482     if (raise_level)
4483     {
4484       level_nr++;
4485       TapeErase();
4486     }
4487   }
4488   else
4489   {
4490     SetGameStatus(GAME_MODE_MAIN);
4491
4492     if (raise_level)
4493     {
4494       level_nr++;
4495       TapeErase();
4496     }
4497
4498     DrawMainMenu();
4499   }
4500 }
4501
4502 int NewHiScore()
4503 {
4504   int k, l;
4505   int position = -1;
4506   boolean one_score_entry_per_name = !program.many_scores_per_name;
4507
4508   LoadScore(level_nr);
4509
4510   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4511       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4512     return -1;
4513
4514   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4515   {
4516     if (local_player->score_final > highscore[k].Score)
4517     {
4518       /* player has made it to the hall of fame */
4519
4520       if (k < MAX_SCORE_ENTRIES - 1)
4521       {
4522         int m = MAX_SCORE_ENTRIES - 1;
4523
4524         if (one_score_entry_per_name)
4525         {
4526           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4527             if (strEqual(setup.player_name, highscore[l].Name))
4528               m = l;
4529
4530           if (m == k)   /* player's new highscore overwrites his old one */
4531             goto put_into_list;
4532         }
4533
4534         for (l = m; l > k; l--)
4535         {
4536           strcpy(highscore[l].Name, highscore[l - 1].Name);
4537           highscore[l].Score = highscore[l - 1].Score;
4538         }
4539       }
4540
4541       put_into_list:
4542
4543       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4544       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4545       highscore[k].Score = local_player->score_final; 
4546       position = k;
4547
4548       break;
4549     }
4550     else if (one_score_entry_per_name &&
4551              !strncmp(setup.player_name, highscore[k].Name,
4552                       MAX_PLAYER_NAME_LEN))
4553       break;    /* player already there with a higher score */
4554   }
4555
4556   if (position >= 0) 
4557     SaveScore(level_nr);
4558
4559   return position;
4560 }
4561
4562 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4563 {
4564   int element = Feld[x][y];
4565   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4566   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4567   int horiz_move = (dx != 0);
4568   int sign = (horiz_move ? dx : dy);
4569   int step = sign * element_info[element].move_stepsize;
4570
4571   /* special values for move stepsize for spring and things on conveyor belt */
4572   if (horiz_move)
4573   {
4574     if (CAN_FALL(element) &&
4575         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4576       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4577     else if (element == EL_SPRING)
4578       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4579   }
4580
4581   return step;
4582 }
4583
4584 inline static int getElementMoveStepsize(int x, int y)
4585 {
4586   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4587 }
4588
4589 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4590 {
4591   if (player->GfxAction != action || player->GfxDir != dir)
4592   {
4593     player->GfxAction = action;
4594     player->GfxDir = dir;
4595     player->Frame = 0;
4596     player->StepFrame = 0;
4597   }
4598 }
4599
4600 static void ResetGfxFrame(int x, int y)
4601 {
4602   int element = Feld[x][y];
4603   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4604
4605   if (graphic_info[graphic].anim_global_sync)
4606     GfxFrame[x][y] = FrameCounter;
4607   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4608     GfxFrame[x][y] = CustomValue[x][y];
4609   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4610     GfxFrame[x][y] = element_info[element].collect_score;
4611   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4612     GfxFrame[x][y] = ChangeDelay[x][y];
4613 }
4614
4615 static void ResetGfxAnimation(int x, int y)
4616 {
4617   GfxAction[x][y] = ACTION_DEFAULT;
4618   GfxDir[x][y] = MovDir[x][y];
4619   GfxFrame[x][y] = 0;
4620
4621   ResetGfxFrame(x, y);
4622 }
4623
4624 static void ResetRandomAnimationValue(int x, int y)
4625 {
4626   GfxRandom[x][y] = INIT_GFX_RANDOM();
4627 }
4628
4629 void InitMovingField(int x, int y, int direction)
4630 {
4631   int element = Feld[x][y];
4632   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4633   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4634   int newx = x + dx;
4635   int newy = y + dy;
4636   boolean is_moving_before, is_moving_after;
4637
4638   /* check if element was/is moving or being moved before/after mode change */
4639   is_moving_before = (WasJustMoving[x][y] != 0);
4640   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4641
4642   /* reset animation only for moving elements which change direction of moving
4643      or which just started or stopped moving
4644      (else CEs with property "can move" / "not moving" are reset each frame) */
4645   if (is_moving_before != is_moving_after ||
4646       direction != MovDir[x][y])
4647     ResetGfxAnimation(x, y);
4648
4649   MovDir[x][y] = direction;
4650   GfxDir[x][y] = direction;
4651
4652   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4653                      direction == MV_DOWN && CAN_FALL(element) ?
4654                      ACTION_FALLING : ACTION_MOVING);
4655
4656   /* this is needed for CEs with property "can move" / "not moving" */
4657
4658   if (is_moving_after)
4659   {
4660     if (Feld[newx][newy] == EL_EMPTY)
4661       Feld[newx][newy] = EL_BLOCKED;
4662
4663     MovDir[newx][newy] = MovDir[x][y];
4664
4665     CustomValue[newx][newy] = CustomValue[x][y];
4666
4667     GfxFrame[newx][newy] = GfxFrame[x][y];
4668     GfxRandom[newx][newy] = GfxRandom[x][y];
4669     GfxAction[newx][newy] = GfxAction[x][y];
4670     GfxDir[newx][newy] = GfxDir[x][y];
4671   }
4672 }
4673
4674 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4675 {
4676   int direction = MovDir[x][y];
4677   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4678   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4679
4680   *goes_to_x = newx;
4681   *goes_to_y = newy;
4682 }
4683
4684 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4685 {
4686   int oldx = x, oldy = y;
4687   int direction = MovDir[x][y];
4688
4689   if (direction == MV_LEFT)
4690     oldx++;
4691   else if (direction == MV_RIGHT)
4692     oldx--;
4693   else if (direction == MV_UP)
4694     oldy++;
4695   else if (direction == MV_DOWN)
4696     oldy--;
4697
4698   *comes_from_x = oldx;
4699   *comes_from_y = oldy;
4700 }
4701
4702 int MovingOrBlocked2Element(int x, int y)
4703 {
4704   int element = Feld[x][y];
4705
4706   if (element == EL_BLOCKED)
4707   {
4708     int oldx, oldy;
4709
4710     Blocked2Moving(x, y, &oldx, &oldy);
4711     return Feld[oldx][oldy];
4712   }
4713   else
4714     return element;
4715 }
4716
4717 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4718 {
4719   /* like MovingOrBlocked2Element(), but if element is moving
4720      and (x,y) is the field the moving element is just leaving,
4721      return EL_BLOCKED instead of the element value */
4722   int element = Feld[x][y];
4723
4724   if (IS_MOVING(x, y))
4725   {
4726     if (element == EL_BLOCKED)
4727     {
4728       int oldx, oldy;
4729
4730       Blocked2Moving(x, y, &oldx, &oldy);
4731       return Feld[oldx][oldy];
4732     }
4733     else
4734       return EL_BLOCKED;
4735   }
4736   else
4737     return element;
4738 }
4739
4740 static void RemoveField(int x, int y)
4741 {
4742   Feld[x][y] = EL_EMPTY;
4743
4744   MovPos[x][y] = 0;
4745   MovDir[x][y] = 0;
4746   MovDelay[x][y] = 0;
4747
4748   CustomValue[x][y] = 0;
4749
4750   AmoebaNr[x][y] = 0;
4751   ChangeDelay[x][y] = 0;
4752   ChangePage[x][y] = -1;
4753   Pushed[x][y] = FALSE;
4754
4755   GfxElement[x][y] = EL_UNDEFINED;
4756   GfxAction[x][y] = ACTION_DEFAULT;
4757   GfxDir[x][y] = MV_NONE;
4758 }
4759
4760 void RemoveMovingField(int x, int y)
4761 {
4762   int oldx = x, oldy = y, newx = x, newy = y;
4763   int element = Feld[x][y];
4764   int next_element = EL_UNDEFINED;
4765
4766   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4767     return;
4768
4769   if (IS_MOVING(x, y))
4770   {
4771     Moving2Blocked(x, y, &newx, &newy);
4772
4773     if (Feld[newx][newy] != EL_BLOCKED)
4774     {
4775       /* element is moving, but target field is not free (blocked), but
4776          already occupied by something different (example: acid pool);
4777          in this case, only remove the moving field, but not the target */
4778
4779       RemoveField(oldx, oldy);
4780
4781       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4782
4783       TEST_DrawLevelField(oldx, oldy);
4784
4785       return;
4786     }
4787   }
4788   else if (element == EL_BLOCKED)
4789   {
4790     Blocked2Moving(x, y, &oldx, &oldy);
4791     if (!IS_MOVING(oldx, oldy))
4792       return;
4793   }
4794
4795   if (element == EL_BLOCKED &&
4796       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4797        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4798        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4799        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4800        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4801        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4802     next_element = get_next_element(Feld[oldx][oldy]);
4803
4804   RemoveField(oldx, oldy);
4805   RemoveField(newx, newy);
4806
4807   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4808
4809   if (next_element != EL_UNDEFINED)
4810     Feld[oldx][oldy] = next_element;
4811
4812   TEST_DrawLevelField(oldx, oldy);
4813   TEST_DrawLevelField(newx, newy);
4814 }
4815
4816 void DrawDynamite(int x, int y)
4817 {
4818   int sx = SCREENX(x), sy = SCREENY(y);
4819   int graphic = el2img(Feld[x][y]);
4820   int frame;
4821
4822   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4823     return;
4824
4825   if (IS_WALKABLE_INSIDE(Back[x][y]))
4826     return;
4827
4828   if (Back[x][y])
4829     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4830   else if (Store[x][y])
4831     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4832
4833   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4834
4835   if (Back[x][y] || Store[x][y])
4836     DrawGraphicThruMask(sx, sy, graphic, frame);
4837   else
4838     DrawGraphic(sx, sy, graphic, frame);
4839 }
4840
4841 void CheckDynamite(int x, int y)
4842 {
4843   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4844   {
4845     MovDelay[x][y]--;
4846
4847     if (MovDelay[x][y] != 0)
4848     {
4849       DrawDynamite(x, y);
4850       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4851
4852       return;
4853     }
4854   }
4855
4856   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4857
4858   Bang(x, y);
4859 }
4860
4861 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4862 {
4863   boolean num_checked_players = 0;
4864   int i;
4865
4866   for (i = 0; i < MAX_PLAYERS; i++)
4867   {
4868     if (stored_player[i].active)
4869     {
4870       int sx = stored_player[i].jx;
4871       int sy = stored_player[i].jy;
4872
4873       if (num_checked_players == 0)
4874       {
4875         *sx1 = *sx2 = sx;
4876         *sy1 = *sy2 = sy;
4877       }
4878       else
4879       {
4880         *sx1 = MIN(*sx1, sx);
4881         *sy1 = MIN(*sy1, sy);
4882         *sx2 = MAX(*sx2, sx);
4883         *sy2 = MAX(*sy2, sy);
4884       }
4885
4886       num_checked_players++;
4887     }
4888   }
4889 }
4890
4891 static boolean checkIfAllPlayersFitToScreen_RND()
4892 {
4893   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4894
4895   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4896
4897   return (sx2 - sx1 < SCR_FIELDX &&
4898           sy2 - sy1 < SCR_FIELDY);
4899 }
4900
4901 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4902 {
4903   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4904
4905   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4906
4907   *sx = (sx1 + sx2) / 2;
4908   *sy = (sy1 + sy2) / 2;
4909 }
4910
4911 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4912                         boolean center_screen, boolean quick_relocation)
4913 {
4914   unsigned int frame_delay_value_old = GetVideoFrameDelay();
4915   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4916   boolean no_delay = (tape.warp_forward);
4917   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4918   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4919   int new_scroll_x, new_scroll_y;
4920
4921   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4922   {
4923     /* case 1: quick relocation inside visible screen (without scrolling) */
4924
4925     RedrawPlayfield();
4926
4927     return;
4928   }
4929
4930   if (!level.shifted_relocation || center_screen)
4931   {
4932     /* relocation _with_ centering of screen */
4933
4934     new_scroll_x = SCROLL_POSITION_X(x);
4935     new_scroll_y = SCROLL_POSITION_Y(y);
4936   }
4937   else
4938   {
4939     /* relocation _without_ centering of screen */
4940
4941     int center_scroll_x = SCROLL_POSITION_X(old_x);
4942     int center_scroll_y = SCROLL_POSITION_Y(old_y);
4943     int offset_x = x + (scroll_x - center_scroll_x);
4944     int offset_y = y + (scroll_y - center_scroll_y);
4945
4946     /* for new screen position, apply previous offset to center position */
4947     new_scroll_x = SCROLL_POSITION_X(offset_x);
4948     new_scroll_y = SCROLL_POSITION_Y(offset_y);
4949   }
4950
4951   if (quick_relocation)
4952   {
4953     /* case 2: quick relocation (redraw without visible scrolling) */
4954
4955     scroll_x = new_scroll_x;
4956     scroll_y = new_scroll_y;
4957
4958     RedrawPlayfield();
4959
4960     return;
4961   }
4962
4963   /* case 3: visible relocation (with scrolling to new position) */
4964
4965   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4966
4967   SetVideoFrameDelay(wait_delay_value);
4968
4969   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4970   {
4971     int dx = 0, dy = 0;
4972     int fx = FX, fy = FY;
4973
4974     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4975     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4976
4977     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4978       break;
4979
4980     scroll_x -= dx;
4981     scroll_y -= dy;
4982
4983     fx += dx * TILEX / 2;
4984     fy += dy * TILEY / 2;
4985
4986     ScrollLevel(dx, dy);
4987     DrawAllPlayers();
4988
4989     /* scroll in two steps of half tile size to make things smoother */
4990     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4991
4992     /* scroll second step to align at full tile size */
4993     BlitScreenToBitmap(window);
4994   }
4995
4996   DrawAllPlayers();
4997   BackToFront();
4998
4999   SetVideoFrameDelay(frame_delay_value_old);
5000 }
5001
5002 void RelocatePlayer(int jx, int jy, int el_player_raw)
5003 {
5004   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5005   int player_nr = GET_PLAYER_NR(el_player);
5006   struct PlayerInfo *player = &stored_player[player_nr];
5007   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5008   boolean no_delay = (tape.warp_forward);
5009   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5010   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5011   int old_jx = player->jx;
5012   int old_jy = player->jy;
5013   int old_element = Feld[old_jx][old_jy];
5014   int element = Feld[jx][jy];
5015   boolean player_relocated = (old_jx != jx || old_jy != jy);
5016
5017   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5018   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5019   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5020   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5021   int leave_side_horiz = move_dir_horiz;
5022   int leave_side_vert  = move_dir_vert;
5023   int enter_side = enter_side_horiz | enter_side_vert;
5024   int leave_side = leave_side_horiz | leave_side_vert;
5025
5026   if (player->GameOver)         /* do not reanimate dead player */
5027     return;
5028
5029   if (!player_relocated)        /* no need to relocate the player */
5030     return;
5031
5032   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5033   {
5034     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5035     DrawLevelField(jx, jy);
5036   }
5037
5038   if (player->present)
5039   {
5040     while (player->MovPos)
5041     {
5042       ScrollPlayer(player, SCROLL_GO_ON);
5043       ScrollScreen(NULL, SCROLL_GO_ON);
5044
5045       AdvanceFrameAndPlayerCounters(player->index_nr);
5046
5047       DrawPlayer(player);
5048
5049       BackToFront_WithFrameDelay(wait_delay_value);
5050     }
5051
5052     DrawPlayer(player);         /* needed here only to cleanup last field */
5053     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5054
5055     player->is_moving = FALSE;
5056   }
5057
5058   if (IS_CUSTOM_ELEMENT(old_element))
5059     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5060                                CE_LEFT_BY_PLAYER,
5061                                player->index_bit, leave_side);
5062
5063   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5064                                       CE_PLAYER_LEAVES_X,
5065                                       player->index_bit, leave_side);
5066
5067   Feld[jx][jy] = el_player;
5068   InitPlayerField(jx, jy, el_player, TRUE);
5069
5070   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5071      possible that the relocation target field did not contain a player element,
5072      but a walkable element, to which the new player was relocated -- in this
5073      case, restore that (already initialized!) element on the player field */
5074   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5075   {
5076     Feld[jx][jy] = element;     /* restore previously existing element */
5077   }
5078
5079   /* only visually relocate centered player */
5080   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5081                      FALSE, level.instant_relocation);
5082
5083   TestIfPlayerTouchesBadThing(jx, jy);
5084   TestIfPlayerTouchesCustomElement(jx, jy);
5085
5086   if (IS_CUSTOM_ELEMENT(element))
5087     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5088                                player->index_bit, enter_side);
5089
5090   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5091                                       player->index_bit, enter_side);
5092
5093   if (player->is_switching)
5094   {
5095     /* ensure that relocation while still switching an element does not cause
5096        a new element to be treated as also switched directly after relocation
5097        (this is important for teleporter switches that teleport the player to
5098        a place where another teleporter switch is in the same direction, which
5099        would then incorrectly be treated as immediately switched before the
5100        direction key that caused the switch was released) */
5101
5102     player->switch_x += jx - old_jx;
5103     player->switch_y += jy - old_jy;
5104   }
5105 }
5106
5107 void Explode(int ex, int ey, int phase, int mode)
5108 {
5109   int x, y;
5110   int last_phase;
5111   int border_element;
5112
5113   /* !!! eliminate this variable !!! */
5114   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5115
5116   if (game.explosions_delayed)
5117   {
5118     ExplodeField[ex][ey] = mode;
5119     return;
5120   }
5121
5122   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5123   {
5124     int center_element = Feld[ex][ey];
5125     int artwork_element, explosion_element;     /* set these values later */
5126
5127     /* remove things displayed in background while burning dynamite */
5128     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5129       Back[ex][ey] = 0;
5130
5131     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5132     {
5133       /* put moving element to center field (and let it explode there) */
5134       center_element = MovingOrBlocked2Element(ex, ey);
5135       RemoveMovingField(ex, ey);
5136       Feld[ex][ey] = center_element;
5137     }
5138
5139     /* now "center_element" is finally determined -- set related values now */
5140     artwork_element = center_element;           /* for custom player artwork */
5141     explosion_element = center_element;         /* for custom player artwork */
5142
5143     if (IS_PLAYER(ex, ey))
5144     {
5145       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5146
5147       artwork_element = stored_player[player_nr].artwork_element;
5148
5149       if (level.use_explosion_element[player_nr])
5150       {
5151         explosion_element = level.explosion_element[player_nr];
5152         artwork_element = explosion_element;
5153       }
5154     }
5155
5156     if (mode == EX_TYPE_NORMAL ||
5157         mode == EX_TYPE_CENTER ||
5158         mode == EX_TYPE_CROSS)
5159       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5160
5161     last_phase = element_info[explosion_element].explosion_delay + 1;
5162
5163     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5164     {
5165       int xx = x - ex + 1;
5166       int yy = y - ey + 1;
5167       int element;
5168
5169       if (!IN_LEV_FIELD(x, y) ||
5170           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5171           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5172         continue;
5173
5174       element = Feld[x][y];
5175
5176       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5177       {
5178         element = MovingOrBlocked2Element(x, y);
5179
5180         if (!IS_EXPLOSION_PROOF(element))
5181           RemoveMovingField(x, y);
5182       }
5183
5184       /* indestructible elements can only explode in center (but not flames) */
5185       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5186                                            mode == EX_TYPE_BORDER)) ||
5187           element == EL_FLAMES)
5188         continue;
5189
5190       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5191          behaviour, for example when touching a yamyam that explodes to rocks
5192          with active deadly shield, a rock is created under the player !!! */
5193       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5194 #if 0
5195       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5196           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5197            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5198 #else
5199       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5200 #endif
5201       {
5202         if (IS_ACTIVE_BOMB(element))
5203         {
5204           /* re-activate things under the bomb like gate or penguin */
5205           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5206           Back[x][y] = 0;
5207         }
5208
5209         continue;
5210       }
5211
5212       /* save walkable background elements while explosion on same tile */
5213       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5214           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5215         Back[x][y] = element;
5216
5217       /* ignite explodable elements reached by other explosion */
5218       if (element == EL_EXPLOSION)
5219         element = Store2[x][y];
5220
5221       if (AmoebaNr[x][y] &&
5222           (element == EL_AMOEBA_FULL ||
5223            element == EL_BD_AMOEBA ||
5224            element == EL_AMOEBA_GROWING))
5225       {
5226         AmoebaCnt[AmoebaNr[x][y]]--;
5227         AmoebaCnt2[AmoebaNr[x][y]]--;
5228       }
5229
5230       RemoveField(x, y);
5231
5232       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5233       {
5234         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5235
5236         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5237
5238         if (PLAYERINFO(ex, ey)->use_murphy)
5239           Store[x][y] = EL_EMPTY;
5240       }
5241
5242       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5243          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5244       else if (ELEM_IS_PLAYER(center_element))
5245         Store[x][y] = EL_EMPTY;
5246       else if (center_element == EL_YAMYAM)
5247         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5248       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5249         Store[x][y] = element_info[center_element].content.e[xx][yy];
5250 #if 1
5251       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5252          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5253          otherwise) -- FIX THIS !!! */
5254       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5255         Store[x][y] = element_info[element].content.e[1][1];
5256 #else
5257       else if (!CAN_EXPLODE(element))
5258         Store[x][y] = element_info[element].content.e[1][1];
5259 #endif
5260       else
5261         Store[x][y] = EL_EMPTY;
5262
5263       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5264           center_element == EL_AMOEBA_TO_DIAMOND)
5265         Store2[x][y] = element;
5266
5267       Feld[x][y] = EL_EXPLOSION;
5268       GfxElement[x][y] = artwork_element;
5269
5270       ExplodePhase[x][y] = 1;
5271       ExplodeDelay[x][y] = last_phase;
5272
5273       Stop[x][y] = TRUE;
5274     }
5275
5276     if (center_element == EL_YAMYAM)
5277       game.yamyam_content_nr =
5278         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5279
5280     return;
5281   }
5282
5283   if (Stop[ex][ey])
5284     return;
5285
5286   x = ex;
5287   y = ey;
5288
5289   if (phase == 1)
5290     GfxFrame[x][y] = 0;         /* restart explosion animation */
5291
5292   last_phase = ExplodeDelay[x][y];
5293
5294   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5295
5296   /* this can happen if the player leaves an explosion just in time */
5297   if (GfxElement[x][y] == EL_UNDEFINED)
5298     GfxElement[x][y] = EL_EMPTY;
5299
5300   border_element = Store2[x][y];
5301   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5302     border_element = StorePlayer[x][y];
5303
5304   if (phase == element_info[border_element].ignition_delay ||
5305       phase == last_phase)
5306   {
5307     boolean border_explosion = FALSE;
5308
5309     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5310         !PLAYER_EXPLOSION_PROTECTED(x, y))
5311     {
5312       KillPlayerUnlessExplosionProtected(x, y);
5313       border_explosion = TRUE;
5314     }
5315     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5316     {
5317       Feld[x][y] = Store2[x][y];
5318       Store2[x][y] = 0;
5319       Bang(x, y);
5320       border_explosion = TRUE;
5321     }
5322     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5323     {
5324       AmoebeUmwandeln(x, y);
5325       Store2[x][y] = 0;
5326       border_explosion = TRUE;
5327     }
5328
5329     /* if an element just explodes due to another explosion (chain-reaction),
5330        do not immediately end the new explosion when it was the last frame of
5331        the explosion (as it would be done in the following "if"-statement!) */
5332     if (border_explosion && phase == last_phase)
5333       return;
5334   }
5335
5336   if (phase == last_phase)
5337   {
5338     int element;
5339
5340     element = Feld[x][y] = Store[x][y];
5341     Store[x][y] = Store2[x][y] = 0;
5342     GfxElement[x][y] = EL_UNDEFINED;
5343
5344     /* player can escape from explosions and might therefore be still alive */
5345     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5346         element <= EL_PLAYER_IS_EXPLODING_4)
5347     {
5348       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5349       int explosion_element = EL_PLAYER_1 + player_nr;
5350       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5351       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5352
5353       if (level.use_explosion_element[player_nr])
5354         explosion_element = level.explosion_element[player_nr];
5355
5356       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5357                     element_info[explosion_element].content.e[xx][yy]);
5358     }
5359
5360     /* restore probably existing indestructible background element */
5361     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5362       element = Feld[x][y] = Back[x][y];
5363     Back[x][y] = 0;
5364
5365     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5366     GfxDir[x][y] = MV_NONE;
5367     ChangeDelay[x][y] = 0;
5368     ChangePage[x][y] = -1;
5369
5370     CustomValue[x][y] = 0;
5371
5372     InitField_WithBug2(x, y, FALSE);
5373
5374     TEST_DrawLevelField(x, y);
5375
5376     TestIfElementTouchesCustomElement(x, y);
5377
5378     if (GFX_CRUMBLED(element))
5379       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5380
5381     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5382       StorePlayer[x][y] = 0;
5383
5384     if (ELEM_IS_PLAYER(element))
5385       RelocatePlayer(x, y, element);
5386   }
5387   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5388   {
5389     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5390     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5391
5392     if (phase == delay)
5393       TEST_DrawLevelFieldCrumbled(x, y);
5394
5395     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5396     {
5397       DrawLevelElement(x, y, Back[x][y]);
5398       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5399     }
5400     else if (IS_WALKABLE_UNDER(Back[x][y]))
5401     {
5402       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5403       DrawLevelElementThruMask(x, y, Back[x][y]);
5404     }
5405     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5406       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5407   }
5408 }
5409
5410 void DynaExplode(int ex, int ey)
5411 {
5412   int i, j;
5413   int dynabomb_element = Feld[ex][ey];
5414   int dynabomb_size = 1;
5415   boolean dynabomb_xl = FALSE;
5416   struct PlayerInfo *player;
5417   static int xy[4][2] =
5418   {
5419     { 0, -1 },
5420     { -1, 0 },
5421     { +1, 0 },
5422     { 0, +1 }
5423   };
5424
5425   if (IS_ACTIVE_BOMB(dynabomb_element))
5426   {
5427     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5428     dynabomb_size = player->dynabomb_size;
5429     dynabomb_xl = player->dynabomb_xl;
5430     player->dynabombs_left++;
5431   }
5432
5433   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5434
5435   for (i = 0; i < NUM_DIRECTIONS; i++)
5436   {
5437     for (j = 1; j <= dynabomb_size; j++)
5438     {
5439       int x = ex + j * xy[i][0];
5440       int y = ey + j * xy[i][1];
5441       int element;
5442
5443       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5444         break;
5445
5446       element = Feld[x][y];
5447
5448       /* do not restart explosions of fields with active bombs */
5449       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5450         continue;
5451
5452       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5453
5454       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5455           !IS_DIGGABLE(element) && !dynabomb_xl)
5456         break;
5457     }
5458   }
5459 }
5460
5461 void Bang(int x, int y)
5462 {
5463   int element = MovingOrBlocked2Element(x, y);
5464   int explosion_type = EX_TYPE_NORMAL;
5465
5466   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5467   {
5468     struct PlayerInfo *player = PLAYERINFO(x, y);
5469
5470     element = Feld[x][y] = player->initial_element;
5471
5472     if (level.use_explosion_element[player->index_nr])
5473     {
5474       int explosion_element = level.explosion_element[player->index_nr];
5475
5476       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5477         explosion_type = EX_TYPE_CROSS;
5478       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5479         explosion_type = EX_TYPE_CENTER;
5480     }
5481   }
5482
5483   switch (element)
5484   {
5485     case EL_BUG:
5486     case EL_SPACESHIP:
5487     case EL_BD_BUTTERFLY:
5488     case EL_BD_FIREFLY:
5489     case EL_YAMYAM:
5490     case EL_DARK_YAMYAM:
5491     case EL_ROBOT:
5492     case EL_PACMAN:
5493     case EL_MOLE:
5494       RaiseScoreElement(element);
5495       break;
5496
5497     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5498     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5499     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5500     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5501     case EL_DYNABOMB_INCREASE_NUMBER:
5502     case EL_DYNABOMB_INCREASE_SIZE:
5503     case EL_DYNABOMB_INCREASE_POWER:
5504       explosion_type = EX_TYPE_DYNA;
5505       break;
5506
5507     case EL_DC_LANDMINE:
5508       explosion_type = EX_TYPE_CENTER;
5509       break;
5510
5511     case EL_PENGUIN:
5512     case EL_LAMP:
5513     case EL_LAMP_ACTIVE:
5514     case EL_AMOEBA_TO_DIAMOND:
5515       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5516         explosion_type = EX_TYPE_CENTER;
5517       break;
5518
5519     default:
5520       if (element_info[element].explosion_type == EXPLODES_CROSS)
5521         explosion_type = EX_TYPE_CROSS;
5522       else if (element_info[element].explosion_type == EXPLODES_1X1)
5523         explosion_type = EX_TYPE_CENTER;
5524       break;
5525   }
5526
5527   if (explosion_type == EX_TYPE_DYNA)
5528     DynaExplode(x, y);
5529   else
5530     Explode(x, y, EX_PHASE_START, explosion_type);
5531
5532   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5533 }
5534
5535 void SplashAcid(int x, int y)
5536 {
5537   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5538       (!IN_LEV_FIELD(x - 1, y - 2) ||
5539        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5540     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
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_RIGHT;
5546
5547   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5548 }
5549
5550 static void InitBeltMovement()
5551 {
5552   static int belt_base_element[4] =
5553   {
5554     EL_CONVEYOR_BELT_1_LEFT,
5555     EL_CONVEYOR_BELT_2_LEFT,
5556     EL_CONVEYOR_BELT_3_LEFT,
5557     EL_CONVEYOR_BELT_4_LEFT
5558   };
5559   static int belt_base_active_element[4] =
5560   {
5561     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5562     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5563     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5564     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5565   };
5566
5567   int x, y, i, j;
5568
5569   /* set frame order for belt animation graphic according to belt direction */
5570   for (i = 0; i < NUM_BELTS; i++)
5571   {
5572     int belt_nr = i;
5573
5574     for (j = 0; j < NUM_BELT_PARTS; j++)
5575     {
5576       int element = belt_base_active_element[belt_nr] + j;
5577       int graphic_1 = el2img(element);
5578       int graphic_2 = el2panelimg(element);
5579
5580       if (game.belt_dir[i] == MV_LEFT)
5581       {
5582         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5583         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5584       }
5585       else
5586       {
5587         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5588         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5589       }
5590     }
5591   }
5592
5593   SCAN_PLAYFIELD(x, y)
5594   {
5595     int element = Feld[x][y];
5596
5597     for (i = 0; i < NUM_BELTS; i++)
5598     {
5599       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5600       {
5601         int e_belt_nr = getBeltNrFromBeltElement(element);
5602         int belt_nr = i;
5603
5604         if (e_belt_nr == belt_nr)
5605         {
5606           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5607
5608           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5609         }
5610       }
5611     }
5612   }
5613 }
5614
5615 static void ToggleBeltSwitch(int x, int y)
5616 {
5617   static int belt_base_element[4] =
5618   {
5619     EL_CONVEYOR_BELT_1_LEFT,
5620     EL_CONVEYOR_BELT_2_LEFT,
5621     EL_CONVEYOR_BELT_3_LEFT,
5622     EL_CONVEYOR_BELT_4_LEFT
5623   };
5624   static int belt_base_active_element[4] =
5625   {
5626     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5627     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5628     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5629     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5630   };
5631   static int belt_base_switch_element[4] =
5632   {
5633     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5634     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5635     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5636     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5637   };
5638   static int belt_move_dir[4] =
5639   {
5640     MV_LEFT,
5641     MV_NONE,
5642     MV_RIGHT,
5643     MV_NONE,
5644   };
5645
5646   int element = Feld[x][y];
5647   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5648   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5649   int belt_dir = belt_move_dir[belt_dir_nr];
5650   int xx, yy, i;
5651
5652   if (!IS_BELT_SWITCH(element))
5653     return;
5654
5655   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5656   game.belt_dir[belt_nr] = belt_dir;
5657
5658   if (belt_dir_nr == 3)
5659     belt_dir_nr = 1;
5660
5661   /* set frame order for belt animation graphic according to belt direction */
5662   for (i = 0; i < NUM_BELT_PARTS; i++)
5663   {
5664     int element = belt_base_active_element[belt_nr] + i;
5665     int graphic_1 = el2img(element);
5666     int graphic_2 = el2panelimg(element);
5667
5668     if (belt_dir == MV_LEFT)
5669     {
5670       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5671       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5672     }
5673     else
5674     {
5675       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5676       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5677     }
5678   }
5679
5680   SCAN_PLAYFIELD(xx, yy)
5681   {
5682     int element = Feld[xx][yy];
5683
5684     if (IS_BELT_SWITCH(element))
5685     {
5686       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5687
5688       if (e_belt_nr == belt_nr)
5689       {
5690         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5691         TEST_DrawLevelField(xx, yy);
5692       }
5693     }
5694     else if (IS_BELT(element) && belt_dir != MV_NONE)
5695     {
5696       int e_belt_nr = getBeltNrFromBeltElement(element);
5697
5698       if (e_belt_nr == belt_nr)
5699       {
5700         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5701
5702         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5703         TEST_DrawLevelField(xx, yy);
5704       }
5705     }
5706     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5707     {
5708       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5709
5710       if (e_belt_nr == belt_nr)
5711       {
5712         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5713
5714         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5715         TEST_DrawLevelField(xx, yy);
5716       }
5717     }
5718   }
5719 }
5720
5721 static void ToggleSwitchgateSwitch(int x, int y)
5722 {
5723   int xx, yy;
5724
5725   game.switchgate_pos = !game.switchgate_pos;
5726
5727   SCAN_PLAYFIELD(xx, yy)
5728   {
5729     int element = Feld[xx][yy];
5730
5731     if (element == EL_SWITCHGATE_SWITCH_UP)
5732     {
5733       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5734       TEST_DrawLevelField(xx, yy);
5735     }
5736     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5737     {
5738       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5739       TEST_DrawLevelField(xx, yy);
5740     }
5741     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5742     {
5743       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5744       TEST_DrawLevelField(xx, yy);
5745     }
5746     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5747     {
5748       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5749       TEST_DrawLevelField(xx, yy);
5750     }
5751     else if (element == EL_SWITCHGATE_OPEN ||
5752              element == EL_SWITCHGATE_OPENING)
5753     {
5754       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5755
5756       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5757     }
5758     else if (element == EL_SWITCHGATE_CLOSED ||
5759              element == EL_SWITCHGATE_CLOSING)
5760     {
5761       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5762
5763       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5764     }
5765   }
5766 }
5767
5768 static int getInvisibleActiveFromInvisibleElement(int element)
5769 {
5770   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5771           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5772           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5773           element);
5774 }
5775
5776 static int getInvisibleFromInvisibleActiveElement(int element)
5777 {
5778   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5779           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5780           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5781           element);
5782 }
5783
5784 static void RedrawAllLightSwitchesAndInvisibleElements()
5785 {
5786   int x, y;
5787
5788   SCAN_PLAYFIELD(x, y)
5789   {
5790     int element = Feld[x][y];
5791
5792     if (element == EL_LIGHT_SWITCH &&
5793         game.light_time_left > 0)
5794     {
5795       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5796       TEST_DrawLevelField(x, y);
5797     }
5798     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5799              game.light_time_left == 0)
5800     {
5801       Feld[x][y] = EL_LIGHT_SWITCH;
5802       TEST_DrawLevelField(x, y);
5803     }
5804     else if (element == EL_EMC_DRIPPER &&
5805              game.light_time_left > 0)
5806     {
5807       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5808       TEST_DrawLevelField(x, y);
5809     }
5810     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5811              game.light_time_left == 0)
5812     {
5813       Feld[x][y] = EL_EMC_DRIPPER;
5814       TEST_DrawLevelField(x, y);
5815     }
5816     else if (element == EL_INVISIBLE_STEELWALL ||
5817              element == EL_INVISIBLE_WALL ||
5818              element == EL_INVISIBLE_SAND)
5819     {
5820       if (game.light_time_left > 0)
5821         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5822
5823       TEST_DrawLevelField(x, y);
5824
5825       /* uncrumble neighbour fields, if needed */
5826       if (element == EL_INVISIBLE_SAND)
5827         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5828     }
5829     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5830              element == EL_INVISIBLE_WALL_ACTIVE ||
5831              element == EL_INVISIBLE_SAND_ACTIVE)
5832     {
5833       if (game.light_time_left == 0)
5834         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5835
5836       TEST_DrawLevelField(x, y);
5837
5838       /* re-crumble neighbour fields, if needed */
5839       if (element == EL_INVISIBLE_SAND)
5840         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5841     }
5842   }
5843 }
5844
5845 static void RedrawAllInvisibleElementsForLenses()
5846 {
5847   int x, y;
5848
5849   SCAN_PLAYFIELD(x, y)
5850   {
5851     int element = Feld[x][y];
5852
5853     if (element == EL_EMC_DRIPPER &&
5854         game.lenses_time_left > 0)
5855     {
5856       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5857       TEST_DrawLevelField(x, y);
5858     }
5859     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5860              game.lenses_time_left == 0)
5861     {
5862       Feld[x][y] = EL_EMC_DRIPPER;
5863       TEST_DrawLevelField(x, y);
5864     }
5865     else if (element == EL_INVISIBLE_STEELWALL ||
5866              element == EL_INVISIBLE_WALL ||
5867              element == EL_INVISIBLE_SAND)
5868     {
5869       if (game.lenses_time_left > 0)
5870         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5871
5872       TEST_DrawLevelField(x, y);
5873
5874       /* uncrumble neighbour fields, if needed */
5875       if (element == EL_INVISIBLE_SAND)
5876         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5877     }
5878     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5879              element == EL_INVISIBLE_WALL_ACTIVE ||
5880              element == EL_INVISIBLE_SAND_ACTIVE)
5881     {
5882       if (game.lenses_time_left == 0)
5883         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5884
5885       TEST_DrawLevelField(x, y);
5886
5887       /* re-crumble neighbour fields, if needed */
5888       if (element == EL_INVISIBLE_SAND)
5889         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5890     }
5891   }
5892 }
5893
5894 static void RedrawAllInvisibleElementsForMagnifier()
5895 {
5896   int x, y;
5897
5898   SCAN_PLAYFIELD(x, y)
5899   {
5900     int element = Feld[x][y];
5901
5902     if (element == EL_EMC_FAKE_GRASS &&
5903         game.magnify_time_left > 0)
5904     {
5905       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5906       TEST_DrawLevelField(x, y);
5907     }
5908     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5909              game.magnify_time_left == 0)
5910     {
5911       Feld[x][y] = EL_EMC_FAKE_GRASS;
5912       TEST_DrawLevelField(x, y);
5913     }
5914     else if (IS_GATE_GRAY(element) &&
5915              game.magnify_time_left > 0)
5916     {
5917       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5918                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5919                     IS_EM_GATE_GRAY(element) ?
5920                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5921                     IS_EMC_GATE_GRAY(element) ?
5922                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5923                     IS_DC_GATE_GRAY(element) ?
5924                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5925                     element);
5926       TEST_DrawLevelField(x, y);
5927     }
5928     else if (IS_GATE_GRAY_ACTIVE(element) &&
5929              game.magnify_time_left == 0)
5930     {
5931       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5932                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5933                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5934                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5935                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5936                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5937                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5938                     EL_DC_GATE_WHITE_GRAY :
5939                     element);
5940       TEST_DrawLevelField(x, y);
5941     }
5942   }
5943 }
5944
5945 static void ToggleLightSwitch(int x, int y)
5946 {
5947   int element = Feld[x][y];
5948
5949   game.light_time_left =
5950     (element == EL_LIGHT_SWITCH ?
5951      level.time_light * FRAMES_PER_SECOND : 0);
5952
5953   RedrawAllLightSwitchesAndInvisibleElements();
5954 }
5955
5956 static void ActivateTimegateSwitch(int x, int y)
5957 {
5958   int xx, yy;
5959
5960   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5961
5962   SCAN_PLAYFIELD(xx, yy)
5963   {
5964     int element = Feld[xx][yy];
5965
5966     if (element == EL_TIMEGATE_CLOSED ||
5967         element == EL_TIMEGATE_CLOSING)
5968     {
5969       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5970       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5971     }
5972
5973     /*
5974     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5975     {
5976       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5977       TEST_DrawLevelField(xx, yy);
5978     }
5979     */
5980
5981   }
5982
5983   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5984                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5985 }
5986
5987 void Impact(int x, int y)
5988 {
5989   boolean last_line = (y == lev_fieldy - 1);
5990   boolean object_hit = FALSE;
5991   boolean impact = (last_line || object_hit);
5992   int element = Feld[x][y];
5993   int smashed = EL_STEELWALL;
5994
5995   if (!last_line)       /* check if element below was hit */
5996   {
5997     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5998       return;
5999
6000     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6001                                          MovDir[x][y + 1] != MV_DOWN ||
6002                                          MovPos[x][y + 1] <= TILEY / 2));
6003
6004     /* do not smash moving elements that left the smashed field in time */
6005     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6006         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6007       object_hit = FALSE;
6008
6009 #if USE_QUICKSAND_IMPACT_BUGFIX
6010     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6011     {
6012       RemoveMovingField(x, y + 1);
6013       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6014       Feld[x][y + 2] = EL_ROCK;
6015       TEST_DrawLevelField(x, y + 2);
6016
6017       object_hit = TRUE;
6018     }
6019
6020     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6021     {
6022       RemoveMovingField(x, y + 1);
6023       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6024       Feld[x][y + 2] = EL_ROCK;
6025       TEST_DrawLevelField(x, y + 2);
6026
6027       object_hit = TRUE;
6028     }
6029 #endif
6030
6031     if (object_hit)
6032       smashed = MovingOrBlocked2Element(x, y + 1);
6033
6034     impact = (last_line || object_hit);
6035   }
6036
6037   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6038   {
6039     SplashAcid(x, y + 1);
6040     return;
6041   }
6042
6043   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6044   /* only reset graphic animation if graphic really changes after impact */
6045   if (impact &&
6046       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6047   {
6048     ResetGfxAnimation(x, y);
6049     TEST_DrawLevelField(x, y);
6050   }
6051
6052   if (impact && CAN_EXPLODE_IMPACT(element))
6053   {
6054     Bang(x, y);
6055     return;
6056   }
6057   else if (impact && element == EL_PEARL &&
6058            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6059   {
6060     ResetGfxAnimation(x, y);
6061
6062     Feld[x][y] = EL_PEARL_BREAKING;
6063     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6064     return;
6065   }
6066   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6067   {
6068     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6069
6070     return;
6071   }
6072
6073   if (impact && element == EL_AMOEBA_DROP)
6074   {
6075     if (object_hit && IS_PLAYER(x, y + 1))
6076       KillPlayerUnlessEnemyProtected(x, y + 1);
6077     else if (object_hit && smashed == EL_PENGUIN)
6078       Bang(x, y + 1);
6079     else
6080     {
6081       Feld[x][y] = EL_AMOEBA_GROWING;
6082       Store[x][y] = EL_AMOEBA_WET;
6083
6084       ResetRandomAnimationValue(x, y);
6085     }
6086     return;
6087   }
6088
6089   if (object_hit)               /* check which object was hit */
6090   {
6091     if ((CAN_PASS_MAGIC_WALL(element) && 
6092          (smashed == EL_MAGIC_WALL ||
6093           smashed == EL_BD_MAGIC_WALL)) ||
6094         (CAN_PASS_DC_MAGIC_WALL(element) &&
6095          smashed == EL_DC_MAGIC_WALL))
6096     {
6097       int xx, yy;
6098       int activated_magic_wall =
6099         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6100          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6101          EL_DC_MAGIC_WALL_ACTIVE);
6102
6103       /* activate magic wall / mill */
6104       SCAN_PLAYFIELD(xx, yy)
6105       {
6106         if (Feld[xx][yy] == smashed)
6107           Feld[xx][yy] = activated_magic_wall;
6108       }
6109
6110       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6111       game.magic_wall_active = TRUE;
6112
6113       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6114                             SND_MAGIC_WALL_ACTIVATING :
6115                             smashed == EL_BD_MAGIC_WALL ?
6116                             SND_BD_MAGIC_WALL_ACTIVATING :
6117                             SND_DC_MAGIC_WALL_ACTIVATING));
6118     }
6119
6120     if (IS_PLAYER(x, y + 1))
6121     {
6122       if (CAN_SMASH_PLAYER(element))
6123       {
6124         KillPlayerUnlessEnemyProtected(x, y + 1);
6125         return;
6126       }
6127     }
6128     else if (smashed == EL_PENGUIN)
6129     {
6130       if (CAN_SMASH_PLAYER(element))
6131       {
6132         Bang(x, y + 1);
6133         return;
6134       }
6135     }
6136     else if (element == EL_BD_DIAMOND)
6137     {
6138       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6139       {
6140         Bang(x, y + 1);
6141         return;
6142       }
6143     }
6144     else if (((element == EL_SP_INFOTRON ||
6145                element == EL_SP_ZONK) &&
6146               (smashed == EL_SP_SNIKSNAK ||
6147                smashed == EL_SP_ELECTRON ||
6148                smashed == EL_SP_DISK_ORANGE)) ||
6149              (element == EL_SP_INFOTRON &&
6150               smashed == EL_SP_DISK_YELLOW))
6151     {
6152       Bang(x, y + 1);
6153       return;
6154     }
6155     else if (CAN_SMASH_EVERYTHING(element))
6156     {
6157       if (IS_CLASSIC_ENEMY(smashed) ||
6158           CAN_EXPLODE_SMASHED(smashed))
6159       {
6160         Bang(x, y + 1);
6161         return;
6162       }
6163       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6164       {
6165         if (smashed == EL_LAMP ||
6166             smashed == EL_LAMP_ACTIVE)
6167         {
6168           Bang(x, y + 1);
6169           return;
6170         }
6171         else if (smashed == EL_NUT)
6172         {
6173           Feld[x][y + 1] = EL_NUT_BREAKING;
6174           PlayLevelSound(x, y, SND_NUT_BREAKING);
6175           RaiseScoreElement(EL_NUT);
6176           return;
6177         }
6178         else if (smashed == EL_PEARL)
6179         {
6180           ResetGfxAnimation(x, y);
6181
6182           Feld[x][y + 1] = EL_PEARL_BREAKING;
6183           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6184           return;
6185         }
6186         else if (smashed == EL_DIAMOND)
6187         {
6188           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6189           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6190           return;
6191         }
6192         else if (IS_BELT_SWITCH(smashed))
6193         {
6194           ToggleBeltSwitch(x, y + 1);
6195         }
6196         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6197                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6198                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6199                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6200         {
6201           ToggleSwitchgateSwitch(x, y + 1);
6202         }
6203         else if (smashed == EL_LIGHT_SWITCH ||
6204                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6205         {
6206           ToggleLightSwitch(x, y + 1);
6207         }
6208         else
6209         {
6210           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6211
6212           CheckElementChangeBySide(x, y + 1, smashed, element,
6213                                    CE_SWITCHED, CH_SIDE_TOP);
6214           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6215                                             CH_SIDE_TOP);
6216         }
6217       }
6218       else
6219       {
6220         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6221       }
6222     }
6223   }
6224
6225   /* play sound of magic wall / mill */
6226   if (!last_line &&
6227       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6228        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6229        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6230   {
6231     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6232       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6233     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6234       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6235     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6236       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6237
6238     return;
6239   }
6240
6241   /* play sound of object that hits the ground */
6242   if (last_line || object_hit)
6243     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6244 }
6245
6246 inline static void TurnRoundExt(int x, int y)
6247 {
6248   static struct
6249   {
6250     int dx, dy;
6251   } move_xy[] =
6252   {
6253     {  0,  0 },
6254     { -1,  0 },
6255     { +1,  0 },
6256     {  0,  0 },
6257     {  0, -1 },
6258     {  0,  0 }, { 0, 0 }, { 0, 0 },
6259     {  0, +1 }
6260   };
6261   static struct
6262   {
6263     int left, right, back;
6264   } turn[] =
6265   {
6266     { 0,        0,              0        },
6267     { MV_DOWN,  MV_UP,          MV_RIGHT },
6268     { MV_UP,    MV_DOWN,        MV_LEFT  },
6269     { 0,        0,              0        },
6270     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6271     { 0,        0,              0        },
6272     { 0,        0,              0        },
6273     { 0,        0,              0        },
6274     { MV_RIGHT, MV_LEFT,        MV_UP    }
6275   };
6276
6277   int element = Feld[x][y];
6278   int move_pattern = element_info[element].move_pattern;
6279
6280   int old_move_dir = MovDir[x][y];
6281   int left_dir  = turn[old_move_dir].left;
6282   int right_dir = turn[old_move_dir].right;
6283   int back_dir  = turn[old_move_dir].back;
6284
6285   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6286   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6287   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6288   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6289
6290   int left_x  = x + left_dx,  left_y  = y + left_dy;
6291   int right_x = x + right_dx, right_y = y + right_dy;
6292   int move_x  = x + move_dx,  move_y  = y + move_dy;
6293
6294   int xx, yy;
6295
6296   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6297   {
6298     TestIfBadThingTouchesOtherBadThing(x, y);
6299
6300     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6301       MovDir[x][y] = right_dir;
6302     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6303       MovDir[x][y] = left_dir;
6304
6305     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6306       MovDelay[x][y] = 9;
6307     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6308       MovDelay[x][y] = 1;
6309   }
6310   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6311   {
6312     TestIfBadThingTouchesOtherBadThing(x, y);
6313
6314     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6315       MovDir[x][y] = left_dir;
6316     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6317       MovDir[x][y] = right_dir;
6318
6319     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6320       MovDelay[x][y] = 9;
6321     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6322       MovDelay[x][y] = 1;
6323   }
6324   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6325   {
6326     TestIfBadThingTouchesOtherBadThing(x, y);
6327
6328     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6329       MovDir[x][y] = left_dir;
6330     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6331       MovDir[x][y] = right_dir;
6332
6333     if (MovDir[x][y] != old_move_dir)
6334       MovDelay[x][y] = 9;
6335   }
6336   else if (element == EL_YAMYAM)
6337   {
6338     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6339     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6340
6341     if (can_turn_left && can_turn_right)
6342       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6343     else if (can_turn_left)
6344       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6345     else if (can_turn_right)
6346       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6347     else
6348       MovDir[x][y] = back_dir;
6349
6350     MovDelay[x][y] = 16 + 16 * RND(3);
6351   }
6352   else if (element == EL_DARK_YAMYAM)
6353   {
6354     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6355                                                          left_x, left_y);
6356     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6357                                                          right_x, right_y);
6358
6359     if (can_turn_left && can_turn_right)
6360       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6361     else if (can_turn_left)
6362       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6363     else if (can_turn_right)
6364       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6365     else
6366       MovDir[x][y] = back_dir;
6367
6368     MovDelay[x][y] = 16 + 16 * RND(3);
6369   }
6370   else if (element == EL_PACMAN)
6371   {
6372     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6373     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6374
6375     if (can_turn_left && can_turn_right)
6376       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6377     else if (can_turn_left)
6378       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6379     else if (can_turn_right)
6380       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6381     else
6382       MovDir[x][y] = back_dir;
6383
6384     MovDelay[x][y] = 6 + RND(40);
6385   }
6386   else if (element == EL_PIG)
6387   {
6388     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6389     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6390     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6391     boolean should_turn_left, should_turn_right, should_move_on;
6392     int rnd_value = 24;
6393     int rnd = RND(rnd_value);
6394
6395     should_turn_left = (can_turn_left &&
6396                         (!can_move_on ||
6397                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6398                                                    y + back_dy + left_dy)));
6399     should_turn_right = (can_turn_right &&
6400                          (!can_move_on ||
6401                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6402                                                     y + back_dy + right_dy)));
6403     should_move_on = (can_move_on &&
6404                       (!can_turn_left ||
6405                        !can_turn_right ||
6406                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6407                                                  y + move_dy + left_dy) ||
6408                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6409                                                  y + move_dy + right_dy)));
6410
6411     if (should_turn_left || should_turn_right || should_move_on)
6412     {
6413       if (should_turn_left && should_turn_right && should_move_on)
6414         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6415                         rnd < 2 * rnd_value / 3 ? right_dir :
6416                         old_move_dir);
6417       else if (should_turn_left && should_turn_right)
6418         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6419       else if (should_turn_left && should_move_on)
6420         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6421       else if (should_turn_right && should_move_on)
6422         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6423       else if (should_turn_left)
6424         MovDir[x][y] = left_dir;
6425       else if (should_turn_right)
6426         MovDir[x][y] = right_dir;
6427       else if (should_move_on)
6428         MovDir[x][y] = old_move_dir;
6429     }
6430     else if (can_move_on && rnd > rnd_value / 8)
6431       MovDir[x][y] = old_move_dir;
6432     else if (can_turn_left && can_turn_right)
6433       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6434     else if (can_turn_left && rnd > rnd_value / 8)
6435       MovDir[x][y] = left_dir;
6436     else if (can_turn_right && rnd > rnd_value/8)
6437       MovDir[x][y] = right_dir;
6438     else
6439       MovDir[x][y] = back_dir;
6440
6441     xx = x + move_xy[MovDir[x][y]].dx;
6442     yy = y + move_xy[MovDir[x][y]].dy;
6443
6444     if (!IN_LEV_FIELD(xx, yy) ||
6445         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6446       MovDir[x][y] = old_move_dir;
6447
6448     MovDelay[x][y] = 0;
6449   }
6450   else if (element == EL_DRAGON)
6451   {
6452     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6453     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6454     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6455     int rnd_value = 24;
6456     int rnd = RND(rnd_value);
6457
6458     if (can_move_on && rnd > rnd_value / 8)
6459       MovDir[x][y] = old_move_dir;
6460     else if (can_turn_left && can_turn_right)
6461       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6462     else if (can_turn_left && rnd > rnd_value / 8)
6463       MovDir[x][y] = left_dir;
6464     else if (can_turn_right && rnd > rnd_value / 8)
6465       MovDir[x][y] = right_dir;
6466     else
6467       MovDir[x][y] = back_dir;
6468
6469     xx = x + move_xy[MovDir[x][y]].dx;
6470     yy = y + move_xy[MovDir[x][y]].dy;
6471
6472     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6473       MovDir[x][y] = old_move_dir;
6474
6475     MovDelay[x][y] = 0;
6476   }
6477   else if (element == EL_MOLE)
6478   {
6479     boolean can_move_on =
6480       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6481                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6482                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6483     if (!can_move_on)
6484     {
6485       boolean can_turn_left =
6486         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6487                               IS_AMOEBOID(Feld[left_x][left_y])));
6488
6489       boolean can_turn_right =
6490         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6491                               IS_AMOEBOID(Feld[right_x][right_y])));
6492
6493       if (can_turn_left && can_turn_right)
6494         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6495       else if (can_turn_left)
6496         MovDir[x][y] = left_dir;
6497       else
6498         MovDir[x][y] = right_dir;
6499     }
6500
6501     if (MovDir[x][y] != old_move_dir)
6502       MovDelay[x][y] = 9;
6503   }
6504   else if (element == EL_BALLOON)
6505   {
6506     MovDir[x][y] = game.wind_direction;
6507     MovDelay[x][y] = 0;
6508   }
6509   else if (element == EL_SPRING)
6510   {
6511     if (MovDir[x][y] & MV_HORIZONTAL)
6512     {
6513       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6514           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6515       {
6516         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6517         ResetGfxAnimation(move_x, move_y);
6518         TEST_DrawLevelField(move_x, move_y);
6519
6520         MovDir[x][y] = back_dir;
6521       }
6522       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6523                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6524         MovDir[x][y] = MV_NONE;
6525     }
6526
6527     MovDelay[x][y] = 0;
6528   }
6529   else if (element == EL_ROBOT ||
6530            element == EL_SATELLITE ||
6531            element == EL_PENGUIN ||
6532            element == EL_EMC_ANDROID)
6533   {
6534     int attr_x = -1, attr_y = -1;
6535
6536     if (AllPlayersGone)
6537     {
6538       attr_x = ExitX;
6539       attr_y = ExitY;
6540     }
6541     else
6542     {
6543       int i;
6544
6545       for (i = 0; i < MAX_PLAYERS; i++)
6546       {
6547         struct PlayerInfo *player = &stored_player[i];
6548         int jx = player->jx, jy = player->jy;
6549
6550         if (!player->active)
6551           continue;
6552
6553         if (attr_x == -1 ||
6554             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6555         {
6556           attr_x = jx;
6557           attr_y = jy;
6558         }
6559       }
6560     }
6561
6562     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6563         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6564          game.engine_version < VERSION_IDENT(3,1,0,0)))
6565     {
6566       attr_x = ZX;
6567       attr_y = ZY;
6568     }
6569
6570     if (element == EL_PENGUIN)
6571     {
6572       int i;
6573       static int xy[4][2] =
6574       {
6575         { 0, -1 },
6576         { -1, 0 },
6577         { +1, 0 },
6578         { 0, +1 }
6579       };
6580
6581       for (i = 0; i < NUM_DIRECTIONS; i++)
6582       {
6583         int ex = x + xy[i][0];
6584         int ey = y + xy[i][1];
6585
6586         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6587                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6588                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6589                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6590         {
6591           attr_x = ex;
6592           attr_y = ey;
6593           break;
6594         }
6595       }
6596     }
6597
6598     MovDir[x][y] = MV_NONE;
6599     if (attr_x < x)
6600       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6601     else if (attr_x > x)
6602       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6603     if (attr_y < y)
6604       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6605     else if (attr_y > y)
6606       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6607
6608     if (element == EL_ROBOT)
6609     {
6610       int newx, newy;
6611
6612       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6613         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6614       Moving2Blocked(x, y, &newx, &newy);
6615
6616       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6617         MovDelay[x][y] = 8 + 8 * !RND(3);
6618       else
6619         MovDelay[x][y] = 16;
6620     }
6621     else if (element == EL_PENGUIN)
6622     {
6623       int newx, newy;
6624
6625       MovDelay[x][y] = 1;
6626
6627       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6628       {
6629         boolean first_horiz = RND(2);
6630         int new_move_dir = MovDir[x][y];
6631
6632         MovDir[x][y] =
6633           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6634         Moving2Blocked(x, y, &newx, &newy);
6635
6636         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6637           return;
6638
6639         MovDir[x][y] =
6640           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6641         Moving2Blocked(x, y, &newx, &newy);
6642
6643         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6644           return;
6645
6646         MovDir[x][y] = old_move_dir;
6647         return;
6648       }
6649     }
6650     else if (element == EL_SATELLITE)
6651     {
6652       int newx, newy;
6653
6654       MovDelay[x][y] = 1;
6655
6656       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6657       {
6658         boolean first_horiz = RND(2);
6659         int new_move_dir = MovDir[x][y];
6660
6661         MovDir[x][y] =
6662           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6663         Moving2Blocked(x, y, &newx, &newy);
6664
6665         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6666           return;
6667
6668         MovDir[x][y] =
6669           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6670         Moving2Blocked(x, y, &newx, &newy);
6671
6672         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6673           return;
6674
6675         MovDir[x][y] = old_move_dir;
6676         return;
6677       }
6678     }
6679     else if (element == EL_EMC_ANDROID)
6680     {
6681       static int check_pos[16] =
6682       {
6683         -1,             /*  0 => (invalid)          */
6684         7,              /*  1 => MV_LEFT            */
6685         3,              /*  2 => MV_RIGHT           */
6686         -1,             /*  3 => (invalid)          */
6687         1,              /*  4 =>            MV_UP   */
6688         0,              /*  5 => MV_LEFT  | MV_UP   */
6689         2,              /*  6 => MV_RIGHT | MV_UP   */
6690         -1,             /*  7 => (invalid)          */
6691         5,              /*  8 =>            MV_DOWN */
6692         6,              /*  9 => MV_LEFT  | MV_DOWN */
6693         4,              /* 10 => MV_RIGHT | MV_DOWN */
6694         -1,             /* 11 => (invalid)          */
6695         -1,             /* 12 => (invalid)          */
6696         -1,             /* 13 => (invalid)          */
6697         -1,             /* 14 => (invalid)          */
6698         -1,             /* 15 => (invalid)          */
6699       };
6700       static struct
6701       {
6702         int dx, dy;
6703         int dir;
6704       } check_xy[8] =
6705       {
6706         { -1, -1,       MV_LEFT  | MV_UP   },
6707         {  0, -1,                  MV_UP   },
6708         { +1, -1,       MV_RIGHT | MV_UP   },
6709         { +1,  0,       MV_RIGHT           },
6710         { +1, +1,       MV_RIGHT | MV_DOWN },
6711         {  0, +1,                  MV_DOWN },
6712         { -1, +1,       MV_LEFT  | MV_DOWN },
6713         { -1,  0,       MV_LEFT            },
6714       };
6715       int start_pos, check_order;
6716       boolean can_clone = FALSE;
6717       int i;
6718
6719       /* check if there is any free field around current position */
6720       for (i = 0; i < 8; i++)
6721       {
6722         int newx = x + check_xy[i].dx;
6723         int newy = y + check_xy[i].dy;
6724
6725         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6726         {
6727           can_clone = TRUE;
6728
6729           break;
6730         }
6731       }
6732
6733       if (can_clone)            /* randomly find an element to clone */
6734       {
6735         can_clone = FALSE;
6736
6737         start_pos = check_pos[RND(8)];
6738         check_order = (RND(2) ? -1 : +1);
6739
6740         for (i = 0; i < 8; i++)
6741         {
6742           int pos_raw = start_pos + i * check_order;
6743           int pos = (pos_raw + 8) % 8;
6744           int newx = x + check_xy[pos].dx;
6745           int newy = y + check_xy[pos].dy;
6746
6747           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6748           {
6749             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6750             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6751
6752             Store[x][y] = Feld[newx][newy];
6753
6754             can_clone = TRUE;
6755
6756             break;
6757           }
6758         }
6759       }
6760
6761       if (can_clone)            /* randomly find a direction to move */
6762       {
6763         can_clone = FALSE;
6764
6765         start_pos = check_pos[RND(8)];
6766         check_order = (RND(2) ? -1 : +1);
6767
6768         for (i = 0; i < 8; i++)
6769         {
6770           int pos_raw = start_pos + i * check_order;
6771           int pos = (pos_raw + 8) % 8;
6772           int newx = x + check_xy[pos].dx;
6773           int newy = y + check_xy[pos].dy;
6774           int new_move_dir = check_xy[pos].dir;
6775
6776           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6777           {
6778             MovDir[x][y] = new_move_dir;
6779             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6780
6781             can_clone = TRUE;
6782
6783             break;
6784           }
6785         }
6786       }
6787
6788       if (can_clone)            /* cloning and moving successful */
6789         return;
6790
6791       /* cannot clone -- try to move towards player */
6792
6793       start_pos = check_pos[MovDir[x][y] & 0x0f];
6794       check_order = (RND(2) ? -1 : +1);
6795
6796       for (i = 0; i < 3; i++)
6797       {
6798         /* first check start_pos, then previous/next or (next/previous) pos */
6799         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6800         int pos = (pos_raw + 8) % 8;
6801         int newx = x + check_xy[pos].dx;
6802         int newy = y + check_xy[pos].dy;
6803         int new_move_dir = check_xy[pos].dir;
6804
6805         if (IS_PLAYER(newx, newy))
6806           break;
6807
6808         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6809         {
6810           MovDir[x][y] = new_move_dir;
6811           MovDelay[x][y] = level.android_move_time * 8 + 1;
6812
6813           break;
6814         }
6815       }
6816     }
6817   }
6818   else if (move_pattern == MV_TURNING_LEFT ||
6819            move_pattern == MV_TURNING_RIGHT ||
6820            move_pattern == MV_TURNING_LEFT_RIGHT ||
6821            move_pattern == MV_TURNING_RIGHT_LEFT ||
6822            move_pattern == MV_TURNING_RANDOM ||
6823            move_pattern == MV_ALL_DIRECTIONS)
6824   {
6825     boolean can_turn_left =
6826       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6827     boolean can_turn_right =
6828       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6829
6830     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6831       return;
6832
6833     if (move_pattern == MV_TURNING_LEFT)
6834       MovDir[x][y] = left_dir;
6835     else if (move_pattern == MV_TURNING_RIGHT)
6836       MovDir[x][y] = right_dir;
6837     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6838       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6839     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6840       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6841     else if (move_pattern == MV_TURNING_RANDOM)
6842       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6843                       can_turn_right && !can_turn_left ? right_dir :
6844                       RND(2) ? left_dir : right_dir);
6845     else if (can_turn_left && can_turn_right)
6846       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6847     else if (can_turn_left)
6848       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6849     else if (can_turn_right)
6850       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6851     else
6852       MovDir[x][y] = back_dir;
6853
6854     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6855   }
6856   else if (move_pattern == MV_HORIZONTAL ||
6857            move_pattern == MV_VERTICAL)
6858   {
6859     if (move_pattern & old_move_dir)
6860       MovDir[x][y] = back_dir;
6861     else if (move_pattern == MV_HORIZONTAL)
6862       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6863     else if (move_pattern == MV_VERTICAL)
6864       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6865
6866     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6867   }
6868   else if (move_pattern & MV_ANY_DIRECTION)
6869   {
6870     MovDir[x][y] = move_pattern;
6871     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6872   }
6873   else if (move_pattern & MV_WIND_DIRECTION)
6874   {
6875     MovDir[x][y] = game.wind_direction;
6876     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6877   }
6878   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6879   {
6880     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6881       MovDir[x][y] = left_dir;
6882     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6883       MovDir[x][y] = right_dir;
6884
6885     if (MovDir[x][y] != old_move_dir)
6886       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6887   }
6888   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6889   {
6890     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6891       MovDir[x][y] = right_dir;
6892     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6893       MovDir[x][y] = left_dir;
6894
6895     if (MovDir[x][y] != old_move_dir)
6896       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6897   }
6898   else if (move_pattern == MV_TOWARDS_PLAYER ||
6899            move_pattern == MV_AWAY_FROM_PLAYER)
6900   {
6901     int attr_x = -1, attr_y = -1;
6902     int newx, newy;
6903     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6904
6905     if (AllPlayersGone)
6906     {
6907       attr_x = ExitX;
6908       attr_y = ExitY;
6909     }
6910     else
6911     {
6912       int i;
6913
6914       for (i = 0; i < MAX_PLAYERS; i++)
6915       {
6916         struct PlayerInfo *player = &stored_player[i];
6917         int jx = player->jx, jy = player->jy;
6918
6919         if (!player->active)
6920           continue;
6921
6922         if (attr_x == -1 ||
6923             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6924         {
6925           attr_x = jx;
6926           attr_y = jy;
6927         }
6928       }
6929     }
6930
6931     MovDir[x][y] = MV_NONE;
6932     if (attr_x < x)
6933       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6934     else if (attr_x > x)
6935       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6936     if (attr_y < y)
6937       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6938     else if (attr_y > y)
6939       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6940
6941     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6942
6943     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6944     {
6945       boolean first_horiz = RND(2);
6946       int new_move_dir = MovDir[x][y];
6947
6948       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6949       {
6950         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6951         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6952
6953         return;
6954       }
6955
6956       MovDir[x][y] =
6957         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6958       Moving2Blocked(x, y, &newx, &newy);
6959
6960       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6961         return;
6962
6963       MovDir[x][y] =
6964         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6965       Moving2Blocked(x, y, &newx, &newy);
6966
6967       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6968         return;
6969
6970       MovDir[x][y] = old_move_dir;
6971     }
6972   }
6973   else if (move_pattern == MV_WHEN_PUSHED ||
6974            move_pattern == MV_WHEN_DROPPED)
6975   {
6976     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6977       MovDir[x][y] = MV_NONE;
6978
6979     MovDelay[x][y] = 0;
6980   }
6981   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6982   {
6983     static int test_xy[7][2] =
6984     {
6985       { 0, -1 },
6986       { -1, 0 },
6987       { +1, 0 },
6988       { 0, +1 },
6989       { 0, -1 },
6990       { -1, 0 },
6991       { +1, 0 },
6992     };
6993     static int test_dir[7] =
6994     {
6995       MV_UP,
6996       MV_LEFT,
6997       MV_RIGHT,
6998       MV_DOWN,
6999       MV_UP,
7000       MV_LEFT,
7001       MV_RIGHT,
7002     };
7003     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7004     int move_preference = -1000000;     /* start with very low preference */
7005     int new_move_dir = MV_NONE;
7006     int start_test = RND(4);
7007     int i;
7008
7009     for (i = 0; i < NUM_DIRECTIONS; i++)
7010     {
7011       int move_dir = test_dir[start_test + i];
7012       int move_dir_preference;
7013
7014       xx = x + test_xy[start_test + i][0];
7015       yy = y + test_xy[start_test + i][1];
7016
7017       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7018           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7019       {
7020         new_move_dir = move_dir;
7021
7022         break;
7023       }
7024
7025       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7026         continue;
7027
7028       move_dir_preference = -1 * RunnerVisit[xx][yy];
7029       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7030         move_dir_preference = PlayerVisit[xx][yy];
7031
7032       if (move_dir_preference > move_preference)
7033       {
7034         /* prefer field that has not been visited for the longest time */
7035         move_preference = move_dir_preference;
7036         new_move_dir = move_dir;
7037       }
7038       else if (move_dir_preference == move_preference &&
7039                move_dir == old_move_dir)
7040       {
7041         /* prefer last direction when all directions are preferred equally */
7042         move_preference = move_dir_preference;
7043         new_move_dir = move_dir;
7044       }
7045     }
7046
7047     MovDir[x][y] = new_move_dir;
7048     if (old_move_dir != new_move_dir)
7049       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7050   }
7051 }
7052
7053 static void TurnRound(int x, int y)
7054 {
7055   int direction = MovDir[x][y];
7056
7057   TurnRoundExt(x, y);
7058
7059   GfxDir[x][y] = MovDir[x][y];
7060
7061   if (direction != MovDir[x][y])
7062     GfxFrame[x][y] = 0;
7063
7064   if (MovDelay[x][y])
7065     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7066
7067   ResetGfxFrame(x, y);
7068 }
7069
7070 static boolean JustBeingPushed(int x, int y)
7071 {
7072   int i;
7073
7074   for (i = 0; i < MAX_PLAYERS; i++)
7075   {
7076     struct PlayerInfo *player = &stored_player[i];
7077
7078     if (player->active && player->is_pushing && player->MovPos)
7079     {
7080       int next_jx = player->jx + (player->jx - player->last_jx);
7081       int next_jy = player->jy + (player->jy - player->last_jy);
7082
7083       if (x == next_jx && y == next_jy)
7084         return TRUE;
7085     }
7086   }
7087
7088   return FALSE;
7089 }
7090
7091 void StartMoving(int x, int y)
7092 {
7093   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7094   int element = Feld[x][y];
7095
7096   if (Stop[x][y])
7097     return;
7098
7099   if (MovDelay[x][y] == 0)
7100     GfxAction[x][y] = ACTION_DEFAULT;
7101
7102   if (CAN_FALL(element) && y < lev_fieldy - 1)
7103   {
7104     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7105         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7106       if (JustBeingPushed(x, y))
7107         return;
7108
7109     if (element == EL_QUICKSAND_FULL)
7110     {
7111       if (IS_FREE(x, y + 1))
7112       {
7113         InitMovingField(x, y, MV_DOWN);
7114         started_moving = TRUE;
7115
7116         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7117 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7118         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7119           Store[x][y] = EL_ROCK;
7120 #else
7121         Store[x][y] = EL_ROCK;
7122 #endif
7123
7124         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7125       }
7126       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7127       {
7128         if (!MovDelay[x][y])
7129         {
7130           MovDelay[x][y] = TILEY + 1;
7131
7132           ResetGfxAnimation(x, y);
7133           ResetGfxAnimation(x, y + 1);
7134         }
7135
7136         if (MovDelay[x][y])
7137         {
7138           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7139           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7140
7141           MovDelay[x][y]--;
7142           if (MovDelay[x][y])
7143             return;
7144         }
7145
7146         Feld[x][y] = EL_QUICKSAND_EMPTY;
7147         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7148         Store[x][y + 1] = Store[x][y];
7149         Store[x][y] = 0;
7150
7151         PlayLevelSoundAction(x, y, ACTION_FILLING);
7152       }
7153       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7154       {
7155         if (!MovDelay[x][y])
7156         {
7157           MovDelay[x][y] = TILEY + 1;
7158
7159           ResetGfxAnimation(x, y);
7160           ResetGfxAnimation(x, y + 1);
7161         }
7162
7163         if (MovDelay[x][y])
7164         {
7165           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7166           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7167
7168           MovDelay[x][y]--;
7169           if (MovDelay[x][y])
7170             return;
7171         }
7172
7173         Feld[x][y] = EL_QUICKSAND_EMPTY;
7174         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7175         Store[x][y + 1] = Store[x][y];
7176         Store[x][y] = 0;
7177
7178         PlayLevelSoundAction(x, y, ACTION_FILLING);
7179       }
7180     }
7181     else if (element == EL_QUICKSAND_FAST_FULL)
7182     {
7183       if (IS_FREE(x, y + 1))
7184       {
7185         InitMovingField(x, y, MV_DOWN);
7186         started_moving = TRUE;
7187
7188         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7189 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7190         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7191           Store[x][y] = EL_ROCK;
7192 #else
7193         Store[x][y] = EL_ROCK;
7194 #endif
7195
7196         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7197       }
7198       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7199       {
7200         if (!MovDelay[x][y])
7201         {
7202           MovDelay[x][y] = TILEY + 1;
7203
7204           ResetGfxAnimation(x, y);
7205           ResetGfxAnimation(x, y + 1);
7206         }
7207
7208         if (MovDelay[x][y])
7209         {
7210           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7211           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7212
7213           MovDelay[x][y]--;
7214           if (MovDelay[x][y])
7215             return;
7216         }
7217
7218         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7219         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7220         Store[x][y + 1] = Store[x][y];
7221         Store[x][y] = 0;
7222
7223         PlayLevelSoundAction(x, y, ACTION_FILLING);
7224       }
7225       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7226       {
7227         if (!MovDelay[x][y])
7228         {
7229           MovDelay[x][y] = TILEY + 1;
7230
7231           ResetGfxAnimation(x, y);
7232           ResetGfxAnimation(x, y + 1);
7233         }
7234
7235         if (MovDelay[x][y])
7236         {
7237           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7238           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7239
7240           MovDelay[x][y]--;
7241           if (MovDelay[x][y])
7242             return;
7243         }
7244
7245         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7246         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7247         Store[x][y + 1] = Store[x][y];
7248         Store[x][y] = 0;
7249
7250         PlayLevelSoundAction(x, y, ACTION_FILLING);
7251       }
7252     }
7253     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7254              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7255     {
7256       InitMovingField(x, y, MV_DOWN);
7257       started_moving = TRUE;
7258
7259       Feld[x][y] = EL_QUICKSAND_FILLING;
7260       Store[x][y] = element;
7261
7262       PlayLevelSoundAction(x, y, ACTION_FILLING);
7263     }
7264     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7265              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7266     {
7267       InitMovingField(x, y, MV_DOWN);
7268       started_moving = TRUE;
7269
7270       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7271       Store[x][y] = element;
7272
7273       PlayLevelSoundAction(x, y, ACTION_FILLING);
7274     }
7275     else if (element == EL_MAGIC_WALL_FULL)
7276     {
7277       if (IS_FREE(x, y + 1))
7278       {
7279         InitMovingField(x, y, MV_DOWN);
7280         started_moving = TRUE;
7281
7282         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7283         Store[x][y] = EL_CHANGED(Store[x][y]);
7284       }
7285       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7286       {
7287         if (!MovDelay[x][y])
7288           MovDelay[x][y] = TILEY / 4 + 1;
7289
7290         if (MovDelay[x][y])
7291         {
7292           MovDelay[x][y]--;
7293           if (MovDelay[x][y])
7294             return;
7295         }
7296
7297         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7298         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7299         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7300         Store[x][y] = 0;
7301       }
7302     }
7303     else if (element == EL_BD_MAGIC_WALL_FULL)
7304     {
7305       if (IS_FREE(x, y + 1))
7306       {
7307         InitMovingField(x, y, MV_DOWN);
7308         started_moving = TRUE;
7309
7310         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7311         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7312       }
7313       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7314       {
7315         if (!MovDelay[x][y])
7316           MovDelay[x][y] = TILEY / 4 + 1;
7317
7318         if (MovDelay[x][y])
7319         {
7320           MovDelay[x][y]--;
7321           if (MovDelay[x][y])
7322             return;
7323         }
7324
7325         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7326         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7327         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7328         Store[x][y] = 0;
7329       }
7330     }
7331     else if (element == EL_DC_MAGIC_WALL_FULL)
7332     {
7333       if (IS_FREE(x, y + 1))
7334       {
7335         InitMovingField(x, y, MV_DOWN);
7336         started_moving = TRUE;
7337
7338         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7339         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7340       }
7341       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7342       {
7343         if (!MovDelay[x][y])
7344           MovDelay[x][y] = TILEY / 4 + 1;
7345
7346         if (MovDelay[x][y])
7347         {
7348           MovDelay[x][y]--;
7349           if (MovDelay[x][y])
7350             return;
7351         }
7352
7353         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7354         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7355         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7356         Store[x][y] = 0;
7357       }
7358     }
7359     else if ((CAN_PASS_MAGIC_WALL(element) &&
7360               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7361                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7362              (CAN_PASS_DC_MAGIC_WALL(element) &&
7363               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7364
7365     {
7366       InitMovingField(x, y, MV_DOWN);
7367       started_moving = TRUE;
7368
7369       Feld[x][y] =
7370         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7371          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7372          EL_DC_MAGIC_WALL_FILLING);
7373       Store[x][y] = element;
7374     }
7375     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7376     {
7377       SplashAcid(x, y + 1);
7378
7379       InitMovingField(x, y, MV_DOWN);
7380       started_moving = TRUE;
7381
7382       Store[x][y] = EL_ACID;
7383     }
7384     else if (
7385              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7386               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7387              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7388               CAN_FALL(element) && WasJustFalling[x][y] &&
7389               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7390
7391              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7392               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7393               (Feld[x][y + 1] == EL_BLOCKED)))
7394     {
7395       /* this is needed for a special case not covered by calling "Impact()"
7396          from "ContinueMoving()": if an element moves to a tile directly below
7397          another element which was just falling on that tile (which was empty
7398          in the previous frame), the falling element above would just stop
7399          instead of smashing the element below (in previous version, the above
7400          element was just checked for "moving" instead of "falling", resulting
7401          in incorrect smashes caused by horizontal movement of the above
7402          element; also, the case of the player being the element to smash was
7403          simply not covered here... :-/ ) */
7404
7405       CheckCollision[x][y] = 0;
7406       CheckImpact[x][y] = 0;
7407
7408       Impact(x, y);
7409     }
7410     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7411     {
7412       if (MovDir[x][y] == MV_NONE)
7413       {
7414         InitMovingField(x, y, MV_DOWN);
7415         started_moving = TRUE;
7416       }
7417     }
7418     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7419     {
7420       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7421         MovDir[x][y] = MV_DOWN;
7422
7423       InitMovingField(x, y, MV_DOWN);
7424       started_moving = TRUE;
7425     }
7426     else if (element == EL_AMOEBA_DROP)
7427     {
7428       Feld[x][y] = EL_AMOEBA_GROWING;
7429       Store[x][y] = EL_AMOEBA_WET;
7430     }
7431     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7432               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7433              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7434              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7435     {
7436       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7437                                 (IS_FREE(x - 1, y + 1) ||
7438                                  Feld[x - 1][y + 1] == EL_ACID));
7439       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7440                                 (IS_FREE(x + 1, y + 1) ||
7441                                  Feld[x + 1][y + 1] == EL_ACID));
7442       boolean can_fall_any  = (can_fall_left || can_fall_right);
7443       boolean can_fall_both = (can_fall_left && can_fall_right);
7444       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7445
7446       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7447       {
7448         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7449           can_fall_right = FALSE;
7450         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7451           can_fall_left = FALSE;
7452         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7453           can_fall_right = FALSE;
7454         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7455           can_fall_left = FALSE;
7456
7457         can_fall_any  = (can_fall_left || can_fall_right);
7458         can_fall_both = FALSE;
7459       }
7460
7461       if (can_fall_both)
7462       {
7463         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7464           can_fall_right = FALSE;       /* slip down on left side */
7465         else
7466           can_fall_left = !(can_fall_right = RND(2));
7467
7468         can_fall_both = FALSE;
7469       }
7470
7471       if (can_fall_any)
7472       {
7473         /* if not determined otherwise, prefer left side for slipping down */
7474         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7475         started_moving = TRUE;
7476       }
7477     }
7478     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7479     {
7480       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7481       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7482       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7483       int belt_dir = game.belt_dir[belt_nr];
7484
7485       if ((belt_dir == MV_LEFT  && left_is_free) ||
7486           (belt_dir == MV_RIGHT && right_is_free))
7487       {
7488         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7489
7490         InitMovingField(x, y, belt_dir);
7491         started_moving = TRUE;
7492
7493         Pushed[x][y] = TRUE;
7494         Pushed[nextx][y] = TRUE;
7495
7496         GfxAction[x][y] = ACTION_DEFAULT;
7497       }
7498       else
7499       {
7500         MovDir[x][y] = 0;       /* if element was moving, stop it */
7501       }
7502     }
7503   }
7504
7505   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7506   if (CAN_MOVE(element) && !started_moving)
7507   {
7508     int move_pattern = element_info[element].move_pattern;
7509     int newx, newy;
7510
7511     Moving2Blocked(x, y, &newx, &newy);
7512
7513     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7514       return;
7515
7516     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7517         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7518     {
7519       WasJustMoving[x][y] = 0;
7520       CheckCollision[x][y] = 0;
7521
7522       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7523
7524       if (Feld[x][y] != element)        /* element has changed */
7525         return;
7526     }
7527
7528     if (!MovDelay[x][y])        /* start new movement phase */
7529     {
7530       /* all objects that can change their move direction after each step
7531          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7532
7533       if (element != EL_YAMYAM &&
7534           element != EL_DARK_YAMYAM &&
7535           element != EL_PACMAN &&
7536           !(move_pattern & MV_ANY_DIRECTION) &&
7537           move_pattern != MV_TURNING_LEFT &&
7538           move_pattern != MV_TURNING_RIGHT &&
7539           move_pattern != MV_TURNING_LEFT_RIGHT &&
7540           move_pattern != MV_TURNING_RIGHT_LEFT &&
7541           move_pattern != MV_TURNING_RANDOM)
7542       {
7543         TurnRound(x, y);
7544
7545         if (MovDelay[x][y] && (element == EL_BUG ||
7546                                element == EL_SPACESHIP ||
7547                                element == EL_SP_SNIKSNAK ||
7548                                element == EL_SP_ELECTRON ||
7549                                element == EL_MOLE))
7550           TEST_DrawLevelField(x, y);
7551       }
7552     }
7553
7554     if (MovDelay[x][y])         /* wait some time before next movement */
7555     {
7556       MovDelay[x][y]--;
7557
7558       if (element == EL_ROBOT ||
7559           element == EL_YAMYAM ||
7560           element == EL_DARK_YAMYAM)
7561       {
7562         DrawLevelElementAnimationIfNeeded(x, y, element);
7563         PlayLevelSoundAction(x, y, ACTION_WAITING);
7564       }
7565       else if (element == EL_SP_ELECTRON)
7566         DrawLevelElementAnimationIfNeeded(x, y, element);
7567       else if (element == EL_DRAGON)
7568       {
7569         int i;
7570         int dir = MovDir[x][y];
7571         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7572         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7573         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7574                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7575                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7576                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7577         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7578
7579         GfxAction[x][y] = ACTION_ATTACKING;
7580
7581         if (IS_PLAYER(x, y))
7582           DrawPlayerField(x, y);
7583         else
7584           TEST_DrawLevelField(x, y);
7585
7586         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7587
7588         for (i = 1; i <= 3; i++)
7589         {
7590           int xx = x + i * dx;
7591           int yy = y + i * dy;
7592           int sx = SCREENX(xx);
7593           int sy = SCREENY(yy);
7594           int flame_graphic = graphic + (i - 1);
7595
7596           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7597             break;
7598
7599           if (MovDelay[x][y])
7600           {
7601             int flamed = MovingOrBlocked2Element(xx, yy);
7602
7603             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7604               Bang(xx, yy);
7605             else
7606               RemoveMovingField(xx, yy);
7607
7608             ChangeDelay[xx][yy] = 0;
7609
7610             Feld[xx][yy] = EL_FLAMES;
7611
7612             if (IN_SCR_FIELD(sx, sy))
7613             {
7614               TEST_DrawLevelFieldCrumbled(xx, yy);
7615               DrawGraphic(sx, sy, flame_graphic, frame);
7616             }
7617           }
7618           else
7619           {
7620             if (Feld[xx][yy] == EL_FLAMES)
7621               Feld[xx][yy] = EL_EMPTY;
7622             TEST_DrawLevelField(xx, yy);
7623           }
7624         }
7625       }
7626
7627       if (MovDelay[x][y])       /* element still has to wait some time */
7628       {
7629         PlayLevelSoundAction(x, y, ACTION_WAITING);
7630
7631         return;
7632       }
7633     }
7634
7635     /* now make next step */
7636
7637     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7638
7639     if (DONT_COLLIDE_WITH(element) &&
7640         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7641         !PLAYER_ENEMY_PROTECTED(newx, newy))
7642     {
7643       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7644
7645       return;
7646     }
7647
7648     else if (CAN_MOVE_INTO_ACID(element) &&
7649              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7650              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7651              (MovDir[x][y] == MV_DOWN ||
7652               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7653     {
7654       SplashAcid(newx, newy);
7655       Store[x][y] = EL_ACID;
7656     }
7657     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7658     {
7659       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7660           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7661           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7662           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7663       {
7664         RemoveField(x, y);
7665         TEST_DrawLevelField(x, y);
7666
7667         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7668         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7669           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7670
7671         local_player->friends_still_needed--;
7672         if (!local_player->friends_still_needed &&
7673             !local_player->GameOver && AllPlayersGone)
7674           PlayerWins(local_player);
7675
7676         return;
7677       }
7678       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7679       {
7680         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7681           TEST_DrawLevelField(newx, newy);
7682         else
7683           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7684       }
7685       else if (!IS_FREE(newx, newy))
7686       {
7687         GfxAction[x][y] = ACTION_WAITING;
7688
7689         if (IS_PLAYER(x, y))
7690           DrawPlayerField(x, y);
7691         else
7692           TEST_DrawLevelField(x, y);
7693
7694         return;
7695       }
7696     }
7697     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7698     {
7699       if (IS_FOOD_PIG(Feld[newx][newy]))
7700       {
7701         if (IS_MOVING(newx, newy))
7702           RemoveMovingField(newx, newy);
7703         else
7704         {
7705           Feld[newx][newy] = EL_EMPTY;
7706           TEST_DrawLevelField(newx, newy);
7707         }
7708
7709         PlayLevelSound(x, y, SND_PIG_DIGGING);
7710       }
7711       else if (!IS_FREE(newx, newy))
7712       {
7713         if (IS_PLAYER(x, y))
7714           DrawPlayerField(x, y);
7715         else
7716           TEST_DrawLevelField(x, y);
7717
7718         return;
7719       }
7720     }
7721     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7722     {
7723       if (Store[x][y] != EL_EMPTY)
7724       {
7725         boolean can_clone = FALSE;
7726         int xx, yy;
7727
7728         /* check if element to clone is still there */
7729         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7730         {
7731           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7732           {
7733             can_clone = TRUE;
7734
7735             break;
7736           }
7737         }
7738
7739         /* cannot clone or target field not free anymore -- do not clone */
7740         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7741           Store[x][y] = EL_EMPTY;
7742       }
7743
7744       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7745       {
7746         if (IS_MV_DIAGONAL(MovDir[x][y]))
7747         {
7748           int diagonal_move_dir = MovDir[x][y];
7749           int stored = Store[x][y];
7750           int change_delay = 8;
7751           int graphic;
7752
7753           /* android is moving diagonally */
7754
7755           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7756
7757           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7758           GfxElement[x][y] = EL_EMC_ANDROID;
7759           GfxAction[x][y] = ACTION_SHRINKING;
7760           GfxDir[x][y] = diagonal_move_dir;
7761           ChangeDelay[x][y] = change_delay;
7762
7763           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7764                                    GfxDir[x][y]);
7765
7766           DrawLevelGraphicAnimation(x, y, graphic);
7767           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7768
7769           if (Feld[newx][newy] == EL_ACID)
7770           {
7771             SplashAcid(newx, newy);
7772
7773             return;
7774           }
7775
7776           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7777
7778           Store[newx][newy] = EL_EMC_ANDROID;
7779           GfxElement[newx][newy] = EL_EMC_ANDROID;
7780           GfxAction[newx][newy] = ACTION_GROWING;
7781           GfxDir[newx][newy] = diagonal_move_dir;
7782           ChangeDelay[newx][newy] = change_delay;
7783
7784           graphic = el_act_dir2img(GfxElement[newx][newy],
7785                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7786
7787           DrawLevelGraphicAnimation(newx, newy, graphic);
7788           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7789
7790           return;
7791         }
7792         else
7793         {
7794           Feld[newx][newy] = EL_EMPTY;
7795           TEST_DrawLevelField(newx, newy);
7796
7797           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7798         }
7799       }
7800       else if (!IS_FREE(newx, newy))
7801       {
7802         return;
7803       }
7804     }
7805     else if (IS_CUSTOM_ELEMENT(element) &&
7806              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7807     {
7808       if (!DigFieldByCE(newx, newy, element))
7809         return;
7810
7811       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7812       {
7813         RunnerVisit[x][y] = FrameCounter;
7814         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7815       }
7816     }
7817     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7818     {
7819       if (!IS_FREE(newx, newy))
7820       {
7821         if (IS_PLAYER(x, y))
7822           DrawPlayerField(x, y);
7823         else
7824           TEST_DrawLevelField(x, y);
7825
7826         return;
7827       }
7828       else
7829       {
7830         boolean wanna_flame = !RND(10);
7831         int dx = newx - x, dy = newy - y;
7832         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7833         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7834         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7835                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7836         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7837                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7838
7839         if ((wanna_flame ||
7840              IS_CLASSIC_ENEMY(element1) ||
7841              IS_CLASSIC_ENEMY(element2)) &&
7842             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7843             element1 != EL_FLAMES && element2 != EL_FLAMES)
7844         {
7845           ResetGfxAnimation(x, y);
7846           GfxAction[x][y] = ACTION_ATTACKING;
7847
7848           if (IS_PLAYER(x, y))
7849             DrawPlayerField(x, y);
7850           else
7851             TEST_DrawLevelField(x, y);
7852
7853           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7854
7855           MovDelay[x][y] = 50;
7856
7857           Feld[newx][newy] = EL_FLAMES;
7858           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7859             Feld[newx1][newy1] = EL_FLAMES;
7860           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7861             Feld[newx2][newy2] = EL_FLAMES;
7862
7863           return;
7864         }
7865       }
7866     }
7867     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7868              Feld[newx][newy] == EL_DIAMOND)
7869     {
7870       if (IS_MOVING(newx, newy))
7871         RemoveMovingField(newx, newy);
7872       else
7873       {
7874         Feld[newx][newy] = EL_EMPTY;
7875         TEST_DrawLevelField(newx, newy);
7876       }
7877
7878       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7879     }
7880     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7881              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7882     {
7883       if (AmoebaNr[newx][newy])
7884       {
7885         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7886         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7887             Feld[newx][newy] == EL_BD_AMOEBA)
7888           AmoebaCnt[AmoebaNr[newx][newy]]--;
7889       }
7890
7891       if (IS_MOVING(newx, newy))
7892       {
7893         RemoveMovingField(newx, newy);
7894       }
7895       else
7896       {
7897         Feld[newx][newy] = EL_EMPTY;
7898         TEST_DrawLevelField(newx, newy);
7899       }
7900
7901       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7902     }
7903     else if ((element == EL_PACMAN || element == EL_MOLE)
7904              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7905     {
7906       if (AmoebaNr[newx][newy])
7907       {
7908         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7909         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7910             Feld[newx][newy] == EL_BD_AMOEBA)
7911           AmoebaCnt[AmoebaNr[newx][newy]]--;
7912       }
7913
7914       if (element == EL_MOLE)
7915       {
7916         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7917         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7918
7919         ResetGfxAnimation(x, y);
7920         GfxAction[x][y] = ACTION_DIGGING;
7921         TEST_DrawLevelField(x, y);
7922
7923         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7924
7925         return;                         /* wait for shrinking amoeba */
7926       }
7927       else      /* element == EL_PACMAN */
7928       {
7929         Feld[newx][newy] = EL_EMPTY;
7930         TEST_DrawLevelField(newx, newy);
7931         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7932       }
7933     }
7934     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7935              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7936               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7937     {
7938       /* wait for shrinking amoeba to completely disappear */
7939       return;
7940     }
7941     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7942     {
7943       /* object was running against a wall */
7944
7945       TurnRound(x, y);
7946
7947       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7948         DrawLevelElementAnimation(x, y, element);
7949
7950       if (DONT_TOUCH(element))
7951         TestIfBadThingTouchesPlayer(x, y);
7952
7953       return;
7954     }
7955
7956     InitMovingField(x, y, MovDir[x][y]);
7957
7958     PlayLevelSoundAction(x, y, ACTION_MOVING);
7959   }
7960
7961   if (MovDir[x][y])
7962     ContinueMoving(x, y);
7963 }
7964
7965 void ContinueMoving(int x, int y)
7966 {
7967   int element = Feld[x][y];
7968   struct ElementInfo *ei = &element_info[element];
7969   int direction = MovDir[x][y];
7970   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7971   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7972   int newx = x + dx, newy = y + dy;
7973   int stored = Store[x][y];
7974   int stored_new = Store[newx][newy];
7975   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7976   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7977   boolean last_line = (newy == lev_fieldy - 1);
7978
7979   MovPos[x][y] += getElementMoveStepsize(x, y);
7980
7981   if (pushed_by_player) /* special case: moving object pushed by player */
7982     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7983
7984   if (ABS(MovPos[x][y]) < TILEX)
7985   {
7986     TEST_DrawLevelField(x, y);
7987
7988     return;     /* element is still moving */
7989   }
7990
7991   /* element reached destination field */
7992
7993   Feld[x][y] = EL_EMPTY;
7994   Feld[newx][newy] = element;
7995   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7996
7997   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7998   {
7999     element = Feld[newx][newy] = EL_ACID;
8000   }
8001   else if (element == EL_MOLE)
8002   {
8003     Feld[x][y] = EL_SAND;
8004
8005     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8006   }
8007   else if (element == EL_QUICKSAND_FILLING)
8008   {
8009     element = Feld[newx][newy] = get_next_element(element);
8010     Store[newx][newy] = Store[x][y];
8011   }
8012   else if (element == EL_QUICKSAND_EMPTYING)
8013   {
8014     Feld[x][y] = get_next_element(element);
8015     element = Feld[newx][newy] = Store[x][y];
8016   }
8017   else if (element == EL_QUICKSAND_FAST_FILLING)
8018   {
8019     element = Feld[newx][newy] = get_next_element(element);
8020     Store[newx][newy] = Store[x][y];
8021   }
8022   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8023   {
8024     Feld[x][y] = get_next_element(element);
8025     element = Feld[newx][newy] = Store[x][y];
8026   }
8027   else if (element == EL_MAGIC_WALL_FILLING)
8028   {
8029     element = Feld[newx][newy] = get_next_element(element);
8030     if (!game.magic_wall_active)
8031       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8032     Store[newx][newy] = Store[x][y];
8033   }
8034   else if (element == EL_MAGIC_WALL_EMPTYING)
8035   {
8036     Feld[x][y] = get_next_element(element);
8037     if (!game.magic_wall_active)
8038       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8039     element = Feld[newx][newy] = Store[x][y];
8040
8041     InitField(newx, newy, FALSE);
8042   }
8043   else if (element == EL_BD_MAGIC_WALL_FILLING)
8044   {
8045     element = Feld[newx][newy] = get_next_element(element);
8046     if (!game.magic_wall_active)
8047       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8048     Store[newx][newy] = Store[x][y];
8049   }
8050   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8051   {
8052     Feld[x][y] = get_next_element(element);
8053     if (!game.magic_wall_active)
8054       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8055     element = Feld[newx][newy] = Store[x][y];
8056
8057     InitField(newx, newy, FALSE);
8058   }
8059   else if (element == EL_DC_MAGIC_WALL_FILLING)
8060   {
8061     element = Feld[newx][newy] = get_next_element(element);
8062     if (!game.magic_wall_active)
8063       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8064     Store[newx][newy] = Store[x][y];
8065   }
8066   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8067   {
8068     Feld[x][y] = get_next_element(element);
8069     if (!game.magic_wall_active)
8070       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8071     element = Feld[newx][newy] = Store[x][y];
8072
8073     InitField(newx, newy, FALSE);
8074   }
8075   else if (element == EL_AMOEBA_DROPPING)
8076   {
8077     Feld[x][y] = get_next_element(element);
8078     element = Feld[newx][newy] = Store[x][y];
8079   }
8080   else if (element == EL_SOKOBAN_OBJECT)
8081   {
8082     if (Back[x][y])
8083       Feld[x][y] = Back[x][y];
8084
8085     if (Back[newx][newy])
8086       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8087
8088     Back[x][y] = Back[newx][newy] = 0;
8089   }
8090
8091   Store[x][y] = EL_EMPTY;
8092   MovPos[x][y] = 0;
8093   MovDir[x][y] = 0;
8094   MovDelay[x][y] = 0;
8095
8096   MovDelay[newx][newy] = 0;
8097
8098   if (CAN_CHANGE_OR_HAS_ACTION(element))
8099   {
8100     /* copy element change control values to new field */
8101     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8102     ChangePage[newx][newy]  = ChangePage[x][y];
8103     ChangeCount[newx][newy] = ChangeCount[x][y];
8104     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8105   }
8106
8107   CustomValue[newx][newy] = CustomValue[x][y];
8108
8109   ChangeDelay[x][y] = 0;
8110   ChangePage[x][y] = -1;
8111   ChangeCount[x][y] = 0;
8112   ChangeEvent[x][y] = -1;
8113
8114   CustomValue[x][y] = 0;
8115
8116   /* copy animation control values to new field */
8117   GfxFrame[newx][newy]  = GfxFrame[x][y];
8118   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8119   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8120   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8121
8122   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8123
8124   /* some elements can leave other elements behind after moving */
8125   if (ei->move_leave_element != EL_EMPTY &&
8126       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8127       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8128   {
8129     int move_leave_element = ei->move_leave_element;
8130
8131     /* this makes it possible to leave the removed element again */
8132     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8133       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8134
8135     Feld[x][y] = move_leave_element;
8136
8137     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8138       MovDir[x][y] = direction;
8139
8140     InitField(x, y, FALSE);
8141
8142     if (GFX_CRUMBLED(Feld[x][y]))
8143       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8144
8145     if (ELEM_IS_PLAYER(move_leave_element))
8146       RelocatePlayer(x, y, move_leave_element);
8147   }
8148
8149   /* do this after checking for left-behind element */
8150   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8151
8152   if (!CAN_MOVE(element) ||
8153       (CAN_FALL(element) && direction == MV_DOWN &&
8154        (element == EL_SPRING ||
8155         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8156         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8157     GfxDir[x][y] = MovDir[newx][newy] = 0;
8158
8159   TEST_DrawLevelField(x, y);
8160   TEST_DrawLevelField(newx, newy);
8161
8162   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8163
8164   /* prevent pushed element from moving on in pushed direction */
8165   if (pushed_by_player && CAN_MOVE(element) &&
8166       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8167       !(element_info[element].move_pattern & direction))
8168     TurnRound(newx, newy);
8169
8170   /* prevent elements on conveyor belt from moving on in last direction */
8171   if (pushed_by_conveyor && CAN_FALL(element) &&
8172       direction & MV_HORIZONTAL)
8173     MovDir[newx][newy] = 0;
8174
8175   if (!pushed_by_player)
8176   {
8177     int nextx = newx + dx, nexty = newy + dy;
8178     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8179
8180     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8181
8182     if (CAN_FALL(element) && direction == MV_DOWN)
8183       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8184
8185     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8186       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8187
8188     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8189       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8190   }
8191
8192   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8193   {
8194     TestIfBadThingTouchesPlayer(newx, newy);
8195     TestIfBadThingTouchesFriend(newx, newy);
8196
8197     if (!IS_CUSTOM_ELEMENT(element))
8198       TestIfBadThingTouchesOtherBadThing(newx, newy);
8199   }
8200   else if (element == EL_PENGUIN)
8201     TestIfFriendTouchesBadThing(newx, newy);
8202
8203   if (DONT_GET_HIT_BY(element))
8204   {
8205     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8206   }
8207
8208   /* give the player one last chance (one more frame) to move away */
8209   if (CAN_FALL(element) && direction == MV_DOWN &&
8210       (last_line || (!IS_FREE(x, newy + 1) &&
8211                      (!IS_PLAYER(x, newy + 1) ||
8212                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8213     Impact(x, newy);
8214
8215   if (pushed_by_player && !game.use_change_when_pushing_bug)
8216   {
8217     int push_side = MV_DIR_OPPOSITE(direction);
8218     struct PlayerInfo *player = PLAYERINFO(x, y);
8219
8220     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8221                                player->index_bit, push_side);
8222     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8223                                         player->index_bit, push_side);
8224   }
8225
8226   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8227     MovDelay[newx][newy] = 1;
8228
8229   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8230
8231   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8232   TestIfElementHitsCustomElement(newx, newy, direction);
8233   TestIfPlayerTouchesCustomElement(newx, newy);
8234   TestIfElementTouchesCustomElement(newx, newy);
8235
8236   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8237       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8238     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8239                              MV_DIR_OPPOSITE(direction));
8240 }
8241
8242 int AmoebeNachbarNr(int ax, int ay)
8243 {
8244   int i;
8245   int element = Feld[ax][ay];
8246   int group_nr = 0;
8247   static int xy[4][2] =
8248   {
8249     { 0, -1 },
8250     { -1, 0 },
8251     { +1, 0 },
8252     { 0, +1 }
8253   };
8254
8255   for (i = 0; i < NUM_DIRECTIONS; i++)
8256   {
8257     int x = ax + xy[i][0];
8258     int y = ay + xy[i][1];
8259
8260     if (!IN_LEV_FIELD(x, y))
8261       continue;
8262
8263     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8264       group_nr = AmoebaNr[x][y];
8265   }
8266
8267   return group_nr;
8268 }
8269
8270 void AmoebenVereinigen(int ax, int ay)
8271 {
8272   int i, x, y, xx, yy;
8273   int new_group_nr = AmoebaNr[ax][ay];
8274   static int xy[4][2] =
8275   {
8276     { 0, -1 },
8277     { -1, 0 },
8278     { +1, 0 },
8279     { 0, +1 }
8280   };
8281
8282   if (new_group_nr == 0)
8283     return;
8284
8285   for (i = 0; i < NUM_DIRECTIONS; i++)
8286   {
8287     x = ax + xy[i][0];
8288     y = ay + xy[i][1];
8289
8290     if (!IN_LEV_FIELD(x, y))
8291       continue;
8292
8293     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8294          Feld[x][y] == EL_BD_AMOEBA ||
8295          Feld[x][y] == EL_AMOEBA_DEAD) &&
8296         AmoebaNr[x][y] != new_group_nr)
8297     {
8298       int old_group_nr = AmoebaNr[x][y];
8299
8300       if (old_group_nr == 0)
8301         return;
8302
8303       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8304       AmoebaCnt[old_group_nr] = 0;
8305       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8306       AmoebaCnt2[old_group_nr] = 0;
8307
8308       SCAN_PLAYFIELD(xx, yy)
8309       {
8310         if (AmoebaNr[xx][yy] == old_group_nr)
8311           AmoebaNr[xx][yy] = new_group_nr;
8312       }
8313     }
8314   }
8315 }
8316
8317 void AmoebeUmwandeln(int ax, int ay)
8318 {
8319   int i, x, y;
8320
8321   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8322   {
8323     int group_nr = AmoebaNr[ax][ay];
8324
8325 #ifdef DEBUG
8326     if (group_nr == 0)
8327     {
8328       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8329       printf("AmoebeUmwandeln(): This should never happen!\n");
8330       return;
8331     }
8332 #endif
8333
8334     SCAN_PLAYFIELD(x, y)
8335     {
8336       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8337       {
8338         AmoebaNr[x][y] = 0;
8339         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8340       }
8341     }
8342
8343     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8344                             SND_AMOEBA_TURNING_TO_GEM :
8345                             SND_AMOEBA_TURNING_TO_ROCK));
8346     Bang(ax, ay);
8347   }
8348   else
8349   {
8350     static int xy[4][2] =
8351     {
8352       { 0, -1 },
8353       { -1, 0 },
8354       { +1, 0 },
8355       { 0, +1 }
8356     };
8357
8358     for (i = 0; i < NUM_DIRECTIONS; i++)
8359     {
8360       x = ax + xy[i][0];
8361       y = ay + xy[i][1];
8362
8363       if (!IN_LEV_FIELD(x, y))
8364         continue;
8365
8366       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8367       {
8368         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8369                               SND_AMOEBA_TURNING_TO_GEM :
8370                               SND_AMOEBA_TURNING_TO_ROCK));
8371         Bang(x, y);
8372       }
8373     }
8374   }
8375 }
8376
8377 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8378 {
8379   int x, y;
8380   int group_nr = AmoebaNr[ax][ay];
8381   boolean done = FALSE;
8382
8383 #ifdef DEBUG
8384   if (group_nr == 0)
8385   {
8386     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8387     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8388     return;
8389   }
8390 #endif
8391
8392   SCAN_PLAYFIELD(x, y)
8393   {
8394     if (AmoebaNr[x][y] == group_nr &&
8395         (Feld[x][y] == EL_AMOEBA_DEAD ||
8396          Feld[x][y] == EL_BD_AMOEBA ||
8397          Feld[x][y] == EL_AMOEBA_GROWING))
8398     {
8399       AmoebaNr[x][y] = 0;
8400       Feld[x][y] = new_element;
8401       InitField(x, y, FALSE);
8402       TEST_DrawLevelField(x, y);
8403       done = TRUE;
8404     }
8405   }
8406
8407   if (done)
8408     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8409                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8410                             SND_BD_AMOEBA_TURNING_TO_GEM));
8411 }
8412
8413 void AmoebeWaechst(int x, int y)
8414 {
8415   static unsigned int sound_delay = 0;
8416   static unsigned int sound_delay_value = 0;
8417
8418   if (!MovDelay[x][y])          /* start new growing cycle */
8419   {
8420     MovDelay[x][y] = 7;
8421
8422     if (DelayReached(&sound_delay, sound_delay_value))
8423     {
8424       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8425       sound_delay_value = 30;
8426     }
8427   }
8428
8429   if (MovDelay[x][y])           /* wait some time before growing bigger */
8430   {
8431     MovDelay[x][y]--;
8432     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8433     {
8434       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8435                                            6 - MovDelay[x][y]);
8436
8437       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8438     }
8439
8440     if (!MovDelay[x][y])
8441     {
8442       Feld[x][y] = Store[x][y];
8443       Store[x][y] = 0;
8444       TEST_DrawLevelField(x, y);
8445     }
8446   }
8447 }
8448
8449 void AmoebaDisappearing(int x, int y)
8450 {
8451   static unsigned int sound_delay = 0;
8452   static unsigned int sound_delay_value = 0;
8453
8454   if (!MovDelay[x][y])          /* start new shrinking cycle */
8455   {
8456     MovDelay[x][y] = 7;
8457
8458     if (DelayReached(&sound_delay, sound_delay_value))
8459       sound_delay_value = 30;
8460   }
8461
8462   if (MovDelay[x][y])           /* wait some time before shrinking */
8463   {
8464     MovDelay[x][y]--;
8465     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8466     {
8467       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8468                                            6 - MovDelay[x][y]);
8469
8470       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8471     }
8472
8473     if (!MovDelay[x][y])
8474     {
8475       Feld[x][y] = EL_EMPTY;
8476       TEST_DrawLevelField(x, y);
8477
8478       /* don't let mole enter this field in this cycle;
8479          (give priority to objects falling to this field from above) */
8480       Stop[x][y] = TRUE;
8481     }
8482   }
8483 }
8484
8485 void AmoebeAbleger(int ax, int ay)
8486 {
8487   int i;
8488   int element = Feld[ax][ay];
8489   int graphic = el2img(element);
8490   int newax = ax, neway = ay;
8491   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8492   static int xy[4][2] =
8493   {
8494     { 0, -1 },
8495     { -1, 0 },
8496     { +1, 0 },
8497     { 0, +1 }
8498   };
8499
8500   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8501   {
8502     Feld[ax][ay] = EL_AMOEBA_DEAD;
8503     TEST_DrawLevelField(ax, ay);
8504     return;
8505   }
8506
8507   if (IS_ANIMATED(graphic))
8508     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8509
8510   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8511     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8512
8513   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8514   {
8515     MovDelay[ax][ay]--;
8516     if (MovDelay[ax][ay])
8517       return;
8518   }
8519
8520   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8521   {
8522     int start = RND(4);
8523     int x = ax + xy[start][0];
8524     int y = ay + xy[start][1];
8525
8526     if (!IN_LEV_FIELD(x, y))
8527       return;
8528
8529     if (IS_FREE(x, y) ||
8530         CAN_GROW_INTO(Feld[x][y]) ||
8531         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8532         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8533     {
8534       newax = x;
8535       neway = y;
8536     }
8537
8538     if (newax == ax && neway == ay)
8539       return;
8540   }
8541   else                          /* normal or "filled" (BD style) amoeba */
8542   {
8543     int start = RND(4);
8544     boolean waiting_for_player = FALSE;
8545
8546     for (i = 0; i < NUM_DIRECTIONS; i++)
8547     {
8548       int j = (start + i) % 4;
8549       int x = ax + xy[j][0];
8550       int y = ay + xy[j][1];
8551
8552       if (!IN_LEV_FIELD(x, y))
8553         continue;
8554
8555       if (IS_FREE(x, y) ||
8556           CAN_GROW_INTO(Feld[x][y]) ||
8557           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8558           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8559       {
8560         newax = x;
8561         neway = y;
8562         break;
8563       }
8564       else if (IS_PLAYER(x, y))
8565         waiting_for_player = TRUE;
8566     }
8567
8568     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8569     {
8570       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8571       {
8572         Feld[ax][ay] = EL_AMOEBA_DEAD;
8573         TEST_DrawLevelField(ax, ay);
8574         AmoebaCnt[AmoebaNr[ax][ay]]--;
8575
8576         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8577         {
8578           if (element == EL_AMOEBA_FULL)
8579             AmoebeUmwandeln(ax, ay);
8580           else if (element == EL_BD_AMOEBA)
8581             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8582         }
8583       }
8584       return;
8585     }
8586     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8587     {
8588       /* amoeba gets larger by growing in some direction */
8589
8590       int new_group_nr = AmoebaNr[ax][ay];
8591
8592 #ifdef DEBUG
8593   if (new_group_nr == 0)
8594   {
8595     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8596     printf("AmoebeAbleger(): This should never happen!\n");
8597     return;
8598   }
8599 #endif
8600
8601       AmoebaNr[newax][neway] = new_group_nr;
8602       AmoebaCnt[new_group_nr]++;
8603       AmoebaCnt2[new_group_nr]++;
8604
8605       /* if amoeba touches other amoeba(s) after growing, unify them */
8606       AmoebenVereinigen(newax, neway);
8607
8608       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8609       {
8610         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8611         return;
8612       }
8613     }
8614   }
8615
8616   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8617       (neway == lev_fieldy - 1 && newax != ax))
8618   {
8619     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8620     Store[newax][neway] = element;
8621   }
8622   else if (neway == ay || element == EL_EMC_DRIPPER)
8623   {
8624     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8625
8626     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8627   }
8628   else
8629   {
8630     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8631     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8632     Store[ax][ay] = EL_AMOEBA_DROP;
8633     ContinueMoving(ax, ay);
8634     return;
8635   }
8636
8637   TEST_DrawLevelField(newax, neway);
8638 }
8639
8640 void Life(int ax, int ay)
8641 {
8642   int x1, y1, x2, y2;
8643   int life_time = 40;
8644   int element = Feld[ax][ay];
8645   int graphic = el2img(element);
8646   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8647                          level.biomaze);
8648   boolean changed = FALSE;
8649
8650   if (IS_ANIMATED(graphic))
8651     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8652
8653   if (Stop[ax][ay])
8654     return;
8655
8656   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8657     MovDelay[ax][ay] = life_time;
8658
8659   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8660   {
8661     MovDelay[ax][ay]--;
8662     if (MovDelay[ax][ay])
8663       return;
8664   }
8665
8666   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8667   {
8668     int xx = ax+x1, yy = ay+y1;
8669     int nachbarn = 0;
8670
8671     if (!IN_LEV_FIELD(xx, yy))
8672       continue;
8673
8674     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8675     {
8676       int x = xx+x2, y = yy+y2;
8677
8678       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8679         continue;
8680
8681       if (((Feld[x][y] == element ||
8682             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8683            !Stop[x][y]) ||
8684           (IS_FREE(x, y) && Stop[x][y]))
8685         nachbarn++;
8686     }
8687
8688     if (xx == ax && yy == ay)           /* field in the middle */
8689     {
8690       if (nachbarn < life_parameter[0] ||
8691           nachbarn > life_parameter[1])
8692       {
8693         Feld[xx][yy] = EL_EMPTY;
8694         if (!Stop[xx][yy])
8695           TEST_DrawLevelField(xx, yy);
8696         Stop[xx][yy] = TRUE;
8697         changed = TRUE;
8698       }
8699     }
8700     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8701     {                                   /* free border field */
8702       if (nachbarn >= life_parameter[2] &&
8703           nachbarn <= life_parameter[3])
8704       {
8705         Feld[xx][yy] = element;
8706         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8707         if (!Stop[xx][yy])
8708           TEST_DrawLevelField(xx, yy);
8709         Stop[xx][yy] = TRUE;
8710         changed = TRUE;
8711       }
8712     }
8713   }
8714
8715   if (changed)
8716     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8717                    SND_GAME_OF_LIFE_GROWING);
8718 }
8719
8720 static void InitRobotWheel(int x, int y)
8721 {
8722   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8723 }
8724
8725 static void RunRobotWheel(int x, int y)
8726 {
8727   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8728 }
8729
8730 static void StopRobotWheel(int x, int y)
8731 {
8732   if (ZX == x && ZY == y)
8733   {
8734     ZX = ZY = -1;
8735
8736     game.robot_wheel_active = FALSE;
8737   }
8738 }
8739
8740 static void InitTimegateWheel(int x, int y)
8741 {
8742   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8743 }
8744
8745 static void RunTimegateWheel(int x, int y)
8746 {
8747   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8748 }
8749
8750 static void InitMagicBallDelay(int x, int y)
8751 {
8752   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8753 }
8754
8755 static void ActivateMagicBall(int bx, int by)
8756 {
8757   int x, y;
8758
8759   if (level.ball_random)
8760   {
8761     int pos_border = RND(8);    /* select one of the eight border elements */
8762     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8763     int xx = pos_content % 3;
8764     int yy = pos_content / 3;
8765
8766     x = bx - 1 + xx;
8767     y = by - 1 + yy;
8768
8769     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8770       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8771   }
8772   else
8773   {
8774     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8775     {
8776       int xx = x - bx + 1;
8777       int yy = y - by + 1;
8778
8779       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8780         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8781     }
8782   }
8783
8784   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8785 }
8786
8787 void CheckExit(int x, int y)
8788 {
8789   if (local_player->gems_still_needed > 0 ||
8790       local_player->sokobanfields_still_needed > 0 ||
8791       local_player->lights_still_needed > 0)
8792   {
8793     int element = Feld[x][y];
8794     int graphic = el2img(element);
8795
8796     if (IS_ANIMATED(graphic))
8797       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8798
8799     return;
8800   }
8801
8802   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8803     return;
8804
8805   Feld[x][y] = EL_EXIT_OPENING;
8806
8807   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8808 }
8809
8810 void CheckExitEM(int x, int y)
8811 {
8812   if (local_player->gems_still_needed > 0 ||
8813       local_player->sokobanfields_still_needed > 0 ||
8814       local_player->lights_still_needed > 0)
8815   {
8816     int element = Feld[x][y];
8817     int graphic = el2img(element);
8818
8819     if (IS_ANIMATED(graphic))
8820       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8821
8822     return;
8823   }
8824
8825   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8826     return;
8827
8828   Feld[x][y] = EL_EM_EXIT_OPENING;
8829
8830   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8831 }
8832
8833 void CheckExitSteel(int x, int y)
8834 {
8835   if (local_player->gems_still_needed > 0 ||
8836       local_player->sokobanfields_still_needed > 0 ||
8837       local_player->lights_still_needed > 0)
8838   {
8839     int element = Feld[x][y];
8840     int graphic = el2img(element);
8841
8842     if (IS_ANIMATED(graphic))
8843       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8844
8845     return;
8846   }
8847
8848   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8849     return;
8850
8851   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8852
8853   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8854 }
8855
8856 void CheckExitSteelEM(int x, int y)
8857 {
8858   if (local_player->gems_still_needed > 0 ||
8859       local_player->sokobanfields_still_needed > 0 ||
8860       local_player->lights_still_needed > 0)
8861   {
8862     int element = Feld[x][y];
8863     int graphic = el2img(element);
8864
8865     if (IS_ANIMATED(graphic))
8866       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8867
8868     return;
8869   }
8870
8871   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8872     return;
8873
8874   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8875
8876   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8877 }
8878
8879 void CheckExitSP(int x, int y)
8880 {
8881   if (local_player->gems_still_needed > 0)
8882   {
8883     int element = Feld[x][y];
8884     int graphic = el2img(element);
8885
8886     if (IS_ANIMATED(graphic))
8887       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8888
8889     return;
8890   }
8891
8892   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8893     return;
8894
8895   Feld[x][y] = EL_SP_EXIT_OPENING;
8896
8897   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8898 }
8899
8900 static void CloseAllOpenTimegates()
8901 {
8902   int x, y;
8903
8904   SCAN_PLAYFIELD(x, y)
8905   {
8906     int element = Feld[x][y];
8907
8908     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8909     {
8910       Feld[x][y] = EL_TIMEGATE_CLOSING;
8911
8912       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8913     }
8914   }
8915 }
8916
8917 void DrawTwinkleOnField(int x, int y)
8918 {
8919   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8920     return;
8921
8922   if (Feld[x][y] == EL_BD_DIAMOND)
8923     return;
8924
8925   if (MovDelay[x][y] == 0)      /* next animation frame */
8926     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8927
8928   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8929   {
8930     MovDelay[x][y]--;
8931
8932     DrawLevelElementAnimation(x, y, Feld[x][y]);
8933
8934     if (MovDelay[x][y] != 0)
8935     {
8936       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8937                                            10 - MovDelay[x][y]);
8938
8939       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8940     }
8941   }
8942 }
8943
8944 void MauerWaechst(int x, int y)
8945 {
8946   int delay = 6;
8947
8948   if (!MovDelay[x][y])          /* next animation frame */
8949     MovDelay[x][y] = 3 * delay;
8950
8951   if (MovDelay[x][y])           /* wait some time before next frame */
8952   {
8953     MovDelay[x][y]--;
8954
8955     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8956     {
8957       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8958       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8959
8960       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8961     }
8962
8963     if (!MovDelay[x][y])
8964     {
8965       if (MovDir[x][y] == MV_LEFT)
8966       {
8967         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8968           TEST_DrawLevelField(x - 1, y);
8969       }
8970       else if (MovDir[x][y] == MV_RIGHT)
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_UP)
8976       {
8977         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8978           TEST_DrawLevelField(x, y - 1);
8979       }
8980       else
8981       {
8982         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8983           TEST_DrawLevelField(x, y + 1);
8984       }
8985
8986       Feld[x][y] = Store[x][y];
8987       Store[x][y] = 0;
8988       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8989       TEST_DrawLevelField(x, y);
8990     }
8991   }
8992 }
8993
8994 void MauerAbleger(int ax, int ay)
8995 {
8996   int element = Feld[ax][ay];
8997   int graphic = el2img(element);
8998   boolean oben_frei = FALSE, unten_frei = FALSE;
8999   boolean links_frei = FALSE, rechts_frei = FALSE;
9000   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9001   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9002   boolean new_wall = FALSE;
9003
9004   if (IS_ANIMATED(graphic))
9005     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9006
9007   if (!MovDelay[ax][ay])        /* start building new wall */
9008     MovDelay[ax][ay] = 6;
9009
9010   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9011   {
9012     MovDelay[ax][ay]--;
9013     if (MovDelay[ax][ay])
9014       return;
9015   }
9016
9017   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9018     oben_frei = TRUE;
9019   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9020     unten_frei = TRUE;
9021   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9022     links_frei = TRUE;
9023   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9024     rechts_frei = TRUE;
9025
9026   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9027       element == EL_EXPANDABLE_WALL_ANY)
9028   {
9029     if (oben_frei)
9030     {
9031       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9032       Store[ax][ay-1] = element;
9033       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9034       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9035         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9036                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9037       new_wall = TRUE;
9038     }
9039     if (unten_frei)
9040     {
9041       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9042       Store[ax][ay+1] = element;
9043       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9044       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9045         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9046                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9047       new_wall = TRUE;
9048     }
9049   }
9050
9051   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9052       element == EL_EXPANDABLE_WALL_ANY ||
9053       element == EL_EXPANDABLE_WALL ||
9054       element == EL_BD_EXPANDABLE_WALL)
9055   {
9056     if (links_frei)
9057     {
9058       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9059       Store[ax-1][ay] = element;
9060       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9061       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9062         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9063                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9064       new_wall = TRUE;
9065     }
9066
9067     if (rechts_frei)
9068     {
9069       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9070       Store[ax+1][ay] = element;
9071       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9072       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9073         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9074                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9075       new_wall = TRUE;
9076     }
9077   }
9078
9079   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9080     TEST_DrawLevelField(ax, ay);
9081
9082   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9083     oben_massiv = TRUE;
9084   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9085     unten_massiv = TRUE;
9086   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9087     links_massiv = TRUE;
9088   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9089     rechts_massiv = TRUE;
9090
9091   if (((oben_massiv && unten_massiv) ||
9092        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9093        element == EL_EXPANDABLE_WALL) &&
9094       ((links_massiv && rechts_massiv) ||
9095        element == EL_EXPANDABLE_WALL_VERTICAL))
9096     Feld[ax][ay] = EL_WALL;
9097
9098   if (new_wall)
9099     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9100 }
9101
9102 void MauerAblegerStahl(int ax, int ay)
9103 {
9104   int element = Feld[ax][ay];
9105   int graphic = el2img(element);
9106   boolean oben_frei = FALSE, unten_frei = FALSE;
9107   boolean links_frei = FALSE, rechts_frei = FALSE;
9108   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9109   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9110   boolean new_wall = FALSE;
9111
9112   if (IS_ANIMATED(graphic))
9113     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9114
9115   if (!MovDelay[ax][ay])        /* start building new wall */
9116     MovDelay[ax][ay] = 6;
9117
9118   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9119   {
9120     MovDelay[ax][ay]--;
9121     if (MovDelay[ax][ay])
9122       return;
9123   }
9124
9125   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9126     oben_frei = TRUE;
9127   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9128     unten_frei = TRUE;
9129   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9130     links_frei = TRUE;
9131   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9132     rechts_frei = TRUE;
9133
9134   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9135       element == EL_EXPANDABLE_STEELWALL_ANY)
9136   {
9137     if (oben_frei)
9138     {
9139       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9140       Store[ax][ay-1] = element;
9141       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9142       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9143         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9144                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9145       new_wall = TRUE;
9146     }
9147     if (unten_frei)
9148     {
9149       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9150       Store[ax][ay+1] = element;
9151       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9152       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9153         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9154                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9155       new_wall = TRUE;
9156     }
9157   }
9158
9159   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9160       element == EL_EXPANDABLE_STEELWALL_ANY)
9161   {
9162     if (links_frei)
9163     {
9164       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9165       Store[ax-1][ay] = element;
9166       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9167       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9168         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9169                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9170       new_wall = TRUE;
9171     }
9172
9173     if (rechts_frei)
9174     {
9175       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9176       Store[ax+1][ay] = element;
9177       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9178       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9179         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9180                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9181       new_wall = TRUE;
9182     }
9183   }
9184
9185   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9186     oben_massiv = TRUE;
9187   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9188     unten_massiv = TRUE;
9189   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9190     links_massiv = TRUE;
9191   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9192     rechts_massiv = TRUE;
9193
9194   if (((oben_massiv && unten_massiv) ||
9195        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9196       ((links_massiv && rechts_massiv) ||
9197        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9198     Feld[ax][ay] = EL_STEELWALL;
9199
9200   if (new_wall)
9201     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9202 }
9203
9204 void CheckForDragon(int x, int y)
9205 {
9206   int i, j;
9207   boolean dragon_found = FALSE;
9208   static int xy[4][2] =
9209   {
9210     { 0, -1 },
9211     { -1, 0 },
9212     { +1, 0 },
9213     { 0, +1 }
9214   };
9215
9216   for (i = 0; i < NUM_DIRECTIONS; i++)
9217   {
9218     for (j = 0; j < 4; j++)
9219     {
9220       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9221
9222       if (IN_LEV_FIELD(xx, yy) &&
9223           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9224       {
9225         if (Feld[xx][yy] == EL_DRAGON)
9226           dragon_found = TRUE;
9227       }
9228       else
9229         break;
9230     }
9231   }
9232
9233   if (!dragon_found)
9234   {
9235     for (i = 0; i < NUM_DIRECTIONS; i++)
9236     {
9237       for (j = 0; j < 3; j++)
9238       {
9239         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9240   
9241         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9242         {
9243           Feld[xx][yy] = EL_EMPTY;
9244           TEST_DrawLevelField(xx, yy);
9245         }
9246         else
9247           break;
9248       }
9249     }
9250   }
9251 }
9252
9253 static void InitBuggyBase(int x, int y)
9254 {
9255   int element = Feld[x][y];
9256   int activating_delay = FRAMES_PER_SECOND / 4;
9257
9258   ChangeDelay[x][y] =
9259     (element == EL_SP_BUGGY_BASE ?
9260      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9261      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9262      activating_delay :
9263      element == EL_SP_BUGGY_BASE_ACTIVE ?
9264      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9265 }
9266
9267 static void WarnBuggyBase(int x, int y)
9268 {
9269   int i;
9270   static int xy[4][2] =
9271   {
9272     { 0, -1 },
9273     { -1, 0 },
9274     { +1, 0 },
9275     { 0, +1 }
9276   };
9277
9278   for (i = 0; i < NUM_DIRECTIONS; i++)
9279   {
9280     int xx = x + xy[i][0];
9281     int yy = y + xy[i][1];
9282
9283     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9284     {
9285       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9286
9287       break;
9288     }
9289   }
9290 }
9291
9292 static void InitTrap(int x, int y)
9293 {
9294   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9295 }
9296
9297 static void ActivateTrap(int x, int y)
9298 {
9299   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9300 }
9301
9302 static void ChangeActiveTrap(int x, int y)
9303 {
9304   int graphic = IMG_TRAP_ACTIVE;
9305
9306   /* if new animation frame was drawn, correct crumbled sand border */
9307   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9308     TEST_DrawLevelFieldCrumbled(x, y);
9309 }
9310
9311 static int getSpecialActionElement(int element, int number, int base_element)
9312 {
9313   return (element != EL_EMPTY ? element :
9314           number != -1 ? base_element + number - 1 :
9315           EL_EMPTY);
9316 }
9317
9318 static int getModifiedActionNumber(int value_old, int operator, int operand,
9319                                    int value_min, int value_max)
9320 {
9321   int value_new = (operator == CA_MODE_SET      ? operand :
9322                    operator == CA_MODE_ADD      ? value_old + operand :
9323                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9324                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9325                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9326                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9327                    value_old);
9328
9329   return (value_new < value_min ? value_min :
9330           value_new > value_max ? value_max :
9331           value_new);
9332 }
9333
9334 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9335 {
9336   struct ElementInfo *ei = &element_info[element];
9337   struct ElementChangeInfo *change = &ei->change_page[page];
9338   int target_element = change->target_element;
9339   int action_type = change->action_type;
9340   int action_mode = change->action_mode;
9341   int action_arg = change->action_arg;
9342   int action_element = change->action_element;
9343   int i;
9344
9345   if (!change->has_action)
9346     return;
9347
9348   /* ---------- determine action paramater values -------------------------- */
9349
9350   int level_time_value =
9351     (level.time > 0 ? TimeLeft :
9352      TimePlayed);
9353
9354   int action_arg_element_raw =
9355     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9356      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9357      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9358      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9359      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9360      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9361      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9362      EL_EMPTY);
9363   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9364
9365   int action_arg_direction =
9366     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9367      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9368      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9369      change->actual_trigger_side :
9370      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9371      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9372      MV_NONE);
9373
9374   int action_arg_number_min =
9375     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9376      CA_ARG_MIN);
9377
9378   int action_arg_number_max =
9379     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9380      action_type == CA_SET_LEVEL_GEMS ? 999 :
9381      action_type == CA_SET_LEVEL_TIME ? 9999 :
9382      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9383      action_type == CA_SET_CE_VALUE ? 9999 :
9384      action_type == CA_SET_CE_SCORE ? 9999 :
9385      CA_ARG_MAX);
9386
9387   int action_arg_number_reset =
9388     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9389      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9390      action_type == CA_SET_LEVEL_TIME ? level.time :
9391      action_type == CA_SET_LEVEL_SCORE ? 0 :
9392      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9393      action_type == CA_SET_CE_SCORE ? 0 :
9394      0);
9395
9396   int action_arg_number =
9397     (action_arg <= CA_ARG_MAX ? action_arg :
9398      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9399      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9400      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9401      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9402      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9403      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9404      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9405      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9406      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9407      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9408      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9409      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9410      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9411      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9412      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9413      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9414      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9415      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9416      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9417      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9418      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9419      -1);
9420
9421   int action_arg_number_old =
9422     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9423      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9424      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9425      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9426      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9427      0);
9428
9429   int action_arg_number_new =
9430     getModifiedActionNumber(action_arg_number_old,
9431                             action_mode, action_arg_number,
9432                             action_arg_number_min, action_arg_number_max);
9433
9434   int trigger_player_bits =
9435     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9436      change->actual_trigger_player_bits : change->trigger_player);
9437
9438   int action_arg_player_bits =
9439     (action_arg >= CA_ARG_PLAYER_1 &&
9440      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9441      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9442      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9443      PLAYER_BITS_ANY);
9444
9445   /* ---------- execute action  -------------------------------------------- */
9446
9447   switch (action_type)
9448   {
9449     case CA_NO_ACTION:
9450     {
9451       return;
9452     }
9453
9454     /* ---------- level actions  ------------------------------------------- */
9455
9456     case CA_RESTART_LEVEL:
9457     {
9458       game.restart_level = TRUE;
9459
9460       break;
9461     }
9462
9463     case CA_SHOW_ENVELOPE:
9464     {
9465       int element = getSpecialActionElement(action_arg_element,
9466                                             action_arg_number, EL_ENVELOPE_1);
9467
9468       if (IS_ENVELOPE(element))
9469         local_player->show_envelope = element;
9470
9471       break;
9472     }
9473
9474     case CA_SET_LEVEL_TIME:
9475     {
9476       if (level.time > 0)       /* only modify limited time value */
9477       {
9478         TimeLeft = action_arg_number_new;
9479
9480         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9481
9482         DisplayGameControlValues();
9483
9484         if (!TimeLeft && setup.time_limit)
9485           for (i = 0; i < MAX_PLAYERS; i++)
9486             KillPlayer(&stored_player[i]);
9487       }
9488
9489       break;
9490     }
9491
9492     case CA_SET_LEVEL_SCORE:
9493     {
9494       local_player->score = action_arg_number_new;
9495
9496       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9497
9498       DisplayGameControlValues();
9499
9500       break;
9501     }
9502
9503     case CA_SET_LEVEL_GEMS:
9504     {
9505       local_player->gems_still_needed = action_arg_number_new;
9506
9507       game.snapshot.collected_item = TRUE;
9508
9509       game_panel_controls[GAME_PANEL_GEMS].value =
9510         local_player->gems_still_needed;
9511
9512       DisplayGameControlValues();
9513
9514       break;
9515     }
9516
9517     case CA_SET_LEVEL_WIND:
9518     {
9519       game.wind_direction = action_arg_direction;
9520
9521       break;
9522     }
9523
9524     case CA_SET_LEVEL_RANDOM_SEED:
9525     {
9526       /* ensure that setting a new random seed while playing is predictable */
9527       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9528
9529       break;
9530     }
9531
9532     /* ---------- player actions  ------------------------------------------ */
9533
9534     case CA_MOVE_PLAYER:
9535     {
9536       /* automatically move to the next field in specified direction */
9537       for (i = 0; i < MAX_PLAYERS; i++)
9538         if (trigger_player_bits & (1 << i))
9539           stored_player[i].programmed_action = action_arg_direction;
9540
9541       break;
9542     }
9543
9544     case CA_EXIT_PLAYER:
9545     {
9546       for (i = 0; i < MAX_PLAYERS; i++)
9547         if (action_arg_player_bits & (1 << i))
9548           PlayerWins(&stored_player[i]);
9549
9550       break;
9551     }
9552
9553     case CA_KILL_PLAYER:
9554     {
9555       for (i = 0; i < MAX_PLAYERS; i++)
9556         if (action_arg_player_bits & (1 << i))
9557           KillPlayer(&stored_player[i]);
9558
9559       break;
9560     }
9561
9562     case CA_SET_PLAYER_KEYS:
9563     {
9564       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9565       int element = getSpecialActionElement(action_arg_element,
9566                                             action_arg_number, EL_KEY_1);
9567
9568       if (IS_KEY(element))
9569       {
9570         for (i = 0; i < MAX_PLAYERS; i++)
9571         {
9572           if (trigger_player_bits & (1 << i))
9573           {
9574             stored_player[i].key[KEY_NR(element)] = key_state;
9575
9576             DrawGameDoorValues();
9577           }
9578         }
9579       }
9580
9581       break;
9582     }
9583
9584     case CA_SET_PLAYER_SPEED:
9585     {
9586       for (i = 0; i < MAX_PLAYERS; i++)
9587       {
9588         if (trigger_player_bits & (1 << i))
9589         {
9590           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9591
9592           if (action_arg == CA_ARG_SPEED_FASTER &&
9593               stored_player[i].cannot_move)
9594           {
9595             action_arg_number = STEPSIZE_VERY_SLOW;
9596           }
9597           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9598                    action_arg == CA_ARG_SPEED_FASTER)
9599           {
9600             action_arg_number = 2;
9601             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9602                            CA_MODE_MULTIPLY);
9603           }
9604           else if (action_arg == CA_ARG_NUMBER_RESET)
9605           {
9606             action_arg_number = level.initial_player_stepsize[i];
9607           }
9608
9609           move_stepsize =
9610             getModifiedActionNumber(move_stepsize,
9611                                     action_mode,
9612                                     action_arg_number,
9613                                     action_arg_number_min,
9614                                     action_arg_number_max);
9615
9616           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9617         }
9618       }
9619
9620       break;
9621     }
9622
9623     case CA_SET_PLAYER_SHIELD:
9624     {
9625       for (i = 0; i < MAX_PLAYERS; i++)
9626       {
9627         if (trigger_player_bits & (1 << i))
9628         {
9629           if (action_arg == CA_ARG_SHIELD_OFF)
9630           {
9631             stored_player[i].shield_normal_time_left = 0;
9632             stored_player[i].shield_deadly_time_left = 0;
9633           }
9634           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9635           {
9636             stored_player[i].shield_normal_time_left = 999999;
9637           }
9638           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9639           {
9640             stored_player[i].shield_normal_time_left = 999999;
9641             stored_player[i].shield_deadly_time_left = 999999;
9642           }
9643         }
9644       }
9645
9646       break;
9647     }
9648
9649     case CA_SET_PLAYER_GRAVITY:
9650     {
9651       for (i = 0; i < MAX_PLAYERS; i++)
9652       {
9653         if (trigger_player_bits & (1 << i))
9654         {
9655           stored_player[i].gravity =
9656             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9657              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9658              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9659              stored_player[i].gravity);
9660         }
9661       }
9662
9663       break;
9664     }
9665
9666     case CA_SET_PLAYER_ARTWORK:
9667     {
9668       for (i = 0; i < MAX_PLAYERS; i++)
9669       {
9670         if (trigger_player_bits & (1 << i))
9671         {
9672           int artwork_element = action_arg_element;
9673
9674           if (action_arg == CA_ARG_ELEMENT_RESET)
9675             artwork_element =
9676               (level.use_artwork_element[i] ? level.artwork_element[i] :
9677                stored_player[i].element_nr);
9678
9679           if (stored_player[i].artwork_element != artwork_element)
9680             stored_player[i].Frame = 0;
9681
9682           stored_player[i].artwork_element = artwork_element;
9683
9684           SetPlayerWaiting(&stored_player[i], FALSE);
9685
9686           /* set number of special actions for bored and sleeping animation */
9687           stored_player[i].num_special_action_bored =
9688             get_num_special_action(artwork_element,
9689                                    ACTION_BORING_1, ACTION_BORING_LAST);
9690           stored_player[i].num_special_action_sleeping =
9691             get_num_special_action(artwork_element,
9692                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9693         }
9694       }
9695
9696       break;
9697     }
9698
9699     case CA_SET_PLAYER_INVENTORY:
9700     {
9701       for (i = 0; i < MAX_PLAYERS; i++)
9702       {
9703         struct PlayerInfo *player = &stored_player[i];
9704         int j, k;
9705
9706         if (trigger_player_bits & (1 << i))
9707         {
9708           int inventory_element = action_arg_element;
9709
9710           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9711               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9712               action_arg == CA_ARG_ELEMENT_ACTION)
9713           {
9714             int element = inventory_element;
9715             int collect_count = element_info[element].collect_count_initial;
9716
9717             if (!IS_CUSTOM_ELEMENT(element))
9718               collect_count = 1;
9719
9720             if (collect_count == 0)
9721               player->inventory_infinite_element = element;
9722             else
9723               for (k = 0; k < collect_count; k++)
9724                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9725                   player->inventory_element[player->inventory_size++] =
9726                     element;
9727           }
9728           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9729                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9730                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9731           {
9732             if (player->inventory_infinite_element != EL_UNDEFINED &&
9733                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9734                                      action_arg_element_raw))
9735               player->inventory_infinite_element = EL_UNDEFINED;
9736
9737             for (k = 0, j = 0; j < player->inventory_size; j++)
9738             {
9739               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9740                                         action_arg_element_raw))
9741                 player->inventory_element[k++] = player->inventory_element[j];
9742             }
9743
9744             player->inventory_size = k;
9745           }
9746           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9747           {
9748             if (player->inventory_size > 0)
9749             {
9750               for (j = 0; j < player->inventory_size - 1; j++)
9751                 player->inventory_element[j] = player->inventory_element[j + 1];
9752
9753               player->inventory_size--;
9754             }
9755           }
9756           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9757           {
9758             if (player->inventory_size > 0)
9759               player->inventory_size--;
9760           }
9761           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9762           {
9763             player->inventory_infinite_element = EL_UNDEFINED;
9764             player->inventory_size = 0;
9765           }
9766           else if (action_arg == CA_ARG_INVENTORY_RESET)
9767           {
9768             player->inventory_infinite_element = EL_UNDEFINED;
9769             player->inventory_size = 0;
9770
9771             if (level.use_initial_inventory[i])
9772             {
9773               for (j = 0; j < level.initial_inventory_size[i]; j++)
9774               {
9775                 int element = level.initial_inventory_content[i][j];
9776                 int collect_count = element_info[element].collect_count_initial;
9777
9778                 if (!IS_CUSTOM_ELEMENT(element))
9779                   collect_count = 1;
9780
9781                 if (collect_count == 0)
9782                   player->inventory_infinite_element = element;
9783                 else
9784                   for (k = 0; k < collect_count; k++)
9785                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9786                       player->inventory_element[player->inventory_size++] =
9787                         element;
9788               }
9789             }
9790           }
9791         }
9792       }
9793
9794       break;
9795     }
9796
9797     /* ---------- CE actions  ---------------------------------------------- */
9798
9799     case CA_SET_CE_VALUE:
9800     {
9801       int last_ce_value = CustomValue[x][y];
9802
9803       CustomValue[x][y] = action_arg_number_new;
9804
9805       if (CustomValue[x][y] != last_ce_value)
9806       {
9807         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9808         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9809
9810         if (CustomValue[x][y] == 0)
9811         {
9812           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9813           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9814         }
9815       }
9816
9817       break;
9818     }
9819
9820     case CA_SET_CE_SCORE:
9821     {
9822       int last_ce_score = ei->collect_score;
9823
9824       ei->collect_score = action_arg_number_new;
9825
9826       if (ei->collect_score != last_ce_score)
9827       {
9828         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9829         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9830
9831         if (ei->collect_score == 0)
9832         {
9833           int xx, yy;
9834
9835           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9836           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9837
9838           /*
9839             This is a very special case that seems to be a mixture between
9840             CheckElementChange() and CheckTriggeredElementChange(): while
9841             the first one only affects single elements that are triggered
9842             directly, the second one affects multiple elements in the playfield
9843             that are triggered indirectly by another element. This is a third
9844             case: Changing the CE score always affects multiple identical CEs,
9845             so every affected CE must be checked, not only the single CE for
9846             which the CE score was changed in the first place (as every instance
9847             of that CE shares the same CE score, and therefore also can change)!
9848           */
9849           SCAN_PLAYFIELD(xx, yy)
9850           {
9851             if (Feld[xx][yy] == element)
9852               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9853                                  CE_SCORE_GETS_ZERO);
9854           }
9855         }
9856       }
9857
9858       break;
9859     }
9860
9861     case CA_SET_CE_ARTWORK:
9862     {
9863       int artwork_element = action_arg_element;
9864       boolean reset_frame = FALSE;
9865       int xx, yy;
9866
9867       if (action_arg == CA_ARG_ELEMENT_RESET)
9868         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9869                            element);
9870
9871       if (ei->gfx_element != artwork_element)
9872         reset_frame = TRUE;
9873
9874       ei->gfx_element = artwork_element;
9875
9876       SCAN_PLAYFIELD(xx, yy)
9877       {
9878         if (Feld[xx][yy] == element)
9879         {
9880           if (reset_frame)
9881           {
9882             ResetGfxAnimation(xx, yy);
9883             ResetRandomAnimationValue(xx, yy);
9884           }
9885
9886           TEST_DrawLevelField(xx, yy);
9887         }
9888       }
9889
9890       break;
9891     }
9892
9893     /* ---------- engine actions  ------------------------------------------ */
9894
9895     case CA_SET_ENGINE_SCAN_MODE:
9896     {
9897       InitPlayfieldScanMode(action_arg);
9898
9899       break;
9900     }
9901
9902     default:
9903       break;
9904   }
9905 }
9906
9907 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9908 {
9909   int old_element = Feld[x][y];
9910   int new_element = GetElementFromGroupElement(element);
9911   int previous_move_direction = MovDir[x][y];
9912   int last_ce_value = CustomValue[x][y];
9913   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9914   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9915   boolean add_player_onto_element = (new_element_is_player &&
9916                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9917                                      IS_WALKABLE(old_element));
9918
9919   if (!add_player_onto_element)
9920   {
9921     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9922       RemoveMovingField(x, y);
9923     else
9924       RemoveField(x, y);
9925
9926     Feld[x][y] = new_element;
9927
9928     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9929       MovDir[x][y] = previous_move_direction;
9930
9931     if (element_info[new_element].use_last_ce_value)
9932       CustomValue[x][y] = last_ce_value;
9933
9934     InitField_WithBug1(x, y, FALSE);
9935
9936     new_element = Feld[x][y];   /* element may have changed */
9937
9938     ResetGfxAnimation(x, y);
9939     ResetRandomAnimationValue(x, y);
9940
9941     TEST_DrawLevelField(x, y);
9942
9943     if (GFX_CRUMBLED(new_element))
9944       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9945   }
9946
9947   /* check if element under the player changes from accessible to unaccessible
9948      (needed for special case of dropping element which then changes) */
9949   /* (must be checked after creating new element for walkable group elements) */
9950   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9951       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9952   {
9953     Bang(x, y);
9954
9955     return;
9956   }
9957
9958   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9959   if (new_element_is_player)
9960     RelocatePlayer(x, y, new_element);
9961
9962   if (is_change)
9963     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9964
9965   TestIfBadThingTouchesPlayer(x, y);
9966   TestIfPlayerTouchesCustomElement(x, y);
9967   TestIfElementTouchesCustomElement(x, y);
9968 }
9969
9970 static void CreateField(int x, int y, int element)
9971 {
9972   CreateFieldExt(x, y, element, FALSE);
9973 }
9974
9975 static void CreateElementFromChange(int x, int y, int element)
9976 {
9977   element = GET_VALID_RUNTIME_ELEMENT(element);
9978
9979   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9980   {
9981     int old_element = Feld[x][y];
9982
9983     /* prevent changed element from moving in same engine frame
9984        unless both old and new element can either fall or move */
9985     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9986         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9987       Stop[x][y] = TRUE;
9988   }
9989
9990   CreateFieldExt(x, y, element, TRUE);
9991 }
9992
9993 static boolean ChangeElement(int x, int y, int element, int page)
9994 {
9995   struct ElementInfo *ei = &element_info[element];
9996   struct ElementChangeInfo *change = &ei->change_page[page];
9997   int ce_value = CustomValue[x][y];
9998   int ce_score = ei->collect_score;
9999   int target_element;
10000   int old_element = Feld[x][y];
10001
10002   /* always use default change event to prevent running into a loop */
10003   if (ChangeEvent[x][y] == -1)
10004     ChangeEvent[x][y] = CE_DELAY;
10005
10006   if (ChangeEvent[x][y] == CE_DELAY)
10007   {
10008     /* reset actual trigger element, trigger player and action element */
10009     change->actual_trigger_element = EL_EMPTY;
10010     change->actual_trigger_player = EL_EMPTY;
10011     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10012     change->actual_trigger_side = CH_SIDE_NONE;
10013     change->actual_trigger_ce_value = 0;
10014     change->actual_trigger_ce_score = 0;
10015   }
10016
10017   /* do not change elements more than a specified maximum number of changes */
10018   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10019     return FALSE;
10020
10021   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10022
10023   if (change->explode)
10024   {
10025     Bang(x, y);
10026
10027     return TRUE;
10028   }
10029
10030   if (change->use_target_content)
10031   {
10032     boolean complete_replace = TRUE;
10033     boolean can_replace[3][3];
10034     int xx, yy;
10035
10036     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10037     {
10038       boolean is_empty;
10039       boolean is_walkable;
10040       boolean is_diggable;
10041       boolean is_collectible;
10042       boolean is_removable;
10043       boolean is_destructible;
10044       int ex = x + xx - 1;
10045       int ey = y + yy - 1;
10046       int content_element = change->target_content.e[xx][yy];
10047       int e;
10048
10049       can_replace[xx][yy] = TRUE;
10050
10051       if (ex == x && ey == y)   /* do not check changing element itself */
10052         continue;
10053
10054       if (content_element == EL_EMPTY_SPACE)
10055       {
10056         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10057
10058         continue;
10059       }
10060
10061       if (!IN_LEV_FIELD(ex, ey))
10062       {
10063         can_replace[xx][yy] = FALSE;
10064         complete_replace = FALSE;
10065
10066         continue;
10067       }
10068
10069       e = Feld[ex][ey];
10070
10071       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10072         e = MovingOrBlocked2Element(ex, ey);
10073
10074       is_empty = (IS_FREE(ex, ey) ||
10075                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10076
10077       is_walkable     = (is_empty || IS_WALKABLE(e));
10078       is_diggable     = (is_empty || IS_DIGGABLE(e));
10079       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10080       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10081       is_removable    = (is_diggable || is_collectible);
10082
10083       can_replace[xx][yy] =
10084         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10085           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10086           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10087           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10088           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10089           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10090          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10091
10092       if (!can_replace[xx][yy])
10093         complete_replace = FALSE;
10094     }
10095
10096     if (!change->only_if_complete || complete_replace)
10097     {
10098       boolean something_has_changed = FALSE;
10099
10100       if (change->only_if_complete && change->use_random_replace &&
10101           RND(100) < change->random_percentage)
10102         return FALSE;
10103
10104       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10105       {
10106         int ex = x + xx - 1;
10107         int ey = y + yy - 1;
10108         int content_element;
10109
10110         if (can_replace[xx][yy] && (!change->use_random_replace ||
10111                                     RND(100) < change->random_percentage))
10112         {
10113           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10114             RemoveMovingField(ex, ey);
10115
10116           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10117
10118           content_element = change->target_content.e[xx][yy];
10119           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10120                                               ce_value, ce_score);
10121
10122           CreateElementFromChange(ex, ey, target_element);
10123
10124           something_has_changed = TRUE;
10125
10126           /* for symmetry reasons, freeze newly created border elements */
10127           if (ex != x || ey != y)
10128             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10129         }
10130       }
10131
10132       if (something_has_changed)
10133       {
10134         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10135         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10136       }
10137     }
10138   }
10139   else
10140   {
10141     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10142                                         ce_value, ce_score);
10143
10144     if (element == EL_DIAGONAL_GROWING ||
10145         element == EL_DIAGONAL_SHRINKING)
10146     {
10147       target_element = Store[x][y];
10148
10149       Store[x][y] = EL_EMPTY;
10150     }
10151
10152     CreateElementFromChange(x, y, target_element);
10153
10154     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10155     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10156   }
10157
10158   /* this uses direct change before indirect change */
10159   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10160
10161   return TRUE;
10162 }
10163
10164 static void HandleElementChange(int x, int y, int page)
10165 {
10166   int element = MovingOrBlocked2Element(x, y);
10167   struct ElementInfo *ei = &element_info[element];
10168   struct ElementChangeInfo *change = &ei->change_page[page];
10169   boolean handle_action_before_change = FALSE;
10170
10171 #ifdef DEBUG
10172   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10173       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10174   {
10175     printf("\n\n");
10176     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10177            x, y, element, element_info[element].token_name);
10178     printf("HandleElementChange(): This should never happen!\n");
10179     printf("\n\n");
10180   }
10181 #endif
10182
10183   /* this can happen with classic bombs on walkable, changing elements */
10184   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10185   {
10186     return;
10187   }
10188
10189   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10190   {
10191     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10192
10193     if (change->can_change)
10194     {
10195       /* !!! not clear why graphic animation should be reset at all here !!! */
10196       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10197       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10198
10199       /*
10200         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10201
10202         When using an animation frame delay of 1 (this only happens with
10203         "sp_zonk.moving.left/right" in the classic graphics), the default
10204         (non-moving) animation shows wrong animation frames (while the
10205         moving animation, like "sp_zonk.moving.left/right", is correct,
10206         so this graphical bug never shows up with the classic graphics).
10207         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10208         be drawn instead of the correct frames 0,1,2,3. This is caused by
10209         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10210         an element change: First when the change delay ("ChangeDelay[][]")
10211         counter has reached zero after decrementing, then a second time in
10212         the next frame (after "GfxFrame[][]" was already incremented) when
10213         "ChangeDelay[][]" is reset to the initial delay value again.
10214
10215         This causes frame 0 to be drawn twice, while the last frame won't
10216         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10217
10218         As some animations may already be cleverly designed around this bug
10219         (at least the "Snake Bite" snake tail animation does this), it cannot
10220         simply be fixed here without breaking such existing animations.
10221         Unfortunately, it cannot easily be detected if a graphics set was
10222         designed "before" or "after" the bug was fixed. As a workaround,
10223         a new graphics set option "game.graphics_engine_version" was added
10224         to be able to specify the game's major release version for which the
10225         graphics set was designed, which can then be used to decide if the
10226         bugfix should be used (version 4 and above) or not (version 3 or
10227         below, or if no version was specified at all, as with old sets).
10228
10229         (The wrong/fixed animation frames can be tested with the test level set
10230         "test_gfxframe" and level "000", which contains a specially prepared
10231         custom element at level position (x/y) == (11/9) which uses the zonk
10232         animation mentioned above. Using "game.graphics_engine_version: 4"
10233         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10234         This can also be seen from the debug output for this test element.)
10235       */
10236
10237       /* when a custom element is about to change (for example by change delay),
10238          do not reset graphic animation when the custom element is moving */
10239       if (game.graphics_engine_version < 4 &&
10240           !IS_MOVING(x, y))
10241       {
10242         ResetGfxAnimation(x, y);
10243         ResetRandomAnimationValue(x, y);
10244       }
10245
10246       if (change->pre_change_function)
10247         change->pre_change_function(x, y);
10248     }
10249   }
10250
10251   ChangeDelay[x][y]--;
10252
10253   if (ChangeDelay[x][y] != 0)           /* continue element change */
10254   {
10255     if (change->can_change)
10256     {
10257       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10258
10259       if (IS_ANIMATED(graphic))
10260         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10261
10262       if (change->change_function)
10263         change->change_function(x, y);
10264     }
10265   }
10266   else                                  /* finish element change */
10267   {
10268     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10269     {
10270       page = ChangePage[x][y];
10271       ChangePage[x][y] = -1;
10272
10273       change = &ei->change_page[page];
10274     }
10275
10276     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10277     {
10278       ChangeDelay[x][y] = 1;            /* try change after next move step */
10279       ChangePage[x][y] = page;          /* remember page to use for change */
10280
10281       return;
10282     }
10283
10284     /* special case: set new level random seed before changing element */
10285     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10286       handle_action_before_change = TRUE;
10287
10288     if (change->has_action && handle_action_before_change)
10289       ExecuteCustomElementAction(x, y, element, page);
10290
10291     if (change->can_change)
10292     {
10293       if (ChangeElement(x, y, element, page))
10294       {
10295         if (change->post_change_function)
10296           change->post_change_function(x, y);
10297       }
10298     }
10299
10300     if (change->has_action && !handle_action_before_change)
10301       ExecuteCustomElementAction(x, y, element, page);
10302   }
10303 }
10304
10305 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10306                                               int trigger_element,
10307                                               int trigger_event,
10308                                               int trigger_player,
10309                                               int trigger_side,
10310                                               int trigger_page)
10311 {
10312   boolean change_done_any = FALSE;
10313   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10314   int i;
10315
10316   if (!(trigger_events[trigger_element][trigger_event]))
10317     return FALSE;
10318
10319   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10320
10321   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10322   {
10323     int element = EL_CUSTOM_START + i;
10324     boolean change_done = FALSE;
10325     int p;
10326
10327     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10328         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10329       continue;
10330
10331     for (p = 0; p < element_info[element].num_change_pages; p++)
10332     {
10333       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10334
10335       if (change->can_change_or_has_action &&
10336           change->has_event[trigger_event] &&
10337           change->trigger_side & trigger_side &&
10338           change->trigger_player & trigger_player &&
10339           change->trigger_page & trigger_page_bits &&
10340           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10341       {
10342         change->actual_trigger_element = trigger_element;
10343         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10344         change->actual_trigger_player_bits = trigger_player;
10345         change->actual_trigger_side = trigger_side;
10346         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10347         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10348
10349         if ((change->can_change && !change_done) || change->has_action)
10350         {
10351           int x, y;
10352
10353           SCAN_PLAYFIELD(x, y)
10354           {
10355             if (Feld[x][y] == element)
10356             {
10357               if (change->can_change && !change_done)
10358               {
10359                 /* if element already changed in this frame, not only prevent
10360                    another element change (checked in ChangeElement()), but
10361                    also prevent additional element actions for this element */
10362
10363                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10364                     !level.use_action_after_change_bug)
10365                   continue;
10366
10367                 ChangeDelay[x][y] = 1;
10368                 ChangeEvent[x][y] = trigger_event;
10369
10370                 HandleElementChange(x, y, p);
10371               }
10372               else if (change->has_action)
10373               {
10374                 /* if element already changed in this frame, not only prevent
10375                    another element change (checked in ChangeElement()), but
10376                    also prevent additional element actions for this element */
10377
10378                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10379                     !level.use_action_after_change_bug)
10380                   continue;
10381
10382                 ExecuteCustomElementAction(x, y, element, p);
10383                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10384               }
10385             }
10386           }
10387
10388           if (change->can_change)
10389           {
10390             change_done = TRUE;
10391             change_done_any = TRUE;
10392           }
10393         }
10394       }
10395     }
10396   }
10397
10398   RECURSION_LOOP_DETECTION_END();
10399
10400   return change_done_any;
10401 }
10402
10403 static boolean CheckElementChangeExt(int x, int y,
10404                                      int element,
10405                                      int trigger_element,
10406                                      int trigger_event,
10407                                      int trigger_player,
10408                                      int trigger_side)
10409 {
10410   boolean change_done = FALSE;
10411   int p;
10412
10413   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10414       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10415     return FALSE;
10416
10417   if (Feld[x][y] == EL_BLOCKED)
10418   {
10419     Blocked2Moving(x, y, &x, &y);
10420     element = Feld[x][y];
10421   }
10422
10423   /* check if element has already changed or is about to change after moving */
10424   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10425        Feld[x][y] != element) ||
10426
10427       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10428        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10429         ChangePage[x][y] != -1)))
10430     return FALSE;
10431
10432   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10433
10434   for (p = 0; p < element_info[element].num_change_pages; p++)
10435   {
10436     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10437
10438     /* check trigger element for all events where the element that is checked
10439        for changing interacts with a directly adjacent element -- this is
10440        different to element changes that affect other elements to change on the
10441        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10442     boolean check_trigger_element =
10443       (trigger_event == CE_TOUCHING_X ||
10444        trigger_event == CE_HITTING_X ||
10445        trigger_event == CE_HIT_BY_X ||
10446        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10447
10448     if (change->can_change_or_has_action &&
10449         change->has_event[trigger_event] &&
10450         change->trigger_side & trigger_side &&
10451         change->trigger_player & trigger_player &&
10452         (!check_trigger_element ||
10453          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10454     {
10455       change->actual_trigger_element = trigger_element;
10456       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10457       change->actual_trigger_player_bits = trigger_player;
10458       change->actual_trigger_side = trigger_side;
10459       change->actual_trigger_ce_value = CustomValue[x][y];
10460       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10461
10462       /* special case: trigger element not at (x,y) position for some events */
10463       if (check_trigger_element)
10464       {
10465         static struct
10466         {
10467           int dx, dy;
10468         } move_xy[] =
10469           {
10470             {  0,  0 },
10471             { -1,  0 },
10472             { +1,  0 },
10473             {  0,  0 },
10474             {  0, -1 },
10475             {  0,  0 }, { 0, 0 }, { 0, 0 },
10476             {  0, +1 }
10477           };
10478
10479         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10480         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10481
10482         change->actual_trigger_ce_value = CustomValue[xx][yy];
10483         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10484       }
10485
10486       if (change->can_change && !change_done)
10487       {
10488         ChangeDelay[x][y] = 1;
10489         ChangeEvent[x][y] = trigger_event;
10490
10491         HandleElementChange(x, y, p);
10492
10493         change_done = TRUE;
10494       }
10495       else if (change->has_action)
10496       {
10497         ExecuteCustomElementAction(x, y, element, p);
10498         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10499       }
10500     }
10501   }
10502
10503   RECURSION_LOOP_DETECTION_END();
10504
10505   return change_done;
10506 }
10507
10508 static void PlayPlayerSound(struct PlayerInfo *player)
10509 {
10510   int jx = player->jx, jy = player->jy;
10511   int sound_element = player->artwork_element;
10512   int last_action = player->last_action_waiting;
10513   int action = player->action_waiting;
10514
10515   if (player->is_waiting)
10516   {
10517     if (action != last_action)
10518       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10519     else
10520       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10521   }
10522   else
10523   {
10524     if (action != last_action)
10525       StopSound(element_info[sound_element].sound[last_action]);
10526
10527     if (last_action == ACTION_SLEEPING)
10528       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10529   }
10530 }
10531
10532 static void PlayAllPlayersSound()
10533 {
10534   int i;
10535
10536   for (i = 0; i < MAX_PLAYERS; i++)
10537     if (stored_player[i].active)
10538       PlayPlayerSound(&stored_player[i]);
10539 }
10540
10541 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10542 {
10543   boolean last_waiting = player->is_waiting;
10544   int move_dir = player->MovDir;
10545
10546   player->dir_waiting = move_dir;
10547   player->last_action_waiting = player->action_waiting;
10548
10549   if (is_waiting)
10550   {
10551     if (!last_waiting)          /* not waiting -> waiting */
10552     {
10553       player->is_waiting = TRUE;
10554
10555       player->frame_counter_bored =
10556         FrameCounter +
10557         game.player_boring_delay_fixed +
10558         GetSimpleRandom(game.player_boring_delay_random);
10559       player->frame_counter_sleeping =
10560         FrameCounter +
10561         game.player_sleeping_delay_fixed +
10562         GetSimpleRandom(game.player_sleeping_delay_random);
10563
10564       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10565     }
10566
10567     if (game.player_sleeping_delay_fixed +
10568         game.player_sleeping_delay_random > 0 &&
10569         player->anim_delay_counter == 0 &&
10570         player->post_delay_counter == 0 &&
10571         FrameCounter >= player->frame_counter_sleeping)
10572       player->is_sleeping = TRUE;
10573     else if (game.player_boring_delay_fixed +
10574              game.player_boring_delay_random > 0 &&
10575              FrameCounter >= player->frame_counter_bored)
10576       player->is_bored = TRUE;
10577
10578     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10579                               player->is_bored ? ACTION_BORING :
10580                               ACTION_WAITING);
10581
10582     if (player->is_sleeping && player->use_murphy)
10583     {
10584       /* special case for sleeping Murphy when leaning against non-free tile */
10585
10586       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10587           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10588            !IS_MOVING(player->jx - 1, player->jy)))
10589         move_dir = MV_LEFT;
10590       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10591                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10592                 !IS_MOVING(player->jx + 1, player->jy)))
10593         move_dir = MV_RIGHT;
10594       else
10595         player->is_sleeping = FALSE;
10596
10597       player->dir_waiting = move_dir;
10598     }
10599
10600     if (player->is_sleeping)
10601     {
10602       if (player->num_special_action_sleeping > 0)
10603       {
10604         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10605         {
10606           int last_special_action = player->special_action_sleeping;
10607           int num_special_action = player->num_special_action_sleeping;
10608           int special_action =
10609             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10610              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10611              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10612              last_special_action + 1 : ACTION_SLEEPING);
10613           int special_graphic =
10614             el_act_dir2img(player->artwork_element, special_action, move_dir);
10615
10616           player->anim_delay_counter =
10617             graphic_info[special_graphic].anim_delay_fixed +
10618             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10619           player->post_delay_counter =
10620             graphic_info[special_graphic].post_delay_fixed +
10621             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10622
10623           player->special_action_sleeping = special_action;
10624         }
10625
10626         if (player->anim_delay_counter > 0)
10627         {
10628           player->action_waiting = player->special_action_sleeping;
10629           player->anim_delay_counter--;
10630         }
10631         else if (player->post_delay_counter > 0)
10632         {
10633           player->post_delay_counter--;
10634         }
10635       }
10636     }
10637     else if (player->is_bored)
10638     {
10639       if (player->num_special_action_bored > 0)
10640       {
10641         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10642         {
10643           int special_action =
10644             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10645           int special_graphic =
10646             el_act_dir2img(player->artwork_element, special_action, move_dir);
10647
10648           player->anim_delay_counter =
10649             graphic_info[special_graphic].anim_delay_fixed +
10650             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10651           player->post_delay_counter =
10652             graphic_info[special_graphic].post_delay_fixed +
10653             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10654
10655           player->special_action_bored = special_action;
10656         }
10657
10658         if (player->anim_delay_counter > 0)
10659         {
10660           player->action_waiting = player->special_action_bored;
10661           player->anim_delay_counter--;
10662         }
10663         else if (player->post_delay_counter > 0)
10664         {
10665           player->post_delay_counter--;
10666         }
10667       }
10668     }
10669   }
10670   else if (last_waiting)        /* waiting -> not waiting */
10671   {
10672     player->is_waiting = FALSE;
10673     player->is_bored = FALSE;
10674     player->is_sleeping = FALSE;
10675
10676     player->frame_counter_bored = -1;
10677     player->frame_counter_sleeping = -1;
10678
10679     player->anim_delay_counter = 0;
10680     player->post_delay_counter = 0;
10681
10682     player->dir_waiting = player->MovDir;
10683     player->action_waiting = ACTION_DEFAULT;
10684
10685     player->special_action_bored = ACTION_DEFAULT;
10686     player->special_action_sleeping = ACTION_DEFAULT;
10687   }
10688 }
10689
10690 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10691 {
10692   if ((!player->is_moving  && player->was_moving) ||
10693       (player->MovPos == 0 && player->was_moving) ||
10694       (player->is_snapping && !player->was_snapping) ||
10695       (player->is_dropping && !player->was_dropping))
10696   {
10697     if (!CheckSaveEngineSnapshotToList())
10698       return;
10699
10700     player->was_moving = FALSE;
10701     player->was_snapping = TRUE;
10702     player->was_dropping = TRUE;
10703   }
10704   else
10705   {
10706     if (player->is_moving)
10707       player->was_moving = TRUE;
10708
10709     if (!player->is_snapping)
10710       player->was_snapping = FALSE;
10711
10712     if (!player->is_dropping)
10713       player->was_dropping = FALSE;
10714   }
10715 }
10716
10717 static void CheckSingleStepMode(struct PlayerInfo *player)
10718 {
10719   if (tape.single_step && tape.recording && !tape.pausing)
10720   {
10721     /* as it is called "single step mode", just return to pause mode when the
10722        player stopped moving after one tile (or never starts moving at all) */
10723     if (!player->is_moving &&
10724         !player->is_pushing &&
10725         !player->is_dropping_pressed)
10726     {
10727       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10728       SnapField(player, 0, 0);                  /* stop snapping */
10729     }
10730   }
10731
10732   CheckSaveEngineSnapshot(player);
10733 }
10734
10735 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10736 {
10737   int left      = player_action & JOY_LEFT;
10738   int right     = player_action & JOY_RIGHT;
10739   int up        = player_action & JOY_UP;
10740   int down      = player_action & JOY_DOWN;
10741   int button1   = player_action & JOY_BUTTON_1;
10742   int button2   = player_action & JOY_BUTTON_2;
10743   int dx        = (left ? -1 : right ? 1 : 0);
10744   int dy        = (up   ? -1 : down  ? 1 : 0);
10745
10746   if (!player->active || tape.pausing)
10747     return 0;
10748
10749   if (player_action)
10750   {
10751     if (button1)
10752       SnapField(player, dx, dy);
10753     else
10754     {
10755       if (button2)
10756         DropElement(player);
10757
10758       MovePlayer(player, dx, dy);
10759     }
10760
10761     CheckSingleStepMode(player);
10762
10763     SetPlayerWaiting(player, FALSE);
10764
10765     return player_action;
10766   }
10767   else
10768   {
10769     /* no actions for this player (no input at player's configured device) */
10770
10771     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10772     SnapField(player, 0, 0);
10773     CheckGravityMovementWhenNotMoving(player);
10774
10775     if (player->MovPos == 0)
10776       SetPlayerWaiting(player, TRUE);
10777
10778     if (player->MovPos == 0)    /* needed for tape.playing */
10779       player->is_moving = FALSE;
10780
10781     player->is_dropping = FALSE;
10782     player->is_dropping_pressed = FALSE;
10783     player->drop_pressed_delay = 0;
10784
10785     CheckSingleStepMode(player);
10786
10787     return 0;
10788   }
10789 }
10790
10791 static void CheckLevelTime()
10792 {
10793   int i;
10794
10795   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10796   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10797   {
10798     if (level.native_em_level->lev->home == 0)  /* all players at home */
10799     {
10800       PlayerWins(local_player);
10801
10802       AllPlayersGone = TRUE;
10803
10804       level.native_em_level->lev->home = -1;
10805     }
10806
10807     if (level.native_em_level->ply[0]->alive == 0 &&
10808         level.native_em_level->ply[1]->alive == 0 &&
10809         level.native_em_level->ply[2]->alive == 0 &&
10810         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10811       AllPlayersGone = TRUE;
10812   }
10813   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10814   {
10815     if (game_sp.LevelSolved &&
10816         !game_sp.GameOver)                              /* game won */
10817     {
10818       PlayerWins(local_player);
10819
10820       game_sp.GameOver = TRUE;
10821
10822       AllPlayersGone = TRUE;
10823     }
10824
10825     if (game_sp.GameOver)                               /* game lost */
10826       AllPlayersGone = TRUE;
10827   }
10828
10829   if (TimeFrames >= FRAMES_PER_SECOND)
10830   {
10831     TimeFrames = 0;
10832     TapeTime++;
10833
10834     for (i = 0; i < MAX_PLAYERS; i++)
10835     {
10836       struct PlayerInfo *player = &stored_player[i];
10837
10838       if (SHIELD_ON(player))
10839       {
10840         player->shield_normal_time_left--;
10841
10842         if (player->shield_deadly_time_left > 0)
10843           player->shield_deadly_time_left--;
10844       }
10845     }
10846
10847     if (!local_player->LevelSolved && !level.use_step_counter)
10848     {
10849       TimePlayed++;
10850
10851       if (TimeLeft > 0)
10852       {
10853         TimeLeft--;
10854
10855         if (TimeLeft <= 10 && setup.time_limit)
10856           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10857
10858         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10859            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10860
10861         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10862
10863         if (!TimeLeft && setup.time_limit)
10864         {
10865           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10866             level.native_em_level->lev->killed_out_of_time = TRUE;
10867           else
10868             for (i = 0; i < MAX_PLAYERS; i++)
10869               KillPlayer(&stored_player[i]);
10870         }
10871       }
10872       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10873       {
10874         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10875       }
10876
10877       level.native_em_level->lev->time =
10878         (game.no_time_limit ? TimePlayed : TimeLeft);
10879     }
10880
10881     if (tape.recording || tape.playing)
10882       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10883   }
10884
10885   if (tape.recording || tape.playing)
10886     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10887
10888   UpdateAndDisplayGameControlValues();
10889 }
10890
10891 void AdvanceFrameAndPlayerCounters(int player_nr)
10892 {
10893   int i;
10894
10895   /* advance frame counters (global frame counter and time frame counter) */
10896   FrameCounter++;
10897   TimeFrames++;
10898
10899   /* advance player counters (counters for move delay, move animation etc.) */
10900   for (i = 0; i < MAX_PLAYERS; i++)
10901   {
10902     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10903     int move_delay_value = stored_player[i].move_delay_value;
10904     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10905
10906     if (!advance_player_counters)       /* not all players may be affected */
10907       continue;
10908
10909     if (move_frames == 0)       /* less than one move per game frame */
10910     {
10911       int stepsize = TILEX / move_delay_value;
10912       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10913       int count = (stored_player[i].is_moving ?
10914                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10915
10916       if (count % delay == 0)
10917         move_frames = 1;
10918     }
10919
10920     stored_player[i].Frame += move_frames;
10921
10922     if (stored_player[i].MovPos != 0)
10923       stored_player[i].StepFrame += move_frames;
10924
10925     if (stored_player[i].move_delay > 0)
10926       stored_player[i].move_delay--;
10927
10928     /* due to bugs in previous versions, counter must count up, not down */
10929     if (stored_player[i].push_delay != -1)
10930       stored_player[i].push_delay++;
10931
10932     if (stored_player[i].drop_delay > 0)
10933       stored_player[i].drop_delay--;
10934
10935     if (stored_player[i].is_dropping_pressed)
10936       stored_player[i].drop_pressed_delay++;
10937   }
10938 }
10939
10940 void StartGameActions(boolean init_network_game, boolean record_tape,
10941                       int random_seed)
10942 {
10943   unsigned int new_random_seed = InitRND(random_seed);
10944
10945   if (record_tape)
10946     TapeStartRecording(new_random_seed);
10947
10948 #if defined(NETWORK_AVALIABLE)
10949   if (init_network_game)
10950   {
10951     SendToServer_StartPlaying();
10952
10953     return;
10954   }
10955 #endif
10956
10957   InitGame();
10958 }
10959
10960 void GameActionsExt()
10961 {
10962 #if 0
10963   static unsigned int game_frame_delay = 0;
10964 #endif
10965   unsigned int game_frame_delay_value;
10966   byte *recorded_player_action;
10967   byte summarized_player_action = 0;
10968   byte tape_action[MAX_PLAYERS];
10969   int i;
10970
10971   /* detect endless loops, caused by custom element programming */
10972   if (recursion_loop_detected && recursion_loop_depth == 0)
10973   {
10974     char *message = getStringCat3("Internal Error! Element ",
10975                                   EL_NAME(recursion_loop_element),
10976                                   " caused endless loop! Quit the game?");
10977
10978     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10979           EL_NAME(recursion_loop_element));
10980
10981     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10982
10983     recursion_loop_detected = FALSE;    /* if game should be continued */
10984
10985     free(message);
10986
10987     return;
10988   }
10989
10990   if (game.restart_level)
10991     StartGameActions(options.network, setup.autorecord, level.random_seed);
10992
10993   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10994   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10995   {
10996     if (level.native_em_level->lev->home == 0)  /* all players at home */
10997     {
10998       PlayerWins(local_player);
10999
11000       AllPlayersGone = TRUE;
11001
11002       level.native_em_level->lev->home = -1;
11003     }
11004
11005     if (level.native_em_level->ply[0]->alive == 0 &&
11006         level.native_em_level->ply[1]->alive == 0 &&
11007         level.native_em_level->ply[2]->alive == 0 &&
11008         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11009       AllPlayersGone = TRUE;
11010   }
11011   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11012   {
11013     if (game_sp.LevelSolved &&
11014         !game_sp.GameOver)                              /* game won */
11015     {
11016       PlayerWins(local_player);
11017
11018       game_sp.GameOver = TRUE;
11019
11020       AllPlayersGone = TRUE;
11021     }
11022
11023     if (game_sp.GameOver)                               /* game lost */
11024       AllPlayersGone = TRUE;
11025   }
11026
11027   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11028     GameWon();
11029
11030   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11031     TapeStop();
11032
11033   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11034     return;
11035
11036   game_frame_delay_value =
11037     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11038
11039   if (tape.playing && tape.warp_forward && !tape.pausing)
11040     game_frame_delay_value = 0;
11041
11042   SetVideoFrameDelay(game_frame_delay_value);
11043
11044 #if 0
11045 #if 0
11046   /* ---------- main game synchronization point ---------- */
11047
11048   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11049
11050   printf("::: skip == %d\n", skip);
11051
11052 #else
11053   /* ---------- main game synchronization point ---------- */
11054
11055   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11056 #endif
11057 #endif
11058
11059   if (network_playing && !network_player_action_received)
11060   {
11061     /* try to get network player actions in time */
11062
11063 #if defined(NETWORK_AVALIABLE)
11064     /* last chance to get network player actions without main loop delay */
11065     HandleNetworking();
11066 #endif
11067
11068     /* game was quit by network peer */
11069     if (game_status != GAME_MODE_PLAYING)
11070       return;
11071
11072     if (!network_player_action_received)
11073       return;           /* failed to get network player actions in time */
11074
11075     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11076   }
11077
11078   if (tape.pausing)
11079     return;
11080
11081   /* at this point we know that we really continue executing the game */
11082
11083   network_player_action_received = FALSE;
11084
11085   /* when playing tape, read previously recorded player input from tape data */
11086   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11087
11088   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11089   if (tape.pausing)
11090     return;
11091
11092   if (tape.set_centered_player)
11093   {
11094     game.centered_player_nr_next = tape.centered_player_nr_next;
11095     game.set_centered_player = TRUE;
11096   }
11097
11098   for (i = 0; i < MAX_PLAYERS; i++)
11099   {
11100     summarized_player_action |= stored_player[i].action;
11101
11102     if (!network_playing && (game.team_mode || tape.playing))
11103       stored_player[i].effective_action = stored_player[i].action;
11104   }
11105
11106 #if defined(NETWORK_AVALIABLE)
11107   if (network_playing)
11108     SendToServer_MovePlayer(summarized_player_action);
11109 #endif
11110
11111   // summarize all actions at local players mapped input device position
11112   // (this allows using different input devices in single player mode)
11113   if (!options.network && !game.team_mode)
11114     stored_player[map_player_action[local_player->index_nr]].effective_action =
11115       summarized_player_action;
11116
11117   if (tape.recording &&
11118       setup.team_mode &&
11119       setup.input_on_focus &&
11120       game.centered_player_nr != -1)
11121   {
11122     for (i = 0; i < MAX_PLAYERS; i++)
11123       stored_player[i].effective_action =
11124         (i == game.centered_player_nr ? summarized_player_action : 0);
11125   }
11126
11127   if (recorded_player_action != NULL)
11128     for (i = 0; i < MAX_PLAYERS; i++)
11129       stored_player[i].effective_action = recorded_player_action[i];
11130
11131   for (i = 0; i < MAX_PLAYERS; i++)
11132   {
11133     tape_action[i] = stored_player[i].effective_action;
11134
11135     /* (this may happen in the RND game engine if a player was not present on
11136        the playfield on level start, but appeared later from a custom element */
11137     if (setup.team_mode &&
11138         tape.recording &&
11139         tape_action[i] &&
11140         !tape.player_participates[i])
11141       tape.player_participates[i] = TRUE;
11142   }
11143
11144   /* only record actions from input devices, but not programmed actions */
11145   if (tape.recording)
11146     TapeRecordAction(tape_action);
11147
11148 #if USE_NEW_PLAYER_ASSIGNMENTS
11149   // !!! also map player actions in single player mode !!!
11150   // if (game.team_mode)
11151   if (1)
11152   {
11153     byte mapped_action[MAX_PLAYERS];
11154
11155 #if DEBUG_PLAYER_ACTIONS
11156     printf(":::");
11157     for (i = 0; i < MAX_PLAYERS; i++)
11158       printf(" %d, ", stored_player[i].effective_action);
11159 #endif
11160
11161     for (i = 0; i < MAX_PLAYERS; i++)
11162       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11163
11164     for (i = 0; i < MAX_PLAYERS; i++)
11165       stored_player[i].effective_action = mapped_action[i];
11166
11167 #if DEBUG_PLAYER_ACTIONS
11168     printf(" =>");
11169     for (i = 0; i < MAX_PLAYERS; i++)
11170       printf(" %d, ", stored_player[i].effective_action);
11171     printf("\n");
11172 #endif
11173   }
11174 #if DEBUG_PLAYER_ACTIONS
11175   else
11176   {
11177     printf(":::");
11178     for (i = 0; i < MAX_PLAYERS; i++)
11179       printf(" %d, ", stored_player[i].effective_action);
11180     printf("\n");
11181   }
11182 #endif
11183 #endif
11184
11185   for (i = 0; i < MAX_PLAYERS; i++)
11186   {
11187     // allow engine snapshot in case of changed movement attempt
11188     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11189         (stored_player[i].effective_action & KEY_MOTION))
11190       game.snapshot.changed_action = TRUE;
11191
11192     // allow engine snapshot in case of snapping/dropping attempt
11193     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11194         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11195       game.snapshot.changed_action = TRUE;
11196
11197     game.snapshot.last_action[i] = stored_player[i].effective_action;
11198   }
11199
11200   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11201   {
11202     GameActions_EM_Main();
11203   }
11204   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11205   {
11206     GameActions_SP_Main();
11207   }
11208   else
11209   {
11210     GameActions_RND_Main();
11211   }
11212
11213   BlitScreenToBitmap(backbuffer);
11214
11215   CheckLevelTime();
11216
11217   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11218
11219   if (global.show_frames_per_second)
11220   {
11221     static unsigned int fps_counter = 0;
11222     static int fps_frames = 0;
11223     unsigned int fps_delay_ms = Counter() - fps_counter;
11224
11225     fps_frames++;
11226
11227     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11228     {
11229       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11230
11231       fps_frames = 0;
11232       fps_counter = Counter();
11233
11234       /* always draw FPS to screen after FPS value was updated */
11235       redraw_mask |= REDRAW_FPS;
11236     }
11237
11238     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11239     if (GetDrawDeactivationMask() == REDRAW_NONE)
11240       redraw_mask |= REDRAW_FPS;
11241   }
11242 }
11243
11244 static void GameActions_CheckSaveEngineSnapshot()
11245 {
11246   if (!game.snapshot.save_snapshot)
11247     return;
11248
11249   // clear flag for saving snapshot _before_ saving snapshot
11250   game.snapshot.save_snapshot = FALSE;
11251
11252   SaveEngineSnapshotToList();
11253 }
11254
11255 void GameActions()
11256 {
11257   GameActionsExt();
11258
11259   GameActions_CheckSaveEngineSnapshot();
11260 }
11261
11262 void GameActions_EM_Main()
11263 {
11264   byte effective_action[MAX_PLAYERS];
11265   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11266   int i;
11267
11268   for (i = 0; i < MAX_PLAYERS; i++)
11269     effective_action[i] = stored_player[i].effective_action;
11270
11271   GameActions_EM(effective_action, warp_mode);
11272 }
11273
11274 void GameActions_SP_Main()
11275 {
11276   byte effective_action[MAX_PLAYERS];
11277   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11278   int i;
11279
11280   for (i = 0; i < MAX_PLAYERS; i++)
11281     effective_action[i] = stored_player[i].effective_action;
11282
11283   GameActions_SP(effective_action, warp_mode);
11284
11285   for (i = 0; i < MAX_PLAYERS; i++)
11286   {
11287     if (stored_player[i].force_dropping)
11288       stored_player[i].action |= KEY_BUTTON_DROP;
11289
11290     stored_player[i].force_dropping = FALSE;
11291   }
11292 }
11293
11294 void GameActions_RND_Main()
11295 {
11296   GameActions_RND();
11297 }
11298
11299 void GameActions_RND()
11300 {
11301   int magic_wall_x = 0, magic_wall_y = 0;
11302   int i, x, y, element, graphic, last_gfx_frame;
11303
11304   InitPlayfieldScanModeVars();
11305
11306   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11307   {
11308     SCAN_PLAYFIELD(x, y)
11309     {
11310       ChangeCount[x][y] = 0;
11311       ChangeEvent[x][y] = -1;
11312     }
11313   }
11314
11315   if (game.set_centered_player)
11316   {
11317     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11318
11319     /* switching to "all players" only possible if all players fit to screen */
11320     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11321     {
11322       game.centered_player_nr_next = game.centered_player_nr;
11323       game.set_centered_player = FALSE;
11324     }
11325
11326     /* do not switch focus to non-existing (or non-active) player */
11327     if (game.centered_player_nr_next >= 0 &&
11328         !stored_player[game.centered_player_nr_next].active)
11329     {
11330       game.centered_player_nr_next = game.centered_player_nr;
11331       game.set_centered_player = FALSE;
11332     }
11333   }
11334
11335   if (game.set_centered_player &&
11336       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11337   {
11338     int sx, sy;
11339
11340     if (game.centered_player_nr_next == -1)
11341     {
11342       setScreenCenteredToAllPlayers(&sx, &sy);
11343     }
11344     else
11345     {
11346       sx = stored_player[game.centered_player_nr_next].jx;
11347       sy = stored_player[game.centered_player_nr_next].jy;
11348     }
11349
11350     game.centered_player_nr = game.centered_player_nr_next;
11351     game.set_centered_player = FALSE;
11352
11353     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11354     DrawGameDoorValues();
11355   }
11356
11357   for (i = 0; i < MAX_PLAYERS; i++)
11358   {
11359     int actual_player_action = stored_player[i].effective_action;
11360
11361 #if 1
11362     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11363        - rnd_equinox_tetrachloride 048
11364        - rnd_equinox_tetrachloride_ii 096
11365        - rnd_emanuel_schmieg 002
11366        - doctor_sloan_ww 001, 020
11367     */
11368     if (stored_player[i].MovPos == 0)
11369       CheckGravityMovement(&stored_player[i]);
11370 #endif
11371
11372     /* overwrite programmed action with tape action */
11373     if (stored_player[i].programmed_action)
11374       actual_player_action = stored_player[i].programmed_action;
11375
11376     PlayerActions(&stored_player[i], actual_player_action);
11377
11378     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11379   }
11380
11381   ScrollScreen(NULL, SCROLL_GO_ON);
11382
11383   /* for backwards compatibility, the following code emulates a fixed bug that
11384      occured when pushing elements (causing elements that just made their last
11385      pushing step to already (if possible) make their first falling step in the
11386      same game frame, which is bad); this code is also needed to use the famous
11387      "spring push bug" which is used in older levels and might be wanted to be
11388      used also in newer levels, but in this case the buggy pushing code is only
11389      affecting the "spring" element and no other elements */
11390
11391   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11392   {
11393     for (i = 0; i < MAX_PLAYERS; i++)
11394     {
11395       struct PlayerInfo *player = &stored_player[i];
11396       int x = player->jx;
11397       int y = player->jy;
11398
11399       if (player->active && player->is_pushing && player->is_moving &&
11400           IS_MOVING(x, y) &&
11401           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11402            Feld[x][y] == EL_SPRING))
11403       {
11404         ContinueMoving(x, y);
11405
11406         /* continue moving after pushing (this is actually a bug) */
11407         if (!IS_MOVING(x, y))
11408           Stop[x][y] = FALSE;
11409       }
11410     }
11411   }
11412
11413   SCAN_PLAYFIELD(x, y)
11414   {
11415     ChangeCount[x][y] = 0;
11416     ChangeEvent[x][y] = -1;
11417
11418     /* this must be handled before main playfield loop */
11419     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11420     {
11421       MovDelay[x][y]--;
11422       if (MovDelay[x][y] <= 0)
11423         RemoveField(x, y);
11424     }
11425
11426     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11427     {
11428       MovDelay[x][y]--;
11429       if (MovDelay[x][y] <= 0)
11430       {
11431         RemoveField(x, y);
11432         TEST_DrawLevelField(x, y);
11433
11434         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11435       }
11436     }
11437
11438 #if DEBUG
11439     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11440     {
11441       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11442       printf("GameActions(): This should never happen!\n");
11443
11444       ChangePage[x][y] = -1;
11445     }
11446 #endif
11447
11448     Stop[x][y] = FALSE;
11449     if (WasJustMoving[x][y] > 0)
11450       WasJustMoving[x][y]--;
11451     if (WasJustFalling[x][y] > 0)
11452       WasJustFalling[x][y]--;
11453     if (CheckCollision[x][y] > 0)
11454       CheckCollision[x][y]--;
11455     if (CheckImpact[x][y] > 0)
11456       CheckImpact[x][y]--;
11457
11458     GfxFrame[x][y]++;
11459
11460     /* reset finished pushing action (not done in ContinueMoving() to allow
11461        continuous pushing animation for elements with zero push delay) */
11462     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11463     {
11464       ResetGfxAnimation(x, y);
11465       TEST_DrawLevelField(x, y);
11466     }
11467
11468 #if DEBUG
11469     if (IS_BLOCKED(x, y))
11470     {
11471       int oldx, oldy;
11472
11473       Blocked2Moving(x, y, &oldx, &oldy);
11474       if (!IS_MOVING(oldx, oldy))
11475       {
11476         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11477         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11478         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11479         printf("GameActions(): This should never happen!\n");
11480       }
11481     }
11482 #endif
11483   }
11484
11485   SCAN_PLAYFIELD(x, y)
11486   {
11487     element = Feld[x][y];
11488     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11489     last_gfx_frame = GfxFrame[x][y];
11490
11491     ResetGfxFrame(x, y);
11492
11493     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11494       DrawLevelGraphicAnimation(x, y, graphic);
11495
11496     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11497         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11498       ResetRandomAnimationValue(x, y);
11499
11500     SetRandomAnimationValue(x, y);
11501
11502     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11503
11504     if (IS_INACTIVE(element))
11505     {
11506       if (IS_ANIMATED(graphic))
11507         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11508
11509       continue;
11510     }
11511
11512     /* this may take place after moving, so 'element' may have changed */
11513     if (IS_CHANGING(x, y) &&
11514         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11515     {
11516       int page = element_info[element].event_page_nr[CE_DELAY];
11517
11518       HandleElementChange(x, y, page);
11519
11520       element = Feld[x][y];
11521       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11522     }
11523
11524     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11525     {
11526       StartMoving(x, y);
11527
11528       element = Feld[x][y];
11529       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11530
11531       if (IS_ANIMATED(graphic) &&
11532           !IS_MOVING(x, y) &&
11533           !Stop[x][y])
11534         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11535
11536       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11537         TEST_DrawTwinkleOnField(x, y);
11538     }
11539     else if (element == EL_ACID)
11540     {
11541       if (!Stop[x][y])
11542         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11543     }
11544     else if ((element == EL_EXIT_OPEN ||
11545               element == EL_EM_EXIT_OPEN ||
11546               element == EL_SP_EXIT_OPEN ||
11547               element == EL_STEEL_EXIT_OPEN ||
11548               element == EL_EM_STEEL_EXIT_OPEN ||
11549               element == EL_SP_TERMINAL ||
11550               element == EL_SP_TERMINAL_ACTIVE ||
11551               element == EL_EXTRA_TIME ||
11552               element == EL_SHIELD_NORMAL ||
11553               element == EL_SHIELD_DEADLY) &&
11554              IS_ANIMATED(graphic))
11555       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11556     else if (IS_MOVING(x, y))
11557       ContinueMoving(x, y);
11558     else if (IS_ACTIVE_BOMB(element))
11559       CheckDynamite(x, y);
11560     else if (element == EL_AMOEBA_GROWING)
11561       AmoebeWaechst(x, y);
11562     else if (element == EL_AMOEBA_SHRINKING)
11563       AmoebaDisappearing(x, y);
11564
11565 #if !USE_NEW_AMOEBA_CODE
11566     else if (IS_AMOEBALIVE(element))
11567       AmoebeAbleger(x, y);
11568 #endif
11569
11570     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11571       Life(x, y);
11572     else if (element == EL_EXIT_CLOSED)
11573       CheckExit(x, y);
11574     else if (element == EL_EM_EXIT_CLOSED)
11575       CheckExitEM(x, y);
11576     else if (element == EL_STEEL_EXIT_CLOSED)
11577       CheckExitSteel(x, y);
11578     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11579       CheckExitSteelEM(x, y);
11580     else if (element == EL_SP_EXIT_CLOSED)
11581       CheckExitSP(x, y);
11582     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11583              element == EL_EXPANDABLE_STEELWALL_GROWING)
11584       MauerWaechst(x, y);
11585     else if (element == EL_EXPANDABLE_WALL ||
11586              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11587              element == EL_EXPANDABLE_WALL_VERTICAL ||
11588              element == EL_EXPANDABLE_WALL_ANY ||
11589              element == EL_BD_EXPANDABLE_WALL)
11590       MauerAbleger(x, y);
11591     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11592              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11593              element == EL_EXPANDABLE_STEELWALL_ANY)
11594       MauerAblegerStahl(x, y);
11595     else if (element == EL_FLAMES)
11596       CheckForDragon(x, y);
11597     else if (element == EL_EXPLOSION)
11598       ; /* drawing of correct explosion animation is handled separately */
11599     else if (element == EL_ELEMENT_SNAPPING ||
11600              element == EL_DIAGONAL_SHRINKING ||
11601              element == EL_DIAGONAL_GROWING)
11602     {
11603       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11604
11605       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11606     }
11607     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11608       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11609
11610     if (IS_BELT_ACTIVE(element))
11611       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11612
11613     if (game.magic_wall_active)
11614     {
11615       int jx = local_player->jx, jy = local_player->jy;
11616
11617       /* play the element sound at the position nearest to the player */
11618       if ((element == EL_MAGIC_WALL_FULL ||
11619            element == EL_MAGIC_WALL_ACTIVE ||
11620            element == EL_MAGIC_WALL_EMPTYING ||
11621            element == EL_BD_MAGIC_WALL_FULL ||
11622            element == EL_BD_MAGIC_WALL_ACTIVE ||
11623            element == EL_BD_MAGIC_WALL_EMPTYING ||
11624            element == EL_DC_MAGIC_WALL_FULL ||
11625            element == EL_DC_MAGIC_WALL_ACTIVE ||
11626            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11627           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11628       {
11629         magic_wall_x = x;
11630         magic_wall_y = y;
11631       }
11632     }
11633   }
11634
11635 #if USE_NEW_AMOEBA_CODE
11636   /* new experimental amoeba growth stuff */
11637   if (!(FrameCounter % 8))
11638   {
11639     static unsigned int random = 1684108901;
11640
11641     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11642     {
11643       x = RND(lev_fieldx);
11644       y = RND(lev_fieldy);
11645       element = Feld[x][y];
11646
11647       if (!IS_PLAYER(x,y) &&
11648           (element == EL_EMPTY ||
11649            CAN_GROW_INTO(element) ||
11650            element == EL_QUICKSAND_EMPTY ||
11651            element == EL_QUICKSAND_FAST_EMPTY ||
11652            element == EL_ACID_SPLASH_LEFT ||
11653            element == EL_ACID_SPLASH_RIGHT))
11654       {
11655         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11656             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11657             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11658             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11659           Feld[x][y] = EL_AMOEBA_DROP;
11660       }
11661
11662       random = random * 129 + 1;
11663     }
11664   }
11665 #endif
11666
11667   game.explosions_delayed = FALSE;
11668
11669   SCAN_PLAYFIELD(x, y)
11670   {
11671     element = Feld[x][y];
11672
11673     if (ExplodeField[x][y])
11674       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11675     else if (element == EL_EXPLOSION)
11676       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11677
11678     ExplodeField[x][y] = EX_TYPE_NONE;
11679   }
11680
11681   game.explosions_delayed = TRUE;
11682
11683   if (game.magic_wall_active)
11684   {
11685     if (!(game.magic_wall_time_left % 4))
11686     {
11687       int element = Feld[magic_wall_x][magic_wall_y];
11688
11689       if (element == EL_BD_MAGIC_WALL_FULL ||
11690           element == EL_BD_MAGIC_WALL_ACTIVE ||
11691           element == EL_BD_MAGIC_WALL_EMPTYING)
11692         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11693       else if (element == EL_DC_MAGIC_WALL_FULL ||
11694                element == EL_DC_MAGIC_WALL_ACTIVE ||
11695                element == EL_DC_MAGIC_WALL_EMPTYING)
11696         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11697       else
11698         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11699     }
11700
11701     if (game.magic_wall_time_left > 0)
11702     {
11703       game.magic_wall_time_left--;
11704
11705       if (!game.magic_wall_time_left)
11706       {
11707         SCAN_PLAYFIELD(x, y)
11708         {
11709           element = Feld[x][y];
11710
11711           if (element == EL_MAGIC_WALL_ACTIVE ||
11712               element == EL_MAGIC_WALL_FULL)
11713           {
11714             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11715             TEST_DrawLevelField(x, y);
11716           }
11717           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11718                    element == EL_BD_MAGIC_WALL_FULL)
11719           {
11720             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11721             TEST_DrawLevelField(x, y);
11722           }
11723           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11724                    element == EL_DC_MAGIC_WALL_FULL)
11725           {
11726             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11727             TEST_DrawLevelField(x, y);
11728           }
11729         }
11730
11731         game.magic_wall_active = FALSE;
11732       }
11733     }
11734   }
11735
11736   if (game.light_time_left > 0)
11737   {
11738     game.light_time_left--;
11739
11740     if (game.light_time_left == 0)
11741       RedrawAllLightSwitchesAndInvisibleElements();
11742   }
11743
11744   if (game.timegate_time_left > 0)
11745   {
11746     game.timegate_time_left--;
11747
11748     if (game.timegate_time_left == 0)
11749       CloseAllOpenTimegates();
11750   }
11751
11752   if (game.lenses_time_left > 0)
11753   {
11754     game.lenses_time_left--;
11755
11756     if (game.lenses_time_left == 0)
11757       RedrawAllInvisibleElementsForLenses();
11758   }
11759
11760   if (game.magnify_time_left > 0)
11761   {
11762     game.magnify_time_left--;
11763
11764     if (game.magnify_time_left == 0)
11765       RedrawAllInvisibleElementsForMagnifier();
11766   }
11767
11768   for (i = 0; i < MAX_PLAYERS; i++)
11769   {
11770     struct PlayerInfo *player = &stored_player[i];
11771
11772     if (SHIELD_ON(player))
11773     {
11774       if (player->shield_deadly_time_left)
11775         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11776       else if (player->shield_normal_time_left)
11777         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11778     }
11779   }
11780
11781 #if USE_DELAYED_GFX_REDRAW
11782   SCAN_PLAYFIELD(x, y)
11783   {
11784     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11785     {
11786       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11787          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11788
11789       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11790         DrawLevelField(x, y);
11791
11792       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11793         DrawLevelFieldCrumbled(x, y);
11794
11795       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11796         DrawLevelFieldCrumbledNeighbours(x, y);
11797
11798       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11799         DrawTwinkleOnField(x, y);
11800     }
11801
11802     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11803   }
11804 #endif
11805
11806   DrawAllPlayers();
11807   PlayAllPlayersSound();
11808
11809   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11810   {
11811     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11812
11813     local_player->show_envelope = 0;
11814   }
11815
11816   /* use random number generator in every frame to make it less predictable */
11817   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11818     RND(1);
11819 }
11820
11821 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11822 {
11823   int min_x = x, min_y = y, max_x = x, max_y = y;
11824   int i;
11825
11826   for (i = 0; i < MAX_PLAYERS; i++)
11827   {
11828     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11829
11830     if (!stored_player[i].active || &stored_player[i] == player)
11831       continue;
11832
11833     min_x = MIN(min_x, jx);
11834     min_y = MIN(min_y, jy);
11835     max_x = MAX(max_x, jx);
11836     max_y = MAX(max_y, jy);
11837   }
11838
11839   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11840 }
11841
11842 static boolean AllPlayersInVisibleScreen()
11843 {
11844   int i;
11845
11846   for (i = 0; i < MAX_PLAYERS; i++)
11847   {
11848     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11849
11850     if (!stored_player[i].active)
11851       continue;
11852
11853     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11854       return FALSE;
11855   }
11856
11857   return TRUE;
11858 }
11859
11860 void ScrollLevel(int dx, int dy)
11861 {
11862   int scroll_offset = 2 * TILEX_VAR;
11863   int x, y;
11864
11865   BlitBitmap(drawto_field, drawto_field,
11866              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11867              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11868              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11869              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11870              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11871              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11872
11873   if (dx != 0)
11874   {
11875     x = (dx == 1 ? BX1 : BX2);
11876     for (y = BY1; y <= BY2; y++)
11877       DrawScreenField(x, y);
11878   }
11879
11880   if (dy != 0)
11881   {
11882     y = (dy == 1 ? BY1 : BY2);
11883     for (x = BX1; x <= BX2; x++)
11884       DrawScreenField(x, y);
11885   }
11886
11887   redraw_mask |= REDRAW_FIELD;
11888 }
11889
11890 static boolean canFallDown(struct PlayerInfo *player)
11891 {
11892   int jx = player->jx, jy = player->jy;
11893
11894   return (IN_LEV_FIELD(jx, jy + 1) &&
11895           (IS_FREE(jx, jy + 1) ||
11896            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11897           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11898           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11899 }
11900
11901 static boolean canPassField(int x, int y, int move_dir)
11902 {
11903   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11904   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11905   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11906   int nextx = x + dx;
11907   int nexty = y + dy;
11908   int element = Feld[x][y];
11909
11910   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11911           !CAN_MOVE(element) &&
11912           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11913           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11914           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11915 }
11916
11917 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11918 {
11919   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11920   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11921   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11922   int newx = x + dx;
11923   int newy = y + dy;
11924
11925   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11926           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11927           (IS_DIGGABLE(Feld[newx][newy]) ||
11928            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11929            canPassField(newx, newy, move_dir)));
11930 }
11931
11932 static void CheckGravityMovement(struct PlayerInfo *player)
11933 {
11934   if (player->gravity && !player->programmed_action)
11935   {
11936     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11937     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11938     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11939     int jx = player->jx, jy = player->jy;
11940     boolean player_is_moving_to_valid_field =
11941       (!player_is_snapping &&
11942        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11943         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11944     boolean player_can_fall_down = canFallDown(player);
11945
11946     if (player_can_fall_down &&
11947         !player_is_moving_to_valid_field)
11948       player->programmed_action = MV_DOWN;
11949   }
11950 }
11951
11952 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11953 {
11954   return CheckGravityMovement(player);
11955
11956   if (player->gravity && !player->programmed_action)
11957   {
11958     int jx = player->jx, jy = player->jy;
11959     boolean field_under_player_is_free =
11960       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11961     boolean player_is_standing_on_valid_field =
11962       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11963        (IS_WALKABLE(Feld[jx][jy]) &&
11964         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11965
11966     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11967       player->programmed_action = MV_DOWN;
11968   }
11969 }
11970
11971 /*
11972   MovePlayerOneStep()
11973   -----------------------------------------------------------------------------
11974   dx, dy:               direction (non-diagonal) to try to move the player to
11975   real_dx, real_dy:     direction as read from input device (can be diagonal)
11976 */
11977
11978 boolean MovePlayerOneStep(struct PlayerInfo *player,
11979                           int dx, int dy, int real_dx, int real_dy)
11980 {
11981   int jx = player->jx, jy = player->jy;
11982   int new_jx = jx + dx, new_jy = jy + dy;
11983   int can_move;
11984   boolean player_can_move = !player->cannot_move;
11985
11986   if (!player->active || (!dx && !dy))
11987     return MP_NO_ACTION;
11988
11989   player->MovDir = (dx < 0 ? MV_LEFT :
11990                     dx > 0 ? MV_RIGHT :
11991                     dy < 0 ? MV_UP :
11992                     dy > 0 ? MV_DOWN :  MV_NONE);
11993
11994   if (!IN_LEV_FIELD(new_jx, new_jy))
11995     return MP_NO_ACTION;
11996
11997   if (!player_can_move)
11998   {
11999     if (player->MovPos == 0)
12000     {
12001       player->is_moving = FALSE;
12002       player->is_digging = FALSE;
12003       player->is_collecting = FALSE;
12004       player->is_snapping = FALSE;
12005       player->is_pushing = FALSE;
12006     }
12007   }
12008
12009   if (!options.network && game.centered_player_nr == -1 &&
12010       !AllPlayersInSight(player, new_jx, new_jy))
12011     return MP_NO_ACTION;
12012
12013   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12014   if (can_move != MP_MOVING)
12015     return can_move;
12016
12017   /* check if DigField() has caused relocation of the player */
12018   if (player->jx != jx || player->jy != jy)
12019     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12020
12021   StorePlayer[jx][jy] = 0;
12022   player->last_jx = jx;
12023   player->last_jy = jy;
12024   player->jx = new_jx;
12025   player->jy = new_jy;
12026   StorePlayer[new_jx][new_jy] = player->element_nr;
12027
12028   if (player->move_delay_value_next != -1)
12029   {
12030     player->move_delay_value = player->move_delay_value_next;
12031     player->move_delay_value_next = -1;
12032   }
12033
12034   player->MovPos =
12035     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12036
12037   player->step_counter++;
12038
12039   PlayerVisit[jx][jy] = FrameCounter;
12040
12041   player->is_moving = TRUE;
12042
12043 #if 1
12044   /* should better be called in MovePlayer(), but this breaks some tapes */
12045   ScrollPlayer(player, SCROLL_INIT);
12046 #endif
12047
12048   return MP_MOVING;
12049 }
12050
12051 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12052 {
12053   int jx = player->jx, jy = player->jy;
12054   int old_jx = jx, old_jy = jy;
12055   int moved = MP_NO_ACTION;
12056
12057   if (!player->active)
12058     return FALSE;
12059
12060   if (!dx && !dy)
12061   {
12062     if (player->MovPos == 0)
12063     {
12064       player->is_moving = FALSE;
12065       player->is_digging = FALSE;
12066       player->is_collecting = FALSE;
12067       player->is_snapping = FALSE;
12068       player->is_pushing = FALSE;
12069     }
12070
12071     return FALSE;
12072   }
12073
12074   if (player->move_delay > 0)
12075     return FALSE;
12076
12077   player->move_delay = -1;              /* set to "uninitialized" value */
12078
12079   /* store if player is automatically moved to next field */
12080   player->is_auto_moving = (player->programmed_action != MV_NONE);
12081
12082   /* remove the last programmed player action */
12083   player->programmed_action = 0;
12084
12085   if (player->MovPos)
12086   {
12087     /* should only happen if pre-1.2 tape recordings are played */
12088     /* this is only for backward compatibility */
12089
12090     int original_move_delay_value = player->move_delay_value;
12091
12092 #if DEBUG
12093     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12094            tape.counter);
12095 #endif
12096
12097     /* scroll remaining steps with finest movement resolution */
12098     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12099
12100     while (player->MovPos)
12101     {
12102       ScrollPlayer(player, SCROLL_GO_ON);
12103       ScrollScreen(NULL, SCROLL_GO_ON);
12104
12105       AdvanceFrameAndPlayerCounters(player->index_nr);
12106
12107       DrawAllPlayers();
12108       BackToFront_WithFrameDelay(0);
12109     }
12110
12111     player->move_delay_value = original_move_delay_value;
12112   }
12113
12114   player->is_active = FALSE;
12115
12116   if (player->last_move_dir & MV_HORIZONTAL)
12117   {
12118     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12119       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12120   }
12121   else
12122   {
12123     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12124       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12125   }
12126
12127   if (!moved && !player->is_active)
12128   {
12129     player->is_moving = FALSE;
12130     player->is_digging = FALSE;
12131     player->is_collecting = FALSE;
12132     player->is_snapping = FALSE;
12133     player->is_pushing = FALSE;
12134   }
12135
12136   jx = player->jx;
12137   jy = player->jy;
12138
12139   if (moved & MP_MOVING && !ScreenMovPos &&
12140       (player->index_nr == game.centered_player_nr ||
12141        game.centered_player_nr == -1))
12142   {
12143     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12144     int offset = game.scroll_delay_value;
12145
12146     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12147     {
12148       /* actual player has left the screen -- scroll in that direction */
12149       if (jx != old_jx)         /* player has moved horizontally */
12150         scroll_x += (jx - old_jx);
12151       else                      /* player has moved vertically */
12152         scroll_y += (jy - old_jy);
12153     }
12154     else
12155     {
12156       if (jx != old_jx)         /* player has moved horizontally */
12157       {
12158         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12159             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12160           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12161
12162         /* don't scroll over playfield boundaries */
12163         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12164           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12165
12166         /* don't scroll more than one field at a time */
12167         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12168
12169         /* don't scroll against the player's moving direction */
12170         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12171             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12172           scroll_x = old_scroll_x;
12173       }
12174       else                      /* player has moved vertically */
12175       {
12176         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12177             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12178           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12179
12180         /* don't scroll over playfield boundaries */
12181         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12182           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12183
12184         /* don't scroll more than one field at a time */
12185         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12186
12187         /* don't scroll against the player's moving direction */
12188         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12189             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12190           scroll_y = old_scroll_y;
12191       }
12192     }
12193
12194     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12195     {
12196       if (!options.network && game.centered_player_nr == -1 &&
12197           !AllPlayersInVisibleScreen())
12198       {
12199         scroll_x = old_scroll_x;
12200         scroll_y = old_scroll_y;
12201       }
12202       else
12203       {
12204         ScrollScreen(player, SCROLL_INIT);
12205         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12206       }
12207     }
12208   }
12209
12210   player->StepFrame = 0;
12211
12212   if (moved & MP_MOVING)
12213   {
12214     if (old_jx != jx && old_jy == jy)
12215       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12216     else if (old_jx == jx && old_jy != jy)
12217       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12218
12219     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12220
12221     player->last_move_dir = player->MovDir;
12222     player->is_moving = TRUE;
12223     player->is_snapping = FALSE;
12224     player->is_switching = FALSE;
12225     player->is_dropping = FALSE;
12226     player->is_dropping_pressed = FALSE;
12227     player->drop_pressed_delay = 0;
12228
12229 #if 0
12230     /* should better be called here than above, but this breaks some tapes */
12231     ScrollPlayer(player, SCROLL_INIT);
12232 #endif
12233   }
12234   else
12235   {
12236     CheckGravityMovementWhenNotMoving(player);
12237
12238     player->is_moving = FALSE;
12239
12240     /* at this point, the player is allowed to move, but cannot move right now
12241        (e.g. because of something blocking the way) -- ensure that the player
12242        is also allowed to move in the next frame (in old versions before 3.1.1,
12243        the player was forced to wait again for eight frames before next try) */
12244
12245     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12246       player->move_delay = 0;   /* allow direct movement in the next frame */
12247   }
12248
12249   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12250     player->move_delay = player->move_delay_value;
12251
12252   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12253   {
12254     TestIfPlayerTouchesBadThing(jx, jy);
12255     TestIfPlayerTouchesCustomElement(jx, jy);
12256   }
12257
12258   if (!player->active)
12259     RemovePlayer(player);
12260
12261   return moved;
12262 }
12263
12264 void ScrollPlayer(struct PlayerInfo *player, int mode)
12265 {
12266   int jx = player->jx, jy = player->jy;
12267   int last_jx = player->last_jx, last_jy = player->last_jy;
12268   int move_stepsize = TILEX / player->move_delay_value;
12269
12270   if (!player->active)
12271     return;
12272
12273   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12274     return;
12275
12276   if (mode == SCROLL_INIT)
12277   {
12278     player->actual_frame_counter = FrameCounter;
12279     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12280
12281     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12282         Feld[last_jx][last_jy] == EL_EMPTY)
12283     {
12284       int last_field_block_delay = 0;   /* start with no blocking at all */
12285       int block_delay_adjustment = player->block_delay_adjustment;
12286
12287       /* if player blocks last field, add delay for exactly one move */
12288       if (player->block_last_field)
12289       {
12290         last_field_block_delay += player->move_delay_value;
12291
12292         /* when blocking enabled, prevent moving up despite gravity */
12293         if (player->gravity && player->MovDir == MV_UP)
12294           block_delay_adjustment = -1;
12295       }
12296
12297       /* add block delay adjustment (also possible when not blocking) */
12298       last_field_block_delay += block_delay_adjustment;
12299
12300       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12301       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12302     }
12303
12304     if (player->MovPos != 0)    /* player has not yet reached destination */
12305       return;
12306   }
12307   else if (!FrameReached(&player->actual_frame_counter, 1))
12308     return;
12309
12310   if (player->MovPos != 0)
12311   {
12312     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12313     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12314
12315     /* before DrawPlayer() to draw correct player graphic for this case */
12316     if (player->MovPos == 0)
12317       CheckGravityMovement(player);
12318   }
12319
12320   if (player->MovPos == 0)      /* player reached destination field */
12321   {
12322     if (player->move_delay_reset_counter > 0)
12323     {
12324       player->move_delay_reset_counter--;
12325
12326       if (player->move_delay_reset_counter == 0)
12327       {
12328         /* continue with normal speed after quickly moving through gate */
12329         HALVE_PLAYER_SPEED(player);
12330
12331         /* be able to make the next move without delay */
12332         player->move_delay = 0;
12333       }
12334     }
12335
12336     player->last_jx = jx;
12337     player->last_jy = jy;
12338
12339     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12340         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12341         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12342         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12343         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12344         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12345         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12346         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12347     {
12348       DrawPlayer(player);       /* needed here only to cleanup last field */
12349       RemovePlayer(player);
12350
12351       if (local_player->friends_still_needed == 0 ||
12352           IS_SP_ELEMENT(Feld[jx][jy]))
12353         PlayerWins(player);
12354     }
12355
12356     /* this breaks one level: "machine", level 000 */
12357     {
12358       int move_direction = player->MovDir;
12359       int enter_side = MV_DIR_OPPOSITE(move_direction);
12360       int leave_side = move_direction;
12361       int old_jx = last_jx;
12362       int old_jy = last_jy;
12363       int old_element = Feld[old_jx][old_jy];
12364       int new_element = Feld[jx][jy];
12365
12366       if (IS_CUSTOM_ELEMENT(old_element))
12367         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12368                                    CE_LEFT_BY_PLAYER,
12369                                    player->index_bit, leave_side);
12370
12371       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12372                                           CE_PLAYER_LEAVES_X,
12373                                           player->index_bit, leave_side);
12374
12375       if (IS_CUSTOM_ELEMENT(new_element))
12376         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12377                                    player->index_bit, enter_side);
12378
12379       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12380                                           CE_PLAYER_ENTERS_X,
12381                                           player->index_bit, enter_side);
12382
12383       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12384                                         CE_MOVE_OF_X, move_direction);
12385     }
12386
12387     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12388     {
12389       TestIfPlayerTouchesBadThing(jx, jy);
12390       TestIfPlayerTouchesCustomElement(jx, jy);
12391
12392       /* needed because pushed element has not yet reached its destination,
12393          so it would trigger a change event at its previous field location */
12394       if (!player->is_pushing)
12395         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12396
12397       if (!player->active)
12398         RemovePlayer(player);
12399     }
12400
12401     if (!local_player->LevelSolved && level.use_step_counter)
12402     {
12403       int i;
12404
12405       TimePlayed++;
12406
12407       if (TimeLeft > 0)
12408       {
12409         TimeLeft--;
12410
12411         if (TimeLeft <= 10 && setup.time_limit)
12412           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12413
12414         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12415
12416         DisplayGameControlValues();
12417
12418         if (!TimeLeft && setup.time_limit)
12419           for (i = 0; i < MAX_PLAYERS; i++)
12420             KillPlayer(&stored_player[i]);
12421       }
12422       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12423       {
12424         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12425
12426         DisplayGameControlValues();
12427       }
12428     }
12429
12430     if (tape.single_step && tape.recording && !tape.pausing &&
12431         !player->programmed_action)
12432       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12433
12434     if (!player->programmed_action)
12435       CheckSaveEngineSnapshot(player);
12436   }
12437 }
12438
12439 void ScrollScreen(struct PlayerInfo *player, int mode)
12440 {
12441   static unsigned int screen_frame_counter = 0;
12442
12443   if (mode == SCROLL_INIT)
12444   {
12445     /* set scrolling step size according to actual player's moving speed */
12446     ScrollStepSize = TILEX / player->move_delay_value;
12447
12448     screen_frame_counter = FrameCounter;
12449     ScreenMovDir = player->MovDir;
12450     ScreenMovPos = player->MovPos;
12451     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12452     return;
12453   }
12454   else if (!FrameReached(&screen_frame_counter, 1))
12455     return;
12456
12457   if (ScreenMovPos)
12458   {
12459     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12460     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12461     redraw_mask |= REDRAW_FIELD;
12462   }
12463   else
12464     ScreenMovDir = MV_NONE;
12465 }
12466
12467 void TestIfPlayerTouchesCustomElement(int x, int y)
12468 {
12469   static int xy[4][2] =
12470   {
12471     { 0, -1 },
12472     { -1, 0 },
12473     { +1, 0 },
12474     { 0, +1 }
12475   };
12476   static int trigger_sides[4][2] =
12477   {
12478     /* center side       border side */
12479     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12480     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12481     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12482     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12483   };
12484   static int touch_dir[4] =
12485   {
12486     MV_LEFT | MV_RIGHT,
12487     MV_UP   | MV_DOWN,
12488     MV_UP   | MV_DOWN,
12489     MV_LEFT | MV_RIGHT
12490   };
12491   int center_element = Feld[x][y];      /* should always be non-moving! */
12492   int i;
12493
12494   for (i = 0; i < NUM_DIRECTIONS; i++)
12495   {
12496     int xx = x + xy[i][0];
12497     int yy = y + xy[i][1];
12498     int center_side = trigger_sides[i][0];
12499     int border_side = trigger_sides[i][1];
12500     int border_element;
12501
12502     if (!IN_LEV_FIELD(xx, yy))
12503       continue;
12504
12505     if (IS_PLAYER(x, y))                /* player found at center element */
12506     {
12507       struct PlayerInfo *player = PLAYERINFO(x, y);
12508
12509       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12510         border_element = Feld[xx][yy];          /* may be moving! */
12511       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12512         border_element = Feld[xx][yy];
12513       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12514         border_element = MovingOrBlocked2Element(xx, yy);
12515       else
12516         continue;               /* center and border element do not touch */
12517
12518       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12519                                  player->index_bit, border_side);
12520       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12521                                           CE_PLAYER_TOUCHES_X,
12522                                           player->index_bit, border_side);
12523
12524       {
12525         /* use player element that is initially defined in the level playfield,
12526            not the player element that corresponds to the runtime player number
12527            (example: a level that contains EL_PLAYER_3 as the only player would
12528            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12529         int player_element = PLAYERINFO(x, y)->initial_element;
12530
12531         CheckElementChangeBySide(xx, yy, border_element, player_element,
12532                                  CE_TOUCHING_X, border_side);
12533       }
12534     }
12535     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12536     {
12537       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12538
12539       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12540       {
12541         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12542           continue;             /* center and border element do not touch */
12543       }
12544
12545       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12546                                  player->index_bit, center_side);
12547       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12548                                           CE_PLAYER_TOUCHES_X,
12549                                           player->index_bit, center_side);
12550
12551       {
12552         /* use player element that is initially defined in the level playfield,
12553            not the player element that corresponds to the runtime player number
12554            (example: a level that contains EL_PLAYER_3 as the only player would
12555            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12556         int player_element = PLAYERINFO(xx, yy)->initial_element;
12557
12558         CheckElementChangeBySide(x, y, center_element, player_element,
12559                                  CE_TOUCHING_X, center_side);
12560       }
12561
12562       break;
12563     }
12564   }
12565 }
12566
12567 void TestIfElementTouchesCustomElement(int x, int y)
12568 {
12569   static int xy[4][2] =
12570   {
12571     { 0, -1 },
12572     { -1, 0 },
12573     { +1, 0 },
12574     { 0, +1 }
12575   };
12576   static int trigger_sides[4][2] =
12577   {
12578     /* center side      border side */
12579     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12580     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12581     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12582     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12583   };
12584   static int touch_dir[4] =
12585   {
12586     MV_LEFT | MV_RIGHT,
12587     MV_UP   | MV_DOWN,
12588     MV_UP   | MV_DOWN,
12589     MV_LEFT | MV_RIGHT
12590   };
12591   boolean change_center_element = FALSE;
12592   int center_element = Feld[x][y];      /* should always be non-moving! */
12593   int border_element_old[NUM_DIRECTIONS];
12594   int i;
12595
12596   for (i = 0; i < NUM_DIRECTIONS; i++)
12597   {
12598     int xx = x + xy[i][0];
12599     int yy = y + xy[i][1];
12600     int border_element;
12601
12602     border_element_old[i] = -1;
12603
12604     if (!IN_LEV_FIELD(xx, yy))
12605       continue;
12606
12607     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12608       border_element = Feld[xx][yy];    /* may be moving! */
12609     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12610       border_element = Feld[xx][yy];
12611     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12612       border_element = MovingOrBlocked2Element(xx, yy);
12613     else
12614       continue;                 /* center and border element do not touch */
12615
12616     border_element_old[i] = border_element;
12617   }
12618
12619   for (i = 0; i < NUM_DIRECTIONS; i++)
12620   {
12621     int xx = x + xy[i][0];
12622     int yy = y + xy[i][1];
12623     int center_side = trigger_sides[i][0];
12624     int border_element = border_element_old[i];
12625
12626     if (border_element == -1)
12627       continue;
12628
12629     /* check for change of border element */
12630     CheckElementChangeBySide(xx, yy, border_element, center_element,
12631                              CE_TOUCHING_X, center_side);
12632
12633     /* (center element cannot be player, so we dont have to check this here) */
12634   }
12635
12636   for (i = 0; i < NUM_DIRECTIONS; i++)
12637   {
12638     int xx = x + xy[i][0];
12639     int yy = y + xy[i][1];
12640     int border_side = trigger_sides[i][1];
12641     int border_element = border_element_old[i];
12642
12643     if (border_element == -1)
12644       continue;
12645
12646     /* check for change of center element (but change it only once) */
12647     if (!change_center_element)
12648       change_center_element =
12649         CheckElementChangeBySide(x, y, center_element, border_element,
12650                                  CE_TOUCHING_X, border_side);
12651
12652     if (IS_PLAYER(xx, yy))
12653     {
12654       /* use player element that is initially defined in the level playfield,
12655          not the player element that corresponds to the runtime player number
12656          (example: a level that contains EL_PLAYER_3 as the only player would
12657          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12658       int player_element = PLAYERINFO(xx, yy)->initial_element;
12659
12660       CheckElementChangeBySide(x, y, center_element, player_element,
12661                                CE_TOUCHING_X, border_side);
12662     }
12663   }
12664 }
12665
12666 void TestIfElementHitsCustomElement(int x, int y, int direction)
12667 {
12668   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12669   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12670   int hitx = x + dx, hity = y + dy;
12671   int hitting_element = Feld[x][y];
12672   int touched_element;
12673
12674   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12675     return;
12676
12677   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12678                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12679
12680   if (IN_LEV_FIELD(hitx, hity))
12681   {
12682     int opposite_direction = MV_DIR_OPPOSITE(direction);
12683     int hitting_side = direction;
12684     int touched_side = opposite_direction;
12685     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12686                           MovDir[hitx][hity] != direction ||
12687                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12688
12689     object_hit = TRUE;
12690
12691     if (object_hit)
12692     {
12693       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12694                                CE_HITTING_X, touched_side);
12695
12696       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12697                                CE_HIT_BY_X, hitting_side);
12698
12699       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12700                                CE_HIT_BY_SOMETHING, opposite_direction);
12701
12702       if (IS_PLAYER(hitx, hity))
12703       {
12704         /* use player element that is initially defined in the level playfield,
12705            not the player element that corresponds to the runtime player number
12706            (example: a level that contains EL_PLAYER_3 as the only player would
12707            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12708         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12709
12710         CheckElementChangeBySide(x, y, hitting_element, player_element,
12711                                  CE_HITTING_X, touched_side);
12712       }
12713     }
12714   }
12715
12716   /* "hitting something" is also true when hitting the playfield border */
12717   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12718                            CE_HITTING_SOMETHING, direction);
12719 }
12720
12721 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12722 {
12723   int i, kill_x = -1, kill_y = -1;
12724
12725   int bad_element = -1;
12726   static int test_xy[4][2] =
12727   {
12728     { 0, -1 },
12729     { -1, 0 },
12730     { +1, 0 },
12731     { 0, +1 }
12732   };
12733   static int test_dir[4] =
12734   {
12735     MV_UP,
12736     MV_LEFT,
12737     MV_RIGHT,
12738     MV_DOWN
12739   };
12740
12741   for (i = 0; i < NUM_DIRECTIONS; i++)
12742   {
12743     int test_x, test_y, test_move_dir, test_element;
12744
12745     test_x = good_x + test_xy[i][0];
12746     test_y = good_y + test_xy[i][1];
12747
12748     if (!IN_LEV_FIELD(test_x, test_y))
12749       continue;
12750
12751     test_move_dir =
12752       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12753
12754     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12755
12756     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12757        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12758     */
12759     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12760         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12761     {
12762       kill_x = test_x;
12763       kill_y = test_y;
12764       bad_element = test_element;
12765
12766       break;
12767     }
12768   }
12769
12770   if (kill_x != -1 || kill_y != -1)
12771   {
12772     if (IS_PLAYER(good_x, good_y))
12773     {
12774       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12775
12776       if (player->shield_deadly_time_left > 0 &&
12777           !IS_INDESTRUCTIBLE(bad_element))
12778         Bang(kill_x, kill_y);
12779       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12780         KillPlayer(player);
12781     }
12782     else
12783       Bang(good_x, good_y);
12784   }
12785 }
12786
12787 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12788 {
12789   int i, kill_x = -1, kill_y = -1;
12790   int bad_element = Feld[bad_x][bad_y];
12791   static int test_xy[4][2] =
12792   {
12793     { 0, -1 },
12794     { -1, 0 },
12795     { +1, 0 },
12796     { 0, +1 }
12797   };
12798   static int touch_dir[4] =
12799   {
12800     MV_LEFT | MV_RIGHT,
12801     MV_UP   | MV_DOWN,
12802     MV_UP   | MV_DOWN,
12803     MV_LEFT | MV_RIGHT
12804   };
12805   static int test_dir[4] =
12806   {
12807     MV_UP,
12808     MV_LEFT,
12809     MV_RIGHT,
12810     MV_DOWN
12811   };
12812
12813   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12814     return;
12815
12816   for (i = 0; i < NUM_DIRECTIONS; i++)
12817   {
12818     int test_x, test_y, test_move_dir, test_element;
12819
12820     test_x = bad_x + test_xy[i][0];
12821     test_y = bad_y + test_xy[i][1];
12822
12823     if (!IN_LEV_FIELD(test_x, test_y))
12824       continue;
12825
12826     test_move_dir =
12827       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12828
12829     test_element = Feld[test_x][test_y];
12830
12831     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12832        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12833     */
12834     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12835         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12836     {
12837       /* good thing is player or penguin that does not move away */
12838       if (IS_PLAYER(test_x, test_y))
12839       {
12840         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12841
12842         if (bad_element == EL_ROBOT && player->is_moving)
12843           continue;     /* robot does not kill player if he is moving */
12844
12845         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12846         {
12847           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12848             continue;           /* center and border element do not touch */
12849         }
12850
12851         kill_x = test_x;
12852         kill_y = test_y;
12853
12854         break;
12855       }
12856       else if (test_element == EL_PENGUIN)
12857       {
12858         kill_x = test_x;
12859         kill_y = test_y;
12860
12861         break;
12862       }
12863     }
12864   }
12865
12866   if (kill_x != -1 || kill_y != -1)
12867   {
12868     if (IS_PLAYER(kill_x, kill_y))
12869     {
12870       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12871
12872       if (player->shield_deadly_time_left > 0 &&
12873           !IS_INDESTRUCTIBLE(bad_element))
12874         Bang(bad_x, bad_y);
12875       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12876         KillPlayer(player);
12877     }
12878     else
12879       Bang(kill_x, kill_y);
12880   }
12881 }
12882
12883 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12884 {
12885   int bad_element = Feld[bad_x][bad_y];
12886   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12887   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12888   int test_x = bad_x + dx, test_y = bad_y + dy;
12889   int test_move_dir, test_element;
12890   int kill_x = -1, kill_y = -1;
12891
12892   if (!IN_LEV_FIELD(test_x, test_y))
12893     return;
12894
12895   test_move_dir =
12896     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12897
12898   test_element = Feld[test_x][test_y];
12899
12900   if (test_move_dir != bad_move_dir)
12901   {
12902     /* good thing can be player or penguin that does not move away */
12903     if (IS_PLAYER(test_x, test_y))
12904     {
12905       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12906
12907       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12908          player as being hit when he is moving towards the bad thing, because
12909          the "get hit by" condition would be lost after the player stops) */
12910       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12911         return;         /* player moves away from bad thing */
12912
12913       kill_x = test_x;
12914       kill_y = test_y;
12915     }
12916     else if (test_element == EL_PENGUIN)
12917     {
12918       kill_x = test_x;
12919       kill_y = test_y;
12920     }
12921   }
12922
12923   if (kill_x != -1 || kill_y != -1)
12924   {
12925     if (IS_PLAYER(kill_x, kill_y))
12926     {
12927       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12928
12929       if (player->shield_deadly_time_left > 0 &&
12930           !IS_INDESTRUCTIBLE(bad_element))
12931         Bang(bad_x, bad_y);
12932       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12933         KillPlayer(player);
12934     }
12935     else
12936       Bang(kill_x, kill_y);
12937   }
12938 }
12939
12940 void TestIfPlayerTouchesBadThing(int x, int y)
12941 {
12942   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12943 }
12944
12945 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12946 {
12947   TestIfGoodThingHitsBadThing(x, y, move_dir);
12948 }
12949
12950 void TestIfBadThingTouchesPlayer(int x, int y)
12951 {
12952   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12953 }
12954
12955 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12956 {
12957   TestIfBadThingHitsGoodThing(x, y, move_dir);
12958 }
12959
12960 void TestIfFriendTouchesBadThing(int x, int y)
12961 {
12962   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12963 }
12964
12965 void TestIfBadThingTouchesFriend(int x, int y)
12966 {
12967   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12968 }
12969
12970 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12971 {
12972   int i, kill_x = bad_x, kill_y = bad_y;
12973   static int xy[4][2] =
12974   {
12975     { 0, -1 },
12976     { -1, 0 },
12977     { +1, 0 },
12978     { 0, +1 }
12979   };
12980
12981   for (i = 0; i < NUM_DIRECTIONS; i++)
12982   {
12983     int x, y, element;
12984
12985     x = bad_x + xy[i][0];
12986     y = bad_y + xy[i][1];
12987     if (!IN_LEV_FIELD(x, y))
12988       continue;
12989
12990     element = Feld[x][y];
12991     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12992         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12993     {
12994       kill_x = x;
12995       kill_y = y;
12996       break;
12997     }
12998   }
12999
13000   if (kill_x != bad_x || kill_y != bad_y)
13001     Bang(bad_x, bad_y);
13002 }
13003
13004 void KillPlayer(struct PlayerInfo *player)
13005 {
13006   int jx = player->jx, jy = player->jy;
13007
13008   if (!player->active)
13009     return;
13010
13011 #if 0
13012   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13013          player->killed, player->active, player->reanimated);
13014 #endif
13015
13016   /* the following code was introduced to prevent an infinite loop when calling
13017      -> Bang()
13018      -> CheckTriggeredElementChangeExt()
13019      -> ExecuteCustomElementAction()
13020      -> KillPlayer()
13021      -> (infinitely repeating the above sequence of function calls)
13022      which occurs when killing the player while having a CE with the setting
13023      "kill player X when explosion of <player X>"; the solution using a new
13024      field "player->killed" was chosen for backwards compatibility, although
13025      clever use of the fields "player->active" etc. would probably also work */
13026 #if 1
13027   if (player->killed)
13028     return;
13029 #endif
13030
13031   player->killed = TRUE;
13032
13033   /* remove accessible field at the player's position */
13034   Feld[jx][jy] = EL_EMPTY;
13035
13036   /* deactivate shield (else Bang()/Explode() would not work right) */
13037   player->shield_normal_time_left = 0;
13038   player->shield_deadly_time_left = 0;
13039
13040 #if 0
13041   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13042          player->killed, player->active, player->reanimated);
13043 #endif
13044
13045   Bang(jx, jy);
13046
13047 #if 0
13048   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13049          player->killed, player->active, player->reanimated);
13050 #endif
13051
13052   if (player->reanimated)       /* killed player may have been reanimated */
13053     player->killed = player->reanimated = FALSE;
13054   else
13055     BuryPlayer(player);
13056 }
13057
13058 static void KillPlayerUnlessEnemyProtected(int x, int y)
13059 {
13060   if (!PLAYER_ENEMY_PROTECTED(x, y))
13061     KillPlayer(PLAYERINFO(x, y));
13062 }
13063
13064 static void KillPlayerUnlessExplosionProtected(int x, int y)
13065 {
13066   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13067     KillPlayer(PLAYERINFO(x, y));
13068 }
13069
13070 void BuryPlayer(struct PlayerInfo *player)
13071 {
13072   int jx = player->jx, jy = player->jy;
13073
13074   if (!player->active)
13075     return;
13076
13077   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13078   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13079
13080   player->GameOver = TRUE;
13081   RemovePlayer(player);
13082 }
13083
13084 void RemovePlayer(struct PlayerInfo *player)
13085 {
13086   int jx = player->jx, jy = player->jy;
13087   int i, found = FALSE;
13088
13089   player->present = FALSE;
13090   player->active = FALSE;
13091
13092   if (!ExplodeField[jx][jy])
13093     StorePlayer[jx][jy] = 0;
13094
13095   if (player->is_moving)
13096     TEST_DrawLevelField(player->last_jx, player->last_jy);
13097
13098   for (i = 0; i < MAX_PLAYERS; i++)
13099     if (stored_player[i].active)
13100       found = TRUE;
13101
13102   if (!found)
13103     AllPlayersGone = TRUE;
13104
13105   ExitX = ZX = jx;
13106   ExitY = ZY = jy;
13107 }
13108
13109 static void setFieldForSnapping(int x, int y, int element, int direction)
13110 {
13111   struct ElementInfo *ei = &element_info[element];
13112   int direction_bit = MV_DIR_TO_BIT(direction);
13113   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13114   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13115                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13116
13117   Feld[x][y] = EL_ELEMENT_SNAPPING;
13118   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13119
13120   ResetGfxAnimation(x, y);
13121
13122   GfxElement[x][y] = element;
13123   GfxAction[x][y] = action;
13124   GfxDir[x][y] = direction;
13125   GfxFrame[x][y] = -1;
13126 }
13127
13128 /*
13129   =============================================================================
13130   checkDiagonalPushing()
13131   -----------------------------------------------------------------------------
13132   check if diagonal input device direction results in pushing of object
13133   (by checking if the alternative direction is walkable, diggable, ...)
13134   =============================================================================
13135 */
13136
13137 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13138                                     int x, int y, int real_dx, int real_dy)
13139 {
13140   int jx, jy, dx, dy, xx, yy;
13141
13142   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13143     return TRUE;
13144
13145   /* diagonal direction: check alternative direction */
13146   jx = player->jx;
13147   jy = player->jy;
13148   dx = x - jx;
13149   dy = y - jy;
13150   xx = jx + (dx == 0 ? real_dx : 0);
13151   yy = jy + (dy == 0 ? real_dy : 0);
13152
13153   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13154 }
13155
13156 /*
13157   =============================================================================
13158   DigField()
13159   -----------------------------------------------------------------------------
13160   x, y:                 field next to player (non-diagonal) to try to dig to
13161   real_dx, real_dy:     direction as read from input device (can be diagonal)
13162   =============================================================================
13163 */
13164
13165 static int DigField(struct PlayerInfo *player,
13166                     int oldx, int oldy, int x, int y,
13167                     int real_dx, int real_dy, int mode)
13168 {
13169   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13170   boolean player_was_pushing = player->is_pushing;
13171   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13172   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13173   int jx = oldx, jy = oldy;
13174   int dx = x - jx, dy = y - jy;
13175   int nextx = x + dx, nexty = y + dy;
13176   int move_direction = (dx == -1 ? MV_LEFT  :
13177                         dx == +1 ? MV_RIGHT :
13178                         dy == -1 ? MV_UP    :
13179                         dy == +1 ? MV_DOWN  : MV_NONE);
13180   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13181   int dig_side = MV_DIR_OPPOSITE(move_direction);
13182   int old_element = Feld[jx][jy];
13183   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13184   int collect_count;
13185
13186   if (is_player)                /* function can also be called by EL_PENGUIN */
13187   {
13188     if (player->MovPos == 0)
13189     {
13190       player->is_digging = FALSE;
13191       player->is_collecting = FALSE;
13192     }
13193
13194     if (player->MovPos == 0)    /* last pushing move finished */
13195       player->is_pushing = FALSE;
13196
13197     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13198     {
13199       player->is_switching = FALSE;
13200       player->push_delay = -1;
13201
13202       return MP_NO_ACTION;
13203     }
13204   }
13205
13206   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13207     old_element = Back[jx][jy];
13208
13209   /* in case of element dropped at player position, check background */
13210   else if (Back[jx][jy] != EL_EMPTY &&
13211            game.engine_version >= VERSION_IDENT(2,2,0,0))
13212     old_element = Back[jx][jy];
13213
13214   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13215     return MP_NO_ACTION;        /* field has no opening in this direction */
13216
13217   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13218     return MP_NO_ACTION;        /* field has no opening in this direction */
13219
13220   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13221   {
13222     SplashAcid(x, y);
13223
13224     Feld[jx][jy] = player->artwork_element;
13225     InitMovingField(jx, jy, MV_DOWN);
13226     Store[jx][jy] = EL_ACID;
13227     ContinueMoving(jx, jy);
13228     BuryPlayer(player);
13229
13230     return MP_DONT_RUN_INTO;
13231   }
13232
13233   if (player_can_move && DONT_RUN_INTO(element))
13234   {
13235     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13236
13237     return MP_DONT_RUN_INTO;
13238   }
13239
13240   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13241     return MP_NO_ACTION;
13242
13243   collect_count = element_info[element].collect_count_initial;
13244
13245   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13246     return MP_NO_ACTION;
13247
13248   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13249     player_can_move = player_can_move_or_snap;
13250
13251   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13252       game.engine_version >= VERSION_IDENT(2,2,0,0))
13253   {
13254     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13255                                player->index_bit, dig_side);
13256     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13257                                         player->index_bit, dig_side);
13258
13259     if (element == EL_DC_LANDMINE)
13260       Bang(x, y);
13261
13262     if (Feld[x][y] != element)          /* field changed by snapping */
13263       return MP_ACTION;
13264
13265     return MP_NO_ACTION;
13266   }
13267
13268   if (player->gravity && is_player && !player->is_auto_moving &&
13269       canFallDown(player) && move_direction != MV_DOWN &&
13270       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13271     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13272
13273   if (player_can_move &&
13274       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13275   {
13276     int sound_element = SND_ELEMENT(element);
13277     int sound_action = ACTION_WALKING;
13278
13279     if (IS_RND_GATE(element))
13280     {
13281       if (!player->key[RND_GATE_NR(element)])
13282         return MP_NO_ACTION;
13283     }
13284     else if (IS_RND_GATE_GRAY(element))
13285     {
13286       if (!player->key[RND_GATE_GRAY_NR(element)])
13287         return MP_NO_ACTION;
13288     }
13289     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13290     {
13291       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13292         return MP_NO_ACTION;
13293     }
13294     else if (element == EL_EXIT_OPEN ||
13295              element == EL_EM_EXIT_OPEN ||
13296              element == EL_EM_EXIT_OPENING ||
13297              element == EL_STEEL_EXIT_OPEN ||
13298              element == EL_EM_STEEL_EXIT_OPEN ||
13299              element == EL_EM_STEEL_EXIT_OPENING ||
13300              element == EL_SP_EXIT_OPEN ||
13301              element == EL_SP_EXIT_OPENING)
13302     {
13303       sound_action = ACTION_PASSING;    /* player is passing exit */
13304     }
13305     else if (element == EL_EMPTY)
13306     {
13307       sound_action = ACTION_MOVING;             /* nothing to walk on */
13308     }
13309
13310     /* play sound from background or player, whatever is available */
13311     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13312       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13313     else
13314       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13315   }
13316   else if (player_can_move &&
13317            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13318   {
13319     if (!ACCESS_FROM(element, opposite_direction))
13320       return MP_NO_ACTION;      /* field not accessible from this direction */
13321
13322     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13323       return MP_NO_ACTION;
13324
13325     if (IS_EM_GATE(element))
13326     {
13327       if (!player->key[EM_GATE_NR(element)])
13328         return MP_NO_ACTION;
13329     }
13330     else if (IS_EM_GATE_GRAY(element))
13331     {
13332       if (!player->key[EM_GATE_GRAY_NR(element)])
13333         return MP_NO_ACTION;
13334     }
13335     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13336     {
13337       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13338         return MP_NO_ACTION;
13339     }
13340     else if (IS_EMC_GATE(element))
13341     {
13342       if (!player->key[EMC_GATE_NR(element)])
13343         return MP_NO_ACTION;
13344     }
13345     else if (IS_EMC_GATE_GRAY(element))
13346     {
13347       if (!player->key[EMC_GATE_GRAY_NR(element)])
13348         return MP_NO_ACTION;
13349     }
13350     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13351     {
13352       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13353         return MP_NO_ACTION;
13354     }
13355     else if (element == EL_DC_GATE_WHITE ||
13356              element == EL_DC_GATE_WHITE_GRAY ||
13357              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13358     {
13359       if (player->num_white_keys == 0)
13360         return MP_NO_ACTION;
13361
13362       player->num_white_keys--;
13363     }
13364     else if (IS_SP_PORT(element))
13365     {
13366       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13367           element == EL_SP_GRAVITY_PORT_RIGHT ||
13368           element == EL_SP_GRAVITY_PORT_UP ||
13369           element == EL_SP_GRAVITY_PORT_DOWN)
13370         player->gravity = !player->gravity;
13371       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13372                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13373                element == EL_SP_GRAVITY_ON_PORT_UP ||
13374                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13375         player->gravity = TRUE;
13376       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13377                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13378                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13379                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13380         player->gravity = FALSE;
13381     }
13382
13383     /* automatically move to the next field with double speed */
13384     player->programmed_action = move_direction;
13385
13386     if (player->move_delay_reset_counter == 0)
13387     {
13388       player->move_delay_reset_counter = 2;     /* two double speed steps */
13389
13390       DOUBLE_PLAYER_SPEED(player);
13391     }
13392
13393     PlayLevelSoundAction(x, y, ACTION_PASSING);
13394   }
13395   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13396   {
13397     RemoveField(x, y);
13398
13399     if (mode != DF_SNAP)
13400     {
13401       GfxElement[x][y] = GFX_ELEMENT(element);
13402       player->is_digging = TRUE;
13403     }
13404
13405     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13406
13407     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13408                                         player->index_bit, dig_side);
13409
13410     if (mode == DF_SNAP)
13411     {
13412       if (level.block_snap_field)
13413         setFieldForSnapping(x, y, element, move_direction);
13414       else
13415         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13416
13417       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13418                                           player->index_bit, dig_side);
13419     }
13420   }
13421   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13422   {
13423     RemoveField(x, y);
13424
13425     if (is_player && mode != DF_SNAP)
13426     {
13427       GfxElement[x][y] = element;
13428       player->is_collecting = TRUE;
13429     }
13430
13431     if (element == EL_SPEED_PILL)
13432     {
13433       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13434     }
13435     else if (element == EL_EXTRA_TIME && level.time > 0)
13436     {
13437       TimeLeft += level.extra_time;
13438
13439       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13440
13441       DisplayGameControlValues();
13442     }
13443     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13444     {
13445       player->shield_normal_time_left += level.shield_normal_time;
13446       if (element == EL_SHIELD_DEADLY)
13447         player->shield_deadly_time_left += level.shield_deadly_time;
13448     }
13449     else if (element == EL_DYNAMITE ||
13450              element == EL_EM_DYNAMITE ||
13451              element == EL_SP_DISK_RED)
13452     {
13453       if (player->inventory_size < MAX_INVENTORY_SIZE)
13454         player->inventory_element[player->inventory_size++] = element;
13455
13456       DrawGameDoorValues();
13457     }
13458     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13459     {
13460       player->dynabomb_count++;
13461       player->dynabombs_left++;
13462     }
13463     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13464     {
13465       player->dynabomb_size++;
13466     }
13467     else if (element == EL_DYNABOMB_INCREASE_POWER)
13468     {
13469       player->dynabomb_xl = TRUE;
13470     }
13471     else if (IS_KEY(element))
13472     {
13473       player->key[KEY_NR(element)] = TRUE;
13474
13475       DrawGameDoorValues();
13476     }
13477     else if (element == EL_DC_KEY_WHITE)
13478     {
13479       player->num_white_keys++;
13480
13481       /* display white keys? */
13482       /* DrawGameDoorValues(); */
13483     }
13484     else if (IS_ENVELOPE(element))
13485     {
13486       player->show_envelope = element;
13487     }
13488     else if (element == EL_EMC_LENSES)
13489     {
13490       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13491
13492       RedrawAllInvisibleElementsForLenses();
13493     }
13494     else if (element == EL_EMC_MAGNIFIER)
13495     {
13496       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13497
13498       RedrawAllInvisibleElementsForMagnifier();
13499     }
13500     else if (IS_DROPPABLE(element) ||
13501              IS_THROWABLE(element))     /* can be collected and dropped */
13502     {
13503       int i;
13504
13505       if (collect_count == 0)
13506         player->inventory_infinite_element = element;
13507       else
13508         for (i = 0; i < collect_count; i++)
13509           if (player->inventory_size < MAX_INVENTORY_SIZE)
13510             player->inventory_element[player->inventory_size++] = element;
13511
13512       DrawGameDoorValues();
13513     }
13514     else if (collect_count > 0)
13515     {
13516       local_player->gems_still_needed -= collect_count;
13517       if (local_player->gems_still_needed < 0)
13518         local_player->gems_still_needed = 0;
13519
13520       game.snapshot.collected_item = TRUE;
13521
13522       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13523
13524       DisplayGameControlValues();
13525     }
13526
13527     RaiseScoreElement(element);
13528     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13529
13530     if (is_player)
13531       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13532                                           player->index_bit, dig_side);
13533
13534     if (mode == DF_SNAP)
13535     {
13536       if (level.block_snap_field)
13537         setFieldForSnapping(x, y, element, move_direction);
13538       else
13539         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13540
13541       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13542                                           player->index_bit, dig_side);
13543     }
13544   }
13545   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13546   {
13547     if (mode == DF_SNAP && element != EL_BD_ROCK)
13548       return MP_NO_ACTION;
13549
13550     if (CAN_FALL(element) && dy)
13551       return MP_NO_ACTION;
13552
13553     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13554         !(element == EL_SPRING && level.use_spring_bug))
13555       return MP_NO_ACTION;
13556
13557     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13558         ((move_direction & MV_VERTICAL &&
13559           ((element_info[element].move_pattern & MV_LEFT &&
13560             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13561            (element_info[element].move_pattern & MV_RIGHT &&
13562             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13563          (move_direction & MV_HORIZONTAL &&
13564           ((element_info[element].move_pattern & MV_UP &&
13565             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13566            (element_info[element].move_pattern & MV_DOWN &&
13567             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13568       return MP_NO_ACTION;
13569
13570     /* do not push elements already moving away faster than player */
13571     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13572         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13573       return MP_NO_ACTION;
13574
13575     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13576     {
13577       if (player->push_delay_value == -1 || !player_was_pushing)
13578         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13579     }
13580     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13581     {
13582       if (player->push_delay_value == -1)
13583         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13584     }
13585     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13586     {
13587       if (!player->is_pushing)
13588         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13589     }
13590
13591     player->is_pushing = TRUE;
13592     player->is_active = TRUE;
13593
13594     if (!(IN_LEV_FIELD(nextx, nexty) &&
13595           (IS_FREE(nextx, nexty) ||
13596            (IS_SB_ELEMENT(element) &&
13597             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13598            (IS_CUSTOM_ELEMENT(element) &&
13599             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13600       return MP_NO_ACTION;
13601
13602     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13603       return MP_NO_ACTION;
13604
13605     if (player->push_delay == -1)       /* new pushing; restart delay */
13606       player->push_delay = 0;
13607
13608     if (player->push_delay < player->push_delay_value &&
13609         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13610         element != EL_SPRING && element != EL_BALLOON)
13611     {
13612       /* make sure that there is no move delay before next try to push */
13613       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13614         player->move_delay = 0;
13615
13616       return MP_NO_ACTION;
13617     }
13618
13619     if (IS_CUSTOM_ELEMENT(element) &&
13620         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13621     {
13622       if (!DigFieldByCE(nextx, nexty, element))
13623         return MP_NO_ACTION;
13624     }
13625
13626     if (IS_SB_ELEMENT(element))
13627     {
13628       if (element == EL_SOKOBAN_FIELD_FULL)
13629       {
13630         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13631         local_player->sokobanfields_still_needed++;
13632       }
13633
13634       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13635       {
13636         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13637         local_player->sokobanfields_still_needed--;
13638       }
13639
13640       Feld[x][y] = EL_SOKOBAN_OBJECT;
13641
13642       if (Back[x][y] == Back[nextx][nexty])
13643         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13644       else if (Back[x][y] != 0)
13645         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13646                                     ACTION_EMPTYING);
13647       else
13648         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13649                                     ACTION_FILLING);
13650
13651       if (local_player->sokobanfields_still_needed == 0 &&
13652           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13653       {
13654         PlayerWins(player);
13655
13656         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13657       }
13658     }
13659     else
13660       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13661
13662     InitMovingField(x, y, move_direction);
13663     GfxAction[x][y] = ACTION_PUSHING;
13664
13665     if (mode == DF_SNAP)
13666       ContinueMoving(x, y);
13667     else
13668       MovPos[x][y] = (dx != 0 ? dx : dy);
13669
13670     Pushed[x][y] = TRUE;
13671     Pushed[nextx][nexty] = TRUE;
13672
13673     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13674       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13675     else
13676       player->push_delay_value = -1;    /* get new value later */
13677
13678     /* check for element change _after_ element has been pushed */
13679     if (game.use_change_when_pushing_bug)
13680     {
13681       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13682                                  player->index_bit, dig_side);
13683       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13684                                           player->index_bit, dig_side);
13685     }
13686   }
13687   else if (IS_SWITCHABLE(element))
13688   {
13689     if (PLAYER_SWITCHING(player, x, y))
13690     {
13691       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13692                                           player->index_bit, dig_side);
13693
13694       return MP_ACTION;
13695     }
13696
13697     player->is_switching = TRUE;
13698     player->switch_x = x;
13699     player->switch_y = y;
13700
13701     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13702
13703     if (element == EL_ROBOT_WHEEL)
13704     {
13705       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13706       ZX = x;
13707       ZY = y;
13708
13709       game.robot_wheel_active = TRUE;
13710
13711       TEST_DrawLevelField(x, y);
13712     }
13713     else if (element == EL_SP_TERMINAL)
13714     {
13715       int xx, yy;
13716
13717       SCAN_PLAYFIELD(xx, yy)
13718       {
13719         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13720         {
13721           Bang(xx, yy);
13722         }
13723         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13724         {
13725           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13726
13727           ResetGfxAnimation(xx, yy);
13728           TEST_DrawLevelField(xx, yy);
13729         }
13730       }
13731     }
13732     else if (IS_BELT_SWITCH(element))
13733     {
13734       ToggleBeltSwitch(x, y);
13735     }
13736     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13737              element == EL_SWITCHGATE_SWITCH_DOWN ||
13738              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13739              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13740     {
13741       ToggleSwitchgateSwitch(x, y);
13742     }
13743     else if (element == EL_LIGHT_SWITCH ||
13744              element == EL_LIGHT_SWITCH_ACTIVE)
13745     {
13746       ToggleLightSwitch(x, y);
13747     }
13748     else if (element == EL_TIMEGATE_SWITCH ||
13749              element == EL_DC_TIMEGATE_SWITCH)
13750     {
13751       ActivateTimegateSwitch(x, y);
13752     }
13753     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13754              element == EL_BALLOON_SWITCH_RIGHT ||
13755              element == EL_BALLOON_SWITCH_UP    ||
13756              element == EL_BALLOON_SWITCH_DOWN  ||
13757              element == EL_BALLOON_SWITCH_NONE  ||
13758              element == EL_BALLOON_SWITCH_ANY)
13759     {
13760       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13761                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13762                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13763                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13764                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13765                              move_direction);
13766     }
13767     else if (element == EL_LAMP)
13768     {
13769       Feld[x][y] = EL_LAMP_ACTIVE;
13770       local_player->lights_still_needed--;
13771
13772       ResetGfxAnimation(x, y);
13773       TEST_DrawLevelField(x, y);
13774     }
13775     else if (element == EL_TIME_ORB_FULL)
13776     {
13777       Feld[x][y] = EL_TIME_ORB_EMPTY;
13778
13779       if (level.time > 0 || level.use_time_orb_bug)
13780       {
13781         TimeLeft += level.time_orb_time;
13782         game.no_time_limit = FALSE;
13783
13784         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13785
13786         DisplayGameControlValues();
13787       }
13788
13789       ResetGfxAnimation(x, y);
13790       TEST_DrawLevelField(x, y);
13791     }
13792     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13793              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13794     {
13795       int xx, yy;
13796
13797       game.ball_state = !game.ball_state;
13798
13799       SCAN_PLAYFIELD(xx, yy)
13800       {
13801         int e = Feld[xx][yy];
13802
13803         if (game.ball_state)
13804         {
13805           if (e == EL_EMC_MAGIC_BALL)
13806             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13807           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13808             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13809         }
13810         else
13811         {
13812           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13813             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13814           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13815             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13816         }
13817       }
13818     }
13819
13820     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13821                                         player->index_bit, dig_side);
13822
13823     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13824                                         player->index_bit, dig_side);
13825
13826     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13827                                         player->index_bit, dig_side);
13828
13829     return MP_ACTION;
13830   }
13831   else
13832   {
13833     if (!PLAYER_SWITCHING(player, x, y))
13834     {
13835       player->is_switching = TRUE;
13836       player->switch_x = x;
13837       player->switch_y = y;
13838
13839       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13840                                  player->index_bit, dig_side);
13841       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13842                                           player->index_bit, dig_side);
13843
13844       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13845                                  player->index_bit, dig_side);
13846       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13847                                           player->index_bit, dig_side);
13848     }
13849
13850     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13851                                player->index_bit, dig_side);
13852     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13853                                         player->index_bit, dig_side);
13854
13855     return MP_NO_ACTION;
13856   }
13857
13858   player->push_delay = -1;
13859
13860   if (is_player)                /* function can also be called by EL_PENGUIN */
13861   {
13862     if (Feld[x][y] != element)          /* really digged/collected something */
13863     {
13864       player->is_collecting = !player->is_digging;
13865       player->is_active = TRUE;
13866     }
13867   }
13868
13869   return MP_MOVING;
13870 }
13871
13872 static boolean DigFieldByCE(int x, int y, int digging_element)
13873 {
13874   int element = Feld[x][y];
13875
13876   if (!IS_FREE(x, y))
13877   {
13878     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13879                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13880                   ACTION_BREAKING);
13881
13882     /* no element can dig solid indestructible elements */
13883     if (IS_INDESTRUCTIBLE(element) &&
13884         !IS_DIGGABLE(element) &&
13885         !IS_COLLECTIBLE(element))
13886       return FALSE;
13887
13888     if (AmoebaNr[x][y] &&
13889         (element == EL_AMOEBA_FULL ||
13890          element == EL_BD_AMOEBA ||
13891          element == EL_AMOEBA_GROWING))
13892     {
13893       AmoebaCnt[AmoebaNr[x][y]]--;
13894       AmoebaCnt2[AmoebaNr[x][y]]--;
13895     }
13896
13897     if (IS_MOVING(x, y))
13898       RemoveMovingField(x, y);
13899     else
13900     {
13901       RemoveField(x, y);
13902       TEST_DrawLevelField(x, y);
13903     }
13904
13905     /* if digged element was about to explode, prevent the explosion */
13906     ExplodeField[x][y] = EX_TYPE_NONE;
13907
13908     PlayLevelSoundAction(x, y, action);
13909   }
13910
13911   Store[x][y] = EL_EMPTY;
13912
13913   /* this makes it possible to leave the removed element again */
13914   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13915     Store[x][y] = element;
13916
13917   return TRUE;
13918 }
13919
13920 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13921 {
13922   int jx = player->jx, jy = player->jy;
13923   int x = jx + dx, y = jy + dy;
13924   int snap_direction = (dx == -1 ? MV_LEFT  :
13925                         dx == +1 ? MV_RIGHT :
13926                         dy == -1 ? MV_UP    :
13927                         dy == +1 ? MV_DOWN  : MV_NONE);
13928   boolean can_continue_snapping = (level.continuous_snapping &&
13929                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13930
13931   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13932     return FALSE;
13933
13934   if (!player->active || !IN_LEV_FIELD(x, y))
13935     return FALSE;
13936
13937   if (dx && dy)
13938     return FALSE;
13939
13940   if (!dx && !dy)
13941   {
13942     if (player->MovPos == 0)
13943       player->is_pushing = FALSE;
13944
13945     player->is_snapping = FALSE;
13946
13947     if (player->MovPos == 0)
13948     {
13949       player->is_moving = FALSE;
13950       player->is_digging = FALSE;
13951       player->is_collecting = FALSE;
13952     }
13953
13954     return FALSE;
13955   }
13956
13957   /* prevent snapping with already pressed snap key when not allowed */
13958   if (player->is_snapping && !can_continue_snapping)
13959     return FALSE;
13960
13961   player->MovDir = snap_direction;
13962
13963   if (player->MovPos == 0)
13964   {
13965     player->is_moving = FALSE;
13966     player->is_digging = FALSE;
13967     player->is_collecting = FALSE;
13968   }
13969
13970   player->is_dropping = FALSE;
13971   player->is_dropping_pressed = FALSE;
13972   player->drop_pressed_delay = 0;
13973
13974   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13975     return FALSE;
13976
13977   player->is_snapping = TRUE;
13978   player->is_active = TRUE;
13979
13980   if (player->MovPos == 0)
13981   {
13982     player->is_moving = FALSE;
13983     player->is_digging = FALSE;
13984     player->is_collecting = FALSE;
13985   }
13986
13987   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13988     TEST_DrawLevelField(player->last_jx, player->last_jy);
13989
13990   TEST_DrawLevelField(x, y);
13991
13992   return TRUE;
13993 }
13994
13995 static boolean DropElement(struct PlayerInfo *player)
13996 {
13997   int old_element, new_element;
13998   int dropx = player->jx, dropy = player->jy;
13999   int drop_direction = player->MovDir;
14000   int drop_side = drop_direction;
14001   int drop_element = get_next_dropped_element(player);
14002
14003   /* do not drop an element on top of another element; when holding drop key
14004      pressed without moving, dropped element must move away before the next
14005      element can be dropped (this is especially important if the next element
14006      is dynamite, which can be placed on background for historical reasons) */
14007   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14008     return MP_ACTION;
14009
14010   if (IS_THROWABLE(drop_element))
14011   {
14012     dropx += GET_DX_FROM_DIR(drop_direction);
14013     dropy += GET_DY_FROM_DIR(drop_direction);
14014
14015     if (!IN_LEV_FIELD(dropx, dropy))
14016       return FALSE;
14017   }
14018
14019   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14020   new_element = drop_element;           /* default: no change when dropping */
14021
14022   /* check if player is active, not moving and ready to drop */
14023   if (!player->active || player->MovPos || player->drop_delay > 0)
14024     return FALSE;
14025
14026   /* check if player has anything that can be dropped */
14027   if (new_element == EL_UNDEFINED)
14028     return FALSE;
14029
14030   /* only set if player has anything that can be dropped */
14031   player->is_dropping_pressed = TRUE;
14032
14033   /* check if drop key was pressed long enough for EM style dynamite */
14034   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14035     return FALSE;
14036
14037   /* check if anything can be dropped at the current position */
14038   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14039     return FALSE;
14040
14041   /* collected custom elements can only be dropped on empty fields */
14042   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14043     return FALSE;
14044
14045   if (old_element != EL_EMPTY)
14046     Back[dropx][dropy] = old_element;   /* store old element on this field */
14047
14048   ResetGfxAnimation(dropx, dropy);
14049   ResetRandomAnimationValue(dropx, dropy);
14050
14051   if (player->inventory_size > 0 ||
14052       player->inventory_infinite_element != EL_UNDEFINED)
14053   {
14054     if (player->inventory_size > 0)
14055     {
14056       player->inventory_size--;
14057
14058       DrawGameDoorValues();
14059
14060       if (new_element == EL_DYNAMITE)
14061         new_element = EL_DYNAMITE_ACTIVE;
14062       else if (new_element == EL_EM_DYNAMITE)
14063         new_element = EL_EM_DYNAMITE_ACTIVE;
14064       else if (new_element == EL_SP_DISK_RED)
14065         new_element = EL_SP_DISK_RED_ACTIVE;
14066     }
14067
14068     Feld[dropx][dropy] = new_element;
14069
14070     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14071       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14072                           el2img(Feld[dropx][dropy]), 0);
14073
14074     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14075
14076     /* needed if previous element just changed to "empty" in the last frame */
14077     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14078
14079     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14080                                player->index_bit, drop_side);
14081     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14082                                         CE_PLAYER_DROPS_X,
14083                                         player->index_bit, drop_side);
14084
14085     TestIfElementTouchesCustomElement(dropx, dropy);
14086   }
14087   else          /* player is dropping a dyna bomb */
14088   {
14089     player->dynabombs_left--;
14090
14091     Feld[dropx][dropy] = new_element;
14092
14093     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14094       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14095                           el2img(Feld[dropx][dropy]), 0);
14096
14097     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14098   }
14099
14100   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14101     InitField_WithBug1(dropx, dropy, FALSE);
14102
14103   new_element = Feld[dropx][dropy];     /* element might have changed */
14104
14105   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14106       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14107   {
14108     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14109       MovDir[dropx][dropy] = drop_direction;
14110
14111     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14112
14113     /* do not cause impact style collision by dropping elements that can fall */
14114     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14115   }
14116
14117   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14118   player->is_dropping = TRUE;
14119
14120   player->drop_pressed_delay = 0;
14121   player->is_dropping_pressed = FALSE;
14122
14123   player->drop_x = dropx;
14124   player->drop_y = dropy;
14125
14126   return TRUE;
14127 }
14128
14129 /* ------------------------------------------------------------------------- */
14130 /* game sound playing functions                                              */
14131 /* ------------------------------------------------------------------------- */
14132
14133 static int *loop_sound_frame = NULL;
14134 static int *loop_sound_volume = NULL;
14135
14136 void InitPlayLevelSound()
14137 {
14138   int num_sounds = getSoundListSize();
14139
14140   checked_free(loop_sound_frame);
14141   checked_free(loop_sound_volume);
14142
14143   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14144   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14145 }
14146
14147 static void PlayLevelSound(int x, int y, int nr)
14148 {
14149   int sx = SCREENX(x), sy = SCREENY(y);
14150   int volume, stereo_position;
14151   int max_distance = 8;
14152   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14153
14154   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14155       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14156     return;
14157
14158   if (!IN_LEV_FIELD(x, y) ||
14159       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14160       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14161     return;
14162
14163   volume = SOUND_MAX_VOLUME;
14164
14165   if (!IN_SCR_FIELD(sx, sy))
14166   {
14167     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14168     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14169
14170     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14171   }
14172
14173   stereo_position = (SOUND_MAX_LEFT +
14174                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14175                      (SCR_FIELDX + 2 * max_distance));
14176
14177   if (IS_LOOP_SOUND(nr))
14178   {
14179     /* This assures that quieter loop sounds do not overwrite louder ones,
14180        while restarting sound volume comparison with each new game frame. */
14181
14182     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14183       return;
14184
14185     loop_sound_volume[nr] = volume;
14186     loop_sound_frame[nr] = FrameCounter;
14187   }
14188
14189   PlaySoundExt(nr, volume, stereo_position, type);
14190 }
14191
14192 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14193 {
14194   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14195                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14196                  y < LEVELY(BY1) ? LEVELY(BY1) :
14197                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14198                  sound_action);
14199 }
14200
14201 static void PlayLevelSoundAction(int x, int y, int action)
14202 {
14203   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14204 }
14205
14206 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14207 {
14208   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14209
14210   if (sound_effect != SND_UNDEFINED)
14211     PlayLevelSound(x, y, sound_effect);
14212 }
14213
14214 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14215                                               int action)
14216 {
14217   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14218
14219   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14220     PlayLevelSound(x, y, sound_effect);
14221 }
14222
14223 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14224 {
14225   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14226
14227   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14228     PlayLevelSound(x, y, sound_effect);
14229 }
14230
14231 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14232 {
14233   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14234
14235   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14236     StopSound(sound_effect);
14237 }
14238
14239 static int getLevelMusicNr()
14240 {
14241   if (levelset.music[level_nr] != MUS_UNDEFINED)
14242     return levelset.music[level_nr];            /* from config file */
14243   else
14244     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14245 }
14246
14247 static void FadeLevelSounds()
14248 {
14249   FadeSounds();
14250 }
14251
14252 static void FadeLevelMusic()
14253 {
14254   int music_nr = getLevelMusicNr();
14255   char *curr_music = getCurrentlyPlayingMusicFilename();
14256   char *next_music = getMusicInfoEntryFilename(music_nr);
14257
14258   if (!strEqual(curr_music, next_music))
14259     FadeMusic();
14260 }
14261
14262 void FadeLevelSoundsAndMusic()
14263 {
14264   FadeLevelSounds();
14265   FadeLevelMusic();
14266 }
14267
14268 static void PlayLevelMusic()
14269 {
14270   int music_nr = getLevelMusicNr();
14271   char *curr_music = getCurrentlyPlayingMusicFilename();
14272   char *next_music = getMusicInfoEntryFilename(music_nr);
14273
14274   if (!strEqual(curr_music, next_music))
14275     PlayMusic(music_nr);
14276 }
14277
14278 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14279 {
14280   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14281   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14282   int x = xx - 1 - offset;
14283   int y = yy - 1 - offset;
14284
14285   switch (sample)
14286   {
14287     case SAMPLE_blank:
14288       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14289       break;
14290
14291     case SAMPLE_roll:
14292       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14293       break;
14294
14295     case SAMPLE_stone:
14296       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14297       break;
14298
14299     case SAMPLE_nut:
14300       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14301       break;
14302
14303     case SAMPLE_crack:
14304       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14305       break;
14306
14307     case SAMPLE_bug:
14308       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14309       break;
14310
14311     case SAMPLE_tank:
14312       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14313       break;
14314
14315     case SAMPLE_android_clone:
14316       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14317       break;
14318
14319     case SAMPLE_android_move:
14320       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14321       break;
14322
14323     case SAMPLE_spring:
14324       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14325       break;
14326
14327     case SAMPLE_slurp:
14328       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14329       break;
14330
14331     case SAMPLE_eater:
14332       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14333       break;
14334
14335     case SAMPLE_eater_eat:
14336       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14337       break;
14338
14339     case SAMPLE_alien:
14340       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14341       break;
14342
14343     case SAMPLE_collect:
14344       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14345       break;
14346
14347     case SAMPLE_diamond:
14348       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14349       break;
14350
14351     case SAMPLE_squash:
14352       /* !!! CHECK THIS !!! */
14353 #if 1
14354       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14355 #else
14356       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14357 #endif
14358       break;
14359
14360     case SAMPLE_wonderfall:
14361       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14362       break;
14363
14364     case SAMPLE_drip:
14365       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14366       break;
14367
14368     case SAMPLE_push:
14369       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14370       break;
14371
14372     case SAMPLE_dirt:
14373       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14374       break;
14375
14376     case SAMPLE_acid:
14377       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14378       break;
14379
14380     case SAMPLE_ball:
14381       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14382       break;
14383
14384     case SAMPLE_grow:
14385       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14386       break;
14387
14388     case SAMPLE_wonder:
14389       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14390       break;
14391
14392     case SAMPLE_door:
14393       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14394       break;
14395
14396     case SAMPLE_exit_open:
14397       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14398       break;
14399
14400     case SAMPLE_exit_leave:
14401       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14402       break;
14403
14404     case SAMPLE_dynamite:
14405       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14406       break;
14407
14408     case SAMPLE_tick:
14409       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14410       break;
14411
14412     case SAMPLE_press:
14413       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14414       break;
14415
14416     case SAMPLE_wheel:
14417       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14418       break;
14419
14420     case SAMPLE_boom:
14421       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14422       break;
14423
14424     case SAMPLE_die:
14425       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14426       break;
14427
14428     case SAMPLE_time:
14429       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14430       break;
14431
14432     default:
14433       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14434       break;
14435   }
14436 }
14437
14438 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14439 {
14440   int element = map_element_SP_to_RND(element_sp);
14441   int action = map_action_SP_to_RND(action_sp);
14442   int offset = (setup.sp_show_border_elements ? 0 : 1);
14443   int x = xx - offset;
14444   int y = yy - offset;
14445
14446   PlayLevelSoundElementAction(x, y, element, action);
14447 }
14448
14449 void RaiseScore(int value)
14450 {
14451   local_player->score += value;
14452
14453   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14454
14455   DisplayGameControlValues();
14456 }
14457
14458 void RaiseScoreElement(int element)
14459 {
14460   switch (element)
14461   {
14462     case EL_EMERALD:
14463     case EL_BD_DIAMOND:
14464     case EL_EMERALD_YELLOW:
14465     case EL_EMERALD_RED:
14466     case EL_EMERALD_PURPLE:
14467     case EL_SP_INFOTRON:
14468       RaiseScore(level.score[SC_EMERALD]);
14469       break;
14470     case EL_DIAMOND:
14471       RaiseScore(level.score[SC_DIAMOND]);
14472       break;
14473     case EL_CRYSTAL:
14474       RaiseScore(level.score[SC_CRYSTAL]);
14475       break;
14476     case EL_PEARL:
14477       RaiseScore(level.score[SC_PEARL]);
14478       break;
14479     case EL_BUG:
14480     case EL_BD_BUTTERFLY:
14481     case EL_SP_ELECTRON:
14482       RaiseScore(level.score[SC_BUG]);
14483       break;
14484     case EL_SPACESHIP:
14485     case EL_BD_FIREFLY:
14486     case EL_SP_SNIKSNAK:
14487       RaiseScore(level.score[SC_SPACESHIP]);
14488       break;
14489     case EL_YAMYAM:
14490     case EL_DARK_YAMYAM:
14491       RaiseScore(level.score[SC_YAMYAM]);
14492       break;
14493     case EL_ROBOT:
14494       RaiseScore(level.score[SC_ROBOT]);
14495       break;
14496     case EL_PACMAN:
14497       RaiseScore(level.score[SC_PACMAN]);
14498       break;
14499     case EL_NUT:
14500       RaiseScore(level.score[SC_NUT]);
14501       break;
14502     case EL_DYNAMITE:
14503     case EL_EM_DYNAMITE:
14504     case EL_SP_DISK_RED:
14505     case EL_DYNABOMB_INCREASE_NUMBER:
14506     case EL_DYNABOMB_INCREASE_SIZE:
14507     case EL_DYNABOMB_INCREASE_POWER:
14508       RaiseScore(level.score[SC_DYNAMITE]);
14509       break;
14510     case EL_SHIELD_NORMAL:
14511     case EL_SHIELD_DEADLY:
14512       RaiseScore(level.score[SC_SHIELD]);
14513       break;
14514     case EL_EXTRA_TIME:
14515       RaiseScore(level.extra_time_score);
14516       break;
14517     case EL_KEY_1:
14518     case EL_KEY_2:
14519     case EL_KEY_3:
14520     case EL_KEY_4:
14521     case EL_EM_KEY_1:
14522     case EL_EM_KEY_2:
14523     case EL_EM_KEY_3:
14524     case EL_EM_KEY_4:
14525     case EL_EMC_KEY_5:
14526     case EL_EMC_KEY_6:
14527     case EL_EMC_KEY_7:
14528     case EL_EMC_KEY_8:
14529     case EL_DC_KEY_WHITE:
14530       RaiseScore(level.score[SC_KEY]);
14531       break;
14532     default:
14533       RaiseScore(element_info[element].collect_score);
14534       break;
14535   }
14536 }
14537
14538 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14539 {
14540   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14541   {
14542     /* closing door required in case of envelope style request dialogs */
14543     if (!skip_request)
14544       CloseDoor(DOOR_CLOSE_1);
14545
14546 #if defined(NETWORK_AVALIABLE)
14547     if (options.network)
14548       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14549     else
14550 #endif
14551     {
14552       if (quick_quit)
14553         FadeSkipNextFadeIn();
14554
14555       SetGameStatus(GAME_MODE_MAIN);
14556
14557       DrawMainMenu();
14558     }
14559   }
14560   else          /* continue playing the game */
14561   {
14562     if (tape.playing && tape.deactivate_display)
14563       TapeDeactivateDisplayOff(TRUE);
14564
14565     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14566
14567     if (tape.playing && tape.deactivate_display)
14568       TapeDeactivateDisplayOn();
14569   }
14570 }
14571
14572 void RequestQuitGame(boolean ask_if_really_quit)
14573 {
14574   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14575   boolean skip_request = AllPlayersGone || quick_quit;
14576
14577   RequestQuitGameExt(skip_request, quick_quit,
14578                      "Do you really want to quit the game?");
14579 }
14580
14581
14582 /* ------------------------------------------------------------------------- */
14583 /* random generator functions                                                */
14584 /* ------------------------------------------------------------------------- */
14585
14586 unsigned int InitEngineRandom_RND(int seed)
14587 {
14588   game.num_random_calls = 0;
14589
14590   return InitEngineRandom(seed);
14591 }
14592
14593 unsigned int RND(int max)
14594 {
14595   if (max > 0)
14596   {
14597     game.num_random_calls++;
14598
14599     return GetEngineRandom(max);
14600   }
14601
14602   return 0;
14603 }
14604
14605
14606 /* ------------------------------------------------------------------------- */
14607 /* game engine snapshot handling functions                                   */
14608 /* ------------------------------------------------------------------------- */
14609
14610 struct EngineSnapshotInfo
14611 {
14612   /* runtime values for custom element collect score */
14613   int collect_score[NUM_CUSTOM_ELEMENTS];
14614
14615   /* runtime values for group element choice position */
14616   int choice_pos[NUM_GROUP_ELEMENTS];
14617
14618   /* runtime values for belt position animations */
14619   int belt_graphic[4][NUM_BELT_PARTS];
14620   int belt_anim_mode[4][NUM_BELT_PARTS];
14621 };
14622
14623 static struct EngineSnapshotInfo engine_snapshot_rnd;
14624 static char *snapshot_level_identifier = NULL;
14625 static int snapshot_level_nr = -1;
14626
14627 static void SaveEngineSnapshotValues_RND()
14628 {
14629   static int belt_base_active_element[4] =
14630   {
14631     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14632     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14633     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14634     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14635   };
14636   int i, j;
14637
14638   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14639   {
14640     int element = EL_CUSTOM_START + i;
14641
14642     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14643   }
14644
14645   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14646   {
14647     int element = EL_GROUP_START + i;
14648
14649     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14650   }
14651
14652   for (i = 0; i < 4; i++)
14653   {
14654     for (j = 0; j < NUM_BELT_PARTS; j++)
14655     {
14656       int element = belt_base_active_element[i] + j;
14657       int graphic = el2img(element);
14658       int anim_mode = graphic_info[graphic].anim_mode;
14659
14660       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14661       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14662     }
14663   }
14664 }
14665
14666 static void LoadEngineSnapshotValues_RND()
14667 {
14668   unsigned int num_random_calls = game.num_random_calls;
14669   int i, j;
14670
14671   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14672   {
14673     int element = EL_CUSTOM_START + i;
14674
14675     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14676   }
14677
14678   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14679   {
14680     int element = EL_GROUP_START + i;
14681
14682     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14683   }
14684
14685   for (i = 0; i < 4; i++)
14686   {
14687     for (j = 0; j < NUM_BELT_PARTS; j++)
14688     {
14689       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14690       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14691
14692       graphic_info[graphic].anim_mode = anim_mode;
14693     }
14694   }
14695
14696   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14697   {
14698     InitRND(tape.random_seed);
14699     for (i = 0; i < num_random_calls; i++)
14700       RND(1);
14701   }
14702
14703   if (game.num_random_calls != num_random_calls)
14704   {
14705     Error(ERR_INFO, "number of random calls out of sync");
14706     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14707     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14708     Error(ERR_EXIT, "this should not happen -- please debug");
14709   }
14710 }
14711
14712 void FreeEngineSnapshotSingle()
14713 {
14714   FreeSnapshotSingle();
14715
14716   setString(&snapshot_level_identifier, NULL);
14717   snapshot_level_nr = -1;
14718 }
14719
14720 void FreeEngineSnapshotList()
14721 {
14722   FreeSnapshotList();
14723 }
14724
14725 ListNode *SaveEngineSnapshotBuffers()
14726 {
14727   ListNode *buffers = NULL;
14728
14729   /* copy some special values to a structure better suited for the snapshot */
14730
14731   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14732     SaveEngineSnapshotValues_RND();
14733   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14734     SaveEngineSnapshotValues_EM();
14735   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14736     SaveEngineSnapshotValues_SP(&buffers);
14737
14738   /* save values stored in special snapshot structure */
14739
14740   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14741     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14742   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14743     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14744   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14745     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14746
14747   /* save further RND engine values */
14748
14749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14752
14753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14757
14758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14763
14764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14767
14768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14769
14770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14771
14772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14774
14775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14777   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14778   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14780   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14783   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14786   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14787   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14788   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14789   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14790   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14791   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14792   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14793
14794   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14795   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14796
14797   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14798   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14799   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14800
14801   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14802   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14803
14804   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14805   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14806   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14807   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14808   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14809
14810   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14811   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14812
14813 #if 0
14814   ListNode *node = engine_snapshot_list_rnd;
14815   int num_bytes = 0;
14816
14817   while (node != NULL)
14818   {
14819     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14820
14821     node = node->next;
14822   }
14823
14824   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14825 #endif
14826
14827   return buffers;
14828 }
14829
14830 void SaveEngineSnapshotSingle()
14831 {
14832   ListNode *buffers = SaveEngineSnapshotBuffers();
14833
14834   /* finally save all snapshot buffers to single snapshot */
14835   SaveSnapshotSingle(buffers);
14836
14837   /* save level identification information */
14838   setString(&snapshot_level_identifier, leveldir_current->identifier);
14839   snapshot_level_nr = level_nr;
14840 }
14841
14842 boolean CheckSaveEngineSnapshotToList()
14843 {
14844   boolean save_snapshot =
14845     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14846      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14847       game.snapshot.changed_action) ||
14848      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14849       game.snapshot.collected_item));
14850
14851   game.snapshot.changed_action = FALSE;
14852   game.snapshot.collected_item = FALSE;
14853   game.snapshot.save_snapshot = save_snapshot;
14854
14855   return save_snapshot;
14856 }
14857
14858 void SaveEngineSnapshotToList()
14859 {
14860   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14861       tape.quick_resume)
14862     return;
14863
14864   ListNode *buffers = SaveEngineSnapshotBuffers();
14865
14866   /* finally save all snapshot buffers to snapshot list */
14867   SaveSnapshotToList(buffers);
14868 }
14869
14870 void SaveEngineSnapshotToListInitial()
14871 {
14872   FreeEngineSnapshotList();
14873
14874   SaveEngineSnapshotToList();
14875 }
14876
14877 void LoadEngineSnapshotValues()
14878 {
14879   /* restore special values from snapshot structure */
14880
14881   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14882     LoadEngineSnapshotValues_RND();
14883   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14884     LoadEngineSnapshotValues_EM();
14885   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14886     LoadEngineSnapshotValues_SP();
14887 }
14888
14889 void LoadEngineSnapshotSingle()
14890 {
14891   LoadSnapshotSingle();
14892
14893   LoadEngineSnapshotValues();
14894 }
14895
14896 void LoadEngineSnapshot_Undo(int steps)
14897 {
14898   LoadSnapshotFromList_Older(steps);
14899
14900   LoadEngineSnapshotValues();
14901 }
14902
14903 void LoadEngineSnapshot_Redo(int steps)
14904 {
14905   LoadSnapshotFromList_Newer(steps);
14906
14907   LoadEngineSnapshotValues();
14908 }
14909
14910 boolean CheckEngineSnapshotSingle()
14911 {
14912   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14913           snapshot_level_nr == level_nr);
14914 }
14915
14916 boolean CheckEngineSnapshotList()
14917 {
14918   return CheckSnapshotList();
14919 }
14920
14921
14922 /* ---------- new game button stuff ---------------------------------------- */
14923
14924 static struct
14925 {
14926   int graphic;
14927   struct XY *pos;
14928   int gadget_id;
14929   char *infotext;
14930 } gamebutton_info[NUM_GAME_BUTTONS] =
14931 {
14932   {
14933     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14934     GAME_CTRL_ID_STOP,                  "stop game"
14935   },
14936   {
14937     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14938     GAME_CTRL_ID_PAUSE,                 "pause game"
14939   },
14940   {
14941     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14942     GAME_CTRL_ID_PLAY,                  "play game"
14943   },
14944   {
14945     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14946     GAME_CTRL_ID_UNDO,                  "undo step"
14947   },
14948   {
14949     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14950     GAME_CTRL_ID_REDO,                  "redo step"
14951   },
14952   {
14953     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14954     GAME_CTRL_ID_SAVE,                  "save game"
14955   },
14956   {
14957     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14958     GAME_CTRL_ID_PAUSE2,                "pause game"
14959   },
14960   {
14961     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14962     GAME_CTRL_ID_LOAD,                  "load game"
14963   },
14964   {
14965     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14966     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14967   },
14968   {
14969     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14970     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14971   },
14972   {
14973     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14974     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14975   }
14976 };
14977
14978 void CreateGameButtons()
14979 {
14980   int i;
14981
14982   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14983   {
14984     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14985     struct XY *pos = gamebutton_info[i].pos;
14986     struct GadgetInfo *gi;
14987     int button_type;
14988     boolean checked;
14989     unsigned int event_mask;
14990     int base_x = (tape.show_game_buttons ? VX : DX);
14991     int base_y = (tape.show_game_buttons ? VY : DY);
14992     int gd_x   = gfx->src_x;
14993     int gd_y   = gfx->src_y;
14994     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14995     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14996     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14997     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14998     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14999     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15000     int id = i;
15001
15002     if (gfx->bitmap == NULL)
15003     {
15004       game_gadget[id] = NULL;
15005
15006       continue;
15007     }
15008
15009     if (id == GAME_CTRL_ID_STOP ||
15010         id == GAME_CTRL_ID_PLAY ||
15011         id == GAME_CTRL_ID_SAVE ||
15012         id == GAME_CTRL_ID_LOAD)
15013     {
15014       button_type = GD_TYPE_NORMAL_BUTTON;
15015       checked = FALSE;
15016       event_mask = GD_EVENT_RELEASED;
15017     }
15018     else if (id == GAME_CTRL_ID_UNDO ||
15019              id == GAME_CTRL_ID_REDO)
15020     {
15021       button_type = GD_TYPE_NORMAL_BUTTON;
15022       checked = FALSE;
15023       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15024     }
15025     else
15026     {
15027       button_type = GD_TYPE_CHECK_BUTTON;
15028       checked =
15029         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15030          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15031          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15032       event_mask = GD_EVENT_PRESSED;
15033     }
15034
15035     gi = CreateGadget(GDI_CUSTOM_ID, id,
15036                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15037                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15038                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15039                       GDI_WIDTH, gfx->width,
15040                       GDI_HEIGHT, gfx->height,
15041                       GDI_TYPE, button_type,
15042                       GDI_STATE, GD_BUTTON_UNPRESSED,
15043                       GDI_CHECKED, checked,
15044                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15045                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15046                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15047                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15048                       GDI_DIRECT_DRAW, FALSE,
15049                       GDI_EVENT_MASK, event_mask,
15050                       GDI_CALLBACK_ACTION, HandleGameButtons,
15051                       GDI_END);
15052
15053     if (gi == NULL)
15054       Error(ERR_EXIT, "cannot create gadget");
15055
15056     game_gadget[id] = gi;
15057   }
15058 }
15059
15060 void FreeGameButtons()
15061 {
15062   int i;
15063
15064   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15065     FreeGadget(game_gadget[i]);
15066 }
15067
15068 static void UnmapGameButtonsAtSamePosition(int id)
15069 {
15070   int i;
15071
15072   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15073     if (i != id &&
15074         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15075         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15076       UnmapGadget(game_gadget[i]);
15077 }
15078
15079 static void UnmapGameButtonsAtSamePosition_All()
15080 {
15081   if (setup.show_snapshot_buttons)
15082   {
15083     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15084     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15085     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15086   }
15087   else
15088   {
15089     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15090     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15091     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15092   }
15093 }
15094
15095 static void MapGameButtonsAtSamePosition(int id)
15096 {
15097   int i;
15098
15099   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15100     if (i != id &&
15101         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15102         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15103       MapGadget(game_gadget[i]);
15104
15105   UnmapGameButtonsAtSamePosition_All();
15106 }
15107
15108 void MapUndoRedoButtons()
15109 {
15110   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15111   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15112
15113   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15114   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15115
15116   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15117 }
15118
15119 void UnmapUndoRedoButtons()
15120 {
15121   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15122   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15123
15124   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15125   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15126
15127   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15128 }
15129
15130 void MapGameButtons()
15131 {
15132   int i;
15133
15134   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15135     if (i != GAME_CTRL_ID_UNDO &&
15136         i != GAME_CTRL_ID_REDO)
15137       MapGadget(game_gadget[i]);
15138
15139   UnmapGameButtonsAtSamePosition_All();
15140
15141   RedrawGameButtons();
15142 }
15143
15144 void UnmapGameButtons()
15145 {
15146   int i;
15147
15148   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15149     UnmapGadget(game_gadget[i]);
15150 }
15151
15152 void RedrawGameButtons()
15153 {
15154   int i;
15155
15156   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15157     RedrawGadget(game_gadget[i]);
15158
15159   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15160   redraw_mask &= ~REDRAW_ALL;
15161 }
15162
15163 void GameUndoRedoExt()
15164 {
15165   ClearPlayerAction();
15166
15167   tape.pausing = TRUE;
15168
15169   RedrawPlayfield();
15170   UpdateAndDisplayGameControlValues();
15171
15172   DrawCompleteVideoDisplay();
15173   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15174   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15175   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15176
15177   BackToFront();
15178 }
15179
15180 void GameUndo(int steps)
15181 {
15182   if (!CheckEngineSnapshotList())
15183     return;
15184
15185   LoadEngineSnapshot_Undo(steps);
15186
15187   GameUndoRedoExt();
15188 }
15189
15190 void GameRedo(int steps)
15191 {
15192   if (!CheckEngineSnapshotList())
15193     return;
15194
15195   LoadEngineSnapshot_Redo(steps);
15196
15197   GameUndoRedoExt();
15198 }
15199
15200 static void HandleGameButtonsExt(int id, int button)
15201 {
15202   static boolean game_undo_executed = FALSE;
15203   int steps = BUTTON_STEPSIZE(button);
15204   boolean handle_game_buttons =
15205     (game_status == GAME_MODE_PLAYING ||
15206      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15207
15208   if (!handle_game_buttons)
15209     return;
15210
15211   switch (id)
15212   {
15213     case GAME_CTRL_ID_STOP:
15214       if (game_status == GAME_MODE_MAIN)
15215         break;
15216
15217       if (tape.playing)
15218         TapeStop();
15219       else
15220         RequestQuitGame(TRUE);
15221
15222       break;
15223
15224     case GAME_CTRL_ID_PAUSE:
15225     case GAME_CTRL_ID_PAUSE2:
15226       if (options.network && game_status == GAME_MODE_PLAYING)
15227       {
15228 #if defined(NETWORK_AVALIABLE)
15229         if (tape.pausing)
15230           SendToServer_ContinuePlaying();
15231         else
15232           SendToServer_PausePlaying();
15233 #endif
15234       }
15235       else
15236         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15237
15238       game_undo_executed = FALSE;
15239
15240       break;
15241
15242     case GAME_CTRL_ID_PLAY:
15243       if (game_status == GAME_MODE_MAIN)
15244       {
15245         StartGameActions(options.network, setup.autorecord, level.random_seed);
15246       }
15247       else if (tape.pausing)
15248       {
15249 #if defined(NETWORK_AVALIABLE)
15250         if (options.network)
15251           SendToServer_ContinuePlaying();
15252         else
15253 #endif
15254           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15255       }
15256       break;
15257
15258     case GAME_CTRL_ID_UNDO:
15259       // Important: When using "save snapshot when collecting an item" mode,
15260       // load last (current) snapshot for first "undo" after pressing "pause"
15261       // (else the last-but-one snapshot would be loaded, because the snapshot
15262       // pointer already points to the last snapshot when pressing "pause",
15263       // which is fine for "every step/move" mode, but not for "every collect")
15264       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15265           !game_undo_executed)
15266         steps--;
15267
15268       game_undo_executed = TRUE;
15269
15270       GameUndo(steps);
15271       break;
15272
15273     case GAME_CTRL_ID_REDO:
15274       GameRedo(steps);
15275       break;
15276
15277     case GAME_CTRL_ID_SAVE:
15278       TapeQuickSave();
15279       break;
15280
15281     case GAME_CTRL_ID_LOAD:
15282       TapeQuickLoad();
15283       break;
15284
15285     case SOUND_CTRL_ID_MUSIC:
15286       if (setup.sound_music)
15287       { 
15288         setup.sound_music = FALSE;
15289
15290         FadeMusic();
15291       }
15292       else if (audio.music_available)
15293       { 
15294         setup.sound = setup.sound_music = TRUE;
15295
15296         SetAudioMode(setup.sound);
15297
15298         PlayLevelMusic();
15299       }
15300       break;
15301
15302     case SOUND_CTRL_ID_LOOPS:
15303       if (setup.sound_loops)
15304         setup.sound_loops = FALSE;
15305       else if (audio.loops_available)
15306       {
15307         setup.sound = setup.sound_loops = TRUE;
15308
15309         SetAudioMode(setup.sound);
15310       }
15311       break;
15312
15313     case SOUND_CTRL_ID_SIMPLE:
15314       if (setup.sound_simple)
15315         setup.sound_simple = FALSE;
15316       else if (audio.sound_available)
15317       {
15318         setup.sound = setup.sound_simple = TRUE;
15319
15320         SetAudioMode(setup.sound);
15321       }
15322       break;
15323
15324     default:
15325       break;
15326   }
15327 }
15328
15329 static void HandleGameButtons(struct GadgetInfo *gi)
15330 {
15331   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15332 }
15333
15334 void HandleSoundButtonKeys(Key key)
15335 {
15336
15337   if (key == setup.shortcut.sound_simple)
15338     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15339   else if (key == setup.shortcut.sound_loops)
15340     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15341   else if (key == setup.shortcut.sound_music)
15342     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15343 }