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