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