removed some remaining unused X11 stuff
[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 "files.h"
19 #include "tape.h"
20 #include "network.h"
21
22
23 /* DEBUG SETTINGS */
24 #define DEBUG_INIT_PLAYER       1
25 #define DEBUG_PLAYER_ACTIONS    0
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* EXPERIMENTAL STUFF */
31 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
32 #define USE_QUICKSAND_IMPACT_BUGFIX     0
33 #define USE_DELAYED_GFX_REDRAW          0
34 #define USE_NEW_PLAYER_ASSIGNMENTS      1
35
36 #if USE_DELAYED_GFX_REDRAW
37 #define TEST_DrawLevelField(x, y)                               \
38         GfxRedraw[x][y] |= GFX_REDRAW_TILE
39 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
41 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
43 #define TEST_DrawTwinkleOnField(x, y)                           \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
45 #else
46 #define TEST_DrawLevelField(x, y)                               \
47              DrawLevelField(x, y)
48 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
49              DrawLevelFieldCrumbled(x, y)
50 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
51              DrawLevelFieldCrumbledNeighbours(x, y)
52 #define TEST_DrawTwinkleOnField(x, y)                           \
53              DrawTwinkleOnField(x, y)
54 #endif
55
56
57 /* for DigField() */
58 #define DF_NO_PUSH              0
59 #define DF_DIG                  1
60 #define DF_SNAP                 2
61
62 /* for MovePlayer() */
63 #define MP_NO_ACTION            0
64 #define MP_MOVING               1
65 #define MP_ACTION               2
66 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
67
68 /* for ScrollPlayer() */
69 #define SCROLL_INIT             0
70 #define SCROLL_GO_ON            1
71
72 /* for Bang()/Explode() */
73 #define EX_PHASE_START          0
74 #define EX_TYPE_NONE            0
75 #define EX_TYPE_NORMAL          (1 << 0)
76 #define EX_TYPE_CENTER          (1 << 1)
77 #define EX_TYPE_BORDER          (1 << 2)
78 #define EX_TYPE_CROSS           (1 << 3)
79 #define EX_TYPE_DYNA            (1 << 4)
80 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
81
82 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
83 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
84 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
85 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
86
87 /* game panel display and control definitions */
88 #define GAME_PANEL_LEVEL_NUMBER                 0
89 #define GAME_PANEL_GEMS                         1
90 #define GAME_PANEL_INVENTORY_COUNT              2
91 #define GAME_PANEL_INVENTORY_FIRST_1            3
92 #define GAME_PANEL_INVENTORY_FIRST_2            4
93 #define GAME_PANEL_INVENTORY_FIRST_3            5
94 #define GAME_PANEL_INVENTORY_FIRST_4            6
95 #define GAME_PANEL_INVENTORY_FIRST_5            7
96 #define GAME_PANEL_INVENTORY_FIRST_6            8
97 #define GAME_PANEL_INVENTORY_FIRST_7            9
98 #define GAME_PANEL_INVENTORY_FIRST_8            10
99 #define GAME_PANEL_INVENTORY_LAST_1             11
100 #define GAME_PANEL_INVENTORY_LAST_2             12
101 #define GAME_PANEL_INVENTORY_LAST_3             13
102 #define GAME_PANEL_INVENTORY_LAST_4             14
103 #define GAME_PANEL_INVENTORY_LAST_5             15
104 #define GAME_PANEL_INVENTORY_LAST_6             16
105 #define GAME_PANEL_INVENTORY_LAST_7             17
106 #define GAME_PANEL_INVENTORY_LAST_8             18
107 #define GAME_PANEL_KEY_1                        19
108 #define GAME_PANEL_KEY_2                        20
109 #define GAME_PANEL_KEY_3                        21
110 #define GAME_PANEL_KEY_4                        22
111 #define GAME_PANEL_KEY_5                        23
112 #define GAME_PANEL_KEY_6                        24
113 #define GAME_PANEL_KEY_7                        25
114 #define GAME_PANEL_KEY_8                        26
115 #define GAME_PANEL_KEY_WHITE                    27
116 #define GAME_PANEL_KEY_WHITE_COUNT              28
117 #define GAME_PANEL_SCORE                        29
118 #define GAME_PANEL_HIGHSCORE                    30
119 #define GAME_PANEL_TIME                         31
120 #define GAME_PANEL_TIME_HH                      32
121 #define GAME_PANEL_TIME_MM                      33
122 #define GAME_PANEL_TIME_SS                      34
123 #define GAME_PANEL_FRAME                        35
124 #define GAME_PANEL_SHIELD_NORMAL                36
125 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
126 #define GAME_PANEL_SHIELD_DEADLY                38
127 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
128 #define GAME_PANEL_EXIT                         40
129 #define GAME_PANEL_EMC_MAGIC_BALL               41
130 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
131 #define GAME_PANEL_LIGHT_SWITCH                 43
132 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
133 #define GAME_PANEL_TIMEGATE_SWITCH              45
134 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
135 #define GAME_PANEL_SWITCHGATE_SWITCH            47
136 #define GAME_PANEL_EMC_LENSES                   48
137 #define GAME_PANEL_EMC_LENSES_TIME              49
138 #define GAME_PANEL_EMC_MAGNIFIER                50
139 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
140 #define GAME_PANEL_BALLOON_SWITCH               52
141 #define GAME_PANEL_DYNABOMB_NUMBER              53
142 #define GAME_PANEL_DYNABOMB_SIZE                54
143 #define GAME_PANEL_DYNABOMB_POWER               55
144 #define GAME_PANEL_PENGUINS                     56
145 #define GAME_PANEL_SOKOBAN_OBJECTS              57
146 #define GAME_PANEL_SOKOBAN_FIELDS               58
147 #define GAME_PANEL_ROBOT_WHEEL                  59
148 #define GAME_PANEL_CONVEYOR_BELT_1              60
149 #define GAME_PANEL_CONVEYOR_BELT_2              61
150 #define GAME_PANEL_CONVEYOR_BELT_3              62
151 #define GAME_PANEL_CONVEYOR_BELT_4              63
152 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
153 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
154 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
155 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
156 #define GAME_PANEL_MAGIC_WALL                   68
157 #define GAME_PANEL_MAGIC_WALL_TIME              69
158 #define GAME_PANEL_GRAVITY_STATE                70
159 #define GAME_PANEL_GRAPHIC_1                    71
160 #define GAME_PANEL_GRAPHIC_2                    72
161 #define GAME_PANEL_GRAPHIC_3                    73
162 #define GAME_PANEL_GRAPHIC_4                    74
163 #define GAME_PANEL_GRAPHIC_5                    75
164 #define GAME_PANEL_GRAPHIC_6                    76
165 #define GAME_PANEL_GRAPHIC_7                    77
166 #define GAME_PANEL_GRAPHIC_8                    78
167 #define GAME_PANEL_ELEMENT_1                    79
168 #define GAME_PANEL_ELEMENT_2                    80
169 #define GAME_PANEL_ELEMENT_3                    81
170 #define GAME_PANEL_ELEMENT_4                    82
171 #define GAME_PANEL_ELEMENT_5                    83
172 #define GAME_PANEL_ELEMENT_6                    84
173 #define GAME_PANEL_ELEMENT_7                    85
174 #define GAME_PANEL_ELEMENT_8                    86
175 #define GAME_PANEL_ELEMENT_COUNT_1              87
176 #define GAME_PANEL_ELEMENT_COUNT_2              88
177 #define GAME_PANEL_ELEMENT_COUNT_3              89
178 #define GAME_PANEL_ELEMENT_COUNT_4              90
179 #define GAME_PANEL_ELEMENT_COUNT_5              91
180 #define GAME_PANEL_ELEMENT_COUNT_6              92
181 #define GAME_PANEL_ELEMENT_COUNT_7              93
182 #define GAME_PANEL_ELEMENT_COUNT_8              94
183 #define GAME_PANEL_CE_SCORE_1                   95
184 #define GAME_PANEL_CE_SCORE_2                   96
185 #define GAME_PANEL_CE_SCORE_3                   97
186 #define GAME_PANEL_CE_SCORE_4                   98
187 #define GAME_PANEL_CE_SCORE_5                   99
188 #define GAME_PANEL_CE_SCORE_6                   100
189 #define GAME_PANEL_CE_SCORE_7                   101
190 #define GAME_PANEL_CE_SCORE_8                   102
191 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
192 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
193 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
194 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
195 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
196 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
197 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
198 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
199 #define GAME_PANEL_PLAYER_NAME                  111
200 #define GAME_PANEL_LEVEL_NAME                   112
201 #define GAME_PANEL_LEVEL_AUTHOR                 113
202
203 #define NUM_GAME_PANEL_CONTROLS                 114
204
205 struct GamePanelOrderInfo
206 {
207   int nr;
208   int sort_priority;
209 };
210
211 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
212
213 struct GamePanelControlInfo
214 {
215   int nr;
216
217   struct TextPosInfo *pos;
218   int type;
219
220   int value, last_value;
221   int frame, last_frame;
222   int gfx_frame;
223   int gfx_random;
224 };
225
226 static struct GamePanelControlInfo game_panel_controls[] =
227 {
228   {
229     GAME_PANEL_LEVEL_NUMBER,
230     &game.panel.level_number,
231     TYPE_INTEGER,
232   },
233   {
234     GAME_PANEL_GEMS,
235     &game.panel.gems,
236     TYPE_INTEGER,
237   },
238   {
239     GAME_PANEL_INVENTORY_COUNT,
240     &game.panel.inventory_count,
241     TYPE_INTEGER,
242   },
243   {
244     GAME_PANEL_INVENTORY_FIRST_1,
245     &game.panel.inventory_first[0],
246     TYPE_ELEMENT,
247   },
248   {
249     GAME_PANEL_INVENTORY_FIRST_2,
250     &game.panel.inventory_first[1],
251     TYPE_ELEMENT,
252   },
253   {
254     GAME_PANEL_INVENTORY_FIRST_3,
255     &game.panel.inventory_first[2],
256     TYPE_ELEMENT,
257   },
258   {
259     GAME_PANEL_INVENTORY_FIRST_4,
260     &game.panel.inventory_first[3],
261     TYPE_ELEMENT,
262   },
263   {
264     GAME_PANEL_INVENTORY_FIRST_5,
265     &game.panel.inventory_first[4],
266     TYPE_ELEMENT,
267   },
268   {
269     GAME_PANEL_INVENTORY_FIRST_6,
270     &game.panel.inventory_first[5],
271     TYPE_ELEMENT,
272   },
273   {
274     GAME_PANEL_INVENTORY_FIRST_7,
275     &game.panel.inventory_first[6],
276     TYPE_ELEMENT,
277   },
278   {
279     GAME_PANEL_INVENTORY_FIRST_8,
280     &game.panel.inventory_first[7],
281     TYPE_ELEMENT,
282   },
283   {
284     GAME_PANEL_INVENTORY_LAST_1,
285     &game.panel.inventory_last[0],
286     TYPE_ELEMENT,
287   },
288   {
289     GAME_PANEL_INVENTORY_LAST_2,
290     &game.panel.inventory_last[1],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_LAST_3,
295     &game.panel.inventory_last[2],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_LAST_4,
300     &game.panel.inventory_last[3],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_LAST_5,
305     &game.panel.inventory_last[4],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_LAST_6,
310     &game.panel.inventory_last[5],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_LAST_7,
315     &game.panel.inventory_last[6],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_LAST_8,
320     &game.panel.inventory_last[7],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_KEY_1,
325     &game.panel.key[0],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_KEY_2,
330     &game.panel.key[1],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_KEY_3,
335     &game.panel.key[2],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_KEY_4,
340     &game.panel.key[3],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_KEY_5,
345     &game.panel.key[4],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_KEY_6,
350     &game.panel.key[5],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_KEY_7,
355     &game.panel.key[6],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_KEY_8,
360     &game.panel.key[7],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_KEY_WHITE,
365     &game.panel.key_white,
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_WHITE_COUNT,
370     &game.panel.key_white_count,
371     TYPE_INTEGER,
372   },
373   {
374     GAME_PANEL_SCORE,
375     &game.panel.score,
376     TYPE_INTEGER,
377   },
378   {
379     GAME_PANEL_HIGHSCORE,
380     &game.panel.highscore,
381     TYPE_INTEGER,
382   },
383   {
384     GAME_PANEL_TIME,
385     &game.panel.time,
386     TYPE_INTEGER,
387   },
388   {
389     GAME_PANEL_TIME_HH,
390     &game.panel.time_hh,
391     TYPE_INTEGER,
392   },
393   {
394     GAME_PANEL_TIME_MM,
395     &game.panel.time_mm,
396     TYPE_INTEGER,
397   },
398   {
399     GAME_PANEL_TIME_SS,
400     &game.panel.time_ss,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_PANEL_FRAME,
405     &game.panel.frame,
406     TYPE_INTEGER,
407   },
408   {
409     GAME_PANEL_SHIELD_NORMAL,
410     &game.panel.shield_normal,
411     TYPE_ELEMENT,
412   },
413   {
414     GAME_PANEL_SHIELD_NORMAL_TIME,
415     &game.panel.shield_normal_time,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_SHIELD_DEADLY,
420     &game.panel.shield_deadly,
421     TYPE_ELEMENT,
422   },
423   {
424     GAME_PANEL_SHIELD_DEADLY_TIME,
425     &game.panel.shield_deadly_time,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_EXIT,
430     &game.panel.exit,
431     TYPE_ELEMENT,
432   },
433   {
434     GAME_PANEL_EMC_MAGIC_BALL,
435     &game.panel.emc_magic_ball,
436     TYPE_ELEMENT,
437   },
438   {
439     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
440     &game.panel.emc_magic_ball_switch,
441     TYPE_ELEMENT,
442   },
443   {
444     GAME_PANEL_LIGHT_SWITCH,
445     &game.panel.light_switch,
446     TYPE_ELEMENT,
447   },
448   {
449     GAME_PANEL_LIGHT_SWITCH_TIME,
450     &game.panel.light_switch_time,
451     TYPE_INTEGER,
452   },
453   {
454     GAME_PANEL_TIMEGATE_SWITCH,
455     &game.panel.timegate_switch,
456     TYPE_ELEMENT,
457   },
458   {
459     GAME_PANEL_TIMEGATE_SWITCH_TIME,
460     &game.panel.timegate_switch_time,
461     TYPE_INTEGER,
462   },
463   {
464     GAME_PANEL_SWITCHGATE_SWITCH,
465     &game.panel.switchgate_switch,
466     TYPE_ELEMENT,
467   },
468   {
469     GAME_PANEL_EMC_LENSES,
470     &game.panel.emc_lenses,
471     TYPE_ELEMENT,
472   },
473   {
474     GAME_PANEL_EMC_LENSES_TIME,
475     &game.panel.emc_lenses_time,
476     TYPE_INTEGER,
477   },
478   {
479     GAME_PANEL_EMC_MAGNIFIER,
480     &game.panel.emc_magnifier,
481     TYPE_ELEMENT,
482   },
483   {
484     GAME_PANEL_EMC_MAGNIFIER_TIME,
485     &game.panel.emc_magnifier_time,
486     TYPE_INTEGER,
487   },
488   {
489     GAME_PANEL_BALLOON_SWITCH,
490     &game.panel.balloon_switch,
491     TYPE_ELEMENT,
492   },
493   {
494     GAME_PANEL_DYNABOMB_NUMBER,
495     &game.panel.dynabomb_number,
496     TYPE_INTEGER,
497   },
498   {
499     GAME_PANEL_DYNABOMB_SIZE,
500     &game.panel.dynabomb_size,
501     TYPE_INTEGER,
502   },
503   {
504     GAME_PANEL_DYNABOMB_POWER,
505     &game.panel.dynabomb_power,
506     TYPE_ELEMENT,
507   },
508   {
509     GAME_PANEL_PENGUINS,
510     &game.panel.penguins,
511     TYPE_INTEGER,
512   },
513   {
514     GAME_PANEL_SOKOBAN_OBJECTS,
515     &game.panel.sokoban_objects,
516     TYPE_INTEGER,
517   },
518   {
519     GAME_PANEL_SOKOBAN_FIELDS,
520     &game.panel.sokoban_fields,
521     TYPE_INTEGER,
522   },
523   {
524     GAME_PANEL_ROBOT_WHEEL,
525     &game.panel.robot_wheel,
526     TYPE_ELEMENT,
527   },
528   {
529     GAME_PANEL_CONVEYOR_BELT_1,
530     &game.panel.conveyor_belt[0],
531     TYPE_ELEMENT,
532   },
533   {
534     GAME_PANEL_CONVEYOR_BELT_2,
535     &game.panel.conveyor_belt[1],
536     TYPE_ELEMENT,
537   },
538   {
539     GAME_PANEL_CONVEYOR_BELT_3,
540     &game.panel.conveyor_belt[2],
541     TYPE_ELEMENT,
542   },
543   {
544     GAME_PANEL_CONVEYOR_BELT_4,
545     &game.panel.conveyor_belt[3],
546     TYPE_ELEMENT,
547   },
548   {
549     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
550     &game.panel.conveyor_belt_switch[0],
551     TYPE_ELEMENT,
552   },
553   {
554     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
555     &game.panel.conveyor_belt_switch[1],
556     TYPE_ELEMENT,
557   },
558   {
559     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
560     &game.panel.conveyor_belt_switch[2],
561     TYPE_ELEMENT,
562   },
563   {
564     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
565     &game.panel.conveyor_belt_switch[3],
566     TYPE_ELEMENT,
567   },
568   {
569     GAME_PANEL_MAGIC_WALL,
570     &game.panel.magic_wall,
571     TYPE_ELEMENT,
572   },
573   {
574     GAME_PANEL_MAGIC_WALL_TIME,
575     &game.panel.magic_wall_time,
576     TYPE_INTEGER,
577   },
578   {
579     GAME_PANEL_GRAVITY_STATE,
580     &game.panel.gravity_state,
581     TYPE_STRING,
582   },
583   {
584     GAME_PANEL_GRAPHIC_1,
585     &game.panel.graphic[0],
586     TYPE_ELEMENT,
587   },
588   {
589     GAME_PANEL_GRAPHIC_2,
590     &game.panel.graphic[1],
591     TYPE_ELEMENT,
592   },
593   {
594     GAME_PANEL_GRAPHIC_3,
595     &game.panel.graphic[2],
596     TYPE_ELEMENT,
597   },
598   {
599     GAME_PANEL_GRAPHIC_4,
600     &game.panel.graphic[3],
601     TYPE_ELEMENT,
602   },
603   {
604     GAME_PANEL_GRAPHIC_5,
605     &game.panel.graphic[4],
606     TYPE_ELEMENT,
607   },
608   {
609     GAME_PANEL_GRAPHIC_6,
610     &game.panel.graphic[5],
611     TYPE_ELEMENT,
612   },
613   {
614     GAME_PANEL_GRAPHIC_7,
615     &game.panel.graphic[6],
616     TYPE_ELEMENT,
617   },
618   {
619     GAME_PANEL_GRAPHIC_8,
620     &game.panel.graphic[7],
621     TYPE_ELEMENT,
622   },
623   {
624     GAME_PANEL_ELEMENT_1,
625     &game.panel.element[0],
626     TYPE_ELEMENT,
627   },
628   {
629     GAME_PANEL_ELEMENT_2,
630     &game.panel.element[1],
631     TYPE_ELEMENT,
632   },
633   {
634     GAME_PANEL_ELEMENT_3,
635     &game.panel.element[2],
636     TYPE_ELEMENT,
637   },
638   {
639     GAME_PANEL_ELEMENT_4,
640     &game.panel.element[3],
641     TYPE_ELEMENT,
642   },
643   {
644     GAME_PANEL_ELEMENT_5,
645     &game.panel.element[4],
646     TYPE_ELEMENT,
647   },
648   {
649     GAME_PANEL_ELEMENT_6,
650     &game.panel.element[5],
651     TYPE_ELEMENT,
652   },
653   {
654     GAME_PANEL_ELEMENT_7,
655     &game.panel.element[6],
656     TYPE_ELEMENT,
657   },
658   {
659     GAME_PANEL_ELEMENT_8,
660     &game.panel.element[7],
661     TYPE_ELEMENT,
662   },
663   {
664     GAME_PANEL_ELEMENT_COUNT_1,
665     &game.panel.element_count[0],
666     TYPE_INTEGER,
667   },
668   {
669     GAME_PANEL_ELEMENT_COUNT_2,
670     &game.panel.element_count[1],
671     TYPE_INTEGER,
672   },
673   {
674     GAME_PANEL_ELEMENT_COUNT_3,
675     &game.panel.element_count[2],
676     TYPE_INTEGER,
677   },
678   {
679     GAME_PANEL_ELEMENT_COUNT_4,
680     &game.panel.element_count[3],
681     TYPE_INTEGER,
682   },
683   {
684     GAME_PANEL_ELEMENT_COUNT_5,
685     &game.panel.element_count[4],
686     TYPE_INTEGER,
687   },
688   {
689     GAME_PANEL_ELEMENT_COUNT_6,
690     &game.panel.element_count[5],
691     TYPE_INTEGER,
692   },
693   {
694     GAME_PANEL_ELEMENT_COUNT_7,
695     &game.panel.element_count[6],
696     TYPE_INTEGER,
697   },
698   {
699     GAME_PANEL_ELEMENT_COUNT_8,
700     &game.panel.element_count[7],
701     TYPE_INTEGER,
702   },
703   {
704     GAME_PANEL_CE_SCORE_1,
705     &game.panel.ce_score[0],
706     TYPE_INTEGER,
707   },
708   {
709     GAME_PANEL_CE_SCORE_2,
710     &game.panel.ce_score[1],
711     TYPE_INTEGER,
712   },
713   {
714     GAME_PANEL_CE_SCORE_3,
715     &game.panel.ce_score[2],
716     TYPE_INTEGER,
717   },
718   {
719     GAME_PANEL_CE_SCORE_4,
720     &game.panel.ce_score[3],
721     TYPE_INTEGER,
722   },
723   {
724     GAME_PANEL_CE_SCORE_5,
725     &game.panel.ce_score[4],
726     TYPE_INTEGER,
727   },
728   {
729     GAME_PANEL_CE_SCORE_6,
730     &game.panel.ce_score[5],
731     TYPE_INTEGER,
732   },
733   {
734     GAME_PANEL_CE_SCORE_7,
735     &game.panel.ce_score[6],
736     TYPE_INTEGER,
737   },
738   {
739     GAME_PANEL_CE_SCORE_8,
740     &game.panel.ce_score[7],
741     TYPE_INTEGER,
742   },
743   {
744     GAME_PANEL_CE_SCORE_1_ELEMENT,
745     &game.panel.ce_score_element[0],
746     TYPE_ELEMENT,
747   },
748   {
749     GAME_PANEL_CE_SCORE_2_ELEMENT,
750     &game.panel.ce_score_element[1],
751     TYPE_ELEMENT,
752   },
753   {
754     GAME_PANEL_CE_SCORE_3_ELEMENT,
755     &game.panel.ce_score_element[2],
756     TYPE_ELEMENT,
757   },
758   {
759     GAME_PANEL_CE_SCORE_4_ELEMENT,
760     &game.panel.ce_score_element[3],
761     TYPE_ELEMENT,
762   },
763   {
764     GAME_PANEL_CE_SCORE_5_ELEMENT,
765     &game.panel.ce_score_element[4],
766     TYPE_ELEMENT,
767   },
768   {
769     GAME_PANEL_CE_SCORE_6_ELEMENT,
770     &game.panel.ce_score_element[5],
771     TYPE_ELEMENT,
772   },
773   {
774     GAME_PANEL_CE_SCORE_7_ELEMENT,
775     &game.panel.ce_score_element[6],
776     TYPE_ELEMENT,
777   },
778   {
779     GAME_PANEL_CE_SCORE_8_ELEMENT,
780     &game.panel.ce_score_element[7],
781     TYPE_ELEMENT,
782   },
783   {
784     GAME_PANEL_PLAYER_NAME,
785     &game.panel.player_name,
786     TYPE_STRING,
787   },
788   {
789     GAME_PANEL_LEVEL_NAME,
790     &game.panel.level_name,
791     TYPE_STRING,
792   },
793   {
794     GAME_PANEL_LEVEL_AUTHOR,
795     &game.panel.level_author,
796     TYPE_STRING,
797   },
798
799   {
800     -1,
801     NULL,
802     -1,
803   }
804 };
805
806 /* values for delayed check of falling and moving elements and for collision */
807 #define CHECK_DELAY_MOVING      3
808 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
809 #define CHECK_DELAY_COLLISION   2
810 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
811
812 /* values for initial player move delay (initial delay counter value) */
813 #define INITIAL_MOVE_DELAY_OFF  -1
814 #define INITIAL_MOVE_DELAY_ON   0
815
816 /* values for player movement speed (which is in fact a delay value) */
817 #define MOVE_DELAY_MIN_SPEED    32
818 #define MOVE_DELAY_NORMAL_SPEED 8
819 #define MOVE_DELAY_HIGH_SPEED   4
820 #define MOVE_DELAY_MAX_SPEED    1
821
822 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
823 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
824
825 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
826 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
827
828 /* values for other actions */
829 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
830 #define MOVE_STEPSIZE_MIN       (1)
831 #define MOVE_STEPSIZE_MAX       (TILEX)
832
833 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
834 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
835
836 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
837
838 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
839                                  RND(element_info[e].push_delay_random))
840 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
841                                  RND(element_info[e].drop_delay_random))
842 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
843                                  RND(element_info[e].move_delay_random))
844 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
845                                     (element_info[e].move_delay_random))
846 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
847                                  RND(element_info[e].ce_value_random_initial))
848 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
849 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
850                                  RND((c)->delay_random * (c)->delay_frames))
851 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
852                                  RND((c)->delay_random))
853
854
855 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
856          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
857
858 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
859         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
860          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
861          (be) + (e) - EL_SELF)
862
863 #define GET_PLAYER_FROM_BITS(p)                                         \
864         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
865
866 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
867         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
868          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
869          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
870          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
871          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
872          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
873          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
874          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
875          (e))
876
877 #define CAN_GROW_INTO(e)                                                \
878         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
879
880 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
881                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
882                                         (condition)))
883
884 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
885                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
886                                         (CAN_MOVE_INTO_ACID(e) &&       \
887                                          Feld[x][y] == EL_ACID) ||      \
888                                         (condition)))
889
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
891                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
892                                         (CAN_MOVE_INTO_ACID(e) &&       \
893                                          Feld[x][y] == EL_ACID) ||      \
894                                         (condition)))
895
896 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
897                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
898                                         (condition) ||                  \
899                                         (CAN_MOVE_INTO_ACID(e) &&       \
900                                          Feld[x][y] == EL_ACID) ||      \
901                                         (DONT_COLLIDE_WITH(e) &&        \
902                                          IS_PLAYER(x, y) &&             \
903                                          !PLAYER_ENEMY_PROTECTED(x, y))))
904
905 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
906         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
907
908 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
909         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
910
911 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
912         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
913
914 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
915         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
916                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
917
918 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
919         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
920
921 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
922         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
923
924 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
925         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
926
927 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
928         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
929
930 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
931         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
932
933 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
934         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
935                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
938                                                  IS_FOOD_PENGUIN(Feld[x][y])))
939 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
940         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
941
942 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
943         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
944
945 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
946         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
947
948 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
949         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
950                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
951
952 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
953
954 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
955                 (!IS_PLAYER(x, y) &&                                    \
956                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
957
958 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
959         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
960
961 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
962 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
963
964 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
965 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
966 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
968
969 /* game button identifiers */
970 #define GAME_CTRL_ID_STOP               0
971 #define GAME_CTRL_ID_PAUSE              1
972 #define GAME_CTRL_ID_PLAY               2
973 #define SOUND_CTRL_ID_MUSIC             3
974 #define SOUND_CTRL_ID_LOOPS             4
975 #define SOUND_CTRL_ID_SIMPLE            5
976 #define GAME_CTRL_ID_SAVE               6
977 #define GAME_CTRL_ID_LOAD               7
978
979 #define NUM_GAME_BUTTONS                8
980
981
982 /* forward declaration for internal use */
983
984 static void CreateField(int, int, int);
985
986 static void ResetGfxAnimation(int, int);
987
988 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
989 static void AdvanceFrameAndPlayerCounters(int);
990
991 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
992 static boolean MovePlayer(struct PlayerInfo *, int, int);
993 static void ScrollPlayer(struct PlayerInfo *, int);
994 static void ScrollScreen(struct PlayerInfo *, int);
995
996 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
997 static boolean DigFieldByCE(int, int, int);
998 static boolean SnapField(struct PlayerInfo *, int, int);
999 static boolean DropElement(struct PlayerInfo *);
1000
1001 static void InitBeltMovement(void);
1002 static void CloseAllOpenTimegates(void);
1003 static void CheckGravityMovement(struct PlayerInfo *);
1004 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1005 static void KillPlayerUnlessEnemyProtected(int, int);
1006 static void KillPlayerUnlessExplosionProtected(int, int);
1007
1008 static void TestIfPlayerTouchesCustomElement(int, int);
1009 static void TestIfElementTouchesCustomElement(int, int);
1010 static void TestIfElementHitsCustomElement(int, int, int);
1011
1012 static void HandleElementChange(int, int, int);
1013 static void ExecuteCustomElementAction(int, int, int, int);
1014 static boolean ChangeElement(int, int, int, int);
1015
1016 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1017 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1018         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1019 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1020         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1021 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1022         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1023 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1024         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1025
1026 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1027 #define CheckElementChange(x, y, e, te, ev)                             \
1028         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1029 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1030         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1031 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1032         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1033
1034 static void PlayLevelSound(int, int, int);
1035 static void PlayLevelSoundNearest(int, int, int);
1036 static void PlayLevelSoundAction(int, int, int);
1037 static void PlayLevelSoundElementAction(int, int, int, int);
1038 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1039 static void PlayLevelSoundActionIfLoop(int, int, int);
1040 static void StopLevelSoundActionIfLoop(int, int, int);
1041 static void PlayLevelMusic();
1042
1043 static void HandleGameButtons(struct GadgetInfo *);
1044
1045 int AmoebeNachbarNr(int, int);
1046 void AmoebeUmwandeln(int, int);
1047 void ContinueMoving(int, int);
1048 void Bang(int, int);
1049 void InitMovDir(int, int);
1050 void InitAmoebaNr(int, int);
1051 int NewHiScore(void);
1052
1053 void TestIfGoodThingHitsBadThing(int, int, int);
1054 void TestIfBadThingHitsGoodThing(int, int, int);
1055 void TestIfPlayerTouchesBadThing(int, int);
1056 void TestIfPlayerRunsIntoBadThing(int, int, int);
1057 void TestIfBadThingTouchesPlayer(int, int);
1058 void TestIfBadThingRunsIntoPlayer(int, int, int);
1059 void TestIfFriendTouchesBadThing(int, int);
1060 void TestIfBadThingTouchesFriend(int, int);
1061 void TestIfBadThingTouchesOtherBadThing(int, int);
1062 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1063
1064 void KillPlayer(struct PlayerInfo *);
1065 void BuryPlayer(struct PlayerInfo *);
1066 void RemovePlayer(struct PlayerInfo *);
1067
1068 static int getInvisibleActiveFromInvisibleElement(int);
1069 static int getInvisibleFromInvisibleActiveElement(int);
1070
1071 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1072
1073 /* for detection of endless loops, caused by custom element programming */
1074 /* (using maximal playfield width x 10 is just a rough approximation) */
1075 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1076
1077 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1078 {                                                                       \
1079   if (recursion_loop_detected)                                          \
1080     return (rc);                                                        \
1081                                                                         \
1082   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1083   {                                                                     \
1084     recursion_loop_detected = TRUE;                                     \
1085     recursion_loop_element = (e);                                       \
1086   }                                                                     \
1087                                                                         \
1088   recursion_loop_depth++;                                               \
1089 }
1090
1091 #define RECURSION_LOOP_DETECTION_END()                                  \
1092 {                                                                       \
1093   recursion_loop_depth--;                                               \
1094 }
1095
1096 static int recursion_loop_depth;
1097 static boolean recursion_loop_detected;
1098 static boolean recursion_loop_element;
1099
1100 static int map_player_action[MAX_PLAYERS];
1101
1102
1103 /* ------------------------------------------------------------------------- */
1104 /* definition of elements that automatically change to other elements after  */
1105 /* a specified time, eventually calling a function when changing             */
1106 /* ------------------------------------------------------------------------- */
1107
1108 /* forward declaration for changer functions */
1109 static void InitBuggyBase(int, int);
1110 static void WarnBuggyBase(int, int);
1111
1112 static void InitTrap(int, int);
1113 static void ActivateTrap(int, int);
1114 static void ChangeActiveTrap(int, int);
1115
1116 static void InitRobotWheel(int, int);
1117 static void RunRobotWheel(int, int);
1118 static void StopRobotWheel(int, int);
1119
1120 static void InitTimegateWheel(int, int);
1121 static void RunTimegateWheel(int, int);
1122
1123 static void InitMagicBallDelay(int, int);
1124 static void ActivateMagicBall(int, int);
1125
1126 struct ChangingElementInfo
1127 {
1128   int element;
1129   int target_element;
1130   int change_delay;
1131   void (*pre_change_function)(int x, int y);
1132   void (*change_function)(int x, int y);
1133   void (*post_change_function)(int x, int y);
1134 };
1135
1136 static struct ChangingElementInfo change_delay_list[] =
1137 {
1138   {
1139     EL_NUT_BREAKING,
1140     EL_EMERALD,
1141     6,
1142     NULL,
1143     NULL,
1144     NULL
1145   },
1146   {
1147     EL_PEARL_BREAKING,
1148     EL_EMPTY,
1149     8,
1150     NULL,
1151     NULL,
1152     NULL
1153   },
1154   {
1155     EL_EXIT_OPENING,
1156     EL_EXIT_OPEN,
1157     29,
1158     NULL,
1159     NULL,
1160     NULL
1161   },
1162   {
1163     EL_EXIT_CLOSING,
1164     EL_EXIT_CLOSED,
1165     29,
1166     NULL,
1167     NULL,
1168     NULL
1169   },
1170   {
1171     EL_STEEL_EXIT_OPENING,
1172     EL_STEEL_EXIT_OPEN,
1173     29,
1174     NULL,
1175     NULL,
1176     NULL
1177   },
1178   {
1179     EL_STEEL_EXIT_CLOSING,
1180     EL_STEEL_EXIT_CLOSED,
1181     29,
1182     NULL,
1183     NULL,
1184     NULL
1185   },
1186   {
1187     EL_EM_EXIT_OPENING,
1188     EL_EM_EXIT_OPEN,
1189     29,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_EM_EXIT_CLOSING,
1196     EL_EMPTY,
1197     29,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EM_STEEL_EXIT_OPENING,
1204     EL_EM_STEEL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EM_STEEL_EXIT_CLOSING,
1212     EL_STEELWALL,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_SP_EXIT_OPENING,
1220     EL_SP_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_SP_EXIT_CLOSING,
1228     EL_SP_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_SWITCHGATE_OPENING,
1236     EL_SWITCHGATE_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_SWITCHGATE_CLOSING,
1244     EL_SWITCHGATE_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_TIMEGATE_OPENING,
1252     EL_TIMEGATE_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_TIMEGATE_CLOSING,
1260     EL_TIMEGATE_CLOSED,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266
1267   {
1268     EL_ACID_SPLASH_LEFT,
1269     EL_EMPTY,
1270     8,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_ACID_SPLASH_RIGHT,
1277     EL_EMPTY,
1278     8,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SP_BUGGY_BASE,
1285     EL_SP_BUGGY_BASE_ACTIVATING,
1286     0,
1287     InitBuggyBase,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SP_BUGGY_BASE_ACTIVATING,
1293     EL_SP_BUGGY_BASE_ACTIVE,
1294     0,
1295     InitBuggyBase,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_SP_BUGGY_BASE_ACTIVE,
1301     EL_SP_BUGGY_BASE,
1302     0,
1303     InitBuggyBase,
1304     WarnBuggyBase,
1305     NULL
1306   },
1307   {
1308     EL_TRAP,
1309     EL_TRAP_ACTIVE,
1310     0,
1311     InitTrap,
1312     NULL,
1313     ActivateTrap
1314   },
1315   {
1316     EL_TRAP_ACTIVE,
1317     EL_TRAP,
1318     31,
1319     NULL,
1320     ChangeActiveTrap,
1321     NULL
1322   },
1323   {
1324     EL_ROBOT_WHEEL_ACTIVE,
1325     EL_ROBOT_WHEEL,
1326     0,
1327     InitRobotWheel,
1328     RunRobotWheel,
1329     StopRobotWheel
1330   },
1331   {
1332     EL_TIMEGATE_SWITCH_ACTIVE,
1333     EL_TIMEGATE_SWITCH,
1334     0,
1335     InitTimegateWheel,
1336     RunTimegateWheel,
1337     NULL
1338   },
1339   {
1340     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1341     EL_DC_TIMEGATE_SWITCH,
1342     0,
1343     InitTimegateWheel,
1344     RunTimegateWheel,
1345     NULL
1346   },
1347   {
1348     EL_EMC_MAGIC_BALL_ACTIVE,
1349     EL_EMC_MAGIC_BALL_ACTIVE,
1350     0,
1351     InitMagicBallDelay,
1352     NULL,
1353     ActivateMagicBall
1354   },
1355   {
1356     EL_EMC_SPRING_BUMPER_ACTIVE,
1357     EL_EMC_SPRING_BUMPER,
1358     8,
1359     NULL,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_DIAGONAL_SHRINKING,
1365     EL_UNDEFINED,
1366     0,
1367     NULL,
1368     NULL,
1369     NULL
1370   },
1371   {
1372     EL_DIAGONAL_GROWING,
1373     EL_UNDEFINED,
1374     0,
1375     NULL,
1376     NULL,
1377     NULL,
1378   },
1379
1380   {
1381     EL_UNDEFINED,
1382     EL_UNDEFINED,
1383     -1,
1384     NULL,
1385     NULL,
1386     NULL
1387   }
1388 };
1389
1390 struct
1391 {
1392   int element;
1393   int push_delay_fixed, push_delay_random;
1394 }
1395 push_delay_list[] =
1396 {
1397   { EL_SPRING,                  0, 0 },
1398   { EL_BALLOON,                 0, 0 },
1399
1400   { EL_SOKOBAN_OBJECT,          2, 0 },
1401   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1402   { EL_SATELLITE,               2, 0 },
1403   { EL_SP_DISK_YELLOW,          2, 0 },
1404
1405   { EL_UNDEFINED,               0, 0 },
1406 };
1407
1408 struct
1409 {
1410   int element;
1411   int move_stepsize;
1412 }
1413 move_stepsize_list[] =
1414 {
1415   { EL_AMOEBA_DROP,             2 },
1416   { EL_AMOEBA_DROPPING,         2 },
1417   { EL_QUICKSAND_FILLING,       1 },
1418   { EL_QUICKSAND_EMPTYING,      1 },
1419   { EL_QUICKSAND_FAST_FILLING,  2 },
1420   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1421   { EL_MAGIC_WALL_FILLING,      2 },
1422   { EL_MAGIC_WALL_EMPTYING,     2 },
1423   { EL_BD_MAGIC_WALL_FILLING,   2 },
1424   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1425   { EL_DC_MAGIC_WALL_FILLING,   2 },
1426   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1427
1428   { EL_UNDEFINED,               0 },
1429 };
1430
1431 struct
1432 {
1433   int element;
1434   int count;
1435 }
1436 collect_count_list[] =
1437 {
1438   { EL_EMERALD,                 1 },
1439   { EL_BD_DIAMOND,              1 },
1440   { EL_EMERALD_YELLOW,          1 },
1441   { EL_EMERALD_RED,             1 },
1442   { EL_EMERALD_PURPLE,          1 },
1443   { EL_DIAMOND,                 3 },
1444   { EL_SP_INFOTRON,             1 },
1445   { EL_PEARL,                   5 },
1446   { EL_CRYSTAL,                 8 },
1447
1448   { EL_UNDEFINED,               0 },
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int direction;
1455 }
1456 access_direction_list[] =
1457 {
1458   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1459   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1460   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1461   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1462   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1463   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1464   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1465   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1466   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1467   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1468   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1469
1470   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1471   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1472   { EL_SP_PORT_UP,                                                   MV_DOWN },
1473   { EL_SP_PORT_DOWN,                                         MV_UP           },
1474   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1475   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1476   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1477   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1478   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1479   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1480   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1481   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1482   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1483   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1484   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1485   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1486   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1487   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1488   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1489
1490   { EL_UNDEFINED,                       MV_NONE                              }
1491 };
1492
1493 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1494
1495 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1496 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1497 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1498                                  IS_JUST_CHANGING(x, y))
1499
1500 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1501
1502 /* static variables for playfield scan mode (scanning forward or backward) */
1503 static int playfield_scan_start_x = 0;
1504 static int playfield_scan_start_y = 0;
1505 static int playfield_scan_delta_x = 1;
1506 static int playfield_scan_delta_y = 1;
1507
1508 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1509                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1510                                      (y) += playfield_scan_delta_y)     \
1511                                 for ((x) = playfield_scan_start_x;      \
1512                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1513                                      (x) += playfield_scan_delta_x)
1514
1515 #ifdef DEBUG
1516 void DEBUG_SetMaximumDynamite()
1517 {
1518   int i;
1519
1520   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1521     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1522       local_player->inventory_element[local_player->inventory_size++] =
1523         EL_DYNAMITE;
1524 }
1525 #endif
1526
1527 static void InitPlayfieldScanModeVars()
1528 {
1529   if (game.use_reverse_scan_direction)
1530   {
1531     playfield_scan_start_x = lev_fieldx - 1;
1532     playfield_scan_start_y = lev_fieldy - 1;
1533
1534     playfield_scan_delta_x = -1;
1535     playfield_scan_delta_y = -1;
1536   }
1537   else
1538   {
1539     playfield_scan_start_x = 0;
1540     playfield_scan_start_y = 0;
1541
1542     playfield_scan_delta_x = 1;
1543     playfield_scan_delta_y = 1;
1544   }
1545 }
1546
1547 static void InitPlayfieldScanMode(int mode)
1548 {
1549   game.use_reverse_scan_direction =
1550     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1551
1552   InitPlayfieldScanModeVars();
1553 }
1554
1555 static int get_move_delay_from_stepsize(int move_stepsize)
1556 {
1557   move_stepsize =
1558     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1559
1560   /* make sure that stepsize value is always a power of 2 */
1561   move_stepsize = (1 << log_2(move_stepsize));
1562
1563   return TILEX / move_stepsize;
1564 }
1565
1566 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1567                                boolean init_game)
1568 {
1569   int player_nr = player->index_nr;
1570   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1571   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1572
1573   /* do no immediately change move delay -- the player might just be moving */
1574   player->move_delay_value_next = move_delay;
1575
1576   /* information if player can move must be set separately */
1577   player->cannot_move = cannot_move;
1578
1579   if (init_game)
1580   {
1581     player->move_delay       = game.initial_move_delay[player_nr];
1582     player->move_delay_value = game.initial_move_delay_value[player_nr];
1583
1584     player->move_delay_value_next = -1;
1585
1586     player->move_delay_reset_counter = 0;
1587   }
1588 }
1589
1590 void GetPlayerConfig()
1591 {
1592   GameFrameDelay = setup.game_frame_delay;
1593
1594   if (!audio.sound_available)
1595     setup.sound_simple = FALSE;
1596
1597   if (!audio.loops_available)
1598     setup.sound_loops = FALSE;
1599
1600   if (!audio.music_available)
1601     setup.sound_music = FALSE;
1602
1603   if (!video.fullscreen_available)
1604     setup.fullscreen = FALSE;
1605
1606   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1607
1608   SetAudioMode(setup.sound);
1609   InitJoysticks();
1610 }
1611
1612 int GetElementFromGroupElement(int element)
1613 {
1614   if (IS_GROUP_ELEMENT(element))
1615   {
1616     struct ElementGroupInfo *group = element_info[element].group;
1617     int last_anim_random_frame = gfx.anim_random_frame;
1618     int element_pos;
1619
1620     if (group->choice_mode == ANIM_RANDOM)
1621       gfx.anim_random_frame = RND(group->num_elements_resolved);
1622
1623     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1624                                     group->choice_mode, 0,
1625                                     group->choice_pos);
1626
1627     if (group->choice_mode == ANIM_RANDOM)
1628       gfx.anim_random_frame = last_anim_random_frame;
1629
1630     group->choice_pos++;
1631
1632     element = group->element_resolved[element_pos];
1633   }
1634
1635   return element;
1636 }
1637
1638 static void InitPlayerField(int x, int y, int element, boolean init_game)
1639 {
1640   if (element == EL_SP_MURPHY)
1641   {
1642     if (init_game)
1643     {
1644       if (stored_player[0].present)
1645       {
1646         Feld[x][y] = EL_SP_MURPHY_CLONE;
1647
1648         return;
1649       }
1650       else
1651       {
1652         stored_player[0].initial_element = element;
1653         stored_player[0].use_murphy = TRUE;
1654
1655         if (!level.use_artwork_element[0])
1656           stored_player[0].artwork_element = EL_SP_MURPHY;
1657       }
1658
1659       Feld[x][y] = EL_PLAYER_1;
1660     }
1661   }
1662
1663   if (init_game)
1664   {
1665     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1666     int jx = player->jx, jy = player->jy;
1667
1668     player->present = TRUE;
1669
1670     player->block_last_field = (element == EL_SP_MURPHY ?
1671                                 level.sp_block_last_field :
1672                                 level.block_last_field);
1673
1674     /* ---------- initialize player's last field block delay --------------- */
1675
1676     /* always start with reliable default value (no adjustment needed) */
1677     player->block_delay_adjustment = 0;
1678
1679     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1680     if (player->block_last_field && element == EL_SP_MURPHY)
1681       player->block_delay_adjustment = 1;
1682
1683     /* special case 2: in game engines before 3.1.1, blocking was different */
1684     if (game.use_block_last_field_bug)
1685       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1686
1687     if (!options.network || player->connected)
1688     {
1689       player->active = TRUE;
1690
1691       /* remove potentially duplicate players */
1692       if (StorePlayer[jx][jy] == Feld[x][y])
1693         StorePlayer[jx][jy] = 0;
1694
1695       StorePlayer[x][y] = Feld[x][y];
1696
1697 #if DEBUG_INIT_PLAYER
1698       if (options.debug)
1699       {
1700         printf("- player element %d activated", player->element_nr);
1701         printf(" (local player is %d and currently %s)\n",
1702                local_player->element_nr,
1703                local_player->active ? "active" : "not active");
1704       }
1705     }
1706 #endif
1707
1708     Feld[x][y] = EL_EMPTY;
1709
1710     player->jx = player->last_jx = x;
1711     player->jy = player->last_jy = y;
1712   }
1713
1714   if (!init_game)
1715   {
1716     int player_nr = GET_PLAYER_NR(element);
1717     struct PlayerInfo *player = &stored_player[player_nr];
1718
1719     if (player->active && player->killed)
1720       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1721   }
1722 }
1723
1724 static void InitField(int x, int y, boolean init_game)
1725 {
1726   int element = Feld[x][y];
1727
1728   switch (element)
1729   {
1730     case EL_SP_MURPHY:
1731     case EL_PLAYER_1:
1732     case EL_PLAYER_2:
1733     case EL_PLAYER_3:
1734     case EL_PLAYER_4:
1735       InitPlayerField(x, y, element, init_game);
1736       break;
1737
1738     case EL_SOKOBAN_FIELD_PLAYER:
1739       element = Feld[x][y] = EL_PLAYER_1;
1740       InitField(x, y, init_game);
1741
1742       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1743       InitField(x, y, init_game);
1744       break;
1745
1746     case EL_SOKOBAN_FIELD_EMPTY:
1747       local_player->sokobanfields_still_needed++;
1748       break;
1749
1750     case EL_STONEBLOCK:
1751       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1752         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1753       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1754         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1755       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1756         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1757       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1758         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1759       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1760         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1761       break;
1762
1763     case EL_BUG:
1764     case EL_BUG_RIGHT:
1765     case EL_BUG_UP:
1766     case EL_BUG_LEFT:
1767     case EL_BUG_DOWN:
1768     case EL_SPACESHIP:
1769     case EL_SPACESHIP_RIGHT:
1770     case EL_SPACESHIP_UP:
1771     case EL_SPACESHIP_LEFT:
1772     case EL_SPACESHIP_DOWN:
1773     case EL_BD_BUTTERFLY:
1774     case EL_BD_BUTTERFLY_RIGHT:
1775     case EL_BD_BUTTERFLY_UP:
1776     case EL_BD_BUTTERFLY_LEFT:
1777     case EL_BD_BUTTERFLY_DOWN:
1778     case EL_BD_FIREFLY:
1779     case EL_BD_FIREFLY_RIGHT:
1780     case EL_BD_FIREFLY_UP:
1781     case EL_BD_FIREFLY_LEFT:
1782     case EL_BD_FIREFLY_DOWN:
1783     case EL_PACMAN_RIGHT:
1784     case EL_PACMAN_UP:
1785     case EL_PACMAN_LEFT:
1786     case EL_PACMAN_DOWN:
1787     case EL_YAMYAM:
1788     case EL_YAMYAM_LEFT:
1789     case EL_YAMYAM_RIGHT:
1790     case EL_YAMYAM_UP:
1791     case EL_YAMYAM_DOWN:
1792     case EL_DARK_YAMYAM:
1793     case EL_ROBOT:
1794     case EL_PACMAN:
1795     case EL_SP_SNIKSNAK:
1796     case EL_SP_ELECTRON:
1797     case EL_MOLE:
1798     case EL_MOLE_LEFT:
1799     case EL_MOLE_RIGHT:
1800     case EL_MOLE_UP:
1801     case EL_MOLE_DOWN:
1802       InitMovDir(x, y);
1803       break;
1804
1805     case EL_AMOEBA_FULL:
1806     case EL_BD_AMOEBA:
1807       InitAmoebaNr(x, y);
1808       break;
1809
1810     case EL_AMOEBA_DROP:
1811       if (y == lev_fieldy - 1)
1812       {
1813         Feld[x][y] = EL_AMOEBA_GROWING;
1814         Store[x][y] = EL_AMOEBA_WET;
1815       }
1816       break;
1817
1818     case EL_DYNAMITE_ACTIVE:
1819     case EL_SP_DISK_RED_ACTIVE:
1820     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1821     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1822     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1823     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1824       MovDelay[x][y] = 96;
1825       break;
1826
1827     case EL_EM_DYNAMITE_ACTIVE:
1828       MovDelay[x][y] = 32;
1829       break;
1830
1831     case EL_LAMP:
1832       local_player->lights_still_needed++;
1833       break;
1834
1835     case EL_PENGUIN:
1836       local_player->friends_still_needed++;
1837       break;
1838
1839     case EL_PIG:
1840     case EL_DRAGON:
1841       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1842       break;
1843
1844     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1845     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1846     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1847     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1848     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1849     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1850     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1851     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1852     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1853     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1854     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1855     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1856       if (init_game)
1857       {
1858         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1859         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1860         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1861
1862         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1863         {
1864           game.belt_dir[belt_nr] = belt_dir;
1865           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1866         }
1867         else    /* more than one switch -- set it like the first switch */
1868         {
1869           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1870         }
1871       }
1872       break;
1873
1874     case EL_LIGHT_SWITCH_ACTIVE:
1875       if (init_game)
1876         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1877       break;
1878
1879     case EL_INVISIBLE_STEELWALL:
1880     case EL_INVISIBLE_WALL:
1881     case EL_INVISIBLE_SAND:
1882       if (game.light_time_left > 0 ||
1883           game.lenses_time_left > 0)
1884         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1885       break;
1886
1887     case EL_EMC_MAGIC_BALL:
1888       if (game.ball_state)
1889         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1890       break;
1891
1892     case EL_EMC_MAGIC_BALL_SWITCH:
1893       if (game.ball_state)
1894         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1895       break;
1896
1897     case EL_TRIGGER_PLAYER:
1898     case EL_TRIGGER_ELEMENT:
1899     case EL_TRIGGER_CE_VALUE:
1900     case EL_TRIGGER_CE_SCORE:
1901     case EL_SELF:
1902     case EL_ANY_ELEMENT:
1903     case EL_CURRENT_CE_VALUE:
1904     case EL_CURRENT_CE_SCORE:
1905     case EL_PREV_CE_1:
1906     case EL_PREV_CE_2:
1907     case EL_PREV_CE_3:
1908     case EL_PREV_CE_4:
1909     case EL_PREV_CE_5:
1910     case EL_PREV_CE_6:
1911     case EL_PREV_CE_7:
1912     case EL_PREV_CE_8:
1913     case EL_NEXT_CE_1:
1914     case EL_NEXT_CE_2:
1915     case EL_NEXT_CE_3:
1916     case EL_NEXT_CE_4:
1917     case EL_NEXT_CE_5:
1918     case EL_NEXT_CE_6:
1919     case EL_NEXT_CE_7:
1920     case EL_NEXT_CE_8:
1921       /* reference elements should not be used on the playfield */
1922       Feld[x][y] = EL_EMPTY;
1923       break;
1924
1925     default:
1926       if (IS_CUSTOM_ELEMENT(element))
1927       {
1928         if (CAN_MOVE(element))
1929           InitMovDir(x, y);
1930
1931         if (!element_info[element].use_last_ce_value || init_game)
1932           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1933       }
1934       else if (IS_GROUP_ELEMENT(element))
1935       {
1936         Feld[x][y] = GetElementFromGroupElement(element);
1937
1938         InitField(x, y, init_game);
1939       }
1940
1941       break;
1942   }
1943
1944   if (!init_game)
1945     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1946 }
1947
1948 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1949 {
1950   InitField(x, y, init_game);
1951
1952   /* not needed to call InitMovDir() -- already done by InitField()! */
1953   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1954       CAN_MOVE(Feld[x][y]))
1955     InitMovDir(x, y);
1956 }
1957
1958 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1959 {
1960   int old_element = Feld[x][y];
1961
1962   InitField(x, y, init_game);
1963
1964   /* not needed to call InitMovDir() -- already done by InitField()! */
1965   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1966       CAN_MOVE(old_element) &&
1967       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1968     InitMovDir(x, y);
1969
1970   /* this case is in fact a combination of not less than three bugs:
1971      first, it calls InitMovDir() for elements that can move, although this is
1972      already done by InitField(); then, it checks the element that was at this
1973      field _before_ the call to InitField() (which can change it); lastly, it
1974      was not called for "mole with direction" elements, which were treated as
1975      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1976   */
1977 }
1978
1979 static int get_key_element_from_nr(int key_nr)
1980 {
1981   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1982                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1983                           EL_EM_KEY_1 : EL_KEY_1);
1984
1985   return key_base_element + key_nr;
1986 }
1987
1988 static int get_next_dropped_element(struct PlayerInfo *player)
1989 {
1990   return (player->inventory_size > 0 ?
1991           player->inventory_element[player->inventory_size - 1] :
1992           player->inventory_infinite_element != EL_UNDEFINED ?
1993           player->inventory_infinite_element :
1994           player->dynabombs_left > 0 ?
1995           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1996           EL_UNDEFINED);
1997 }
1998
1999 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2000 {
2001   /* pos >= 0: get element from bottom of the stack;
2002      pos <  0: get element from top of the stack */
2003
2004   if (pos < 0)
2005   {
2006     int min_inventory_size = -pos;
2007     int inventory_pos = player->inventory_size - min_inventory_size;
2008     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2009
2010     return (player->inventory_size >= min_inventory_size ?
2011             player->inventory_element[inventory_pos] :
2012             player->inventory_infinite_element != EL_UNDEFINED ?
2013             player->inventory_infinite_element :
2014             player->dynabombs_left >= min_dynabombs_left ?
2015             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2016             EL_UNDEFINED);
2017   }
2018   else
2019   {
2020     int min_dynabombs_left = pos + 1;
2021     int min_inventory_size = pos + 1 - player->dynabombs_left;
2022     int inventory_pos = pos - player->dynabombs_left;
2023
2024     return (player->inventory_infinite_element != EL_UNDEFINED ?
2025             player->inventory_infinite_element :
2026             player->dynabombs_left >= min_dynabombs_left ?
2027             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2028             player->inventory_size >= min_inventory_size ?
2029             player->inventory_element[inventory_pos] :
2030             EL_UNDEFINED);
2031   }
2032 }
2033
2034 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2035 {
2036   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2037   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2038   int compare_result;
2039
2040   if (gpo1->sort_priority != gpo2->sort_priority)
2041     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2042   else
2043     compare_result = gpo1->nr - gpo2->nr;
2044
2045   return compare_result;
2046 }
2047
2048 void InitGameControlValues()
2049 {
2050   int i;
2051
2052   for (i = 0; game_panel_controls[i].nr != -1; i++)
2053   {
2054     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2055     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2056     struct TextPosInfo *pos = gpc->pos;
2057     int nr = gpc->nr;
2058     int type = gpc->type;
2059
2060     if (nr != i)
2061     {
2062       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2063       Error(ERR_EXIT, "this should not happen -- please debug");
2064     }
2065
2066     /* force update of game controls after initialization */
2067     gpc->value = gpc->last_value = -1;
2068     gpc->frame = gpc->last_frame = -1;
2069     gpc->gfx_frame = -1;
2070
2071     /* determine panel value width for later calculation of alignment */
2072     if (type == TYPE_INTEGER || type == TYPE_STRING)
2073     {
2074       pos->width = pos->size * getFontWidth(pos->font);
2075       pos->height = getFontHeight(pos->font);
2076     }
2077     else if (type == TYPE_ELEMENT)
2078     {
2079       pos->width = pos->size;
2080       pos->height = pos->size;
2081     }
2082
2083     /* fill structure for game panel draw order */
2084     gpo->nr = gpc->nr;
2085     gpo->sort_priority = pos->sort_priority;
2086   }
2087
2088   /* sort game panel controls according to sort_priority and control number */
2089   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2090         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2091 }
2092
2093 void UpdatePlayfieldElementCount()
2094 {
2095   boolean use_element_count = FALSE;
2096   int i, j, x, y;
2097
2098   /* first check if it is needed at all to calculate playfield element count */
2099   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2100     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2101       use_element_count = TRUE;
2102
2103   if (!use_element_count)
2104     return;
2105
2106   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2107     element_info[i].element_count = 0;
2108
2109   SCAN_PLAYFIELD(x, y)
2110   {
2111     element_info[Feld[x][y]].element_count++;
2112   }
2113
2114   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2115     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2116       if (IS_IN_GROUP(j, i))
2117         element_info[EL_GROUP_START + i].element_count +=
2118           element_info[j].element_count;
2119 }
2120
2121 void UpdateGameControlValues()
2122 {
2123   int i, k;
2124   int time = (local_player->LevelSolved ?
2125               local_player->LevelSolved_CountingTime :
2126               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2127               level.native_em_level->lev->time :
2128               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2129               level.native_sp_level->game_sp->time_played :
2130               game.no_time_limit ? TimePlayed : TimeLeft);
2131   int score = (local_player->LevelSolved ?
2132                local_player->LevelSolved_CountingScore :
2133                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2134                level.native_em_level->lev->score :
2135                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2136                level.native_sp_level->game_sp->score :
2137                local_player->score);
2138   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2139               level.native_em_level->lev->required :
2140               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2141               level.native_sp_level->game_sp->infotrons_still_needed :
2142               local_player->gems_still_needed);
2143   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2144                      level.native_em_level->lev->required > 0 :
2145                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2146                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2147                      local_player->gems_still_needed > 0 ||
2148                      local_player->sokobanfields_still_needed > 0 ||
2149                      local_player->lights_still_needed > 0);
2150
2151   UpdatePlayfieldElementCount();
2152
2153   /* update game panel control values */
2154
2155   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2156   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2157
2158   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2159   for (i = 0; i < MAX_NUM_KEYS; i++)
2160     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2161   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2162   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2163
2164   if (game.centered_player_nr == -1)
2165   {
2166     for (i = 0; i < MAX_PLAYERS; i++)
2167     {
2168       /* only one player in Supaplex game engine */
2169       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2170         break;
2171
2172       for (k = 0; k < MAX_NUM_KEYS; k++)
2173       {
2174         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2175         {
2176           if (level.native_em_level->ply[i]->keys & (1 << k))
2177             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2178               get_key_element_from_nr(k);
2179         }
2180         else if (stored_player[i].key[k])
2181           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182             get_key_element_from_nr(k);
2183       }
2184
2185       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2186         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2187           level.native_em_level->ply[i]->dynamite;
2188       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2189         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2190           level.native_sp_level->game_sp->red_disk_count;
2191       else
2192         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2193           stored_player[i].inventory_size;
2194
2195       if (stored_player[i].num_white_keys > 0)
2196         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2197           EL_DC_KEY_WHITE;
2198
2199       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2200         stored_player[i].num_white_keys;
2201     }
2202   }
2203   else
2204   {
2205     int player_nr = game.centered_player_nr;
2206
2207     for (k = 0; k < MAX_NUM_KEYS; k++)
2208     {
2209       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2210       {
2211         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2212           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2213             get_key_element_from_nr(k);
2214       }
2215       else if (stored_player[player_nr].key[k])
2216         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217           get_key_element_from_nr(k);
2218     }
2219
2220     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2221       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2222         level.native_em_level->ply[player_nr]->dynamite;
2223     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2224       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2225         level.native_sp_level->game_sp->red_disk_count;
2226     else
2227       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228         stored_player[player_nr].inventory_size;
2229
2230     if (stored_player[player_nr].num_white_keys > 0)
2231       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2232
2233     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2234       stored_player[player_nr].num_white_keys;
2235   }
2236
2237   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2238   {
2239     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2240       get_inventory_element_from_pos(local_player, i);
2241     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2242       get_inventory_element_from_pos(local_player, -i - 1);
2243   }
2244
2245   game_panel_controls[GAME_PANEL_SCORE].value = score;
2246   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2247
2248   game_panel_controls[GAME_PANEL_TIME].value = time;
2249
2250   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2251   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2252   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2253
2254   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2255
2256   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2257     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2258      EL_EMPTY);
2259   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2260     local_player->shield_normal_time_left;
2261   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2262     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2263      EL_EMPTY);
2264   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2265     local_player->shield_deadly_time_left;
2266
2267   game_panel_controls[GAME_PANEL_EXIT].value =
2268     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2269
2270   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2271     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2272   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2273     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2274      EL_EMC_MAGIC_BALL_SWITCH);
2275
2276   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2277     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2278   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2279     game.light_time_left;
2280
2281   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2282     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2283   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2284     game.timegate_time_left;
2285
2286   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2287     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2288
2289   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2290     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2291   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2292     game.lenses_time_left;
2293
2294   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2295     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2296   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2297     game.magnify_time_left;
2298
2299   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2300     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2301      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2302      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2303      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2304      EL_BALLOON_SWITCH_NONE);
2305
2306   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2307     local_player->dynabomb_count;
2308   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2309     local_player->dynabomb_size;
2310   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2311     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2312
2313   game_panel_controls[GAME_PANEL_PENGUINS].value =
2314     local_player->friends_still_needed;
2315
2316   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2317     local_player->sokobanfields_still_needed;
2318   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2319     local_player->sokobanfields_still_needed;
2320
2321   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2322     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2323
2324   for (i = 0; i < NUM_BELTS; i++)
2325   {
2326     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2327       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2328        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2329     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2330       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2331   }
2332
2333   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2334     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2335   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2336     game.magic_wall_time_left;
2337
2338   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2339     local_player->gravity;
2340
2341   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2342     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2343
2344   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2345     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2346       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2347        game.panel.element[i].id : EL_UNDEFINED);
2348
2349   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2350     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2351       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2352        element_info[game.panel.element_count[i].id].element_count : 0);
2353
2354   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2355     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2356       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2357        element_info[game.panel.ce_score[i].id].collect_score : 0);
2358
2359   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2360     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2361       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2362        element_info[game.panel.ce_score_element[i].id].collect_score :
2363        EL_UNDEFINED);
2364
2365   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2366   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2367   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2368
2369   /* update game panel control frames */
2370
2371   for (i = 0; game_panel_controls[i].nr != -1; i++)
2372   {
2373     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2374
2375     if (gpc->type == TYPE_ELEMENT)
2376     {
2377       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2378       {
2379         int last_anim_random_frame = gfx.anim_random_frame;
2380         int element = gpc->value;
2381         int graphic = el2panelimg(element);
2382
2383         if (gpc->value != gpc->last_value)
2384         {
2385           gpc->gfx_frame = 0;
2386           gpc->gfx_random = INIT_GFX_RANDOM();
2387         }
2388         else
2389         {
2390           gpc->gfx_frame++;
2391
2392           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2393               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2394             gpc->gfx_random = INIT_GFX_RANDOM();
2395         }
2396
2397         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2398           gfx.anim_random_frame = gpc->gfx_random;
2399
2400         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2401           gpc->gfx_frame = element_info[element].collect_score;
2402
2403         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2404                                               gpc->gfx_frame);
2405
2406         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407           gfx.anim_random_frame = last_anim_random_frame;
2408       }
2409     }
2410   }
2411 }
2412
2413 void DisplayGameControlValues()
2414 {
2415   boolean redraw_panel = FALSE;
2416   int i;
2417
2418   for (i = 0; game_panel_controls[i].nr != -1; i++)
2419   {
2420     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2421
2422     if (PANEL_DEACTIVATED(gpc->pos))
2423       continue;
2424
2425     if (gpc->value == gpc->last_value &&
2426         gpc->frame == gpc->last_frame)
2427       continue;
2428
2429     redraw_panel = TRUE;
2430   }
2431
2432   if (!redraw_panel)
2433     return;
2434
2435   /* copy default game door content to main double buffer */
2436
2437   /* !!! CHECK AGAIN !!! */
2438   SetPanelBackground();
2439   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2440   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2441
2442   /* redraw game control buttons */
2443   RedrawGameButtons();
2444
2445   game_status = GAME_MODE_PSEUDO_PANEL;
2446
2447   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2448   {
2449     int nr = game_panel_order[i].nr;
2450     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2451     struct TextPosInfo *pos = gpc->pos;
2452     int type = gpc->type;
2453     int value = gpc->value;
2454     int frame = gpc->frame;
2455     int size = pos->size;
2456     int font = pos->font;
2457     boolean draw_masked = pos->draw_masked;
2458     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2459
2460     if (PANEL_DEACTIVATED(pos))
2461       continue;
2462
2463     gpc->last_value = value;
2464     gpc->last_frame = frame;
2465
2466     if (type == TYPE_INTEGER)
2467     {
2468       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2469           nr == GAME_PANEL_TIME)
2470       {
2471         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2472
2473         if (use_dynamic_size)           /* use dynamic number of digits */
2474         {
2475           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2476           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2477           int size2 = size1 + 1;
2478           int font1 = pos->font;
2479           int font2 = pos->font_alt;
2480
2481           size = (value < value_change ? size1 : size2);
2482           font = (value < value_change ? font1 : font2);
2483         }
2484       }
2485
2486       /* correct text size if "digits" is zero or less */
2487       if (size <= 0)
2488         size = strlen(int2str(value, size));
2489
2490       /* dynamically correct text alignment */
2491       pos->width = size * getFontWidth(font);
2492
2493       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2494                   int2str(value, size), font, mask_mode);
2495     }
2496     else if (type == TYPE_ELEMENT)
2497     {
2498       int element, graphic;
2499       Bitmap *src_bitmap;
2500       int src_x, src_y;
2501       int width, height;
2502       int dst_x = PANEL_XPOS(pos);
2503       int dst_y = PANEL_YPOS(pos);
2504
2505       if (value != EL_UNDEFINED && value != EL_EMPTY)
2506       {
2507         element = value;
2508         graphic = el2panelimg(value);
2509
2510         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2511
2512         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2513           size = TILESIZE;
2514
2515         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2516                               &src_x, &src_y);
2517
2518         width  = graphic_info[graphic].width  * size / TILESIZE;
2519         height = graphic_info[graphic].height * size / TILESIZE;
2520
2521         if (draw_masked)
2522           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2523                            dst_x, dst_y);
2524         else
2525           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2526                      dst_x, dst_y);
2527       }
2528     }
2529     else if (type == TYPE_STRING)
2530     {
2531       boolean active = (value != 0);
2532       char *state_normal = "off";
2533       char *state_active = "on";
2534       char *state = (active ? state_active : state_normal);
2535       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2536                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2537                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2538                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2539
2540       if (nr == GAME_PANEL_GRAVITY_STATE)
2541       {
2542         int font1 = pos->font;          /* (used for normal state) */
2543         int font2 = pos->font_alt;      /* (used for active state) */
2544
2545         font = (active ? font2 : font1);
2546       }
2547
2548       if (s != NULL)
2549       {
2550         char *s_cut;
2551
2552         if (size <= 0)
2553         {
2554           /* don't truncate output if "chars" is zero or less */
2555           size = strlen(s);
2556
2557           /* dynamically correct text alignment */
2558           pos->width = size * getFontWidth(font);
2559         }
2560
2561         s_cut = getStringCopyN(s, size);
2562
2563         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2564                     s_cut, font, mask_mode);
2565
2566         free(s_cut);
2567       }
2568     }
2569
2570     redraw_mask |= REDRAW_DOOR_1;
2571   }
2572
2573   game_status = GAME_MODE_PLAYING;
2574 }
2575
2576 void UpdateAndDisplayGameControlValues()
2577 {
2578   if (tape.warp_forward)
2579     return;
2580
2581   UpdateGameControlValues();
2582   DisplayGameControlValues();
2583 }
2584
2585 void UpdateGameDoorValues()
2586 {
2587   UpdateGameControlValues();
2588 }
2589
2590 void DrawGameDoorValues()
2591 {
2592   DisplayGameControlValues();
2593 }
2594
2595
2596 /*
2597   =============================================================================
2598   InitGameEngine()
2599   -----------------------------------------------------------------------------
2600   initialize game engine due to level / tape version number
2601   =============================================================================
2602 */
2603
2604 static void InitGameEngine()
2605 {
2606   int i, j, k, l, x, y;
2607
2608   /* set game engine from tape file when re-playing, else from level file */
2609   game.engine_version = (tape.playing ? tape.engine_version :
2610                          level.game_version);
2611
2612   /* set single or multi-player game mode (needed for re-playing tapes) */
2613   game.team_mode = setup.team_mode;
2614
2615   if (tape.playing)
2616   {
2617     int num_players = 0;
2618
2619     for (i = 0; i < MAX_PLAYERS; i++)
2620       if (tape.player_participates[i])
2621         num_players++;
2622
2623     /* multi-player tapes contain input data for more than one player */
2624     game.team_mode = (num_players > 1);
2625   }
2626
2627   /* ---------------------------------------------------------------------- */
2628   /* set flags for bugs and changes according to active game engine version */
2629   /* ---------------------------------------------------------------------- */
2630
2631   /*
2632     Summary of bugfix/change:
2633     Fixed handling for custom elements that change when pushed by the player.
2634
2635     Fixed/changed in version:
2636     3.1.0
2637
2638     Description:
2639     Before 3.1.0, custom elements that "change when pushing" changed directly
2640     after the player started pushing them (until then handled in "DigField()").
2641     Since 3.1.0, these custom elements are not changed until the "pushing"
2642     move of the element is finished (now handled in "ContinueMoving()").
2643
2644     Affected levels/tapes:
2645     The first condition is generally needed for all levels/tapes before version
2646     3.1.0, which might use the old behaviour before it was changed; known tapes
2647     that are affected are some tapes from the level set "Walpurgis Gardens" by
2648     Jamie Cullen.
2649     The second condition is an exception from the above case and is needed for
2650     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2651     above (including some development versions of 3.1.0), but before it was
2652     known that this change would break tapes like the above and was fixed in
2653     3.1.1, so that the changed behaviour was active although the engine version
2654     while recording maybe was before 3.1.0. There is at least one tape that is
2655     affected by this exception, which is the tape for the one-level set "Bug
2656     Machine" by Juergen Bonhagen.
2657   */
2658
2659   game.use_change_when_pushing_bug =
2660     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2661      !(tape.playing &&
2662        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2663        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2664
2665   /*
2666     Summary of bugfix/change:
2667     Fixed handling for blocking the field the player leaves when moving.
2668
2669     Fixed/changed in version:
2670     3.1.1
2671
2672     Description:
2673     Before 3.1.1, when "block last field when moving" was enabled, the field
2674     the player is leaving when moving was blocked for the time of the move,
2675     and was directly unblocked afterwards. This resulted in the last field
2676     being blocked for exactly one less than the number of frames of one player
2677     move. Additionally, even when blocking was disabled, the last field was
2678     blocked for exactly one frame.
2679     Since 3.1.1, due to changes in player movement handling, the last field
2680     is not blocked at all when blocking is disabled. When blocking is enabled,
2681     the last field is blocked for exactly the number of frames of one player
2682     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2683     last field is blocked for exactly one more than the number of frames of
2684     one player move.
2685
2686     Affected levels/tapes:
2687     (!!! yet to be determined -- probably many !!!)
2688   */
2689
2690   game.use_block_last_field_bug =
2691     (game.engine_version < VERSION_IDENT(3,1,1,0));
2692
2693   /* ---------------------------------------------------------------------- */
2694
2695   /* set maximal allowed number of custom element changes per game frame */
2696   game.max_num_changes_per_frame = 1;
2697
2698   /* default scan direction: scan playfield from top/left to bottom/right */
2699   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2700
2701   /* dynamically adjust element properties according to game engine version */
2702   InitElementPropertiesEngine(game.engine_version);
2703
2704 #if 0
2705   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2706   printf("          tape version == %06d [%s] [file: %06d]\n",
2707          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2708          tape.file_version);
2709   printf("       => game.engine_version == %06d\n", game.engine_version);
2710 #endif
2711
2712   /* ---------- initialize player's initial move delay --------------------- */
2713
2714   /* dynamically adjust player properties according to level information */
2715   for (i = 0; i < MAX_PLAYERS; i++)
2716     game.initial_move_delay_value[i] =
2717       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2718
2719   /* dynamically adjust player properties according to game engine version */
2720   for (i = 0; i < MAX_PLAYERS; i++)
2721     game.initial_move_delay[i] =
2722       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2723        game.initial_move_delay_value[i] : 0);
2724
2725   /* ---------- initialize player's initial push delay --------------------- */
2726
2727   /* dynamically adjust player properties according to game engine version */
2728   game.initial_push_delay_value =
2729     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2730
2731   /* ---------- initialize changing elements ------------------------------- */
2732
2733   /* initialize changing elements information */
2734   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2735   {
2736     struct ElementInfo *ei = &element_info[i];
2737
2738     /* this pointer might have been changed in the level editor */
2739     ei->change = &ei->change_page[0];
2740
2741     if (!IS_CUSTOM_ELEMENT(i))
2742     {
2743       ei->change->target_element = EL_EMPTY_SPACE;
2744       ei->change->delay_fixed = 0;
2745       ei->change->delay_random = 0;
2746       ei->change->delay_frames = 1;
2747     }
2748
2749     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2750     {
2751       ei->has_change_event[j] = FALSE;
2752
2753       ei->event_page_nr[j] = 0;
2754       ei->event_page[j] = &ei->change_page[0];
2755     }
2756   }
2757
2758   /* add changing elements from pre-defined list */
2759   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2760   {
2761     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2762     struct ElementInfo *ei = &element_info[ch_delay->element];
2763
2764     ei->change->target_element       = ch_delay->target_element;
2765     ei->change->delay_fixed          = ch_delay->change_delay;
2766
2767     ei->change->pre_change_function  = ch_delay->pre_change_function;
2768     ei->change->change_function      = ch_delay->change_function;
2769     ei->change->post_change_function = ch_delay->post_change_function;
2770
2771     ei->change->can_change = TRUE;
2772     ei->change->can_change_or_has_action = TRUE;
2773
2774     ei->has_change_event[CE_DELAY] = TRUE;
2775
2776     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2777     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2778   }
2779
2780   /* ---------- initialize internal run-time variables --------------------- */
2781
2782   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2783   {
2784     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2785
2786     for (j = 0; j < ei->num_change_pages; j++)
2787     {
2788       ei->change_page[j].can_change_or_has_action =
2789         (ei->change_page[j].can_change |
2790          ei->change_page[j].has_action);
2791     }
2792   }
2793
2794   /* add change events from custom element configuration */
2795   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2796   {
2797     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2798
2799     for (j = 0; j < ei->num_change_pages; j++)
2800     {
2801       if (!ei->change_page[j].can_change_or_has_action)
2802         continue;
2803
2804       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2805       {
2806         /* only add event page for the first page found with this event */
2807         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2808         {
2809           ei->has_change_event[k] = TRUE;
2810
2811           ei->event_page_nr[k] = j;
2812           ei->event_page[k] = &ei->change_page[j];
2813         }
2814       }
2815     }
2816   }
2817
2818   /* ---------- initialize reference elements in change conditions --------- */
2819
2820   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2821   {
2822     int element = EL_CUSTOM_START + i;
2823     struct ElementInfo *ei = &element_info[element];
2824
2825     for (j = 0; j < ei->num_change_pages; j++)
2826     {
2827       int trigger_element = ei->change_page[j].initial_trigger_element;
2828
2829       if (trigger_element >= EL_PREV_CE_8 &&
2830           trigger_element <= EL_NEXT_CE_8)
2831         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2832
2833       ei->change_page[j].trigger_element = trigger_element;
2834     }
2835   }
2836
2837   /* ---------- initialize run-time trigger player and element ------------- */
2838
2839   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2840   {
2841     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2842
2843     for (j = 0; j < ei->num_change_pages; j++)
2844     {
2845       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2846       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2847       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2848       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2849       ei->change_page[j].actual_trigger_ce_value = 0;
2850       ei->change_page[j].actual_trigger_ce_score = 0;
2851     }
2852   }
2853
2854   /* ---------- initialize trigger events ---------------------------------- */
2855
2856   /* initialize trigger events information */
2857   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2858     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2859       trigger_events[i][j] = FALSE;
2860
2861   /* add trigger events from element change event properties */
2862   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2863   {
2864     struct ElementInfo *ei = &element_info[i];
2865
2866     for (j = 0; j < ei->num_change_pages; j++)
2867     {
2868       if (!ei->change_page[j].can_change_or_has_action)
2869         continue;
2870
2871       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2872       {
2873         int trigger_element = ei->change_page[j].trigger_element;
2874
2875         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2876         {
2877           if (ei->change_page[j].has_event[k])
2878           {
2879             if (IS_GROUP_ELEMENT(trigger_element))
2880             {
2881               struct ElementGroupInfo *group =
2882                 element_info[trigger_element].group;
2883
2884               for (l = 0; l < group->num_elements_resolved; l++)
2885                 trigger_events[group->element_resolved[l]][k] = TRUE;
2886             }
2887             else if (trigger_element == EL_ANY_ELEMENT)
2888               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2889                 trigger_events[l][k] = TRUE;
2890             else
2891               trigger_events[trigger_element][k] = TRUE;
2892           }
2893         }
2894       }
2895     }
2896   }
2897
2898   /* ---------- initialize push delay -------------------------------------- */
2899
2900   /* initialize push delay values to default */
2901   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2902   {
2903     if (!IS_CUSTOM_ELEMENT(i))
2904     {
2905       /* set default push delay values (corrected since version 3.0.7-1) */
2906       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2907       {
2908         element_info[i].push_delay_fixed = 2;
2909         element_info[i].push_delay_random = 8;
2910       }
2911       else
2912       {
2913         element_info[i].push_delay_fixed = 8;
2914         element_info[i].push_delay_random = 8;
2915       }
2916     }
2917   }
2918
2919   /* set push delay value for certain elements from pre-defined list */
2920   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2921   {
2922     int e = push_delay_list[i].element;
2923
2924     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2925     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2926   }
2927
2928   /* set push delay value for Supaplex elements for newer engine versions */
2929   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2930   {
2931     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2932     {
2933       if (IS_SP_ELEMENT(i))
2934       {
2935         /* set SP push delay to just enough to push under a falling zonk */
2936         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2937
2938         element_info[i].push_delay_fixed  = delay;
2939         element_info[i].push_delay_random = 0;
2940       }
2941     }
2942   }
2943
2944   /* ---------- initialize move stepsize ----------------------------------- */
2945
2946   /* initialize move stepsize values to default */
2947   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2948     if (!IS_CUSTOM_ELEMENT(i))
2949       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2950
2951   /* set move stepsize value for certain elements from pre-defined list */
2952   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2953   {
2954     int e = move_stepsize_list[i].element;
2955
2956     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2957   }
2958
2959   /* ---------- initialize collect score ----------------------------------- */
2960
2961   /* initialize collect score values for custom elements from initial value */
2962   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2963     if (IS_CUSTOM_ELEMENT(i))
2964       element_info[i].collect_score = element_info[i].collect_score_initial;
2965
2966   /* ---------- initialize collect count ----------------------------------- */
2967
2968   /* initialize collect count values for non-custom elements */
2969   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2970     if (!IS_CUSTOM_ELEMENT(i))
2971       element_info[i].collect_count_initial = 0;
2972
2973   /* add collect count values for all elements from pre-defined list */
2974   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2975     element_info[collect_count_list[i].element].collect_count_initial =
2976       collect_count_list[i].count;
2977
2978   /* ---------- initialize access direction -------------------------------- */
2979
2980   /* initialize access direction values to default (access from every side) */
2981   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2982     if (!IS_CUSTOM_ELEMENT(i))
2983       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2984
2985   /* set access direction value for certain elements from pre-defined list */
2986   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2987     element_info[access_direction_list[i].element].access_direction =
2988       access_direction_list[i].direction;
2989
2990   /* ---------- initialize explosion content ------------------------------- */
2991   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2992   {
2993     if (IS_CUSTOM_ELEMENT(i))
2994       continue;
2995
2996     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2997     {
2998       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2999
3000       element_info[i].content.e[x][y] =
3001         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3002          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3003          i == EL_PLAYER_3 ? EL_EMERALD :
3004          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3005          i == EL_MOLE ? EL_EMERALD_RED :
3006          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3007          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3008          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3009          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3010          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3011          i == EL_WALL_EMERALD ? EL_EMERALD :
3012          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3013          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3014          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3015          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3016          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3017          i == EL_WALL_PEARL ? EL_PEARL :
3018          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3019          EL_EMPTY);
3020     }
3021   }
3022
3023   /* ---------- initialize recursion detection ------------------------------ */
3024   recursion_loop_depth = 0;
3025   recursion_loop_detected = FALSE;
3026   recursion_loop_element = EL_UNDEFINED;
3027
3028   /* ---------- initialize graphics engine ---------------------------------- */
3029   game.scroll_delay_value =
3030     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3031      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3032   game.scroll_delay_value =
3033     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3034 }
3035
3036 int get_num_special_action(int element, int action_first, int action_last)
3037 {
3038   int num_special_action = 0;
3039   int i, j;
3040
3041   for (i = action_first; i <= action_last; i++)
3042   {
3043     boolean found = FALSE;
3044
3045     for (j = 0; j < NUM_DIRECTIONS; j++)
3046       if (el_act_dir2img(element, i, j) !=
3047           el_act_dir2img(element, ACTION_DEFAULT, j))
3048         found = TRUE;
3049
3050     if (found)
3051       num_special_action++;
3052     else
3053       break;
3054   }
3055
3056   return num_special_action;
3057 }
3058
3059
3060 /*
3061   =============================================================================
3062   InitGame()
3063   -----------------------------------------------------------------------------
3064   initialize and start new game
3065   =============================================================================
3066 */
3067
3068 void InitGame()
3069 {
3070   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3071   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3072
3073   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3074   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3075   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3076   int initial_move_dir = MV_DOWN;
3077   int i, j, x, y;
3078
3079   game_status = GAME_MODE_PLAYING;
3080
3081   StopAnimation();
3082
3083   if (!game.restart_level)
3084     CloseDoor(DOOR_CLOSE_1);
3085
3086   if (level_editor_test_game)
3087     FadeSkipNextFadeIn();
3088   else
3089     FadeSetEnterScreen();
3090
3091   FadeOut(REDRAW_FIELD);
3092
3093   /* needed if different viewport properties defined for playing */
3094   ChangeViewportPropertiesIfNeeded();
3095
3096   DrawCompleteVideoDisplay();
3097
3098   InitGameEngine();
3099   InitGameControlValues();
3100
3101   /* don't play tapes over network */
3102   network_playing = (options.network && !tape.playing);
3103
3104   for (i = 0; i < MAX_PLAYERS; i++)
3105   {
3106     struct PlayerInfo *player = &stored_player[i];
3107
3108     player->index_nr = i;
3109     player->index_bit = (1 << i);
3110     player->element_nr = EL_PLAYER_1 + i;
3111
3112     player->present = FALSE;
3113     player->active = FALSE;
3114     player->mapped = FALSE;
3115
3116     player->killed = FALSE;
3117     player->reanimated = FALSE;
3118
3119     player->action = 0;
3120     player->effective_action = 0;
3121     player->programmed_action = 0;
3122
3123     player->score = 0;
3124     player->score_final = 0;
3125
3126     player->gems_still_needed = level.gems_needed;
3127     player->sokobanfields_still_needed = 0;
3128     player->lights_still_needed = 0;
3129     player->friends_still_needed = 0;
3130
3131     for (j = 0; j < MAX_NUM_KEYS; j++)
3132       player->key[j] = FALSE;
3133
3134     player->num_white_keys = 0;
3135
3136     player->dynabomb_count = 0;
3137     player->dynabomb_size = 1;
3138     player->dynabombs_left = 0;
3139     player->dynabomb_xl = FALSE;
3140
3141     player->MovDir = initial_move_dir;
3142     player->MovPos = 0;
3143     player->GfxPos = 0;
3144     player->GfxDir = initial_move_dir;
3145     player->GfxAction = ACTION_DEFAULT;
3146     player->Frame = 0;
3147     player->StepFrame = 0;
3148
3149     player->initial_element = player->element_nr;
3150     player->artwork_element =
3151       (level.use_artwork_element[i] ? level.artwork_element[i] :
3152        player->element_nr);
3153     player->use_murphy = FALSE;
3154
3155     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3156     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3157
3158     player->gravity = level.initial_player_gravity[i];
3159
3160     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3161
3162     player->actual_frame_counter = 0;
3163
3164     player->step_counter = 0;
3165
3166     player->last_move_dir = initial_move_dir;
3167
3168     player->is_active = FALSE;
3169
3170     player->is_waiting = FALSE;
3171     player->is_moving = FALSE;
3172     player->is_auto_moving = FALSE;
3173     player->is_digging = FALSE;
3174     player->is_snapping = FALSE;
3175     player->is_collecting = FALSE;
3176     player->is_pushing = FALSE;
3177     player->is_switching = FALSE;
3178     player->is_dropping = FALSE;
3179     player->is_dropping_pressed = FALSE;
3180
3181     player->is_bored = FALSE;
3182     player->is_sleeping = FALSE;
3183
3184     player->frame_counter_bored = -1;
3185     player->frame_counter_sleeping = -1;
3186
3187     player->anim_delay_counter = 0;
3188     player->post_delay_counter = 0;
3189
3190     player->dir_waiting = initial_move_dir;
3191     player->action_waiting = ACTION_DEFAULT;
3192     player->last_action_waiting = ACTION_DEFAULT;
3193     player->special_action_bored = ACTION_DEFAULT;
3194     player->special_action_sleeping = ACTION_DEFAULT;
3195
3196     player->switch_x = -1;
3197     player->switch_y = -1;
3198
3199     player->drop_x = -1;
3200     player->drop_y = -1;
3201
3202     player->show_envelope = 0;
3203
3204     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3205
3206     player->push_delay       = -1;      /* initialized when pushing starts */
3207     player->push_delay_value = game.initial_push_delay_value;
3208
3209     player->drop_delay = 0;
3210     player->drop_pressed_delay = 0;
3211
3212     player->last_jx = -1;
3213     player->last_jy = -1;
3214     player->jx = -1;
3215     player->jy = -1;
3216
3217     player->shield_normal_time_left = 0;
3218     player->shield_deadly_time_left = 0;
3219
3220     player->inventory_infinite_element = EL_UNDEFINED;
3221     player->inventory_size = 0;
3222
3223     if (level.use_initial_inventory[i])
3224     {
3225       for (j = 0; j < level.initial_inventory_size[i]; j++)
3226       {
3227         int element = level.initial_inventory_content[i][j];
3228         int collect_count = element_info[element].collect_count_initial;
3229         int k;
3230
3231         if (!IS_CUSTOM_ELEMENT(element))
3232           collect_count = 1;
3233
3234         if (collect_count == 0)
3235           player->inventory_infinite_element = element;
3236         else
3237           for (k = 0; k < collect_count; k++)
3238             if (player->inventory_size < MAX_INVENTORY_SIZE)
3239               player->inventory_element[player->inventory_size++] = element;
3240       }
3241     }
3242
3243     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3244     SnapField(player, 0, 0);
3245
3246     player->LevelSolved = FALSE;
3247     player->GameOver = FALSE;
3248
3249     player->LevelSolved_GameWon = FALSE;
3250     player->LevelSolved_GameEnd = FALSE;
3251     player->LevelSolved_PanelOff = FALSE;
3252     player->LevelSolved_SaveTape = FALSE;
3253     player->LevelSolved_SaveScore = FALSE;
3254     player->LevelSolved_CountingTime = 0;
3255     player->LevelSolved_CountingScore = 0;
3256
3257     map_player_action[i] = i;
3258   }
3259
3260   network_player_action_received = FALSE;
3261
3262 #if defined(NETWORK_AVALIABLE)
3263   /* initial null action */
3264   if (network_playing)
3265     SendToServer_MovePlayer(MV_NONE);
3266 #endif
3267
3268   ZX = ZY = -1;
3269   ExitX = ExitY = -1;
3270
3271   FrameCounter = 0;
3272   TimeFrames = 0;
3273   TimePlayed = 0;
3274   TimeLeft = level.time;
3275   TapeTime = 0;
3276
3277   ScreenMovDir = MV_NONE;
3278   ScreenMovPos = 0;
3279   ScreenGfxPos = 0;
3280
3281   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3282
3283   AllPlayersGone = FALSE;
3284
3285   game.no_time_limit = (level.time == 0);
3286
3287   game.yamyam_content_nr = 0;
3288   game.robot_wheel_active = FALSE;
3289   game.magic_wall_active = FALSE;
3290   game.magic_wall_time_left = 0;
3291   game.light_time_left = 0;
3292   game.timegate_time_left = 0;
3293   game.switchgate_pos = 0;
3294   game.wind_direction = level.wind_direction_initial;
3295
3296   game.lenses_time_left = 0;
3297   game.magnify_time_left = 0;
3298
3299   game.ball_state = level.ball_state_initial;
3300   game.ball_content_nr = 0;
3301
3302   game.envelope_active = FALSE;
3303
3304   /* set focus to local player for network games, else to all players */
3305   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3306   game.centered_player_nr_next = game.centered_player_nr;
3307   game.set_centered_player = FALSE;
3308
3309   if (network_playing && tape.recording)
3310   {
3311     /* store client dependent player focus when recording network games */
3312     tape.centered_player_nr_next = game.centered_player_nr_next;
3313     tape.set_centered_player = TRUE;
3314   }
3315
3316   for (i = 0; i < NUM_BELTS; i++)
3317   {
3318     game.belt_dir[i] = MV_NONE;
3319     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3320   }
3321
3322   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3323     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3324
3325 #if DEBUG_INIT_PLAYER
3326   if (options.debug)
3327   {
3328     printf("Player status at level initialization:\n");
3329   }
3330 #endif
3331
3332   SCAN_PLAYFIELD(x, y)
3333   {
3334     Feld[x][y] = level.field[x][y];
3335     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3336     ChangeDelay[x][y] = 0;
3337     ChangePage[x][y] = -1;
3338     CustomValue[x][y] = 0;              /* initialized in InitField() */
3339     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3340     AmoebaNr[x][y] = 0;
3341     WasJustMoving[x][y] = 0;
3342     WasJustFalling[x][y] = 0;
3343     CheckCollision[x][y] = 0;
3344     CheckImpact[x][y] = 0;
3345     Stop[x][y] = FALSE;
3346     Pushed[x][y] = FALSE;
3347
3348     ChangeCount[x][y] = 0;
3349     ChangeEvent[x][y] = -1;
3350
3351     ExplodePhase[x][y] = 0;
3352     ExplodeDelay[x][y] = 0;
3353     ExplodeField[x][y] = EX_TYPE_NONE;
3354
3355     RunnerVisit[x][y] = 0;
3356     PlayerVisit[x][y] = 0;
3357
3358     GfxFrame[x][y] = 0;
3359     GfxRandom[x][y] = INIT_GFX_RANDOM();
3360     GfxElement[x][y] = EL_UNDEFINED;
3361     GfxAction[x][y] = ACTION_DEFAULT;
3362     GfxDir[x][y] = MV_NONE;
3363     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3364   }
3365
3366   SCAN_PLAYFIELD(x, y)
3367   {
3368     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3369       emulate_bd = FALSE;
3370     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3371       emulate_sb = FALSE;
3372     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3373       emulate_sp = FALSE;
3374
3375     InitField(x, y, TRUE);
3376
3377     ResetGfxAnimation(x, y);
3378   }
3379
3380   InitBeltMovement();
3381
3382   for (i = 0; i < MAX_PLAYERS; i++)
3383   {
3384     struct PlayerInfo *player = &stored_player[i];
3385
3386     /* set number of special actions for bored and sleeping animation */
3387     player->num_special_action_bored =
3388       get_num_special_action(player->artwork_element,
3389                              ACTION_BORING_1, ACTION_BORING_LAST);
3390     player->num_special_action_sleeping =
3391       get_num_special_action(player->artwork_element,
3392                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3393   }
3394
3395   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3396                     emulate_sb ? EMU_SOKOBAN :
3397                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3398
3399   /* initialize type of slippery elements */
3400   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3401   {
3402     if (!IS_CUSTOM_ELEMENT(i))
3403     {
3404       /* default: elements slip down either to the left or right randomly */
3405       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3406
3407       /* SP style elements prefer to slip down on the left side */
3408       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3409         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3410
3411       /* BD style elements prefer to slip down on the left side */
3412       if (game.emulation == EMU_BOULDERDASH)
3413         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3414     }
3415   }
3416
3417   /* initialize explosion and ignition delay */
3418   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3419   {
3420     if (!IS_CUSTOM_ELEMENT(i))
3421     {
3422       int num_phase = 8;
3423       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3424                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3425                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3426       int last_phase = (num_phase + 1) * delay;
3427       int half_phase = (num_phase / 2) * delay;
3428
3429       element_info[i].explosion_delay = last_phase - 1;
3430       element_info[i].ignition_delay = half_phase;
3431
3432       if (i == EL_BLACK_ORB)
3433         element_info[i].ignition_delay = 1;
3434     }
3435   }
3436
3437   /* correct non-moving belts to start moving left */
3438   for (i = 0; i < NUM_BELTS; i++)
3439     if (game.belt_dir[i] == MV_NONE)
3440       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3441
3442 #if USE_NEW_PLAYER_ASSIGNMENTS
3443   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3444   /* choose default local player */
3445   local_player = &stored_player[0];
3446
3447   for (i = 0; i < MAX_PLAYERS; i++)
3448     stored_player[i].connected = FALSE;
3449
3450   local_player->connected = TRUE;
3451   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3452
3453   if (tape.playing)
3454   {
3455     for (i = 0; i < MAX_PLAYERS; i++)
3456       stored_player[i].connected = tape.player_participates[i];
3457   }
3458   else if (game.team_mode && !options.network)
3459   {
3460     /* try to guess locally connected team mode players (needed for correct
3461        assignment of player figures from level to locally playing players) */
3462
3463     for (i = 0; i < MAX_PLAYERS; i++)
3464       if (setup.input[i].use_joystick ||
3465           setup.input[i].key.left != KSYM_UNDEFINED)
3466         stored_player[i].connected = TRUE;
3467   }
3468
3469 #if DEBUG_INIT_PLAYER
3470   if (options.debug)
3471   {
3472     printf("Player status after level initialization:\n");
3473
3474     for (i = 0; i < MAX_PLAYERS; i++)
3475     {
3476       struct PlayerInfo *player = &stored_player[i];
3477
3478       printf("- player %d: present == %d, connected == %d, active == %d",
3479              i + 1,
3480              player->present,
3481              player->connected,
3482              player->active);
3483
3484       if (local_player == player)
3485         printf(" (local player)");
3486
3487       printf("\n");
3488     }
3489   }
3490 #endif
3491
3492 #if DEBUG_INIT_PLAYER
3493   if (options.debug)
3494     printf("Reassigning players ...\n");
3495 #endif
3496
3497   /* check if any connected player was not found in playfield */
3498   for (i = 0; i < MAX_PLAYERS; i++)
3499   {
3500     struct PlayerInfo *player = &stored_player[i];
3501
3502     if (player->connected && !player->present)
3503     {
3504       struct PlayerInfo *field_player = NULL;
3505
3506 #if DEBUG_INIT_PLAYER
3507       if (options.debug)
3508         printf("- looking for field player for player %d ...\n", i + 1);
3509 #endif
3510
3511       /* assign first free player found that is present in the playfield */
3512
3513       /* first try: look for unmapped playfield player that is not connected */
3514       for (j = 0; j < MAX_PLAYERS; j++)
3515         if (field_player == NULL &&
3516             stored_player[j].present &&
3517             !stored_player[j].mapped &&
3518             !stored_player[j].connected)
3519           field_player = &stored_player[j];
3520
3521       /* second try: look for *any* unmapped playfield player */
3522       for (j = 0; j < MAX_PLAYERS; j++)
3523         if (field_player == NULL &&
3524             stored_player[j].present &&
3525             !stored_player[j].mapped)
3526           field_player = &stored_player[j];
3527
3528       if (field_player != NULL)
3529       {
3530         int jx = field_player->jx, jy = field_player->jy;
3531
3532 #if DEBUG_INIT_PLAYER
3533         if (options.debug)
3534           printf("- found player %d\n", field_player->index_nr + 1);
3535 #endif
3536
3537         player->present = FALSE;
3538         player->active = FALSE;
3539
3540         field_player->present = TRUE;
3541         field_player->active = TRUE;
3542
3543         /*
3544         player->initial_element = field_player->initial_element;
3545         player->artwork_element = field_player->artwork_element;
3546
3547         player->block_last_field       = field_player->block_last_field;
3548         player->block_delay_adjustment = field_player->block_delay_adjustment;
3549         */
3550
3551         StorePlayer[jx][jy] = field_player->element_nr;
3552
3553         field_player->jx = field_player->last_jx = jx;
3554         field_player->jy = field_player->last_jy = jy;
3555
3556         if (local_player == player)
3557           local_player = field_player;
3558
3559         map_player_action[field_player->index_nr] = i;
3560
3561         field_player->mapped = TRUE;
3562
3563 #if DEBUG_INIT_PLAYER
3564         if (options.debug)
3565           printf("- map_player_action[%d] == %d\n",
3566                  field_player->index_nr + 1, i + 1);
3567 #endif
3568       }
3569     }
3570
3571     if (player->connected && player->present)
3572       player->mapped = TRUE;
3573   }
3574
3575 #if DEBUG_INIT_PLAYER
3576   if (options.debug)
3577   {
3578     printf("Player status after player assignment (first stage):\n");
3579
3580     for (i = 0; i < MAX_PLAYERS; i++)
3581     {
3582       struct PlayerInfo *player = &stored_player[i];
3583
3584       printf("- player %d: present == %d, connected == %d, active == %d",
3585              i + 1,
3586              player->present,
3587              player->connected,
3588              player->active);
3589
3590       if (local_player == player)
3591         printf(" (local player)");
3592
3593       printf("\n");
3594     }
3595   }
3596 #endif
3597
3598 #else
3599
3600   /* check if any connected player was not found in playfield */
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     if (player->connected && !player->present)
3606     {
3607       for (j = 0; j < MAX_PLAYERS; j++)
3608       {
3609         struct PlayerInfo *field_player = &stored_player[j];
3610         int jx = field_player->jx, jy = field_player->jy;
3611
3612         /* assign first free player found that is present in the playfield */
3613         if (field_player->present && !field_player->connected)
3614         {
3615           player->present = TRUE;
3616           player->active = TRUE;
3617
3618           field_player->present = FALSE;
3619           field_player->active = FALSE;
3620
3621           player->initial_element = field_player->initial_element;
3622           player->artwork_element = field_player->artwork_element;
3623
3624           player->block_last_field       = field_player->block_last_field;
3625           player->block_delay_adjustment = field_player->block_delay_adjustment;
3626
3627           StorePlayer[jx][jy] = player->element_nr;
3628
3629           player->jx = player->last_jx = jx;
3630           player->jy = player->last_jy = jy;
3631
3632           break;
3633         }
3634       }
3635     }
3636   }
3637 #endif
3638
3639 #if 0
3640   printf("::: local_player->present == %d\n", local_player->present);
3641 #endif
3642
3643   if (tape.playing)
3644   {
3645     /* when playing a tape, eliminate all players who do not participate */
3646
3647 #if USE_NEW_PLAYER_ASSIGNMENTS
3648
3649     if (!game.team_mode)
3650     {
3651       for (i = 0; i < MAX_PLAYERS; i++)
3652       {
3653         if (stored_player[i].active &&
3654             !tape.player_participates[map_player_action[i]])
3655         {
3656           struct PlayerInfo *player = &stored_player[i];
3657           int jx = player->jx, jy = player->jy;
3658
3659 #if DEBUG_INIT_PLAYER
3660           if (options.debug)
3661             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3662 #endif
3663
3664           player->active = FALSE;
3665           StorePlayer[jx][jy] = 0;
3666           Feld[jx][jy] = EL_EMPTY;
3667         }
3668       }
3669     }
3670
3671 #else
3672
3673     for (i = 0; i < MAX_PLAYERS; i++)
3674     {
3675       if (stored_player[i].active &&
3676           !tape.player_participates[i])
3677       {
3678         struct PlayerInfo *player = &stored_player[i];
3679         int jx = player->jx, jy = player->jy;
3680
3681         player->active = FALSE;
3682         StorePlayer[jx][jy] = 0;
3683         Feld[jx][jy] = EL_EMPTY;
3684       }
3685     }
3686 #endif
3687   }
3688   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3689   {
3690     /* when in single player mode, eliminate all but the first active player */
3691
3692     for (i = 0; i < MAX_PLAYERS; i++)
3693     {
3694       if (stored_player[i].active)
3695       {
3696         for (j = i + 1; j < MAX_PLAYERS; j++)
3697         {
3698           if (stored_player[j].active)
3699           {
3700             struct PlayerInfo *player = &stored_player[j];
3701             int jx = player->jx, jy = player->jy;
3702
3703             player->active = FALSE;
3704             player->present = FALSE;
3705
3706             StorePlayer[jx][jy] = 0;
3707             Feld[jx][jy] = EL_EMPTY;
3708           }
3709         }
3710       }
3711     }
3712   }
3713
3714   /* when recording the game, store which players take part in the game */
3715   if (tape.recording)
3716   {
3717 #if USE_NEW_PLAYER_ASSIGNMENTS
3718     for (i = 0; i < MAX_PLAYERS; i++)
3719       if (stored_player[i].connected)
3720         tape.player_participates[i] = TRUE;
3721 #else
3722     for (i = 0; i < MAX_PLAYERS; i++)
3723       if (stored_player[i].active)
3724         tape.player_participates[i] = TRUE;
3725 #endif
3726   }
3727
3728 #if DEBUG_INIT_PLAYER
3729   if (options.debug)
3730   {
3731     printf("Player status after player assignment (final stage):\n");
3732
3733     for (i = 0; i < MAX_PLAYERS; i++)
3734     {
3735       struct PlayerInfo *player = &stored_player[i];
3736
3737       printf("- player %d: present == %d, connected == %d, active == %d",
3738              i + 1,
3739              player->present,
3740              player->connected,
3741              player->active);
3742
3743       if (local_player == player)
3744         printf(" (local player)");
3745
3746       printf("\n");
3747     }
3748   }
3749 #endif
3750
3751   if (BorderElement == EL_EMPTY)
3752   {
3753     SBX_Left = 0;
3754     SBX_Right = lev_fieldx - SCR_FIELDX;
3755     SBY_Upper = 0;
3756     SBY_Lower = lev_fieldy - SCR_FIELDY;
3757   }
3758   else
3759   {
3760     SBX_Left = -1;
3761     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3762     SBY_Upper = -1;
3763     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3764   }
3765
3766   if (full_lev_fieldx <= SCR_FIELDX)
3767     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3768   if (full_lev_fieldy <= SCR_FIELDY)
3769     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3770
3771   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3772     SBX_Left--;
3773   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3774     SBY_Upper--;
3775
3776   /* if local player not found, look for custom element that might create
3777      the player (make some assumptions about the right custom element) */
3778   if (!local_player->present)
3779   {
3780     int start_x = 0, start_y = 0;
3781     int found_rating = 0;
3782     int found_element = EL_UNDEFINED;
3783     int player_nr = local_player->index_nr;
3784
3785     SCAN_PLAYFIELD(x, y)
3786     {
3787       int element = Feld[x][y];
3788       int content;
3789       int xx, yy;
3790       boolean is_player;
3791
3792       if (level.use_start_element[player_nr] &&
3793           level.start_element[player_nr] == element &&
3794           found_rating < 4)
3795       {
3796         start_x = x;
3797         start_y = y;
3798
3799         found_rating = 4;
3800         found_element = element;
3801       }
3802
3803       if (!IS_CUSTOM_ELEMENT(element))
3804         continue;
3805
3806       if (CAN_CHANGE(element))
3807       {
3808         for (i = 0; i < element_info[element].num_change_pages; i++)
3809         {
3810           /* check for player created from custom element as single target */
3811           content = element_info[element].change_page[i].target_element;
3812           is_player = ELEM_IS_PLAYER(content);
3813
3814           if (is_player && (found_rating < 3 ||
3815                             (found_rating == 3 && element < found_element)))
3816           {
3817             start_x = x;
3818             start_y = y;
3819
3820             found_rating = 3;
3821             found_element = element;
3822           }
3823         }
3824       }
3825
3826       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3827       {
3828         /* check for player created from custom element as explosion content */
3829         content = element_info[element].content.e[xx][yy];
3830         is_player = ELEM_IS_PLAYER(content);
3831
3832         if (is_player && (found_rating < 2 ||
3833                           (found_rating == 2 && element < found_element)))
3834         {
3835           start_x = x + xx - 1;
3836           start_y = y + yy - 1;
3837
3838           found_rating = 2;
3839           found_element = element;
3840         }
3841
3842         if (!CAN_CHANGE(element))
3843           continue;
3844
3845         for (i = 0; i < element_info[element].num_change_pages; i++)
3846         {
3847           /* check for player created from custom element as extended target */
3848           content =
3849             element_info[element].change_page[i].target_content.e[xx][yy];
3850
3851           is_player = ELEM_IS_PLAYER(content);
3852
3853           if (is_player && (found_rating < 1 ||
3854                             (found_rating == 1 && element < found_element)))
3855           {
3856             start_x = x + xx - 1;
3857             start_y = y + yy - 1;
3858
3859             found_rating = 1;
3860             found_element = element;
3861           }
3862         }
3863       }
3864     }
3865
3866     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3867                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3868                 start_x - MIDPOSX);
3869
3870     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3871                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3872                 start_y - MIDPOSY);
3873   }
3874   else
3875   {
3876     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3877                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3878                 local_player->jx - MIDPOSX);
3879
3880     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3881                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3882                 local_player->jy - MIDPOSY);
3883   }
3884
3885   /* !!! FIX THIS (START) !!! */
3886   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3887   {
3888     InitGameEngine_EM();
3889   }
3890   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3891   {
3892     InitGameEngine_SP();
3893   }
3894   else
3895   {
3896     DrawLevel(REDRAW_FIELD);
3897     DrawAllPlayers();
3898
3899     /* after drawing the level, correct some elements */
3900     if (game.timegate_time_left == 0)
3901       CloseAllOpenTimegates();
3902   }
3903
3904   /* blit playfield from scroll buffer to normal back buffer for fading in */
3905   BlitScreenToBitmap(backbuffer);
3906
3907   redraw_mask |= REDRAW_FROM_BACKBUFFER;
3908   /* !!! FIX THIS (END) !!! */
3909
3910   FadeIn(REDRAW_FIELD);
3911
3912   if (!game.restart_level)
3913   {
3914     /* copy default game door content to main double buffer */
3915
3916     /* !!! CHECK AGAIN !!! */
3917     SetPanelBackground();
3918     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3919     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3920   }
3921
3922   SetPanelBackground();
3923   SetDrawBackgroundMask(REDRAW_DOOR_1);
3924
3925   UpdateAndDisplayGameControlValues();
3926
3927   if (!game.restart_level)
3928   {
3929     UnmapGameButtons();
3930     UnmapTapeButtons();
3931     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3932     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3933     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3934     MapGameButtons();
3935     MapTapeButtons();
3936
3937     /* copy actual game door content to door double buffer for OpenDoor() */
3938     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3939
3940     OpenDoor(DOOR_OPEN_ALL);
3941
3942     PlaySound(SND_GAME_STARTING);
3943
3944     if (setup.sound_music)
3945       PlayLevelMusic();
3946
3947     KeyboardAutoRepeatOffUnlessAutoplay();
3948
3949 #if DEBUG_INIT_PLAYER
3950     if (options.debug)
3951     {
3952       printf("Player status (final):\n");
3953
3954       for (i = 0; i < MAX_PLAYERS; i++)
3955       {
3956         struct PlayerInfo *player = &stored_player[i];
3957
3958         printf("- player %d: present == %d, connected == %d, active == %d",
3959                i + 1,
3960                player->present,
3961                player->connected,
3962                player->active);
3963
3964         if (local_player == player)
3965           printf(" (local player)");
3966
3967         printf("\n");
3968       }
3969     }
3970 #endif
3971   }
3972
3973   UnmapAllGadgets();
3974
3975   MapGameButtons();
3976   MapTapeButtons();
3977
3978   if (!game.restart_level && !tape.playing)
3979   {
3980     LevelStats_incPlayed(level_nr);
3981
3982     SaveLevelSetup_SeriesInfo();
3983   }
3984
3985   game.restart_level = FALSE;
3986 }
3987
3988 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3989 {
3990   /* this is used for non-R'n'D game engines to update certain engine values */
3991
3992   /* needed to determine if sounds are played within the visible screen area */
3993   scroll_x = actual_scroll_x;
3994   scroll_y = actual_scroll_y;
3995 }
3996
3997 void InitMovDir(int x, int y)
3998 {
3999   int i, element = Feld[x][y];
4000   static int xy[4][2] =
4001   {
4002     {  0, +1 },
4003     { +1,  0 },
4004     {  0, -1 },
4005     { -1,  0 }
4006   };
4007   static int direction[3][4] =
4008   {
4009     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4010     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4011     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4012   };
4013
4014   switch (element)
4015   {
4016     case EL_BUG_RIGHT:
4017     case EL_BUG_UP:
4018     case EL_BUG_LEFT:
4019     case EL_BUG_DOWN:
4020       Feld[x][y] = EL_BUG;
4021       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4022       break;
4023
4024     case EL_SPACESHIP_RIGHT:
4025     case EL_SPACESHIP_UP:
4026     case EL_SPACESHIP_LEFT:
4027     case EL_SPACESHIP_DOWN:
4028       Feld[x][y] = EL_SPACESHIP;
4029       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4030       break;
4031
4032     case EL_BD_BUTTERFLY_RIGHT:
4033     case EL_BD_BUTTERFLY_UP:
4034     case EL_BD_BUTTERFLY_LEFT:
4035     case EL_BD_BUTTERFLY_DOWN:
4036       Feld[x][y] = EL_BD_BUTTERFLY;
4037       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4038       break;
4039
4040     case EL_BD_FIREFLY_RIGHT:
4041     case EL_BD_FIREFLY_UP:
4042     case EL_BD_FIREFLY_LEFT:
4043     case EL_BD_FIREFLY_DOWN:
4044       Feld[x][y] = EL_BD_FIREFLY;
4045       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4046       break;
4047
4048     case EL_PACMAN_RIGHT:
4049     case EL_PACMAN_UP:
4050     case EL_PACMAN_LEFT:
4051     case EL_PACMAN_DOWN:
4052       Feld[x][y] = EL_PACMAN;
4053       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4054       break;
4055
4056     case EL_YAMYAM_LEFT:
4057     case EL_YAMYAM_RIGHT:
4058     case EL_YAMYAM_UP:
4059     case EL_YAMYAM_DOWN:
4060       Feld[x][y] = EL_YAMYAM;
4061       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4062       break;
4063
4064     case EL_SP_SNIKSNAK:
4065       MovDir[x][y] = MV_UP;
4066       break;
4067
4068     case EL_SP_ELECTRON:
4069       MovDir[x][y] = MV_LEFT;
4070       break;
4071
4072     case EL_MOLE_LEFT:
4073     case EL_MOLE_RIGHT:
4074     case EL_MOLE_UP:
4075     case EL_MOLE_DOWN:
4076       Feld[x][y] = EL_MOLE;
4077       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4078       break;
4079
4080     default:
4081       if (IS_CUSTOM_ELEMENT(element))
4082       {
4083         struct ElementInfo *ei = &element_info[element];
4084         int move_direction_initial = ei->move_direction_initial;
4085         int move_pattern = ei->move_pattern;
4086
4087         if (move_direction_initial == MV_START_PREVIOUS)
4088         {
4089           if (MovDir[x][y] != MV_NONE)
4090             return;
4091
4092           move_direction_initial = MV_START_AUTOMATIC;
4093         }
4094
4095         if (move_direction_initial == MV_START_RANDOM)
4096           MovDir[x][y] = 1 << RND(4);
4097         else if (move_direction_initial & MV_ANY_DIRECTION)
4098           MovDir[x][y] = move_direction_initial;
4099         else if (move_pattern == MV_ALL_DIRECTIONS ||
4100                  move_pattern == MV_TURNING_LEFT ||
4101                  move_pattern == MV_TURNING_RIGHT ||
4102                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4103                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4104                  move_pattern == MV_TURNING_RANDOM)
4105           MovDir[x][y] = 1 << RND(4);
4106         else if (move_pattern == MV_HORIZONTAL)
4107           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4108         else if (move_pattern == MV_VERTICAL)
4109           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4110         else if (move_pattern & MV_ANY_DIRECTION)
4111           MovDir[x][y] = element_info[element].move_pattern;
4112         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4113                  move_pattern == MV_ALONG_RIGHT_SIDE)
4114         {
4115           /* use random direction as default start direction */
4116           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4117             MovDir[x][y] = 1 << RND(4);
4118
4119           for (i = 0; i < NUM_DIRECTIONS; i++)
4120           {
4121             int x1 = x + xy[i][0];
4122             int y1 = y + xy[i][1];
4123
4124             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4125             {
4126               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4127                 MovDir[x][y] = direction[0][i];
4128               else
4129                 MovDir[x][y] = direction[1][i];
4130
4131               break;
4132             }
4133           }
4134         }                
4135       }
4136       else
4137       {
4138         MovDir[x][y] = 1 << RND(4);
4139
4140         if (element != EL_BUG &&
4141             element != EL_SPACESHIP &&
4142             element != EL_BD_BUTTERFLY &&
4143             element != EL_BD_FIREFLY)
4144           break;
4145
4146         for (i = 0; i < NUM_DIRECTIONS; i++)
4147         {
4148           int x1 = x + xy[i][0];
4149           int y1 = y + xy[i][1];
4150
4151           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4152           {
4153             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4154             {
4155               MovDir[x][y] = direction[0][i];
4156               break;
4157             }
4158             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4159                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4160             {
4161               MovDir[x][y] = direction[1][i];
4162               break;
4163             }
4164           }
4165         }
4166       }
4167       break;
4168   }
4169
4170   GfxDir[x][y] = MovDir[x][y];
4171 }
4172
4173 void InitAmoebaNr(int x, int y)
4174 {
4175   int i;
4176   int group_nr = AmoebeNachbarNr(x, y);
4177
4178   if (group_nr == 0)
4179   {
4180     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4181     {
4182       if (AmoebaCnt[i] == 0)
4183       {
4184         group_nr = i;
4185         break;
4186       }
4187     }
4188   }
4189
4190   AmoebaNr[x][y] = group_nr;
4191   AmoebaCnt[group_nr]++;
4192   AmoebaCnt2[group_nr]++;
4193 }
4194
4195 static void PlayerWins(struct PlayerInfo *player)
4196 {
4197   player->LevelSolved = TRUE;
4198   player->GameOver = TRUE;
4199
4200   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4201                          level.native_em_level->lev->score : player->score);
4202
4203   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4204                                       TimeLeft);
4205   player->LevelSolved_CountingScore = player->score_final;
4206 }
4207
4208 void GameWon()
4209 {
4210   static int time, time_final;
4211   static int score, score_final;
4212   static int game_over_delay_1 = 0;
4213   static int game_over_delay_2 = 0;
4214   int game_over_delay_value_1 = 50;
4215   int game_over_delay_value_2 = 50;
4216
4217   if (!local_player->LevelSolved_GameWon)
4218   {
4219     int i;
4220
4221     /* do not start end game actions before the player stops moving (to exit) */
4222     if (local_player->MovPos)
4223       return;
4224
4225     local_player->LevelSolved_GameWon = TRUE;
4226     local_player->LevelSolved_SaveTape = tape.recording;
4227     local_player->LevelSolved_SaveScore = !tape.playing;
4228
4229     if (!tape.playing)
4230     {
4231       LevelStats_incSolved(level_nr);
4232
4233       SaveLevelSetup_SeriesInfo();
4234     }
4235
4236     if (tape.auto_play)         /* tape might already be stopped here */
4237       tape.auto_play_level_solved = TRUE;
4238
4239     TapeStop();
4240
4241     game_over_delay_1 = game_over_delay_value_1;
4242     game_over_delay_2 = game_over_delay_value_2;
4243
4244     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4245     score = score_final = local_player->score_final;
4246
4247     if (TimeLeft > 0)
4248     {
4249       time_final = 0;
4250       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4251     }
4252     else if (game.no_time_limit && TimePlayed < 999)
4253     {
4254       time_final = 999;
4255       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4256     }
4257
4258     local_player->score_final = score_final;
4259
4260     if (level_editor_test_game)
4261     {
4262       time = time_final;
4263       score = score_final;
4264
4265       local_player->LevelSolved_CountingTime = time;
4266       local_player->LevelSolved_CountingScore = score;
4267
4268       game_panel_controls[GAME_PANEL_TIME].value = time;
4269       game_panel_controls[GAME_PANEL_SCORE].value = score;
4270
4271       DisplayGameControlValues();
4272     }
4273
4274     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4275     {
4276       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4277       {
4278         /* close exit door after last player */
4279         if ((AllPlayersGone &&
4280              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4281               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4282               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4283             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4284             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4285         {
4286           int element = Feld[ExitX][ExitY];
4287
4288           Feld[ExitX][ExitY] =
4289             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4290              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4291              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4292              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4293              EL_EM_STEEL_EXIT_CLOSING);
4294
4295           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4296         }
4297
4298         /* player disappears */
4299         DrawLevelField(ExitX, ExitY);
4300       }
4301
4302       for (i = 0; i < MAX_PLAYERS; i++)
4303       {
4304         struct PlayerInfo *player = &stored_player[i];
4305
4306         if (player->present)
4307         {
4308           RemovePlayer(player);
4309
4310           /* player disappears */
4311           DrawLevelField(player->jx, player->jy);
4312         }
4313       }
4314     }
4315
4316     PlaySound(SND_GAME_WINNING);
4317   }
4318
4319   if (game_over_delay_1 > 0)
4320   {
4321     game_over_delay_1--;
4322
4323     return;
4324   }
4325
4326   if (time != time_final)
4327   {
4328     int time_to_go = ABS(time_final - time);
4329     int time_count_dir = (time < time_final ? +1 : -1);
4330     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4331
4332     time  += time_count_steps * time_count_dir;
4333     score += time_count_steps * level.score[SC_TIME_BONUS];
4334
4335     local_player->LevelSolved_CountingTime = time;
4336     local_player->LevelSolved_CountingScore = score;
4337
4338     game_panel_controls[GAME_PANEL_TIME].value = time;
4339     game_panel_controls[GAME_PANEL_SCORE].value = score;
4340
4341     DisplayGameControlValues();
4342
4343     if (time == time_final)
4344       StopSound(SND_GAME_LEVELTIME_BONUS);
4345     else if (setup.sound_loops)
4346       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4347     else
4348       PlaySound(SND_GAME_LEVELTIME_BONUS);
4349
4350     return;
4351   }
4352
4353   local_player->LevelSolved_PanelOff = TRUE;
4354
4355   if (game_over_delay_2 > 0)
4356   {
4357     game_over_delay_2--;
4358
4359     return;
4360   }
4361
4362   GameEnd();
4363 }
4364
4365 void GameEnd()
4366 {
4367   int hi_pos;
4368   boolean raise_level = FALSE;
4369
4370   local_player->LevelSolved_GameEnd = TRUE;
4371
4372   CloseDoor(DOOR_CLOSE_1);
4373
4374   if (local_player->LevelSolved_SaveTape)
4375   {
4376     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4377   }
4378
4379   if (level_editor_test_game)
4380   {
4381     game_status = GAME_MODE_MAIN;
4382
4383     DrawAndFadeInMainMenu(REDRAW_FIELD);
4384
4385     return;
4386   }
4387
4388   if (!local_player->LevelSolved_SaveScore)
4389   {
4390     FadeOut(REDRAW_FIELD);
4391
4392     game_status = GAME_MODE_MAIN;
4393
4394     DrawAndFadeInMainMenu(REDRAW_FIELD);
4395
4396     return;
4397   }
4398
4399   if (level_nr == leveldir_current->handicap_level)
4400   {
4401     leveldir_current->handicap_level++;
4402
4403     SaveLevelSetup_SeriesInfo();
4404   }
4405
4406   if (level_nr < leveldir_current->last_level)
4407     raise_level = TRUE;                 /* advance to next level */
4408
4409   if ((hi_pos = NewHiScore()) >= 0) 
4410   {
4411     game_status = GAME_MODE_SCORES;
4412
4413     DrawHallOfFame(hi_pos);
4414
4415     if (raise_level)
4416     {
4417       level_nr++;
4418       TapeErase();
4419     }
4420   }
4421   else
4422   {
4423     FadeOut(REDRAW_FIELD);
4424
4425     game_status = GAME_MODE_MAIN;
4426
4427     if (raise_level)
4428     {
4429       level_nr++;
4430       TapeErase();
4431     }
4432
4433     DrawAndFadeInMainMenu(REDRAW_FIELD);
4434   }
4435 }
4436
4437 int NewHiScore()
4438 {
4439   int k, l;
4440   int position = -1;
4441
4442   LoadScore(level_nr);
4443
4444   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4445       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4446     return -1;
4447
4448   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4449   {
4450     if (local_player->score_final > highscore[k].Score)
4451     {
4452       /* player has made it to the hall of fame */
4453
4454       if (k < MAX_SCORE_ENTRIES - 1)
4455       {
4456         int m = MAX_SCORE_ENTRIES - 1;
4457
4458 #ifdef ONE_PER_NAME
4459         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4460           if (strEqual(setup.player_name, highscore[l].Name))
4461             m = l;
4462         if (m == k)     /* player's new highscore overwrites his old one */
4463           goto put_into_list;
4464 #endif
4465
4466         for (l = m; l > k; l--)
4467         {
4468           strcpy(highscore[l].Name, highscore[l - 1].Name);
4469           highscore[l].Score = highscore[l - 1].Score;
4470         }
4471       }
4472
4473 #ifdef ONE_PER_NAME
4474       put_into_list:
4475 #endif
4476       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4477       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4478       highscore[k].Score = local_player->score_final; 
4479       position = k;
4480       break;
4481     }
4482
4483 #ifdef ONE_PER_NAME
4484     else if (!strncmp(setup.player_name, highscore[k].Name,
4485                       MAX_PLAYER_NAME_LEN))
4486       break;    /* player already there with a higher score */
4487 #endif
4488
4489   }
4490
4491   if (position >= 0) 
4492     SaveScore(level_nr);
4493
4494   return position;
4495 }
4496
4497 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4498 {
4499   int element = Feld[x][y];
4500   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4501   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4502   int horiz_move = (dx != 0);
4503   int sign = (horiz_move ? dx : dy);
4504   int step = sign * element_info[element].move_stepsize;
4505
4506   /* special values for move stepsize for spring and things on conveyor belt */
4507   if (horiz_move)
4508   {
4509     if (CAN_FALL(element) &&
4510         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4511       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4512     else if (element == EL_SPRING)
4513       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4514   }
4515
4516   return step;
4517 }
4518
4519 inline static int getElementMoveStepsize(int x, int y)
4520 {
4521   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4522 }
4523
4524 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4525 {
4526   if (player->GfxAction != action || player->GfxDir != dir)
4527   {
4528     player->GfxAction = action;
4529     player->GfxDir = dir;
4530     player->Frame = 0;
4531     player->StepFrame = 0;
4532   }
4533 }
4534
4535 static void ResetGfxFrame(int x, int y, boolean redraw)
4536 {
4537   int element = Feld[x][y];
4538   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4539   int last_gfx_frame = GfxFrame[x][y];
4540
4541   if (graphic_info[graphic].anim_global_sync)
4542     GfxFrame[x][y] = FrameCounter;
4543   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4544     GfxFrame[x][y] = CustomValue[x][y];
4545   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4546     GfxFrame[x][y] = element_info[element].collect_score;
4547   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4548     GfxFrame[x][y] = ChangeDelay[x][y];
4549
4550   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4551     DrawLevelGraphicAnimation(x, y, graphic);
4552 }
4553
4554 static void ResetGfxAnimation(int x, int y)
4555 {
4556   GfxAction[x][y] = ACTION_DEFAULT;
4557   GfxDir[x][y] = MovDir[x][y];
4558   GfxFrame[x][y] = 0;
4559
4560   ResetGfxFrame(x, y, FALSE);
4561 }
4562
4563 static void ResetRandomAnimationValue(int x, int y)
4564 {
4565   GfxRandom[x][y] = INIT_GFX_RANDOM();
4566 }
4567
4568 void InitMovingField(int x, int y, int direction)
4569 {
4570   int element = Feld[x][y];
4571   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4572   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4573   int newx = x + dx;
4574   int newy = y + dy;
4575   boolean is_moving_before, is_moving_after;
4576
4577   /* check if element was/is moving or being moved before/after mode change */
4578   is_moving_before = (WasJustMoving[x][y] != 0);
4579   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4580
4581   /* reset animation only for moving elements which change direction of moving
4582      or which just started or stopped moving
4583      (else CEs with property "can move" / "not moving" are reset each frame) */
4584   if (is_moving_before != is_moving_after ||
4585       direction != MovDir[x][y])
4586     ResetGfxAnimation(x, y);
4587
4588   MovDir[x][y] = direction;
4589   GfxDir[x][y] = direction;
4590
4591   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4592                      direction == MV_DOWN && CAN_FALL(element) ?
4593                      ACTION_FALLING : ACTION_MOVING);
4594
4595   /* this is needed for CEs with property "can move" / "not moving" */
4596
4597   if (is_moving_after)
4598   {
4599     if (Feld[newx][newy] == EL_EMPTY)
4600       Feld[newx][newy] = EL_BLOCKED;
4601
4602     MovDir[newx][newy] = MovDir[x][y];
4603
4604     CustomValue[newx][newy] = CustomValue[x][y];
4605
4606     GfxFrame[newx][newy] = GfxFrame[x][y];
4607     GfxRandom[newx][newy] = GfxRandom[x][y];
4608     GfxAction[newx][newy] = GfxAction[x][y];
4609     GfxDir[newx][newy] = GfxDir[x][y];
4610   }
4611 }
4612
4613 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4614 {
4615   int direction = MovDir[x][y];
4616   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4617   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4618
4619   *goes_to_x = newx;
4620   *goes_to_y = newy;
4621 }
4622
4623 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4624 {
4625   int oldx = x, oldy = y;
4626   int direction = MovDir[x][y];
4627
4628   if (direction == MV_LEFT)
4629     oldx++;
4630   else if (direction == MV_RIGHT)
4631     oldx--;
4632   else if (direction == MV_UP)
4633     oldy++;
4634   else if (direction == MV_DOWN)
4635     oldy--;
4636
4637   *comes_from_x = oldx;
4638   *comes_from_y = oldy;
4639 }
4640
4641 int MovingOrBlocked2Element(int x, int y)
4642 {
4643   int element = Feld[x][y];
4644
4645   if (element == EL_BLOCKED)
4646   {
4647     int oldx, oldy;
4648
4649     Blocked2Moving(x, y, &oldx, &oldy);
4650     return Feld[oldx][oldy];
4651   }
4652   else
4653     return element;
4654 }
4655
4656 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4657 {
4658   /* like MovingOrBlocked2Element(), but if element is moving
4659      and (x,y) is the field the moving element is just leaving,
4660      return EL_BLOCKED instead of the element value */
4661   int element = Feld[x][y];
4662
4663   if (IS_MOVING(x, y))
4664   {
4665     if (element == EL_BLOCKED)
4666     {
4667       int oldx, oldy;
4668
4669       Blocked2Moving(x, y, &oldx, &oldy);
4670       return Feld[oldx][oldy];
4671     }
4672     else
4673       return EL_BLOCKED;
4674   }
4675   else
4676     return element;
4677 }
4678
4679 static void RemoveField(int x, int y)
4680 {
4681   Feld[x][y] = EL_EMPTY;
4682
4683   MovPos[x][y] = 0;
4684   MovDir[x][y] = 0;
4685   MovDelay[x][y] = 0;
4686
4687   CustomValue[x][y] = 0;
4688
4689   AmoebaNr[x][y] = 0;
4690   ChangeDelay[x][y] = 0;
4691   ChangePage[x][y] = -1;
4692   Pushed[x][y] = FALSE;
4693
4694   GfxElement[x][y] = EL_UNDEFINED;
4695   GfxAction[x][y] = ACTION_DEFAULT;
4696   GfxDir[x][y] = MV_NONE;
4697 }
4698
4699 void RemoveMovingField(int x, int y)
4700 {
4701   int oldx = x, oldy = y, newx = x, newy = y;
4702   int element = Feld[x][y];
4703   int next_element = EL_UNDEFINED;
4704
4705   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4706     return;
4707
4708   if (IS_MOVING(x, y))
4709   {
4710     Moving2Blocked(x, y, &newx, &newy);
4711
4712     if (Feld[newx][newy] != EL_BLOCKED)
4713     {
4714       /* element is moving, but target field is not free (blocked), but
4715          already occupied by something different (example: acid pool);
4716          in this case, only remove the moving field, but not the target */
4717
4718       RemoveField(oldx, oldy);
4719
4720       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4721
4722       TEST_DrawLevelField(oldx, oldy);
4723
4724       return;
4725     }
4726   }
4727   else if (element == EL_BLOCKED)
4728   {
4729     Blocked2Moving(x, y, &oldx, &oldy);
4730     if (!IS_MOVING(oldx, oldy))
4731       return;
4732   }
4733
4734   if (element == EL_BLOCKED &&
4735       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4736        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4737        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4738        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4739        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4740        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4741     next_element = get_next_element(Feld[oldx][oldy]);
4742
4743   RemoveField(oldx, oldy);
4744   RemoveField(newx, newy);
4745
4746   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4747
4748   if (next_element != EL_UNDEFINED)
4749     Feld[oldx][oldy] = next_element;
4750
4751   TEST_DrawLevelField(oldx, oldy);
4752   TEST_DrawLevelField(newx, newy);
4753 }
4754
4755 void DrawDynamite(int x, int y)
4756 {
4757   int sx = SCREENX(x), sy = SCREENY(y);
4758   int graphic = el2img(Feld[x][y]);
4759   int frame;
4760
4761   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4762     return;
4763
4764   if (IS_WALKABLE_INSIDE(Back[x][y]))
4765     return;
4766
4767   if (Back[x][y])
4768     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4769   else if (Store[x][y])
4770     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4771
4772   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4773
4774   if (Back[x][y] || Store[x][y])
4775     DrawGraphicThruMask(sx, sy, graphic, frame);
4776   else
4777     DrawGraphic(sx, sy, graphic, frame);
4778 }
4779
4780 void CheckDynamite(int x, int y)
4781 {
4782   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4783   {
4784     MovDelay[x][y]--;
4785
4786     if (MovDelay[x][y] != 0)
4787     {
4788       DrawDynamite(x, y);
4789       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4790
4791       return;
4792     }
4793   }
4794
4795   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4796
4797   Bang(x, y);
4798 }
4799
4800 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4801 {
4802   boolean num_checked_players = 0;
4803   int i;
4804
4805   for (i = 0; i < MAX_PLAYERS; i++)
4806   {
4807     if (stored_player[i].active)
4808     {
4809       int sx = stored_player[i].jx;
4810       int sy = stored_player[i].jy;
4811
4812       if (num_checked_players == 0)
4813       {
4814         *sx1 = *sx2 = sx;
4815         *sy1 = *sy2 = sy;
4816       }
4817       else
4818       {
4819         *sx1 = MIN(*sx1, sx);
4820         *sy1 = MIN(*sy1, sy);
4821         *sx2 = MAX(*sx2, sx);
4822         *sy2 = MAX(*sy2, sy);
4823       }
4824
4825       num_checked_players++;
4826     }
4827   }
4828 }
4829
4830 static boolean checkIfAllPlayersFitToScreen_RND()
4831 {
4832   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4833
4834   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4835
4836   return (sx2 - sx1 < SCR_FIELDX &&
4837           sy2 - sy1 < SCR_FIELDY);
4838 }
4839
4840 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4841 {
4842   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4843
4844   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4845
4846   *sx = (sx1 + sx2) / 2;
4847   *sy = (sy1 + sy2) / 2;
4848 }
4849
4850 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4851                         boolean center_screen, boolean quick_relocation)
4852 {
4853   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4854   boolean no_delay = (tape.warp_forward);
4855   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4856   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4857
4858   if (quick_relocation)
4859   {
4860     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4861     {
4862       if (!level.shifted_relocation || center_screen)
4863       {
4864         /* quick relocation (without scrolling), with centering of screen */
4865
4866         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4867                     x > SBX_Right + MIDPOSX ? SBX_Right :
4868                     x - MIDPOSX);
4869
4870         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4871                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4872                     y - MIDPOSY);
4873       }
4874       else
4875       {
4876         /* quick relocation (without scrolling), but do not center screen */
4877
4878         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4879                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4880                                old_x - MIDPOSX);
4881
4882         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4883                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4884                                old_y - MIDPOSY);
4885
4886         int offset_x = x + (scroll_x - center_scroll_x);
4887         int offset_y = y + (scroll_y - center_scroll_y);
4888
4889         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4890                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4891                     offset_x - MIDPOSX);
4892
4893         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4894                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4895                     offset_y - MIDPOSY);
4896       }
4897     }
4898     else
4899     {
4900       if (!level.shifted_relocation || center_screen)
4901       {
4902         /* quick relocation (without scrolling), with centering of screen */
4903
4904         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4905                     x > SBX_Right + MIDPOSX ? SBX_Right :
4906                     x - MIDPOSX);
4907
4908         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4909                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4910                     y - MIDPOSY);
4911       }
4912       else
4913       {
4914         /* quick relocation (without scrolling), but do not center screen */
4915
4916         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4917                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4918                                old_x - MIDPOSX);
4919
4920         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4921                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4922                                old_y - MIDPOSY);
4923
4924         int offset_x = x + (scroll_x - center_scroll_x);
4925         int offset_y = y + (scroll_y - center_scroll_y);
4926
4927         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4928                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4929                     offset_x - MIDPOSX);
4930
4931         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4932                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4933                     offset_y - MIDPOSY);
4934       }
4935     }
4936
4937     RedrawPlayfield(TRUE, 0,0,0,0);
4938   }
4939   else
4940   {
4941     int scroll_xx, scroll_yy;
4942
4943     if (!level.shifted_relocation || center_screen)
4944     {
4945       /* visible relocation (with scrolling), with centering of screen */
4946
4947       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4948                    x > SBX_Right + MIDPOSX ? SBX_Right :
4949                    x - MIDPOSX);
4950
4951       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4952                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4953                    y - MIDPOSY);
4954     }
4955     else
4956     {
4957       /* visible relocation (with scrolling), but do not center screen */
4958
4959       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4960                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4961                              old_x - MIDPOSX);
4962
4963       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4964                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4965                              old_y - MIDPOSY);
4966
4967       int offset_x = x + (scroll_x - center_scroll_x);
4968       int offset_y = y + (scroll_y - center_scroll_y);
4969
4970       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4971                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4972                    offset_x - MIDPOSX);
4973
4974       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4975                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4976                    offset_y - MIDPOSY);
4977     }
4978
4979
4980     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4981
4982     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4983     {
4984       int dx = 0, dy = 0;
4985       int fx = FX, fy = FY;
4986
4987       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4988       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4989
4990       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4991         break;
4992
4993       scroll_x -= dx;
4994       scroll_y -= dy;
4995
4996       fx += dx * TILEX / 2;
4997       fy += dy * TILEY / 2;
4998
4999       ScrollLevel(dx, dy);
5000       DrawAllPlayers();
5001
5002       /* scroll in two steps of half tile size to make things smoother */
5003       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5004       Delay(wait_delay_value);
5005
5006       /* scroll second step to align at full tile size */
5007       BackToFront();
5008       Delay(wait_delay_value);
5009     }
5010
5011     DrawAllPlayers();
5012     BackToFront();
5013     Delay(wait_delay_value);
5014   }
5015 }
5016
5017 void RelocatePlayer(int jx, int jy, int el_player_raw)
5018 {
5019   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5020   int player_nr = GET_PLAYER_NR(el_player);
5021   struct PlayerInfo *player = &stored_player[player_nr];
5022   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5023   boolean no_delay = (tape.warp_forward);
5024   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5025   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5026   int old_jx = player->jx;
5027   int old_jy = player->jy;
5028   int old_element = Feld[old_jx][old_jy];
5029   int element = Feld[jx][jy];
5030   boolean player_relocated = (old_jx != jx || old_jy != jy);
5031
5032   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5033   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5034   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5035   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5036   int leave_side_horiz = move_dir_horiz;
5037   int leave_side_vert  = move_dir_vert;
5038   int enter_side = enter_side_horiz | enter_side_vert;
5039   int leave_side = leave_side_horiz | leave_side_vert;
5040
5041   if (player->GameOver)         /* do not reanimate dead player */
5042     return;
5043
5044   if (!player_relocated)        /* no need to relocate the player */
5045     return;
5046
5047   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5048   {
5049     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5050     DrawLevelField(jx, jy);
5051   }
5052
5053   if (player->present)
5054   {
5055     while (player->MovPos)
5056     {
5057       ScrollPlayer(player, SCROLL_GO_ON);
5058       ScrollScreen(NULL, SCROLL_GO_ON);
5059
5060       AdvanceFrameAndPlayerCounters(player->index_nr);
5061
5062       DrawPlayer(player);
5063
5064       BackToFront();
5065       Delay(wait_delay_value);
5066     }
5067
5068     DrawPlayer(player);         /* needed here only to cleanup last field */
5069     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5070
5071     player->is_moving = FALSE;
5072   }
5073
5074   if (IS_CUSTOM_ELEMENT(old_element))
5075     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5076                                CE_LEFT_BY_PLAYER,
5077                                player->index_bit, leave_side);
5078
5079   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5080                                       CE_PLAYER_LEAVES_X,
5081                                       player->index_bit, leave_side);
5082
5083   Feld[jx][jy] = el_player;
5084   InitPlayerField(jx, jy, el_player, TRUE);
5085
5086   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5087      possible that the relocation target field did not contain a player element,
5088      but a walkable element, to which the new player was relocated -- in this
5089      case, restore that (already initialized!) element on the player field */
5090   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5091   {
5092     Feld[jx][jy] = element;     /* restore previously existing element */
5093   }
5094
5095   /* only visually relocate centered player */
5096   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5097                      FALSE, level.instant_relocation);
5098
5099   TestIfPlayerTouchesBadThing(jx, jy);
5100   TestIfPlayerTouchesCustomElement(jx, jy);
5101
5102   if (IS_CUSTOM_ELEMENT(element))
5103     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5104                                player->index_bit, enter_side);
5105
5106   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5107                                       player->index_bit, enter_side);
5108
5109   if (player->is_switching)
5110   {
5111     /* ensure that relocation while still switching an element does not cause
5112        a new element to be treated as also switched directly after relocation
5113        (this is important for teleporter switches that teleport the player to
5114        a place where another teleporter switch is in the same direction, which
5115        would then incorrectly be treated as immediately switched before the
5116        direction key that caused the switch was released) */
5117
5118     player->switch_x += jx - old_jx;
5119     player->switch_y += jy - old_jy;
5120   }
5121 }
5122
5123 void Explode(int ex, int ey, int phase, int mode)
5124 {
5125   int x, y;
5126   int last_phase;
5127   int border_element;
5128
5129   /* !!! eliminate this variable !!! */
5130   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5131
5132   if (game.explosions_delayed)
5133   {
5134     ExplodeField[ex][ey] = mode;
5135     return;
5136   }
5137
5138   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5139   {
5140     int center_element = Feld[ex][ey];
5141     int artwork_element, explosion_element;     /* set these values later */
5142
5143     /* remove things displayed in background while burning dynamite */
5144     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5145       Back[ex][ey] = 0;
5146
5147     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5148     {
5149       /* put moving element to center field (and let it explode there) */
5150       center_element = MovingOrBlocked2Element(ex, ey);
5151       RemoveMovingField(ex, ey);
5152       Feld[ex][ey] = center_element;
5153     }
5154
5155     /* now "center_element" is finally determined -- set related values now */
5156     artwork_element = center_element;           /* for custom player artwork */
5157     explosion_element = center_element;         /* for custom player artwork */
5158
5159     if (IS_PLAYER(ex, ey))
5160     {
5161       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5162
5163       artwork_element = stored_player[player_nr].artwork_element;
5164
5165       if (level.use_explosion_element[player_nr])
5166       {
5167         explosion_element = level.explosion_element[player_nr];
5168         artwork_element = explosion_element;
5169       }
5170     }
5171
5172     if (mode == EX_TYPE_NORMAL ||
5173         mode == EX_TYPE_CENTER ||
5174         mode == EX_TYPE_CROSS)
5175       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5176
5177     last_phase = element_info[explosion_element].explosion_delay + 1;
5178
5179     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5180     {
5181       int xx = x - ex + 1;
5182       int yy = y - ey + 1;
5183       int element;
5184
5185       if (!IN_LEV_FIELD(x, y) ||
5186           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5187           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5188         continue;
5189
5190       element = Feld[x][y];
5191
5192       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5193       {
5194         element = MovingOrBlocked2Element(x, y);
5195
5196         if (!IS_EXPLOSION_PROOF(element))
5197           RemoveMovingField(x, y);
5198       }
5199
5200       /* indestructible elements can only explode in center (but not flames) */
5201       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5202                                            mode == EX_TYPE_BORDER)) ||
5203           element == EL_FLAMES)
5204         continue;
5205
5206       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5207          behaviour, for example when touching a yamyam that explodes to rocks
5208          with active deadly shield, a rock is created under the player !!! */
5209       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5210 #if 0
5211       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5212           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5213            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5214 #else
5215       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5216 #endif
5217       {
5218         if (IS_ACTIVE_BOMB(element))
5219         {
5220           /* re-activate things under the bomb like gate or penguin */
5221           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5222           Back[x][y] = 0;
5223         }
5224
5225         continue;
5226       }
5227
5228       /* save walkable background elements while explosion on same tile */
5229       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5230           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5231         Back[x][y] = element;
5232
5233       /* ignite explodable elements reached by other explosion */
5234       if (element == EL_EXPLOSION)
5235         element = Store2[x][y];
5236
5237       if (AmoebaNr[x][y] &&
5238           (element == EL_AMOEBA_FULL ||
5239            element == EL_BD_AMOEBA ||
5240            element == EL_AMOEBA_GROWING))
5241       {
5242         AmoebaCnt[AmoebaNr[x][y]]--;
5243         AmoebaCnt2[AmoebaNr[x][y]]--;
5244       }
5245
5246       RemoveField(x, y);
5247
5248       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5249       {
5250         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5251
5252         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5253
5254         if (PLAYERINFO(ex, ey)->use_murphy)
5255           Store[x][y] = EL_EMPTY;
5256       }
5257
5258       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5259          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5260       else if (ELEM_IS_PLAYER(center_element))
5261         Store[x][y] = EL_EMPTY;
5262       else if (center_element == EL_YAMYAM)
5263         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5264       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5265         Store[x][y] = element_info[center_element].content.e[xx][yy];
5266 #if 1
5267       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5268          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5269          otherwise) -- FIX THIS !!! */
5270       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5271         Store[x][y] = element_info[element].content.e[1][1];
5272 #else
5273       else if (!CAN_EXPLODE(element))
5274         Store[x][y] = element_info[element].content.e[1][1];
5275 #endif
5276       else
5277         Store[x][y] = EL_EMPTY;
5278
5279       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5280           center_element == EL_AMOEBA_TO_DIAMOND)
5281         Store2[x][y] = element;
5282
5283       Feld[x][y] = EL_EXPLOSION;
5284       GfxElement[x][y] = artwork_element;
5285
5286       ExplodePhase[x][y] = 1;
5287       ExplodeDelay[x][y] = last_phase;
5288
5289       Stop[x][y] = TRUE;
5290     }
5291
5292     if (center_element == EL_YAMYAM)
5293       game.yamyam_content_nr =
5294         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5295
5296     return;
5297   }
5298
5299   if (Stop[ex][ey])
5300     return;
5301
5302   x = ex;
5303   y = ey;
5304
5305   if (phase == 1)
5306     GfxFrame[x][y] = 0;         /* restart explosion animation */
5307
5308   last_phase = ExplodeDelay[x][y];
5309
5310   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5311
5312   /* this can happen if the player leaves an explosion just in time */
5313   if (GfxElement[x][y] == EL_UNDEFINED)
5314     GfxElement[x][y] = EL_EMPTY;
5315
5316   border_element = Store2[x][y];
5317   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5318     border_element = StorePlayer[x][y];
5319
5320   if (phase == element_info[border_element].ignition_delay ||
5321       phase == last_phase)
5322   {
5323     boolean border_explosion = FALSE;
5324
5325     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5326         !PLAYER_EXPLOSION_PROTECTED(x, y))
5327     {
5328       KillPlayerUnlessExplosionProtected(x, y);
5329       border_explosion = TRUE;
5330     }
5331     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5332     {
5333       Feld[x][y] = Store2[x][y];
5334       Store2[x][y] = 0;
5335       Bang(x, y);
5336       border_explosion = TRUE;
5337     }
5338     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5339     {
5340       AmoebeUmwandeln(x, y);
5341       Store2[x][y] = 0;
5342       border_explosion = TRUE;
5343     }
5344
5345     /* if an element just explodes due to another explosion (chain-reaction),
5346        do not immediately end the new explosion when it was the last frame of
5347        the explosion (as it would be done in the following "if"-statement!) */
5348     if (border_explosion && phase == last_phase)
5349       return;
5350   }
5351
5352   if (phase == last_phase)
5353   {
5354     int element;
5355
5356     element = Feld[x][y] = Store[x][y];
5357     Store[x][y] = Store2[x][y] = 0;
5358     GfxElement[x][y] = EL_UNDEFINED;
5359
5360     /* player can escape from explosions and might therefore be still alive */
5361     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5362         element <= EL_PLAYER_IS_EXPLODING_4)
5363     {
5364       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5365       int explosion_element = EL_PLAYER_1 + player_nr;
5366       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5367       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5368
5369       if (level.use_explosion_element[player_nr])
5370         explosion_element = level.explosion_element[player_nr];
5371
5372       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5373                     element_info[explosion_element].content.e[xx][yy]);
5374     }
5375
5376     /* restore probably existing indestructible background element */
5377     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5378       element = Feld[x][y] = Back[x][y];
5379     Back[x][y] = 0;
5380
5381     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5382     GfxDir[x][y] = MV_NONE;
5383     ChangeDelay[x][y] = 0;
5384     ChangePage[x][y] = -1;
5385
5386     CustomValue[x][y] = 0;
5387
5388     InitField_WithBug2(x, y, FALSE);
5389
5390     TEST_DrawLevelField(x, y);
5391
5392     TestIfElementTouchesCustomElement(x, y);
5393
5394     if (GFX_CRUMBLED(element))
5395       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5396
5397     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5398       StorePlayer[x][y] = 0;
5399
5400     if (ELEM_IS_PLAYER(element))
5401       RelocatePlayer(x, y, element);
5402   }
5403   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5404   {
5405     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5406     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5407
5408     if (phase == delay)
5409       TEST_DrawLevelFieldCrumbled(x, y);
5410
5411     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5412     {
5413       DrawLevelElement(x, y, Back[x][y]);
5414       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5415     }
5416     else if (IS_WALKABLE_UNDER(Back[x][y]))
5417     {
5418       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5419       DrawLevelElementThruMask(x, y, Back[x][y]);
5420     }
5421     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5422       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5423   }
5424 }
5425
5426 void DynaExplode(int ex, int ey)
5427 {
5428   int i, j;
5429   int dynabomb_element = Feld[ex][ey];
5430   int dynabomb_size = 1;
5431   boolean dynabomb_xl = FALSE;
5432   struct PlayerInfo *player;
5433   static int xy[4][2] =
5434   {
5435     { 0, -1 },
5436     { -1, 0 },
5437     { +1, 0 },
5438     { 0, +1 }
5439   };
5440
5441   if (IS_ACTIVE_BOMB(dynabomb_element))
5442   {
5443     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5444     dynabomb_size = player->dynabomb_size;
5445     dynabomb_xl = player->dynabomb_xl;
5446     player->dynabombs_left++;
5447   }
5448
5449   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5450
5451   for (i = 0; i < NUM_DIRECTIONS; i++)
5452   {
5453     for (j = 1; j <= dynabomb_size; j++)
5454     {
5455       int x = ex + j * xy[i][0];
5456       int y = ey + j * xy[i][1];
5457       int element;
5458
5459       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5460         break;
5461
5462       element = Feld[x][y];
5463
5464       /* do not restart explosions of fields with active bombs */
5465       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5466         continue;
5467
5468       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5469
5470       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5471           !IS_DIGGABLE(element) && !dynabomb_xl)
5472         break;
5473     }
5474   }
5475 }
5476
5477 void Bang(int x, int y)
5478 {
5479   int element = MovingOrBlocked2Element(x, y);
5480   int explosion_type = EX_TYPE_NORMAL;
5481
5482   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5483   {
5484     struct PlayerInfo *player = PLAYERINFO(x, y);
5485
5486     element = Feld[x][y] = player->initial_element;
5487
5488     if (level.use_explosion_element[player->index_nr])
5489     {
5490       int explosion_element = level.explosion_element[player->index_nr];
5491
5492       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5493         explosion_type = EX_TYPE_CROSS;
5494       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5495         explosion_type = EX_TYPE_CENTER;
5496     }
5497   }
5498
5499   switch (element)
5500   {
5501     case EL_BUG:
5502     case EL_SPACESHIP:
5503     case EL_BD_BUTTERFLY:
5504     case EL_BD_FIREFLY:
5505     case EL_YAMYAM:
5506     case EL_DARK_YAMYAM:
5507     case EL_ROBOT:
5508     case EL_PACMAN:
5509     case EL_MOLE:
5510       RaiseScoreElement(element);
5511       break;
5512
5513     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5514     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5515     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5516     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5517     case EL_DYNABOMB_INCREASE_NUMBER:
5518     case EL_DYNABOMB_INCREASE_SIZE:
5519     case EL_DYNABOMB_INCREASE_POWER:
5520       explosion_type = EX_TYPE_DYNA;
5521       break;
5522
5523     case EL_DC_LANDMINE:
5524       explosion_type = EX_TYPE_CENTER;
5525       break;
5526
5527     case EL_PENGUIN:
5528     case EL_LAMP:
5529     case EL_LAMP_ACTIVE:
5530     case EL_AMOEBA_TO_DIAMOND:
5531       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5532         explosion_type = EX_TYPE_CENTER;
5533       break;
5534
5535     default:
5536       if (element_info[element].explosion_type == EXPLODES_CROSS)
5537         explosion_type = EX_TYPE_CROSS;
5538       else if (element_info[element].explosion_type == EXPLODES_1X1)
5539         explosion_type = EX_TYPE_CENTER;
5540       break;
5541   }
5542
5543   if (explosion_type == EX_TYPE_DYNA)
5544     DynaExplode(x, y);
5545   else
5546     Explode(x, y, EX_PHASE_START, explosion_type);
5547
5548   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5549 }
5550
5551 void SplashAcid(int x, int y)
5552 {
5553   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5554       (!IN_LEV_FIELD(x - 1, y - 2) ||
5555        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5556     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5557
5558   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5559       (!IN_LEV_FIELD(x + 1, y - 2) ||
5560        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5561     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5562
5563   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5564 }
5565
5566 static void InitBeltMovement()
5567 {
5568   static int belt_base_element[4] =
5569   {
5570     EL_CONVEYOR_BELT_1_LEFT,
5571     EL_CONVEYOR_BELT_2_LEFT,
5572     EL_CONVEYOR_BELT_3_LEFT,
5573     EL_CONVEYOR_BELT_4_LEFT
5574   };
5575   static int belt_base_active_element[4] =
5576   {
5577     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5578     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5579     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5580     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5581   };
5582
5583   int x, y, i, j;
5584
5585   /* set frame order for belt animation graphic according to belt direction */
5586   for (i = 0; i < NUM_BELTS; i++)
5587   {
5588     int belt_nr = i;
5589
5590     for (j = 0; j < NUM_BELT_PARTS; j++)
5591     {
5592       int element = belt_base_active_element[belt_nr] + j;
5593       int graphic_1 = el2img(element);
5594       int graphic_2 = el2panelimg(element);
5595
5596       if (game.belt_dir[i] == MV_LEFT)
5597       {
5598         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5599         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5600       }
5601       else
5602       {
5603         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5604         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5605       }
5606     }
5607   }
5608
5609   SCAN_PLAYFIELD(x, y)
5610   {
5611     int element = Feld[x][y];
5612
5613     for (i = 0; i < NUM_BELTS; i++)
5614     {
5615       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5616       {
5617         int e_belt_nr = getBeltNrFromBeltElement(element);
5618         int belt_nr = i;
5619
5620         if (e_belt_nr == belt_nr)
5621         {
5622           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5623
5624           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5625         }
5626       }
5627     }
5628   }
5629 }
5630
5631 static void ToggleBeltSwitch(int x, int y)
5632 {
5633   static int belt_base_element[4] =
5634   {
5635     EL_CONVEYOR_BELT_1_LEFT,
5636     EL_CONVEYOR_BELT_2_LEFT,
5637     EL_CONVEYOR_BELT_3_LEFT,
5638     EL_CONVEYOR_BELT_4_LEFT
5639   };
5640   static int belt_base_active_element[4] =
5641   {
5642     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5643     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5644     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5645     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5646   };
5647   static int belt_base_switch_element[4] =
5648   {
5649     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5650     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5651     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5652     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5653   };
5654   static int belt_move_dir[4] =
5655   {
5656     MV_LEFT,
5657     MV_NONE,
5658     MV_RIGHT,
5659     MV_NONE,
5660   };
5661
5662   int element = Feld[x][y];
5663   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5664   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5665   int belt_dir = belt_move_dir[belt_dir_nr];
5666   int xx, yy, i;
5667
5668   if (!IS_BELT_SWITCH(element))
5669     return;
5670
5671   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5672   game.belt_dir[belt_nr] = belt_dir;
5673
5674   if (belt_dir_nr == 3)
5675     belt_dir_nr = 1;
5676
5677   /* set frame order for belt animation graphic according to belt direction */
5678   for (i = 0; i < NUM_BELT_PARTS; i++)
5679   {
5680     int element = belt_base_active_element[belt_nr] + i;
5681     int graphic_1 = el2img(element);
5682     int graphic_2 = el2panelimg(element);
5683
5684     if (belt_dir == MV_LEFT)
5685     {
5686       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5687       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5688     }
5689     else
5690     {
5691       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5692       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5693     }
5694   }
5695
5696   SCAN_PLAYFIELD(xx, yy)
5697   {
5698     int element = Feld[xx][yy];
5699
5700     if (IS_BELT_SWITCH(element))
5701     {
5702       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5703
5704       if (e_belt_nr == belt_nr)
5705       {
5706         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5707         TEST_DrawLevelField(xx, yy);
5708       }
5709     }
5710     else if (IS_BELT(element) && belt_dir != MV_NONE)
5711     {
5712       int e_belt_nr = getBeltNrFromBeltElement(element);
5713
5714       if (e_belt_nr == belt_nr)
5715       {
5716         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5717
5718         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5719         TEST_DrawLevelField(xx, yy);
5720       }
5721     }
5722     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5723     {
5724       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5725
5726       if (e_belt_nr == belt_nr)
5727       {
5728         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5729
5730         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5731         TEST_DrawLevelField(xx, yy);
5732       }
5733     }
5734   }
5735 }
5736
5737 static void ToggleSwitchgateSwitch(int x, int y)
5738 {
5739   int xx, yy;
5740
5741   game.switchgate_pos = !game.switchgate_pos;
5742
5743   SCAN_PLAYFIELD(xx, yy)
5744   {
5745     int element = Feld[xx][yy];
5746
5747     if (element == EL_SWITCHGATE_SWITCH_UP)
5748     {
5749       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5750       TEST_DrawLevelField(xx, yy);
5751     }
5752     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5753     {
5754       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5755       TEST_DrawLevelField(xx, yy);
5756     }
5757     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5758     {
5759       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5760       TEST_DrawLevelField(xx, yy);
5761     }
5762     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5763     {
5764       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5765       TEST_DrawLevelField(xx, yy);
5766     }
5767     else if (element == EL_SWITCHGATE_OPEN ||
5768              element == EL_SWITCHGATE_OPENING)
5769     {
5770       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5771
5772       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5773     }
5774     else if (element == EL_SWITCHGATE_CLOSED ||
5775              element == EL_SWITCHGATE_CLOSING)
5776     {
5777       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5778
5779       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5780     }
5781   }
5782 }
5783
5784 static int getInvisibleActiveFromInvisibleElement(int element)
5785 {
5786   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5787           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5788           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5789           element);
5790 }
5791
5792 static int getInvisibleFromInvisibleActiveElement(int element)
5793 {
5794   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5795           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5796           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5797           element);
5798 }
5799
5800 static void RedrawAllLightSwitchesAndInvisibleElements()
5801 {
5802   int x, y;
5803
5804   SCAN_PLAYFIELD(x, y)
5805   {
5806     int element = Feld[x][y];
5807
5808     if (element == EL_LIGHT_SWITCH &&
5809         game.light_time_left > 0)
5810     {
5811       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5812       TEST_DrawLevelField(x, y);
5813     }
5814     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5815              game.light_time_left == 0)
5816     {
5817       Feld[x][y] = EL_LIGHT_SWITCH;
5818       TEST_DrawLevelField(x, y);
5819     }
5820     else if (element == EL_EMC_DRIPPER &&
5821              game.light_time_left > 0)
5822     {
5823       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5824       TEST_DrawLevelField(x, y);
5825     }
5826     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5827              game.light_time_left == 0)
5828     {
5829       Feld[x][y] = EL_EMC_DRIPPER;
5830       TEST_DrawLevelField(x, y);
5831     }
5832     else if (element == EL_INVISIBLE_STEELWALL ||
5833              element == EL_INVISIBLE_WALL ||
5834              element == EL_INVISIBLE_SAND)
5835     {
5836       if (game.light_time_left > 0)
5837         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5838
5839       TEST_DrawLevelField(x, y);
5840
5841       /* uncrumble neighbour fields, if needed */
5842       if (element == EL_INVISIBLE_SAND)
5843         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5844     }
5845     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5846              element == EL_INVISIBLE_WALL_ACTIVE ||
5847              element == EL_INVISIBLE_SAND_ACTIVE)
5848     {
5849       if (game.light_time_left == 0)
5850         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5851
5852       TEST_DrawLevelField(x, y);
5853
5854       /* re-crumble neighbour fields, if needed */
5855       if (element == EL_INVISIBLE_SAND)
5856         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5857     }
5858   }
5859 }
5860
5861 static void RedrawAllInvisibleElementsForLenses()
5862 {
5863   int x, y;
5864
5865   SCAN_PLAYFIELD(x, y)
5866   {
5867     int element = Feld[x][y];
5868
5869     if (element == EL_EMC_DRIPPER &&
5870         game.lenses_time_left > 0)
5871     {
5872       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5873       TEST_DrawLevelField(x, y);
5874     }
5875     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5876              game.lenses_time_left == 0)
5877     {
5878       Feld[x][y] = EL_EMC_DRIPPER;
5879       TEST_DrawLevelField(x, y);
5880     }
5881     else if (element == EL_INVISIBLE_STEELWALL ||
5882              element == EL_INVISIBLE_WALL ||
5883              element == EL_INVISIBLE_SAND)
5884     {
5885       if (game.lenses_time_left > 0)
5886         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5887
5888       TEST_DrawLevelField(x, y);
5889
5890       /* uncrumble neighbour fields, if needed */
5891       if (element == EL_INVISIBLE_SAND)
5892         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5893     }
5894     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5895              element == EL_INVISIBLE_WALL_ACTIVE ||
5896              element == EL_INVISIBLE_SAND_ACTIVE)
5897     {
5898       if (game.lenses_time_left == 0)
5899         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5900
5901       TEST_DrawLevelField(x, y);
5902
5903       /* re-crumble neighbour fields, if needed */
5904       if (element == EL_INVISIBLE_SAND)
5905         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5906     }
5907   }
5908 }
5909
5910 static void RedrawAllInvisibleElementsForMagnifier()
5911 {
5912   int x, y;
5913
5914   SCAN_PLAYFIELD(x, y)
5915   {
5916     int element = Feld[x][y];
5917
5918     if (element == EL_EMC_FAKE_GRASS &&
5919         game.magnify_time_left > 0)
5920     {
5921       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5922       TEST_DrawLevelField(x, y);
5923     }
5924     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5925              game.magnify_time_left == 0)
5926     {
5927       Feld[x][y] = EL_EMC_FAKE_GRASS;
5928       TEST_DrawLevelField(x, y);
5929     }
5930     else if (IS_GATE_GRAY(element) &&
5931              game.magnify_time_left > 0)
5932     {
5933       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5934                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5935                     IS_EM_GATE_GRAY(element) ?
5936                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5937                     IS_EMC_GATE_GRAY(element) ?
5938                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5939                     IS_DC_GATE_GRAY(element) ?
5940                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5941                     element);
5942       TEST_DrawLevelField(x, y);
5943     }
5944     else if (IS_GATE_GRAY_ACTIVE(element) &&
5945              game.magnify_time_left == 0)
5946     {
5947       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5948                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5949                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5950                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5951                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5952                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5953                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5954                     EL_DC_GATE_WHITE_GRAY :
5955                     element);
5956       TEST_DrawLevelField(x, y);
5957     }
5958   }
5959 }
5960
5961 static void ToggleLightSwitch(int x, int y)
5962 {
5963   int element = Feld[x][y];
5964
5965   game.light_time_left =
5966     (element == EL_LIGHT_SWITCH ?
5967      level.time_light * FRAMES_PER_SECOND : 0);
5968
5969   RedrawAllLightSwitchesAndInvisibleElements();
5970 }
5971
5972 static void ActivateTimegateSwitch(int x, int y)
5973 {
5974   int xx, yy;
5975
5976   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5977
5978   SCAN_PLAYFIELD(xx, yy)
5979   {
5980     int element = Feld[xx][yy];
5981
5982     if (element == EL_TIMEGATE_CLOSED ||
5983         element == EL_TIMEGATE_CLOSING)
5984     {
5985       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5986       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5987     }
5988
5989     /*
5990     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5991     {
5992       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5993       TEST_DrawLevelField(xx, yy);
5994     }
5995     */
5996
5997   }
5998
5999   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6000                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6001 }
6002
6003 void Impact(int x, int y)
6004 {
6005   boolean last_line = (y == lev_fieldy - 1);
6006   boolean object_hit = FALSE;
6007   boolean impact = (last_line || object_hit);
6008   int element = Feld[x][y];
6009   int smashed = EL_STEELWALL;
6010
6011   if (!last_line)       /* check if element below was hit */
6012   {
6013     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6014       return;
6015
6016     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6017                                          MovDir[x][y + 1] != MV_DOWN ||
6018                                          MovPos[x][y + 1] <= TILEY / 2));
6019
6020     /* do not smash moving elements that left the smashed field in time */
6021     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6022         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6023       object_hit = FALSE;
6024
6025 #if USE_QUICKSAND_IMPACT_BUGFIX
6026     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6027     {
6028       RemoveMovingField(x, y + 1);
6029       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6030       Feld[x][y + 2] = EL_ROCK;
6031       TEST_DrawLevelField(x, y + 2);
6032
6033       object_hit = TRUE;
6034     }
6035
6036     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6037     {
6038       RemoveMovingField(x, y + 1);
6039       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6040       Feld[x][y + 2] = EL_ROCK;
6041       TEST_DrawLevelField(x, y + 2);
6042
6043       object_hit = TRUE;
6044     }
6045 #endif
6046
6047     if (object_hit)
6048       smashed = MovingOrBlocked2Element(x, y + 1);
6049
6050     impact = (last_line || object_hit);
6051   }
6052
6053   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6054   {
6055     SplashAcid(x, y + 1);
6056     return;
6057   }
6058
6059   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6060   /* only reset graphic animation if graphic really changes after impact */
6061   if (impact &&
6062       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6063   {
6064     ResetGfxAnimation(x, y);
6065     TEST_DrawLevelField(x, y);
6066   }
6067
6068   if (impact && CAN_EXPLODE_IMPACT(element))
6069   {
6070     Bang(x, y);
6071     return;
6072   }
6073   else if (impact && element == EL_PEARL &&
6074            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6075   {
6076     ResetGfxAnimation(x, y);
6077
6078     Feld[x][y] = EL_PEARL_BREAKING;
6079     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6080     return;
6081   }
6082   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6083   {
6084     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6085
6086     return;
6087   }
6088
6089   if (impact && element == EL_AMOEBA_DROP)
6090   {
6091     if (object_hit && IS_PLAYER(x, y + 1))
6092       KillPlayerUnlessEnemyProtected(x, y + 1);
6093     else if (object_hit && smashed == EL_PENGUIN)
6094       Bang(x, y + 1);
6095     else
6096     {
6097       Feld[x][y] = EL_AMOEBA_GROWING;
6098       Store[x][y] = EL_AMOEBA_WET;
6099
6100       ResetRandomAnimationValue(x, y);
6101     }
6102     return;
6103   }
6104
6105   if (object_hit)               /* check which object was hit */
6106   {
6107     if ((CAN_PASS_MAGIC_WALL(element) && 
6108          (smashed == EL_MAGIC_WALL ||
6109           smashed == EL_BD_MAGIC_WALL)) ||
6110         (CAN_PASS_DC_MAGIC_WALL(element) &&
6111          smashed == EL_DC_MAGIC_WALL))
6112     {
6113       int xx, yy;
6114       int activated_magic_wall =
6115         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6116          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6117          EL_DC_MAGIC_WALL_ACTIVE);
6118
6119       /* activate magic wall / mill */
6120       SCAN_PLAYFIELD(xx, yy)
6121       {
6122         if (Feld[xx][yy] == smashed)
6123           Feld[xx][yy] = activated_magic_wall;
6124       }
6125
6126       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6127       game.magic_wall_active = TRUE;
6128
6129       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6130                             SND_MAGIC_WALL_ACTIVATING :
6131                             smashed == EL_BD_MAGIC_WALL ?
6132                             SND_BD_MAGIC_WALL_ACTIVATING :
6133                             SND_DC_MAGIC_WALL_ACTIVATING));
6134     }
6135
6136     if (IS_PLAYER(x, y + 1))
6137     {
6138       if (CAN_SMASH_PLAYER(element))
6139       {
6140         KillPlayerUnlessEnemyProtected(x, y + 1);
6141         return;
6142       }
6143     }
6144     else if (smashed == EL_PENGUIN)
6145     {
6146       if (CAN_SMASH_PLAYER(element))
6147       {
6148         Bang(x, y + 1);
6149         return;
6150       }
6151     }
6152     else if (element == EL_BD_DIAMOND)
6153     {
6154       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6155       {
6156         Bang(x, y + 1);
6157         return;
6158       }
6159     }
6160     else if (((element == EL_SP_INFOTRON ||
6161                element == EL_SP_ZONK) &&
6162               (smashed == EL_SP_SNIKSNAK ||
6163                smashed == EL_SP_ELECTRON ||
6164                smashed == EL_SP_DISK_ORANGE)) ||
6165              (element == EL_SP_INFOTRON &&
6166               smashed == EL_SP_DISK_YELLOW))
6167     {
6168       Bang(x, y + 1);
6169       return;
6170     }
6171     else if (CAN_SMASH_EVERYTHING(element))
6172     {
6173       if (IS_CLASSIC_ENEMY(smashed) ||
6174           CAN_EXPLODE_SMASHED(smashed))
6175       {
6176         Bang(x, y + 1);
6177         return;
6178       }
6179       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6180       {
6181         if (smashed == EL_LAMP ||
6182             smashed == EL_LAMP_ACTIVE)
6183         {
6184           Bang(x, y + 1);
6185           return;
6186         }
6187         else if (smashed == EL_NUT)
6188         {
6189           Feld[x][y + 1] = EL_NUT_BREAKING;
6190           PlayLevelSound(x, y, SND_NUT_BREAKING);
6191           RaiseScoreElement(EL_NUT);
6192           return;
6193         }
6194         else if (smashed == EL_PEARL)
6195         {
6196           ResetGfxAnimation(x, y);
6197
6198           Feld[x][y + 1] = EL_PEARL_BREAKING;
6199           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6200           return;
6201         }
6202         else if (smashed == EL_DIAMOND)
6203         {
6204           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6205           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6206           return;
6207         }
6208         else if (IS_BELT_SWITCH(smashed))
6209         {
6210           ToggleBeltSwitch(x, y + 1);
6211         }
6212         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6213                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6214                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6215                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6216         {
6217           ToggleSwitchgateSwitch(x, y + 1);
6218         }
6219         else if (smashed == EL_LIGHT_SWITCH ||
6220                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6221         {
6222           ToggleLightSwitch(x, y + 1);
6223         }
6224         else
6225         {
6226           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6227
6228           CheckElementChangeBySide(x, y + 1, smashed, element,
6229                                    CE_SWITCHED, CH_SIDE_TOP);
6230           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6231                                             CH_SIDE_TOP);
6232         }
6233       }
6234       else
6235       {
6236         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6237       }
6238     }
6239   }
6240
6241   /* play sound of magic wall / mill */
6242   if (!last_line &&
6243       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6244        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6245        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6246   {
6247     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6248       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6249     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6250       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6251     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6252       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6253
6254     return;
6255   }
6256
6257   /* play sound of object that hits the ground */
6258   if (last_line || object_hit)
6259     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6260 }
6261
6262 inline static void TurnRoundExt(int x, int y)
6263 {
6264   static struct
6265   {
6266     int dx, dy;
6267   } move_xy[] =
6268   {
6269     {  0,  0 },
6270     { -1,  0 },
6271     { +1,  0 },
6272     {  0,  0 },
6273     {  0, -1 },
6274     {  0,  0 }, { 0, 0 }, { 0, 0 },
6275     {  0, +1 }
6276   };
6277   static struct
6278   {
6279     int left, right, back;
6280   } turn[] =
6281   {
6282     { 0,        0,              0        },
6283     { MV_DOWN,  MV_UP,          MV_RIGHT },
6284     { MV_UP,    MV_DOWN,        MV_LEFT  },
6285     { 0,        0,              0        },
6286     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6287     { 0,        0,              0        },
6288     { 0,        0,              0        },
6289     { 0,        0,              0        },
6290     { MV_RIGHT, MV_LEFT,        MV_UP    }
6291   };
6292
6293   int element = Feld[x][y];
6294   int move_pattern = element_info[element].move_pattern;
6295
6296   int old_move_dir = MovDir[x][y];
6297   int left_dir  = turn[old_move_dir].left;
6298   int right_dir = turn[old_move_dir].right;
6299   int back_dir  = turn[old_move_dir].back;
6300
6301   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6302   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6303   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6304   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6305
6306   int left_x  = x + left_dx,  left_y  = y + left_dy;
6307   int right_x = x + right_dx, right_y = y + right_dy;
6308   int move_x  = x + move_dx,  move_y  = y + move_dy;
6309
6310   int xx, yy;
6311
6312   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6313   {
6314     TestIfBadThingTouchesOtherBadThing(x, y);
6315
6316     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6317       MovDir[x][y] = right_dir;
6318     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6319       MovDir[x][y] = left_dir;
6320
6321     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6322       MovDelay[x][y] = 9;
6323     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6324       MovDelay[x][y] = 1;
6325   }
6326   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6327   {
6328     TestIfBadThingTouchesOtherBadThing(x, y);
6329
6330     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6331       MovDir[x][y] = left_dir;
6332     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6333       MovDir[x][y] = right_dir;
6334
6335     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6336       MovDelay[x][y] = 9;
6337     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6338       MovDelay[x][y] = 1;
6339   }
6340   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6341   {
6342     TestIfBadThingTouchesOtherBadThing(x, y);
6343
6344     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6345       MovDir[x][y] = left_dir;
6346     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6347       MovDir[x][y] = right_dir;
6348
6349     if (MovDir[x][y] != old_move_dir)
6350       MovDelay[x][y] = 9;
6351   }
6352   else if (element == EL_YAMYAM)
6353   {
6354     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6355     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6356
6357     if (can_turn_left && can_turn_right)
6358       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6359     else if (can_turn_left)
6360       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6361     else if (can_turn_right)
6362       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6363     else
6364       MovDir[x][y] = back_dir;
6365
6366     MovDelay[x][y] = 16 + 16 * RND(3);
6367   }
6368   else if (element == EL_DARK_YAMYAM)
6369   {
6370     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6371                                                          left_x, left_y);
6372     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6373                                                          right_x, right_y);
6374
6375     if (can_turn_left && can_turn_right)
6376       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6377     else if (can_turn_left)
6378       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6379     else if (can_turn_right)
6380       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6381     else
6382       MovDir[x][y] = back_dir;
6383
6384     MovDelay[x][y] = 16 + 16 * RND(3);
6385   }
6386   else if (element == EL_PACMAN)
6387   {
6388     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6389     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6390
6391     if (can_turn_left && can_turn_right)
6392       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6393     else if (can_turn_left)
6394       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6395     else if (can_turn_right)
6396       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6397     else
6398       MovDir[x][y] = back_dir;
6399
6400     MovDelay[x][y] = 6 + RND(40);
6401   }
6402   else if (element == EL_PIG)
6403   {
6404     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6405     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6406     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6407     boolean should_turn_left, should_turn_right, should_move_on;
6408     int rnd_value = 24;
6409     int rnd = RND(rnd_value);
6410
6411     should_turn_left = (can_turn_left &&
6412                         (!can_move_on ||
6413                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6414                                                    y + back_dy + left_dy)));
6415     should_turn_right = (can_turn_right &&
6416                          (!can_move_on ||
6417                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6418                                                     y + back_dy + right_dy)));
6419     should_move_on = (can_move_on &&
6420                       (!can_turn_left ||
6421                        !can_turn_right ||
6422                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6423                                                  y + move_dy + left_dy) ||
6424                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6425                                                  y + move_dy + right_dy)));
6426
6427     if (should_turn_left || should_turn_right || should_move_on)
6428     {
6429       if (should_turn_left && should_turn_right && should_move_on)
6430         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6431                         rnd < 2 * rnd_value / 3 ? right_dir :
6432                         old_move_dir);
6433       else if (should_turn_left && should_turn_right)
6434         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6435       else if (should_turn_left && should_move_on)
6436         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6437       else if (should_turn_right && should_move_on)
6438         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6439       else if (should_turn_left)
6440         MovDir[x][y] = left_dir;
6441       else if (should_turn_right)
6442         MovDir[x][y] = right_dir;
6443       else if (should_move_on)
6444         MovDir[x][y] = old_move_dir;
6445     }
6446     else if (can_move_on && rnd > rnd_value / 8)
6447       MovDir[x][y] = old_move_dir;
6448     else if (can_turn_left && can_turn_right)
6449       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6450     else if (can_turn_left && rnd > rnd_value / 8)
6451       MovDir[x][y] = left_dir;
6452     else if (can_turn_right && rnd > rnd_value/8)
6453       MovDir[x][y] = right_dir;
6454     else
6455       MovDir[x][y] = back_dir;
6456
6457     xx = x + move_xy[MovDir[x][y]].dx;
6458     yy = y + move_xy[MovDir[x][y]].dy;
6459
6460     if (!IN_LEV_FIELD(xx, yy) ||
6461         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6462       MovDir[x][y] = old_move_dir;
6463
6464     MovDelay[x][y] = 0;
6465   }
6466   else if (element == EL_DRAGON)
6467   {
6468     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6469     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6470     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6471     int rnd_value = 24;
6472     int rnd = RND(rnd_value);
6473
6474     if (can_move_on && rnd > rnd_value / 8)
6475       MovDir[x][y] = old_move_dir;
6476     else if (can_turn_left && can_turn_right)
6477       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6478     else if (can_turn_left && rnd > rnd_value / 8)
6479       MovDir[x][y] = left_dir;
6480     else if (can_turn_right && rnd > rnd_value / 8)
6481       MovDir[x][y] = right_dir;
6482     else
6483       MovDir[x][y] = back_dir;
6484
6485     xx = x + move_xy[MovDir[x][y]].dx;
6486     yy = y + move_xy[MovDir[x][y]].dy;
6487
6488     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6489       MovDir[x][y] = old_move_dir;
6490
6491     MovDelay[x][y] = 0;
6492   }
6493   else if (element == EL_MOLE)
6494   {
6495     boolean can_move_on =
6496       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6497                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6498                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6499     if (!can_move_on)
6500     {
6501       boolean can_turn_left =
6502         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6503                               IS_AMOEBOID(Feld[left_x][left_y])));
6504
6505       boolean can_turn_right =
6506         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6507                               IS_AMOEBOID(Feld[right_x][right_y])));
6508
6509       if (can_turn_left && can_turn_right)
6510         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6511       else if (can_turn_left)
6512         MovDir[x][y] = left_dir;
6513       else
6514         MovDir[x][y] = right_dir;
6515     }
6516
6517     if (MovDir[x][y] != old_move_dir)
6518       MovDelay[x][y] = 9;
6519   }
6520   else if (element == EL_BALLOON)
6521   {
6522     MovDir[x][y] = game.wind_direction;
6523     MovDelay[x][y] = 0;
6524   }
6525   else if (element == EL_SPRING)
6526   {
6527     if (MovDir[x][y] & MV_HORIZONTAL)
6528     {
6529       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6530           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6531       {
6532         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6533         ResetGfxAnimation(move_x, move_y);
6534         TEST_DrawLevelField(move_x, move_y);
6535
6536         MovDir[x][y] = back_dir;
6537       }
6538       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6539                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6540         MovDir[x][y] = MV_NONE;
6541     }
6542
6543     MovDelay[x][y] = 0;
6544   }
6545   else if (element == EL_ROBOT ||
6546            element == EL_SATELLITE ||
6547            element == EL_PENGUIN ||
6548            element == EL_EMC_ANDROID)
6549   {
6550     int attr_x = -1, attr_y = -1;
6551
6552     if (AllPlayersGone)
6553     {
6554       attr_x = ExitX;
6555       attr_y = ExitY;
6556     }
6557     else
6558     {
6559       int i;
6560
6561       for (i = 0; i < MAX_PLAYERS; i++)
6562       {
6563         struct PlayerInfo *player = &stored_player[i];
6564         int jx = player->jx, jy = player->jy;
6565
6566         if (!player->active)
6567           continue;
6568
6569         if (attr_x == -1 ||
6570             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6571         {
6572           attr_x = jx;
6573           attr_y = jy;
6574         }
6575       }
6576     }
6577
6578     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6579         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6580          game.engine_version < VERSION_IDENT(3,1,0,0)))
6581     {
6582       attr_x = ZX;
6583       attr_y = ZY;
6584     }
6585
6586     if (element == EL_PENGUIN)
6587     {
6588       int i;
6589       static int xy[4][2] =
6590       {
6591         { 0, -1 },
6592         { -1, 0 },
6593         { +1, 0 },
6594         { 0, +1 }
6595       };
6596
6597       for (i = 0; i < NUM_DIRECTIONS; i++)
6598       {
6599         int ex = x + xy[i][0];
6600         int ey = y + xy[i][1];
6601
6602         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6603                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6604                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6605                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6606         {
6607           attr_x = ex;
6608           attr_y = ey;
6609           break;
6610         }
6611       }
6612     }
6613
6614     MovDir[x][y] = MV_NONE;
6615     if (attr_x < x)
6616       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6617     else if (attr_x > x)
6618       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6619     if (attr_y < y)
6620       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6621     else if (attr_y > y)
6622       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6623
6624     if (element == EL_ROBOT)
6625     {
6626       int newx, newy;
6627
6628       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6629         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6630       Moving2Blocked(x, y, &newx, &newy);
6631
6632       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6633         MovDelay[x][y] = 8 + 8 * !RND(3);
6634       else
6635         MovDelay[x][y] = 16;
6636     }
6637     else if (element == EL_PENGUIN)
6638     {
6639       int newx, newy;
6640
6641       MovDelay[x][y] = 1;
6642
6643       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6644       {
6645         boolean first_horiz = RND(2);
6646         int new_move_dir = MovDir[x][y];
6647
6648         MovDir[x][y] =
6649           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6650         Moving2Blocked(x, y, &newx, &newy);
6651
6652         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6653           return;
6654
6655         MovDir[x][y] =
6656           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6657         Moving2Blocked(x, y, &newx, &newy);
6658
6659         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6660           return;
6661
6662         MovDir[x][y] = old_move_dir;
6663         return;
6664       }
6665     }
6666     else if (element == EL_SATELLITE)
6667     {
6668       int newx, newy;
6669
6670       MovDelay[x][y] = 1;
6671
6672       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6673       {
6674         boolean first_horiz = RND(2);
6675         int new_move_dir = MovDir[x][y];
6676
6677         MovDir[x][y] =
6678           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6679         Moving2Blocked(x, y, &newx, &newy);
6680
6681         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6682           return;
6683
6684         MovDir[x][y] =
6685           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6686         Moving2Blocked(x, y, &newx, &newy);
6687
6688         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6689           return;
6690
6691         MovDir[x][y] = old_move_dir;
6692         return;
6693       }
6694     }
6695     else if (element == EL_EMC_ANDROID)
6696     {
6697       static int check_pos[16] =
6698       {
6699         -1,             /*  0 => (invalid)          */
6700         7,              /*  1 => MV_LEFT            */
6701         3,              /*  2 => MV_RIGHT           */
6702         -1,             /*  3 => (invalid)          */
6703         1,              /*  4 =>            MV_UP   */
6704         0,              /*  5 => MV_LEFT  | MV_UP   */
6705         2,              /*  6 => MV_RIGHT | MV_UP   */
6706         -1,             /*  7 => (invalid)          */
6707         5,              /*  8 =>            MV_DOWN */
6708         6,              /*  9 => MV_LEFT  | MV_DOWN */
6709         4,              /* 10 => MV_RIGHT | MV_DOWN */
6710         -1,             /* 11 => (invalid)          */
6711         -1,             /* 12 => (invalid)          */
6712         -1,             /* 13 => (invalid)          */
6713         -1,             /* 14 => (invalid)          */
6714         -1,             /* 15 => (invalid)          */
6715       };
6716       static struct
6717       {
6718         int dx, dy;
6719         int dir;
6720       } check_xy[8] =
6721       {
6722         { -1, -1,       MV_LEFT  | MV_UP   },
6723         {  0, -1,                  MV_UP   },
6724         { +1, -1,       MV_RIGHT | MV_UP   },
6725         { +1,  0,       MV_RIGHT           },
6726         { +1, +1,       MV_RIGHT | MV_DOWN },
6727         {  0, +1,                  MV_DOWN },
6728         { -1, +1,       MV_LEFT  | MV_DOWN },
6729         { -1,  0,       MV_LEFT            },
6730       };
6731       int start_pos, check_order;
6732       boolean can_clone = FALSE;
6733       int i;
6734
6735       /* check if there is any free field around current position */
6736       for (i = 0; i < 8; i++)
6737       {
6738         int newx = x + check_xy[i].dx;
6739         int newy = y + check_xy[i].dy;
6740
6741         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6742         {
6743           can_clone = TRUE;
6744
6745           break;
6746         }
6747       }
6748
6749       if (can_clone)            /* randomly find an element to clone */
6750       {
6751         can_clone = FALSE;
6752
6753         start_pos = check_pos[RND(8)];
6754         check_order = (RND(2) ? -1 : +1);
6755
6756         for (i = 0; i < 8; i++)
6757         {
6758           int pos_raw = start_pos + i * check_order;
6759           int pos = (pos_raw + 8) % 8;
6760           int newx = x + check_xy[pos].dx;
6761           int newy = y + check_xy[pos].dy;
6762
6763           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6764           {
6765             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6766             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6767
6768             Store[x][y] = Feld[newx][newy];
6769
6770             can_clone = TRUE;
6771
6772             break;
6773           }
6774         }
6775       }
6776
6777       if (can_clone)            /* randomly find a direction to move */
6778       {
6779         can_clone = FALSE;
6780
6781         start_pos = check_pos[RND(8)];
6782         check_order = (RND(2) ? -1 : +1);
6783
6784         for (i = 0; i < 8; i++)
6785         {
6786           int pos_raw = start_pos + i * check_order;
6787           int pos = (pos_raw + 8) % 8;
6788           int newx = x + check_xy[pos].dx;
6789           int newy = y + check_xy[pos].dy;
6790           int new_move_dir = check_xy[pos].dir;
6791
6792           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6793           {
6794             MovDir[x][y] = new_move_dir;
6795             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6796
6797             can_clone = TRUE;
6798
6799             break;
6800           }
6801         }
6802       }
6803
6804       if (can_clone)            /* cloning and moving successful */
6805         return;
6806
6807       /* cannot clone -- try to move towards player */
6808
6809       start_pos = check_pos[MovDir[x][y] & 0x0f];
6810       check_order = (RND(2) ? -1 : +1);
6811
6812       for (i = 0; i < 3; i++)
6813       {
6814         /* first check start_pos, then previous/next or (next/previous) pos */
6815         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6816         int pos = (pos_raw + 8) % 8;
6817         int newx = x + check_xy[pos].dx;
6818         int newy = y + check_xy[pos].dy;
6819         int new_move_dir = check_xy[pos].dir;
6820
6821         if (IS_PLAYER(newx, newy))
6822           break;
6823
6824         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6825         {
6826           MovDir[x][y] = new_move_dir;
6827           MovDelay[x][y] = level.android_move_time * 8 + 1;
6828
6829           break;
6830         }
6831       }
6832     }
6833   }
6834   else if (move_pattern == MV_TURNING_LEFT ||
6835            move_pattern == MV_TURNING_RIGHT ||
6836            move_pattern == MV_TURNING_LEFT_RIGHT ||
6837            move_pattern == MV_TURNING_RIGHT_LEFT ||
6838            move_pattern == MV_TURNING_RANDOM ||
6839            move_pattern == MV_ALL_DIRECTIONS)
6840   {
6841     boolean can_turn_left =
6842       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6843     boolean can_turn_right =
6844       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6845
6846     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6847       return;
6848
6849     if (move_pattern == MV_TURNING_LEFT)
6850       MovDir[x][y] = left_dir;
6851     else if (move_pattern == MV_TURNING_RIGHT)
6852       MovDir[x][y] = right_dir;
6853     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6854       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6855     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6856       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6857     else if (move_pattern == MV_TURNING_RANDOM)
6858       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6859                       can_turn_right && !can_turn_left ? right_dir :
6860                       RND(2) ? left_dir : right_dir);
6861     else if (can_turn_left && can_turn_right)
6862       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6863     else if (can_turn_left)
6864       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6865     else if (can_turn_right)
6866       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6867     else
6868       MovDir[x][y] = back_dir;
6869
6870     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6871   }
6872   else if (move_pattern == MV_HORIZONTAL ||
6873            move_pattern == MV_VERTICAL)
6874   {
6875     if (move_pattern & old_move_dir)
6876       MovDir[x][y] = back_dir;
6877     else if (move_pattern == MV_HORIZONTAL)
6878       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6879     else if (move_pattern == MV_VERTICAL)
6880       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6881
6882     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6883   }
6884   else if (move_pattern & MV_ANY_DIRECTION)
6885   {
6886     MovDir[x][y] = move_pattern;
6887     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6888   }
6889   else if (move_pattern & MV_WIND_DIRECTION)
6890   {
6891     MovDir[x][y] = game.wind_direction;
6892     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6893   }
6894   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6895   {
6896     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6897       MovDir[x][y] = left_dir;
6898     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6899       MovDir[x][y] = right_dir;
6900
6901     if (MovDir[x][y] != old_move_dir)
6902       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6903   }
6904   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6905   {
6906     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6907       MovDir[x][y] = right_dir;
6908     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6909       MovDir[x][y] = left_dir;
6910
6911     if (MovDir[x][y] != old_move_dir)
6912       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6913   }
6914   else if (move_pattern == MV_TOWARDS_PLAYER ||
6915            move_pattern == MV_AWAY_FROM_PLAYER)
6916   {
6917     int attr_x = -1, attr_y = -1;
6918     int newx, newy;
6919     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6920
6921     if (AllPlayersGone)
6922     {
6923       attr_x = ExitX;
6924       attr_y = ExitY;
6925     }
6926     else
6927     {
6928       int i;
6929
6930       for (i = 0; i < MAX_PLAYERS; i++)
6931       {
6932         struct PlayerInfo *player = &stored_player[i];
6933         int jx = player->jx, jy = player->jy;
6934
6935         if (!player->active)
6936           continue;
6937
6938         if (attr_x == -1 ||
6939             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6940         {
6941           attr_x = jx;
6942           attr_y = jy;
6943         }
6944       }
6945     }
6946
6947     MovDir[x][y] = MV_NONE;
6948     if (attr_x < x)
6949       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6950     else if (attr_x > x)
6951       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6952     if (attr_y < y)
6953       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6954     else if (attr_y > y)
6955       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6956
6957     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6958
6959     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6960     {
6961       boolean first_horiz = RND(2);
6962       int new_move_dir = MovDir[x][y];
6963
6964       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6965       {
6966         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6967         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6968
6969         return;
6970       }
6971
6972       MovDir[x][y] =
6973         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6974       Moving2Blocked(x, y, &newx, &newy);
6975
6976       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6977         return;
6978
6979       MovDir[x][y] =
6980         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6981       Moving2Blocked(x, y, &newx, &newy);
6982
6983       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6984         return;
6985
6986       MovDir[x][y] = old_move_dir;
6987     }
6988   }
6989   else if (move_pattern == MV_WHEN_PUSHED ||
6990            move_pattern == MV_WHEN_DROPPED)
6991   {
6992     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6993       MovDir[x][y] = MV_NONE;
6994
6995     MovDelay[x][y] = 0;
6996   }
6997   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6998   {
6999     static int test_xy[7][2] =
7000     {
7001       { 0, -1 },
7002       { -1, 0 },
7003       { +1, 0 },
7004       { 0, +1 },
7005       { 0, -1 },
7006       { -1, 0 },
7007       { +1, 0 },
7008     };
7009     static int test_dir[7] =
7010     {
7011       MV_UP,
7012       MV_LEFT,
7013       MV_RIGHT,
7014       MV_DOWN,
7015       MV_UP,
7016       MV_LEFT,
7017       MV_RIGHT,
7018     };
7019     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7020     int move_preference = -1000000;     /* start with very low preference */
7021     int new_move_dir = MV_NONE;
7022     int start_test = RND(4);
7023     int i;
7024
7025     for (i = 0; i < NUM_DIRECTIONS; i++)
7026     {
7027       int move_dir = test_dir[start_test + i];
7028       int move_dir_preference;
7029
7030       xx = x + test_xy[start_test + i][0];
7031       yy = y + test_xy[start_test + i][1];
7032
7033       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7034           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7035       {
7036         new_move_dir = move_dir;
7037
7038         break;
7039       }
7040
7041       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7042         continue;
7043
7044       move_dir_preference = -1 * RunnerVisit[xx][yy];
7045       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7046         move_dir_preference = PlayerVisit[xx][yy];
7047
7048       if (move_dir_preference > move_preference)
7049       {
7050         /* prefer field that has not been visited for the longest time */
7051         move_preference = move_dir_preference;
7052         new_move_dir = move_dir;
7053       }
7054       else if (move_dir_preference == move_preference &&
7055                move_dir == old_move_dir)
7056       {
7057         /* prefer last direction when all directions are preferred equally */
7058         move_preference = move_dir_preference;
7059         new_move_dir = move_dir;
7060       }
7061     }
7062
7063     MovDir[x][y] = new_move_dir;
7064     if (old_move_dir != new_move_dir)
7065       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7066   }
7067 }
7068
7069 static void TurnRound(int x, int y)
7070 {
7071   int direction = MovDir[x][y];
7072
7073   TurnRoundExt(x, y);
7074
7075   GfxDir[x][y] = MovDir[x][y];
7076
7077   if (direction != MovDir[x][y])
7078     GfxFrame[x][y] = 0;
7079
7080   if (MovDelay[x][y])
7081     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7082
7083   ResetGfxFrame(x, y, FALSE);
7084 }
7085
7086 static boolean JustBeingPushed(int x, int y)
7087 {
7088   int i;
7089
7090   for (i = 0; i < MAX_PLAYERS; i++)
7091   {
7092     struct PlayerInfo *player = &stored_player[i];
7093
7094     if (player->active && player->is_pushing && player->MovPos)
7095     {
7096       int next_jx = player->jx + (player->jx - player->last_jx);
7097       int next_jy = player->jy + (player->jy - player->last_jy);
7098
7099       if (x == next_jx && y == next_jy)
7100         return TRUE;
7101     }
7102   }
7103
7104   return FALSE;
7105 }
7106
7107 void StartMoving(int x, int y)
7108 {
7109   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7110   int element = Feld[x][y];
7111
7112   if (Stop[x][y])
7113     return;
7114
7115   if (MovDelay[x][y] == 0)
7116     GfxAction[x][y] = ACTION_DEFAULT;
7117
7118   if (CAN_FALL(element) && y < lev_fieldy - 1)
7119   {
7120     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7121         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7122       if (JustBeingPushed(x, y))
7123         return;
7124
7125     if (element == EL_QUICKSAND_FULL)
7126     {
7127       if (IS_FREE(x, y + 1))
7128       {
7129         InitMovingField(x, y, MV_DOWN);
7130         started_moving = TRUE;
7131
7132         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7133 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7134         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7135           Store[x][y] = EL_ROCK;
7136 #else
7137         Store[x][y] = EL_ROCK;
7138 #endif
7139
7140         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7141       }
7142       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7143       {
7144         if (!MovDelay[x][y])
7145         {
7146           MovDelay[x][y] = TILEY + 1;
7147
7148           ResetGfxAnimation(x, y);
7149           ResetGfxAnimation(x, y + 1);
7150         }
7151
7152         if (MovDelay[x][y])
7153         {
7154           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7155           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7156
7157           MovDelay[x][y]--;
7158           if (MovDelay[x][y])
7159             return;
7160         }
7161
7162         Feld[x][y] = EL_QUICKSAND_EMPTY;
7163         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7164         Store[x][y + 1] = Store[x][y];
7165         Store[x][y] = 0;
7166
7167         PlayLevelSoundAction(x, y, ACTION_FILLING);
7168       }
7169       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7170       {
7171         if (!MovDelay[x][y])
7172         {
7173           MovDelay[x][y] = TILEY + 1;
7174
7175           ResetGfxAnimation(x, y);
7176           ResetGfxAnimation(x, y + 1);
7177         }
7178
7179         if (MovDelay[x][y])
7180         {
7181           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7182           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7183
7184           MovDelay[x][y]--;
7185           if (MovDelay[x][y])
7186             return;
7187         }
7188
7189         Feld[x][y] = EL_QUICKSAND_EMPTY;
7190         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7191         Store[x][y + 1] = Store[x][y];
7192         Store[x][y] = 0;
7193
7194         PlayLevelSoundAction(x, y, ACTION_FILLING);
7195       }
7196     }
7197     else if (element == EL_QUICKSAND_FAST_FULL)
7198     {
7199       if (IS_FREE(x, y + 1))
7200       {
7201         InitMovingField(x, y, MV_DOWN);
7202         started_moving = TRUE;
7203
7204         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7205 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7206         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7207           Store[x][y] = EL_ROCK;
7208 #else
7209         Store[x][y] = EL_ROCK;
7210 #endif
7211
7212         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7213       }
7214       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7215       {
7216         if (!MovDelay[x][y])
7217         {
7218           MovDelay[x][y] = TILEY + 1;
7219
7220           ResetGfxAnimation(x, y);
7221           ResetGfxAnimation(x, y + 1);
7222         }
7223
7224         if (MovDelay[x][y])
7225         {
7226           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7227           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7228
7229           MovDelay[x][y]--;
7230           if (MovDelay[x][y])
7231             return;
7232         }
7233
7234         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7235         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7236         Store[x][y + 1] = Store[x][y];
7237         Store[x][y] = 0;
7238
7239         PlayLevelSoundAction(x, y, ACTION_FILLING);
7240       }
7241       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7242       {
7243         if (!MovDelay[x][y])
7244         {
7245           MovDelay[x][y] = TILEY + 1;
7246
7247           ResetGfxAnimation(x, y);
7248           ResetGfxAnimation(x, y + 1);
7249         }
7250
7251         if (MovDelay[x][y])
7252         {
7253           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7254           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7255
7256           MovDelay[x][y]--;
7257           if (MovDelay[x][y])
7258             return;
7259         }
7260
7261         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7262         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7263         Store[x][y + 1] = Store[x][y];
7264         Store[x][y] = 0;
7265
7266         PlayLevelSoundAction(x, y, ACTION_FILLING);
7267       }
7268     }
7269     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7270              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7271     {
7272       InitMovingField(x, y, MV_DOWN);
7273       started_moving = TRUE;
7274
7275       Feld[x][y] = EL_QUICKSAND_FILLING;
7276       Store[x][y] = element;
7277
7278       PlayLevelSoundAction(x, y, ACTION_FILLING);
7279     }
7280     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7281              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7282     {
7283       InitMovingField(x, y, MV_DOWN);
7284       started_moving = TRUE;
7285
7286       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7287       Store[x][y] = element;
7288
7289       PlayLevelSoundAction(x, y, ACTION_FILLING);
7290     }
7291     else if (element == EL_MAGIC_WALL_FULL)
7292     {
7293       if (IS_FREE(x, y + 1))
7294       {
7295         InitMovingField(x, y, MV_DOWN);
7296         started_moving = TRUE;
7297
7298         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7299         Store[x][y] = EL_CHANGED(Store[x][y]);
7300       }
7301       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7302       {
7303         if (!MovDelay[x][y])
7304           MovDelay[x][y] = TILEY / 4 + 1;
7305
7306         if (MovDelay[x][y])
7307         {
7308           MovDelay[x][y]--;
7309           if (MovDelay[x][y])
7310             return;
7311         }
7312
7313         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7314         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7315         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7316         Store[x][y] = 0;
7317       }
7318     }
7319     else if (element == EL_BD_MAGIC_WALL_FULL)
7320     {
7321       if (IS_FREE(x, y + 1))
7322       {
7323         InitMovingField(x, y, MV_DOWN);
7324         started_moving = TRUE;
7325
7326         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7327         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7328       }
7329       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7330       {
7331         if (!MovDelay[x][y])
7332           MovDelay[x][y] = TILEY / 4 + 1;
7333
7334         if (MovDelay[x][y])
7335         {
7336           MovDelay[x][y]--;
7337           if (MovDelay[x][y])
7338             return;
7339         }
7340
7341         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7342         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7343         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7344         Store[x][y] = 0;
7345       }
7346     }
7347     else if (element == EL_DC_MAGIC_WALL_FULL)
7348     {
7349       if (IS_FREE(x, y + 1))
7350       {
7351         InitMovingField(x, y, MV_DOWN);
7352         started_moving = TRUE;
7353
7354         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7355         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7356       }
7357       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7358       {
7359         if (!MovDelay[x][y])
7360           MovDelay[x][y] = TILEY / 4 + 1;
7361
7362         if (MovDelay[x][y])
7363         {
7364           MovDelay[x][y]--;
7365           if (MovDelay[x][y])
7366             return;
7367         }
7368
7369         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7370         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7371         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7372         Store[x][y] = 0;
7373       }
7374     }
7375     else if ((CAN_PASS_MAGIC_WALL(element) &&
7376               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7377                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7378              (CAN_PASS_DC_MAGIC_WALL(element) &&
7379               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7380
7381     {
7382       InitMovingField(x, y, MV_DOWN);
7383       started_moving = TRUE;
7384
7385       Feld[x][y] =
7386         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7387          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7388          EL_DC_MAGIC_WALL_FILLING);
7389       Store[x][y] = element;
7390     }
7391     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7392     {
7393       SplashAcid(x, y + 1);
7394
7395       InitMovingField(x, y, MV_DOWN);
7396       started_moving = TRUE;
7397
7398       Store[x][y] = EL_ACID;
7399     }
7400     else if (
7401              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7402               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7403              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7404               CAN_FALL(element) && WasJustFalling[x][y] &&
7405               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7406
7407              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7408               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7409               (Feld[x][y + 1] == EL_BLOCKED)))
7410     {
7411       /* this is needed for a special case not covered by calling "Impact()"
7412          from "ContinueMoving()": if an element moves to a tile directly below
7413          another element which was just falling on that tile (which was empty
7414          in the previous frame), the falling element above would just stop
7415          instead of smashing the element below (in previous version, the above
7416          element was just checked for "moving" instead of "falling", resulting
7417          in incorrect smashes caused by horizontal movement of the above
7418          element; also, the case of the player being the element to smash was
7419          simply not covered here... :-/ ) */
7420
7421       CheckCollision[x][y] = 0;
7422       CheckImpact[x][y] = 0;
7423
7424       Impact(x, y);
7425     }
7426     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7427     {
7428       if (MovDir[x][y] == MV_NONE)
7429       {
7430         InitMovingField(x, y, MV_DOWN);
7431         started_moving = TRUE;
7432       }
7433     }
7434     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7435     {
7436       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7437         MovDir[x][y] = MV_DOWN;
7438
7439       InitMovingField(x, y, MV_DOWN);
7440       started_moving = TRUE;
7441     }
7442     else if (element == EL_AMOEBA_DROP)
7443     {
7444       Feld[x][y] = EL_AMOEBA_GROWING;
7445       Store[x][y] = EL_AMOEBA_WET;
7446     }
7447     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7448               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7449              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7450              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7451     {
7452       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7453                                 (IS_FREE(x - 1, y + 1) ||
7454                                  Feld[x - 1][y + 1] == EL_ACID));
7455       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7456                                 (IS_FREE(x + 1, y + 1) ||
7457                                  Feld[x + 1][y + 1] == EL_ACID));
7458       boolean can_fall_any  = (can_fall_left || can_fall_right);
7459       boolean can_fall_both = (can_fall_left && can_fall_right);
7460       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7461
7462       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7463       {
7464         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7465           can_fall_right = FALSE;
7466         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7467           can_fall_left = FALSE;
7468         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7469           can_fall_right = FALSE;
7470         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7471           can_fall_left = FALSE;
7472
7473         can_fall_any  = (can_fall_left || can_fall_right);
7474         can_fall_both = FALSE;
7475       }
7476
7477       if (can_fall_both)
7478       {
7479         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7480           can_fall_right = FALSE;       /* slip down on left side */
7481         else
7482           can_fall_left = !(can_fall_right = RND(2));
7483
7484         can_fall_both = FALSE;
7485       }
7486
7487       if (can_fall_any)
7488       {
7489         /* if not determined otherwise, prefer left side for slipping down */
7490         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7491         started_moving = TRUE;
7492       }
7493     }
7494     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7495     {
7496       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7497       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7498       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7499       int belt_dir = game.belt_dir[belt_nr];
7500
7501       if ((belt_dir == MV_LEFT  && left_is_free) ||
7502           (belt_dir == MV_RIGHT && right_is_free))
7503       {
7504         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7505
7506         InitMovingField(x, y, belt_dir);
7507         started_moving = TRUE;
7508
7509         Pushed[x][y] = TRUE;
7510         Pushed[nextx][y] = TRUE;
7511
7512         GfxAction[x][y] = ACTION_DEFAULT;
7513       }
7514       else
7515       {
7516         MovDir[x][y] = 0;       /* if element was moving, stop it */
7517       }
7518     }
7519   }
7520
7521   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7522   if (CAN_MOVE(element) && !started_moving)
7523   {
7524     int move_pattern = element_info[element].move_pattern;
7525     int newx, newy;
7526
7527     Moving2Blocked(x, y, &newx, &newy);
7528
7529     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7530       return;
7531
7532     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7533         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7534     {
7535       WasJustMoving[x][y] = 0;
7536       CheckCollision[x][y] = 0;
7537
7538       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7539
7540       if (Feld[x][y] != element)        /* element has changed */
7541         return;
7542     }
7543
7544     if (!MovDelay[x][y])        /* start new movement phase */
7545     {
7546       /* all objects that can change their move direction after each step
7547          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7548
7549       if (element != EL_YAMYAM &&
7550           element != EL_DARK_YAMYAM &&
7551           element != EL_PACMAN &&
7552           !(move_pattern & MV_ANY_DIRECTION) &&
7553           move_pattern != MV_TURNING_LEFT &&
7554           move_pattern != MV_TURNING_RIGHT &&
7555           move_pattern != MV_TURNING_LEFT_RIGHT &&
7556           move_pattern != MV_TURNING_RIGHT_LEFT &&
7557           move_pattern != MV_TURNING_RANDOM)
7558       {
7559         TurnRound(x, y);
7560
7561         if (MovDelay[x][y] && (element == EL_BUG ||
7562                                element == EL_SPACESHIP ||
7563                                element == EL_SP_SNIKSNAK ||
7564                                element == EL_SP_ELECTRON ||
7565                                element == EL_MOLE))
7566           TEST_DrawLevelField(x, y);
7567       }
7568     }
7569
7570     if (MovDelay[x][y])         /* wait some time before next movement */
7571     {
7572       MovDelay[x][y]--;
7573
7574       if (element == EL_ROBOT ||
7575           element == EL_YAMYAM ||
7576           element == EL_DARK_YAMYAM)
7577       {
7578         DrawLevelElementAnimationIfNeeded(x, y, element);
7579         PlayLevelSoundAction(x, y, ACTION_WAITING);
7580       }
7581       else if (element == EL_SP_ELECTRON)
7582         DrawLevelElementAnimationIfNeeded(x, y, element);
7583       else if (element == EL_DRAGON)
7584       {
7585         int i;
7586         int dir = MovDir[x][y];
7587         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7588         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7589         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7590                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7591                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7592                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7593         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7594
7595         GfxAction[x][y] = ACTION_ATTACKING;
7596
7597         if (IS_PLAYER(x, y))
7598           DrawPlayerField(x, y);
7599         else
7600           TEST_DrawLevelField(x, y);
7601
7602         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7603
7604         for (i = 1; i <= 3; i++)
7605         {
7606           int xx = x + i * dx;
7607           int yy = y + i * dy;
7608           int sx = SCREENX(xx);
7609           int sy = SCREENY(yy);
7610           int flame_graphic = graphic + (i - 1);
7611
7612           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7613             break;
7614
7615           if (MovDelay[x][y])
7616           {
7617             int flamed = MovingOrBlocked2Element(xx, yy);
7618
7619             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7620               Bang(xx, yy);
7621             else
7622               RemoveMovingField(xx, yy);
7623
7624             ChangeDelay[xx][yy] = 0;
7625
7626             Feld[xx][yy] = EL_FLAMES;
7627
7628             if (IN_SCR_FIELD(sx, sy))
7629             {
7630               TEST_DrawLevelFieldCrumbled(xx, yy);
7631               DrawGraphic(sx, sy, flame_graphic, frame);
7632             }
7633           }
7634           else
7635           {
7636             if (Feld[xx][yy] == EL_FLAMES)
7637               Feld[xx][yy] = EL_EMPTY;
7638             TEST_DrawLevelField(xx, yy);
7639           }
7640         }
7641       }
7642
7643       if (MovDelay[x][y])       /* element still has to wait some time */
7644       {
7645         PlayLevelSoundAction(x, y, ACTION_WAITING);
7646
7647         return;
7648       }
7649     }
7650
7651     /* now make next step */
7652
7653     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7654
7655     if (DONT_COLLIDE_WITH(element) &&
7656         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7657         !PLAYER_ENEMY_PROTECTED(newx, newy))
7658     {
7659       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7660
7661       return;
7662     }
7663
7664     else if (CAN_MOVE_INTO_ACID(element) &&
7665              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7666              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7667              (MovDir[x][y] == MV_DOWN ||
7668               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7669     {
7670       SplashAcid(newx, newy);
7671       Store[x][y] = EL_ACID;
7672     }
7673     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7674     {
7675       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7676           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7677           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7678           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7679       {
7680         RemoveField(x, y);
7681         TEST_DrawLevelField(x, y);
7682
7683         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7684         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7685           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7686
7687         local_player->friends_still_needed--;
7688         if (!local_player->friends_still_needed &&
7689             !local_player->GameOver && AllPlayersGone)
7690           PlayerWins(local_player);
7691
7692         return;
7693       }
7694       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7695       {
7696         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7697           TEST_DrawLevelField(newx, newy);
7698         else
7699           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7700       }
7701       else if (!IS_FREE(newx, newy))
7702       {
7703         GfxAction[x][y] = ACTION_WAITING;
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_PIG && IN_LEV_FIELD(newx, newy))
7714     {
7715       if (IS_FOOD_PIG(Feld[newx][newy]))
7716       {
7717         if (IS_MOVING(newx, newy))
7718           RemoveMovingField(newx, newy);
7719         else
7720         {
7721           Feld[newx][newy] = EL_EMPTY;
7722           TEST_DrawLevelField(newx, newy);
7723         }
7724
7725         PlayLevelSound(x, y, SND_PIG_DIGGING);
7726       }
7727       else if (!IS_FREE(newx, newy))
7728       {
7729         if (IS_PLAYER(x, y))
7730           DrawPlayerField(x, y);
7731         else
7732           TEST_DrawLevelField(x, y);
7733
7734         return;
7735       }
7736     }
7737     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7738     {
7739       if (Store[x][y] != EL_EMPTY)
7740       {
7741         boolean can_clone = FALSE;
7742         int xx, yy;
7743
7744         /* check if element to clone is still there */
7745         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7746         {
7747           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7748           {
7749             can_clone = TRUE;
7750
7751             break;
7752           }
7753         }
7754
7755         /* cannot clone or target field not free anymore -- do not clone */
7756         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7757           Store[x][y] = EL_EMPTY;
7758       }
7759
7760       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7761       {
7762         if (IS_MV_DIAGONAL(MovDir[x][y]))
7763         {
7764           int diagonal_move_dir = MovDir[x][y];
7765           int stored = Store[x][y];
7766           int change_delay = 8;
7767           int graphic;
7768
7769           /* android is moving diagonally */
7770
7771           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7772
7773           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7774           GfxElement[x][y] = EL_EMC_ANDROID;
7775           GfxAction[x][y] = ACTION_SHRINKING;
7776           GfxDir[x][y] = diagonal_move_dir;
7777           ChangeDelay[x][y] = change_delay;
7778
7779           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7780                                    GfxDir[x][y]);
7781
7782           DrawLevelGraphicAnimation(x, y, graphic);
7783           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7784
7785           if (Feld[newx][newy] == EL_ACID)
7786           {
7787             SplashAcid(newx, newy);
7788
7789             return;
7790           }
7791
7792           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7793
7794           Store[newx][newy] = EL_EMC_ANDROID;
7795           GfxElement[newx][newy] = EL_EMC_ANDROID;
7796           GfxAction[newx][newy] = ACTION_GROWING;
7797           GfxDir[newx][newy] = diagonal_move_dir;
7798           ChangeDelay[newx][newy] = change_delay;
7799
7800           graphic = el_act_dir2img(GfxElement[newx][newy],
7801                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7802
7803           DrawLevelGraphicAnimation(newx, newy, graphic);
7804           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7805
7806           return;
7807         }
7808         else
7809         {
7810           Feld[newx][newy] = EL_EMPTY;
7811           TEST_DrawLevelField(newx, newy);
7812
7813           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7814         }
7815       }
7816       else if (!IS_FREE(newx, newy))
7817       {
7818         return;
7819       }
7820     }
7821     else if (IS_CUSTOM_ELEMENT(element) &&
7822              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7823     {
7824       if (!DigFieldByCE(newx, newy, element))
7825         return;
7826
7827       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7828       {
7829         RunnerVisit[x][y] = FrameCounter;
7830         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7831       }
7832     }
7833     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7834     {
7835       if (!IS_FREE(newx, newy))
7836       {
7837         if (IS_PLAYER(x, y))
7838           DrawPlayerField(x, y);
7839         else
7840           TEST_DrawLevelField(x, y);
7841
7842         return;
7843       }
7844       else
7845       {
7846         boolean wanna_flame = !RND(10);
7847         int dx = newx - x, dy = newy - y;
7848         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7849         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7850         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7851                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7852         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7853                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7854
7855         if ((wanna_flame ||
7856              IS_CLASSIC_ENEMY(element1) ||
7857              IS_CLASSIC_ENEMY(element2)) &&
7858             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7859             element1 != EL_FLAMES && element2 != EL_FLAMES)
7860         {
7861           ResetGfxAnimation(x, y);
7862           GfxAction[x][y] = ACTION_ATTACKING;
7863
7864           if (IS_PLAYER(x, y))
7865             DrawPlayerField(x, y);
7866           else
7867             TEST_DrawLevelField(x, y);
7868
7869           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7870
7871           MovDelay[x][y] = 50;
7872
7873           Feld[newx][newy] = EL_FLAMES;
7874           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7875             Feld[newx1][newy1] = EL_FLAMES;
7876           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7877             Feld[newx2][newy2] = EL_FLAMES;
7878
7879           return;
7880         }
7881       }
7882     }
7883     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7884              Feld[newx][newy] == EL_DIAMOND)
7885     {
7886       if (IS_MOVING(newx, newy))
7887         RemoveMovingField(newx, newy);
7888       else
7889       {
7890         Feld[newx][newy] = EL_EMPTY;
7891         TEST_DrawLevelField(newx, newy);
7892       }
7893
7894       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7895     }
7896     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7897              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7898     {
7899       if (AmoebaNr[newx][newy])
7900       {
7901         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7902         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7903             Feld[newx][newy] == EL_BD_AMOEBA)
7904           AmoebaCnt[AmoebaNr[newx][newy]]--;
7905       }
7906
7907       if (IS_MOVING(newx, newy))
7908       {
7909         RemoveMovingField(newx, newy);
7910       }
7911       else
7912       {
7913         Feld[newx][newy] = EL_EMPTY;
7914         TEST_DrawLevelField(newx, newy);
7915       }
7916
7917       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7918     }
7919     else if ((element == EL_PACMAN || element == EL_MOLE)
7920              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7921     {
7922       if (AmoebaNr[newx][newy])
7923       {
7924         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7925         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7926             Feld[newx][newy] == EL_BD_AMOEBA)
7927           AmoebaCnt[AmoebaNr[newx][newy]]--;
7928       }
7929
7930       if (element == EL_MOLE)
7931       {
7932         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7933         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7934
7935         ResetGfxAnimation(x, y);
7936         GfxAction[x][y] = ACTION_DIGGING;
7937         TEST_DrawLevelField(x, y);
7938
7939         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7940
7941         return;                         /* wait for shrinking amoeba */
7942       }
7943       else      /* element == EL_PACMAN */
7944       {
7945         Feld[newx][newy] = EL_EMPTY;
7946         TEST_DrawLevelField(newx, newy);
7947         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7948       }
7949     }
7950     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7951              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7952               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7953     {
7954       /* wait for shrinking amoeba to completely disappear */
7955       return;
7956     }
7957     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7958     {
7959       /* object was running against a wall */
7960
7961       TurnRound(x, y);
7962
7963       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7964         DrawLevelElementAnimation(x, y, element);
7965
7966       if (DONT_TOUCH(element))
7967         TestIfBadThingTouchesPlayer(x, y);
7968
7969       return;
7970     }
7971
7972     InitMovingField(x, y, MovDir[x][y]);
7973
7974     PlayLevelSoundAction(x, y, ACTION_MOVING);
7975   }
7976
7977   if (MovDir[x][y])
7978     ContinueMoving(x, y);
7979 }
7980
7981 void ContinueMoving(int x, int y)
7982 {
7983   int element = Feld[x][y];
7984   struct ElementInfo *ei = &element_info[element];
7985   int direction = MovDir[x][y];
7986   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7987   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7988   int newx = x + dx, newy = y + dy;
7989   int stored = Store[x][y];
7990   int stored_new = Store[newx][newy];
7991   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7992   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7993   boolean last_line = (newy == lev_fieldy - 1);
7994
7995   MovPos[x][y] += getElementMoveStepsize(x, y);
7996
7997   if (pushed_by_player) /* special case: moving object pushed by player */
7998     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7999
8000   if (ABS(MovPos[x][y]) < TILEX)
8001   {
8002     TEST_DrawLevelField(x, y);
8003
8004     return;     /* element is still moving */
8005   }
8006
8007   /* element reached destination field */
8008
8009   Feld[x][y] = EL_EMPTY;
8010   Feld[newx][newy] = element;
8011   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8012
8013   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8014   {
8015     element = Feld[newx][newy] = EL_ACID;
8016   }
8017   else if (element == EL_MOLE)
8018   {
8019     Feld[x][y] = EL_SAND;
8020
8021     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8022   }
8023   else if (element == EL_QUICKSAND_FILLING)
8024   {
8025     element = Feld[newx][newy] = get_next_element(element);
8026     Store[newx][newy] = Store[x][y];
8027   }
8028   else if (element == EL_QUICKSAND_EMPTYING)
8029   {
8030     Feld[x][y] = get_next_element(element);
8031     element = Feld[newx][newy] = Store[x][y];
8032   }
8033   else if (element == EL_QUICKSAND_FAST_FILLING)
8034   {
8035     element = Feld[newx][newy] = get_next_element(element);
8036     Store[newx][newy] = Store[x][y];
8037   }
8038   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8039   {
8040     Feld[x][y] = get_next_element(element);
8041     element = Feld[newx][newy] = Store[x][y];
8042   }
8043   else if (element == EL_MAGIC_WALL_FILLING)
8044   {
8045     element = Feld[newx][newy] = get_next_element(element);
8046     if (!game.magic_wall_active)
8047       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8048     Store[newx][newy] = Store[x][y];
8049   }
8050   else if (element == EL_MAGIC_WALL_EMPTYING)
8051   {
8052     Feld[x][y] = get_next_element(element);
8053     if (!game.magic_wall_active)
8054       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8055     element = Feld[newx][newy] = Store[x][y];
8056
8057     InitField(newx, newy, FALSE);
8058   }
8059   else if (element == EL_BD_MAGIC_WALL_FILLING)
8060   {
8061     element = Feld[newx][newy] = get_next_element(element);
8062     if (!game.magic_wall_active)
8063       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8064     Store[newx][newy] = Store[x][y];
8065   }
8066   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8067   {
8068     Feld[x][y] = get_next_element(element);
8069     if (!game.magic_wall_active)
8070       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8071     element = Feld[newx][newy] = Store[x][y];
8072
8073     InitField(newx, newy, FALSE);
8074   }
8075   else if (element == EL_DC_MAGIC_WALL_FILLING)
8076   {
8077     element = Feld[newx][newy] = get_next_element(element);
8078     if (!game.magic_wall_active)
8079       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8080     Store[newx][newy] = Store[x][y];
8081   }
8082   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8083   {
8084     Feld[x][y] = get_next_element(element);
8085     if (!game.magic_wall_active)
8086       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8087     element = Feld[newx][newy] = Store[x][y];
8088
8089     InitField(newx, newy, FALSE);
8090   }
8091   else if (element == EL_AMOEBA_DROPPING)
8092   {
8093     Feld[x][y] = get_next_element(element);
8094     element = Feld[newx][newy] = Store[x][y];
8095   }
8096   else if (element == EL_SOKOBAN_OBJECT)
8097   {
8098     if (Back[x][y])
8099       Feld[x][y] = Back[x][y];
8100
8101     if (Back[newx][newy])
8102       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8103
8104     Back[x][y] = Back[newx][newy] = 0;
8105   }
8106
8107   Store[x][y] = EL_EMPTY;
8108   MovPos[x][y] = 0;
8109   MovDir[x][y] = 0;
8110   MovDelay[x][y] = 0;
8111
8112   MovDelay[newx][newy] = 0;
8113
8114   if (CAN_CHANGE_OR_HAS_ACTION(element))
8115   {
8116     /* copy element change control values to new field */
8117     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8118     ChangePage[newx][newy]  = ChangePage[x][y];
8119     ChangeCount[newx][newy] = ChangeCount[x][y];
8120     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8121   }
8122
8123   CustomValue[newx][newy] = CustomValue[x][y];
8124
8125   ChangeDelay[x][y] = 0;
8126   ChangePage[x][y] = -1;
8127   ChangeCount[x][y] = 0;
8128   ChangeEvent[x][y] = -1;
8129
8130   CustomValue[x][y] = 0;
8131
8132   /* copy animation control values to new field */
8133   GfxFrame[newx][newy]  = GfxFrame[x][y];
8134   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8135   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8136   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8137
8138   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8139
8140   /* some elements can leave other elements behind after moving */
8141   if (ei->move_leave_element != EL_EMPTY &&
8142       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8143       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8144   {
8145     int move_leave_element = ei->move_leave_element;
8146
8147     /* this makes it possible to leave the removed element again */
8148     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8149       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8150
8151     Feld[x][y] = move_leave_element;
8152
8153     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8154       MovDir[x][y] = direction;
8155
8156     InitField(x, y, FALSE);
8157
8158     if (GFX_CRUMBLED(Feld[x][y]))
8159       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8160
8161     if (ELEM_IS_PLAYER(move_leave_element))
8162       RelocatePlayer(x, y, move_leave_element);
8163   }
8164
8165   /* do this after checking for left-behind element */
8166   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8167
8168   if (!CAN_MOVE(element) ||
8169       (CAN_FALL(element) && direction == MV_DOWN &&
8170        (element == EL_SPRING ||
8171         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8172         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8173     GfxDir[x][y] = MovDir[newx][newy] = 0;
8174
8175   TEST_DrawLevelField(x, y);
8176   TEST_DrawLevelField(newx, newy);
8177
8178   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8179
8180   /* prevent pushed element from moving on in pushed direction */
8181   if (pushed_by_player && CAN_MOVE(element) &&
8182       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8183       !(element_info[element].move_pattern & direction))
8184     TurnRound(newx, newy);
8185
8186   /* prevent elements on conveyor belt from moving on in last direction */
8187   if (pushed_by_conveyor && CAN_FALL(element) &&
8188       direction & MV_HORIZONTAL)
8189     MovDir[newx][newy] = 0;
8190
8191   if (!pushed_by_player)
8192   {
8193     int nextx = newx + dx, nexty = newy + dy;
8194     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8195
8196     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8197
8198     if (CAN_FALL(element) && direction == MV_DOWN)
8199       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8200
8201     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8202       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8203
8204     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8205       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8206   }
8207
8208   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8209   {
8210     TestIfBadThingTouchesPlayer(newx, newy);
8211     TestIfBadThingTouchesFriend(newx, newy);
8212
8213     if (!IS_CUSTOM_ELEMENT(element))
8214       TestIfBadThingTouchesOtherBadThing(newx, newy);
8215   }
8216   else if (element == EL_PENGUIN)
8217     TestIfFriendTouchesBadThing(newx, newy);
8218
8219   if (DONT_GET_HIT_BY(element))
8220   {
8221     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8222   }
8223
8224   /* give the player one last chance (one more frame) to move away */
8225   if (CAN_FALL(element) && direction == MV_DOWN &&
8226       (last_line || (!IS_FREE(x, newy + 1) &&
8227                      (!IS_PLAYER(x, newy + 1) ||
8228                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8229     Impact(x, newy);
8230
8231   if (pushed_by_player && !game.use_change_when_pushing_bug)
8232   {
8233     int push_side = MV_DIR_OPPOSITE(direction);
8234     struct PlayerInfo *player = PLAYERINFO(x, y);
8235
8236     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8237                                player->index_bit, push_side);
8238     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8239                                         player->index_bit, push_side);
8240   }
8241
8242   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8243     MovDelay[newx][newy] = 1;
8244
8245   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8246
8247   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8248   TestIfElementHitsCustomElement(newx, newy, direction);
8249   TestIfPlayerTouchesCustomElement(newx, newy);
8250   TestIfElementTouchesCustomElement(newx, newy);
8251
8252   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8253       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8254     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8255                              MV_DIR_OPPOSITE(direction));
8256 }
8257
8258 int AmoebeNachbarNr(int ax, int ay)
8259 {
8260   int i;
8261   int element = Feld[ax][ay];
8262   int group_nr = 0;
8263   static int xy[4][2] =
8264   {
8265     { 0, -1 },
8266     { -1, 0 },
8267     { +1, 0 },
8268     { 0, +1 }
8269   };
8270
8271   for (i = 0; i < NUM_DIRECTIONS; i++)
8272   {
8273     int x = ax + xy[i][0];
8274     int y = ay + xy[i][1];
8275
8276     if (!IN_LEV_FIELD(x, y))
8277       continue;
8278
8279     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8280       group_nr = AmoebaNr[x][y];
8281   }
8282
8283   return group_nr;
8284 }
8285
8286 void AmoebenVereinigen(int ax, int ay)
8287 {
8288   int i, x, y, xx, yy;
8289   int new_group_nr = AmoebaNr[ax][ay];
8290   static int xy[4][2] =
8291   {
8292     { 0, -1 },
8293     { -1, 0 },
8294     { +1, 0 },
8295     { 0, +1 }
8296   };
8297
8298   if (new_group_nr == 0)
8299     return;
8300
8301   for (i = 0; i < NUM_DIRECTIONS; i++)
8302   {
8303     x = ax + xy[i][0];
8304     y = ay + xy[i][1];
8305
8306     if (!IN_LEV_FIELD(x, y))
8307       continue;
8308
8309     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8310          Feld[x][y] == EL_BD_AMOEBA ||
8311          Feld[x][y] == EL_AMOEBA_DEAD) &&
8312         AmoebaNr[x][y] != new_group_nr)
8313     {
8314       int old_group_nr = AmoebaNr[x][y];
8315
8316       if (old_group_nr == 0)
8317         return;
8318
8319       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8320       AmoebaCnt[old_group_nr] = 0;
8321       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8322       AmoebaCnt2[old_group_nr] = 0;
8323
8324       SCAN_PLAYFIELD(xx, yy)
8325       {
8326         if (AmoebaNr[xx][yy] == old_group_nr)
8327           AmoebaNr[xx][yy] = new_group_nr;
8328       }
8329     }
8330   }
8331 }
8332
8333 void AmoebeUmwandeln(int ax, int ay)
8334 {
8335   int i, x, y;
8336
8337   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8338   {
8339     int group_nr = AmoebaNr[ax][ay];
8340
8341 #ifdef DEBUG
8342     if (group_nr == 0)
8343     {
8344       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8345       printf("AmoebeUmwandeln(): This should never happen!\n");
8346       return;
8347     }
8348 #endif
8349
8350     SCAN_PLAYFIELD(x, y)
8351     {
8352       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8353       {
8354         AmoebaNr[x][y] = 0;
8355         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8356       }
8357     }
8358
8359     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8360                             SND_AMOEBA_TURNING_TO_GEM :
8361                             SND_AMOEBA_TURNING_TO_ROCK));
8362     Bang(ax, ay);
8363   }
8364   else
8365   {
8366     static int xy[4][2] =
8367     {
8368       { 0, -1 },
8369       { -1, 0 },
8370       { +1, 0 },
8371       { 0, +1 }
8372     };
8373
8374     for (i = 0; i < NUM_DIRECTIONS; i++)
8375     {
8376       x = ax + xy[i][0];
8377       y = ay + xy[i][1];
8378
8379       if (!IN_LEV_FIELD(x, y))
8380         continue;
8381
8382       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8383       {
8384         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8385                               SND_AMOEBA_TURNING_TO_GEM :
8386                               SND_AMOEBA_TURNING_TO_ROCK));
8387         Bang(x, y);
8388       }
8389     }
8390   }
8391 }
8392
8393 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8394 {
8395   int x, y;
8396   int group_nr = AmoebaNr[ax][ay];
8397   boolean done = FALSE;
8398
8399 #ifdef DEBUG
8400   if (group_nr == 0)
8401   {
8402     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8403     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8404     return;
8405   }
8406 #endif
8407
8408   SCAN_PLAYFIELD(x, y)
8409   {
8410     if (AmoebaNr[x][y] == group_nr &&
8411         (Feld[x][y] == EL_AMOEBA_DEAD ||
8412          Feld[x][y] == EL_BD_AMOEBA ||
8413          Feld[x][y] == EL_AMOEBA_GROWING))
8414     {
8415       AmoebaNr[x][y] = 0;
8416       Feld[x][y] = new_element;
8417       InitField(x, y, FALSE);
8418       TEST_DrawLevelField(x, y);
8419       done = TRUE;
8420     }
8421   }
8422
8423   if (done)
8424     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8425                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8426                             SND_BD_AMOEBA_TURNING_TO_GEM));
8427 }
8428
8429 void AmoebeWaechst(int x, int y)
8430 {
8431   static unsigned int sound_delay = 0;
8432   static unsigned int sound_delay_value = 0;
8433
8434   if (!MovDelay[x][y])          /* start new growing cycle */
8435   {
8436     MovDelay[x][y] = 7;
8437
8438     if (DelayReached(&sound_delay, sound_delay_value))
8439     {
8440       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8441       sound_delay_value = 30;
8442     }
8443   }
8444
8445   if (MovDelay[x][y])           /* wait some time before growing bigger */
8446   {
8447     MovDelay[x][y]--;
8448     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8449     {
8450       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8451                                            6 - MovDelay[x][y]);
8452
8453       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8454     }
8455
8456     if (!MovDelay[x][y])
8457     {
8458       Feld[x][y] = Store[x][y];
8459       Store[x][y] = 0;
8460       TEST_DrawLevelField(x, y);
8461     }
8462   }
8463 }
8464
8465 void AmoebaDisappearing(int x, int y)
8466 {
8467   static unsigned int sound_delay = 0;
8468   static unsigned int sound_delay_value = 0;
8469
8470   if (!MovDelay[x][y])          /* start new shrinking cycle */
8471   {
8472     MovDelay[x][y] = 7;
8473
8474     if (DelayReached(&sound_delay, sound_delay_value))
8475       sound_delay_value = 30;
8476   }
8477
8478   if (MovDelay[x][y])           /* wait some time before shrinking */
8479   {
8480     MovDelay[x][y]--;
8481     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8482     {
8483       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8484                                            6 - MovDelay[x][y]);
8485
8486       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8487     }
8488
8489     if (!MovDelay[x][y])
8490     {
8491       Feld[x][y] = EL_EMPTY;
8492       TEST_DrawLevelField(x, y);
8493
8494       /* don't let mole enter this field in this cycle;
8495          (give priority to objects falling to this field from above) */
8496       Stop[x][y] = TRUE;
8497     }
8498   }
8499 }
8500
8501 void AmoebeAbleger(int ax, int ay)
8502 {
8503   int i;
8504   int element = Feld[ax][ay];
8505   int graphic = el2img(element);
8506   int newax = ax, neway = ay;
8507   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8508   static int xy[4][2] =
8509   {
8510     { 0, -1 },
8511     { -1, 0 },
8512     { +1, 0 },
8513     { 0, +1 }
8514   };
8515
8516   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8517   {
8518     Feld[ax][ay] = EL_AMOEBA_DEAD;
8519     TEST_DrawLevelField(ax, ay);
8520     return;
8521   }
8522
8523   if (IS_ANIMATED(graphic))
8524     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8525
8526   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8527     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8528
8529   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8530   {
8531     MovDelay[ax][ay]--;
8532     if (MovDelay[ax][ay])
8533       return;
8534   }
8535
8536   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8537   {
8538     int start = RND(4);
8539     int x = ax + xy[start][0];
8540     int y = ay + xy[start][1];
8541
8542     if (!IN_LEV_FIELD(x, y))
8543       return;
8544
8545     if (IS_FREE(x, y) ||
8546         CAN_GROW_INTO(Feld[x][y]) ||
8547         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8548         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8549     {
8550       newax = x;
8551       neway = y;
8552     }
8553
8554     if (newax == ax && neway == ay)
8555       return;
8556   }
8557   else                          /* normal or "filled" (BD style) amoeba */
8558   {
8559     int start = RND(4);
8560     boolean waiting_for_player = FALSE;
8561
8562     for (i = 0; i < NUM_DIRECTIONS; i++)
8563     {
8564       int j = (start + i) % 4;
8565       int x = ax + xy[j][0];
8566       int y = ay + xy[j][1];
8567
8568       if (!IN_LEV_FIELD(x, y))
8569         continue;
8570
8571       if (IS_FREE(x, y) ||
8572           CAN_GROW_INTO(Feld[x][y]) ||
8573           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8574           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8575       {
8576         newax = x;
8577         neway = y;
8578         break;
8579       }
8580       else if (IS_PLAYER(x, y))
8581         waiting_for_player = TRUE;
8582     }
8583
8584     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8585     {
8586       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8587       {
8588         Feld[ax][ay] = EL_AMOEBA_DEAD;
8589         TEST_DrawLevelField(ax, ay);
8590         AmoebaCnt[AmoebaNr[ax][ay]]--;
8591
8592         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8593         {
8594           if (element == EL_AMOEBA_FULL)
8595             AmoebeUmwandeln(ax, ay);
8596           else if (element == EL_BD_AMOEBA)
8597             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8598         }
8599       }
8600       return;
8601     }
8602     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8603     {
8604       /* amoeba gets larger by growing in some direction */
8605
8606       int new_group_nr = AmoebaNr[ax][ay];
8607
8608 #ifdef DEBUG
8609   if (new_group_nr == 0)
8610   {
8611     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8612     printf("AmoebeAbleger(): This should never happen!\n");
8613     return;
8614   }
8615 #endif
8616
8617       AmoebaNr[newax][neway] = new_group_nr;
8618       AmoebaCnt[new_group_nr]++;
8619       AmoebaCnt2[new_group_nr]++;
8620
8621       /* if amoeba touches other amoeba(s) after growing, unify them */
8622       AmoebenVereinigen(newax, neway);
8623
8624       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8625       {
8626         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8627         return;
8628       }
8629     }
8630   }
8631
8632   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8633       (neway == lev_fieldy - 1 && newax != ax))
8634   {
8635     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8636     Store[newax][neway] = element;
8637   }
8638   else if (neway == ay || element == EL_EMC_DRIPPER)
8639   {
8640     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8641
8642     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8643   }
8644   else
8645   {
8646     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8647     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8648     Store[ax][ay] = EL_AMOEBA_DROP;
8649     ContinueMoving(ax, ay);
8650     return;
8651   }
8652
8653   TEST_DrawLevelField(newax, neway);
8654 }
8655
8656 void Life(int ax, int ay)
8657 {
8658   int x1, y1, x2, y2;
8659   int life_time = 40;
8660   int element = Feld[ax][ay];
8661   int graphic = el2img(element);
8662   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8663                          level.biomaze);
8664   boolean changed = FALSE;
8665
8666   if (IS_ANIMATED(graphic))
8667     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8668
8669   if (Stop[ax][ay])
8670     return;
8671
8672   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8673     MovDelay[ax][ay] = life_time;
8674
8675   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8676   {
8677     MovDelay[ax][ay]--;
8678     if (MovDelay[ax][ay])
8679       return;
8680   }
8681
8682   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8683   {
8684     int xx = ax+x1, yy = ay+y1;
8685     int nachbarn = 0;
8686
8687     if (!IN_LEV_FIELD(xx, yy))
8688       continue;
8689
8690     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8691     {
8692       int x = xx+x2, y = yy+y2;
8693
8694       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8695         continue;
8696
8697       if (((Feld[x][y] == element ||
8698             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8699            !Stop[x][y]) ||
8700           (IS_FREE(x, y) && Stop[x][y]))
8701         nachbarn++;
8702     }
8703
8704     if (xx == ax && yy == ay)           /* field in the middle */
8705     {
8706       if (nachbarn < life_parameter[0] ||
8707           nachbarn > life_parameter[1])
8708       {
8709         Feld[xx][yy] = EL_EMPTY;
8710         if (!Stop[xx][yy])
8711           TEST_DrawLevelField(xx, yy);
8712         Stop[xx][yy] = TRUE;
8713         changed = TRUE;
8714       }
8715     }
8716     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8717     {                                   /* free border field */
8718       if (nachbarn >= life_parameter[2] &&
8719           nachbarn <= life_parameter[3])
8720       {
8721         Feld[xx][yy] = element;
8722         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8723         if (!Stop[xx][yy])
8724           TEST_DrawLevelField(xx, yy);
8725         Stop[xx][yy] = TRUE;
8726         changed = TRUE;
8727       }
8728     }
8729   }
8730
8731   if (changed)
8732     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8733                    SND_GAME_OF_LIFE_GROWING);
8734 }
8735
8736 static void InitRobotWheel(int x, int y)
8737 {
8738   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8739 }
8740
8741 static void RunRobotWheel(int x, int y)
8742 {
8743   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8744 }
8745
8746 static void StopRobotWheel(int x, int y)
8747 {
8748   if (ZX == x && ZY == y)
8749   {
8750     ZX = ZY = -1;
8751
8752     game.robot_wheel_active = FALSE;
8753   }
8754 }
8755
8756 static void InitTimegateWheel(int x, int y)
8757 {
8758   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8759 }
8760
8761 static void RunTimegateWheel(int x, int y)
8762 {
8763   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8764 }
8765
8766 static void InitMagicBallDelay(int x, int y)
8767 {
8768   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8769 }
8770
8771 static void ActivateMagicBall(int bx, int by)
8772 {
8773   int x, y;
8774
8775   if (level.ball_random)
8776   {
8777     int pos_border = RND(8);    /* select one of the eight border elements */
8778     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8779     int xx = pos_content % 3;
8780     int yy = pos_content / 3;
8781
8782     x = bx - 1 + xx;
8783     y = by - 1 + yy;
8784
8785     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8786       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8787   }
8788   else
8789   {
8790     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8791     {
8792       int xx = x - bx + 1;
8793       int yy = y - by + 1;
8794
8795       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8796         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8797     }
8798   }
8799
8800   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8801 }
8802
8803 void CheckExit(int x, int y)
8804 {
8805   if (local_player->gems_still_needed > 0 ||
8806       local_player->sokobanfields_still_needed > 0 ||
8807       local_player->lights_still_needed > 0)
8808   {
8809     int element = Feld[x][y];
8810     int graphic = el2img(element);
8811
8812     if (IS_ANIMATED(graphic))
8813       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8814
8815     return;
8816   }
8817
8818   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8819     return;
8820
8821   Feld[x][y] = EL_EXIT_OPENING;
8822
8823   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8824 }
8825
8826 void CheckExitEM(int x, int y)
8827 {
8828   if (local_player->gems_still_needed > 0 ||
8829       local_player->sokobanfields_still_needed > 0 ||
8830       local_player->lights_still_needed > 0)
8831   {
8832     int element = Feld[x][y];
8833     int graphic = el2img(element);
8834
8835     if (IS_ANIMATED(graphic))
8836       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8837
8838     return;
8839   }
8840
8841   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8842     return;
8843
8844   Feld[x][y] = EL_EM_EXIT_OPENING;
8845
8846   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8847 }
8848
8849 void CheckExitSteel(int x, int y)
8850 {
8851   if (local_player->gems_still_needed > 0 ||
8852       local_player->sokobanfields_still_needed > 0 ||
8853       local_player->lights_still_needed > 0)
8854   {
8855     int element = Feld[x][y];
8856     int graphic = el2img(element);
8857
8858     if (IS_ANIMATED(graphic))
8859       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8860
8861     return;
8862   }
8863
8864   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8865     return;
8866
8867   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8868
8869   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8870 }
8871
8872 void CheckExitSteelEM(int x, int y)
8873 {
8874   if (local_player->gems_still_needed > 0 ||
8875       local_player->sokobanfields_still_needed > 0 ||
8876       local_player->lights_still_needed > 0)
8877   {
8878     int element = Feld[x][y];
8879     int graphic = el2img(element);
8880
8881     if (IS_ANIMATED(graphic))
8882       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8883
8884     return;
8885   }
8886
8887   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8888     return;
8889
8890   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8891
8892   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8893 }
8894
8895 void CheckExitSP(int x, int y)
8896 {
8897   if (local_player->gems_still_needed > 0)
8898   {
8899     int element = Feld[x][y];
8900     int graphic = el2img(element);
8901
8902     if (IS_ANIMATED(graphic))
8903       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8904
8905     return;
8906   }
8907
8908   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8909     return;
8910
8911   Feld[x][y] = EL_SP_EXIT_OPENING;
8912
8913   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8914 }
8915
8916 static void CloseAllOpenTimegates()
8917 {
8918   int x, y;
8919
8920   SCAN_PLAYFIELD(x, y)
8921   {
8922     int element = Feld[x][y];
8923
8924     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8925     {
8926       Feld[x][y] = EL_TIMEGATE_CLOSING;
8927
8928       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8929     }
8930   }
8931 }
8932
8933 void DrawTwinkleOnField(int x, int y)
8934 {
8935   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8936     return;
8937
8938   if (Feld[x][y] == EL_BD_DIAMOND)
8939     return;
8940
8941   if (MovDelay[x][y] == 0)      /* next animation frame */
8942     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8943
8944   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8945   {
8946     MovDelay[x][y]--;
8947
8948     DrawLevelElementAnimation(x, y, Feld[x][y]);
8949
8950     if (MovDelay[x][y] != 0)
8951     {
8952       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8953                                            10 - MovDelay[x][y]);
8954
8955       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8956     }
8957   }
8958 }
8959
8960 void MauerWaechst(int x, int y)
8961 {
8962   int delay = 6;
8963
8964   if (!MovDelay[x][y])          /* next animation frame */
8965     MovDelay[x][y] = 3 * delay;
8966
8967   if (MovDelay[x][y])           /* wait some time before next frame */
8968   {
8969     MovDelay[x][y]--;
8970
8971     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8972     {
8973       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8974       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8975
8976       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8977     }
8978
8979     if (!MovDelay[x][y])
8980     {
8981       if (MovDir[x][y] == MV_LEFT)
8982       {
8983         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8984           TEST_DrawLevelField(x - 1, y);
8985       }
8986       else if (MovDir[x][y] == MV_RIGHT)
8987       {
8988         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8989           TEST_DrawLevelField(x + 1, y);
8990       }
8991       else if (MovDir[x][y] == MV_UP)
8992       {
8993         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8994           TEST_DrawLevelField(x, y - 1);
8995       }
8996       else
8997       {
8998         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8999           TEST_DrawLevelField(x, y + 1);
9000       }
9001
9002       Feld[x][y] = Store[x][y];
9003       Store[x][y] = 0;
9004       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9005       TEST_DrawLevelField(x, y);
9006     }
9007   }
9008 }
9009
9010 void MauerAbleger(int ax, int ay)
9011 {
9012   int element = Feld[ax][ay];
9013   int graphic = el2img(element);
9014   boolean oben_frei = FALSE, unten_frei = FALSE;
9015   boolean links_frei = FALSE, rechts_frei = FALSE;
9016   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9017   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9018   boolean new_wall = FALSE;
9019
9020   if (IS_ANIMATED(graphic))
9021     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9022
9023   if (!MovDelay[ax][ay])        /* start building new wall */
9024     MovDelay[ax][ay] = 6;
9025
9026   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9027   {
9028     MovDelay[ax][ay]--;
9029     if (MovDelay[ax][ay])
9030       return;
9031   }
9032
9033   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9034     oben_frei = TRUE;
9035   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9036     unten_frei = TRUE;
9037   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9038     links_frei = TRUE;
9039   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9040     rechts_frei = TRUE;
9041
9042   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9043       element == EL_EXPANDABLE_WALL_ANY)
9044   {
9045     if (oben_frei)
9046     {
9047       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9048       Store[ax][ay-1] = element;
9049       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9050       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9051         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9052                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9053       new_wall = TRUE;
9054     }
9055     if (unten_frei)
9056     {
9057       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9058       Store[ax][ay+1] = element;
9059       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9060       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9061         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9062                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9063       new_wall = TRUE;
9064     }
9065   }
9066
9067   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9068       element == EL_EXPANDABLE_WALL_ANY ||
9069       element == EL_EXPANDABLE_WALL ||
9070       element == EL_BD_EXPANDABLE_WALL)
9071   {
9072     if (links_frei)
9073     {
9074       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9075       Store[ax-1][ay] = element;
9076       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9077       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9078         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9079                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9080       new_wall = TRUE;
9081     }
9082
9083     if (rechts_frei)
9084     {
9085       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9086       Store[ax+1][ay] = element;
9087       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9088       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9089         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9090                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9091       new_wall = TRUE;
9092     }
9093   }
9094
9095   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9096     TEST_DrawLevelField(ax, ay);
9097
9098   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9099     oben_massiv = TRUE;
9100   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9101     unten_massiv = TRUE;
9102   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9103     links_massiv = TRUE;
9104   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9105     rechts_massiv = TRUE;
9106
9107   if (((oben_massiv && unten_massiv) ||
9108        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9109        element == EL_EXPANDABLE_WALL) &&
9110       ((links_massiv && rechts_massiv) ||
9111        element == EL_EXPANDABLE_WALL_VERTICAL))
9112     Feld[ax][ay] = EL_WALL;
9113
9114   if (new_wall)
9115     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9116 }
9117
9118 void MauerAblegerStahl(int ax, int ay)
9119 {
9120   int element = Feld[ax][ay];
9121   int graphic = el2img(element);
9122   boolean oben_frei = FALSE, unten_frei = FALSE;
9123   boolean links_frei = FALSE, rechts_frei = FALSE;
9124   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9125   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9126   boolean new_wall = FALSE;
9127
9128   if (IS_ANIMATED(graphic))
9129     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9130
9131   if (!MovDelay[ax][ay])        /* start building new wall */
9132     MovDelay[ax][ay] = 6;
9133
9134   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9135   {
9136     MovDelay[ax][ay]--;
9137     if (MovDelay[ax][ay])
9138       return;
9139   }
9140
9141   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9142     oben_frei = TRUE;
9143   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9144     unten_frei = TRUE;
9145   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9146     links_frei = TRUE;
9147   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9148     rechts_frei = TRUE;
9149
9150   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9151       element == EL_EXPANDABLE_STEELWALL_ANY)
9152   {
9153     if (oben_frei)
9154     {
9155       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9156       Store[ax][ay-1] = element;
9157       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9158       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9159         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9160                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9161       new_wall = TRUE;
9162     }
9163     if (unten_frei)
9164     {
9165       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9166       Store[ax][ay+1] = element;
9167       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9168       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9169         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9170                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9171       new_wall = TRUE;
9172     }
9173   }
9174
9175   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9176       element == EL_EXPANDABLE_STEELWALL_ANY)
9177   {
9178     if (links_frei)
9179     {
9180       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9181       Store[ax-1][ay] = element;
9182       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9183       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9184         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9185                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9186       new_wall = TRUE;
9187     }
9188
9189     if (rechts_frei)
9190     {
9191       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9192       Store[ax+1][ay] = element;
9193       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9194       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9195         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9196                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9197       new_wall = TRUE;
9198     }
9199   }
9200
9201   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9202     oben_massiv = TRUE;
9203   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9204     unten_massiv = TRUE;
9205   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9206     links_massiv = TRUE;
9207   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9208     rechts_massiv = TRUE;
9209
9210   if (((oben_massiv && unten_massiv) ||
9211        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9212       ((links_massiv && rechts_massiv) ||
9213        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9214     Feld[ax][ay] = EL_STEELWALL;
9215
9216   if (new_wall)
9217     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9218 }
9219
9220 void CheckForDragon(int x, int y)
9221 {
9222   int i, j;
9223   boolean dragon_found = FALSE;
9224   static int xy[4][2] =
9225   {
9226     { 0, -1 },
9227     { -1, 0 },
9228     { +1, 0 },
9229     { 0, +1 }
9230   };
9231
9232   for (i = 0; i < NUM_DIRECTIONS; i++)
9233   {
9234     for (j = 0; j < 4; j++)
9235     {
9236       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9237
9238       if (IN_LEV_FIELD(xx, yy) &&
9239           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9240       {
9241         if (Feld[xx][yy] == EL_DRAGON)
9242           dragon_found = TRUE;
9243       }
9244       else
9245         break;
9246     }
9247   }
9248
9249   if (!dragon_found)
9250   {
9251     for (i = 0; i < NUM_DIRECTIONS; i++)
9252     {
9253       for (j = 0; j < 3; j++)
9254       {
9255         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9256   
9257         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9258         {
9259           Feld[xx][yy] = EL_EMPTY;
9260           TEST_DrawLevelField(xx, yy);
9261         }
9262         else
9263           break;
9264       }
9265     }
9266   }
9267 }
9268
9269 static void InitBuggyBase(int x, int y)
9270 {
9271   int element = Feld[x][y];
9272   int activating_delay = FRAMES_PER_SECOND / 4;
9273
9274   ChangeDelay[x][y] =
9275     (element == EL_SP_BUGGY_BASE ?
9276      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9277      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9278      activating_delay :
9279      element == EL_SP_BUGGY_BASE_ACTIVE ?
9280      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9281 }
9282
9283 static void WarnBuggyBase(int x, int y)
9284 {
9285   int i;
9286   static int xy[4][2] =
9287   {
9288     { 0, -1 },
9289     { -1, 0 },
9290     { +1, 0 },
9291     { 0, +1 }
9292   };
9293
9294   for (i = 0; i < NUM_DIRECTIONS; i++)
9295   {
9296     int xx = x + xy[i][0];
9297     int yy = y + xy[i][1];
9298
9299     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9300     {
9301       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9302
9303       break;
9304     }
9305   }
9306 }
9307
9308 static void InitTrap(int x, int y)
9309 {
9310   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9311 }
9312
9313 static void ActivateTrap(int x, int y)
9314 {
9315   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9316 }
9317
9318 static void ChangeActiveTrap(int x, int y)
9319 {
9320   int graphic = IMG_TRAP_ACTIVE;
9321
9322   /* if new animation frame was drawn, correct crumbled sand border */
9323   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9324     TEST_DrawLevelFieldCrumbled(x, y);
9325 }
9326
9327 static int getSpecialActionElement(int element, int number, int base_element)
9328 {
9329   return (element != EL_EMPTY ? element :
9330           number != -1 ? base_element + number - 1 :
9331           EL_EMPTY);
9332 }
9333
9334 static int getModifiedActionNumber(int value_old, int operator, int operand,
9335                                    int value_min, int value_max)
9336 {
9337   int value_new = (operator == CA_MODE_SET      ? operand :
9338                    operator == CA_MODE_ADD      ? value_old + operand :
9339                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9340                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9341                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9342                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9343                    value_old);
9344
9345   return (value_new < value_min ? value_min :
9346           value_new > value_max ? value_max :
9347           value_new);
9348 }
9349
9350 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9351 {
9352   struct ElementInfo *ei = &element_info[element];
9353   struct ElementChangeInfo *change = &ei->change_page[page];
9354   int target_element = change->target_element;
9355   int action_type = change->action_type;
9356   int action_mode = change->action_mode;
9357   int action_arg = change->action_arg;
9358   int action_element = change->action_element;
9359   int i;
9360
9361   if (!change->has_action)
9362     return;
9363
9364   /* ---------- determine action paramater values -------------------------- */
9365
9366   int level_time_value =
9367     (level.time > 0 ? TimeLeft :
9368      TimePlayed);
9369
9370   int action_arg_element_raw =
9371     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9372      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9373      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9374      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9375      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9376      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9377      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9378      EL_EMPTY);
9379   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9380
9381   int action_arg_direction =
9382     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9383      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9384      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9385      change->actual_trigger_side :
9386      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9387      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9388      MV_NONE);
9389
9390   int action_arg_number_min =
9391     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9392      CA_ARG_MIN);
9393
9394   int action_arg_number_max =
9395     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9396      action_type == CA_SET_LEVEL_GEMS ? 999 :
9397      action_type == CA_SET_LEVEL_TIME ? 9999 :
9398      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9399      action_type == CA_SET_CE_VALUE ? 9999 :
9400      action_type == CA_SET_CE_SCORE ? 9999 :
9401      CA_ARG_MAX);
9402
9403   int action_arg_number_reset =
9404     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9405      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9406      action_type == CA_SET_LEVEL_TIME ? level.time :
9407      action_type == CA_SET_LEVEL_SCORE ? 0 :
9408      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9409      action_type == CA_SET_CE_SCORE ? 0 :
9410      0);
9411
9412   int action_arg_number =
9413     (action_arg <= CA_ARG_MAX ? action_arg :
9414      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9415      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9416      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9417      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9418      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9419      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9420      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9421      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9422      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9423      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9424      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9425      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9426      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9427      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9428      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9429      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9430      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9431      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9432      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9433      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9434      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9435      -1);
9436
9437   int action_arg_number_old =
9438     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9439      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9440      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9441      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9442      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9443      0);
9444
9445   int action_arg_number_new =
9446     getModifiedActionNumber(action_arg_number_old,
9447                             action_mode, action_arg_number,
9448                             action_arg_number_min, action_arg_number_max);
9449
9450   int trigger_player_bits =
9451     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9452      change->actual_trigger_player_bits : change->trigger_player);
9453
9454   int action_arg_player_bits =
9455     (action_arg >= CA_ARG_PLAYER_1 &&
9456      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9457      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9458      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9459      PLAYER_BITS_ANY);
9460
9461   /* ---------- execute action  -------------------------------------------- */
9462
9463   switch (action_type)
9464   {
9465     case CA_NO_ACTION:
9466     {
9467       return;
9468     }
9469
9470     /* ---------- level actions  ------------------------------------------- */
9471
9472     case CA_RESTART_LEVEL:
9473     {
9474       game.restart_level = TRUE;
9475
9476       break;
9477     }
9478
9479     case CA_SHOW_ENVELOPE:
9480     {
9481       int element = getSpecialActionElement(action_arg_element,
9482                                             action_arg_number, EL_ENVELOPE_1);
9483
9484       if (IS_ENVELOPE(element))
9485         local_player->show_envelope = element;
9486
9487       break;
9488     }
9489
9490     case CA_SET_LEVEL_TIME:
9491     {
9492       if (level.time > 0)       /* only modify limited time value */
9493       {
9494         TimeLeft = action_arg_number_new;
9495
9496         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9497
9498         DisplayGameControlValues();
9499
9500         if (!TimeLeft && setup.time_limit)
9501           for (i = 0; i < MAX_PLAYERS; i++)
9502             KillPlayer(&stored_player[i]);
9503       }
9504
9505       break;
9506     }
9507
9508     case CA_SET_LEVEL_SCORE:
9509     {
9510       local_player->score = action_arg_number_new;
9511
9512       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9513
9514       DisplayGameControlValues();
9515
9516       break;
9517     }
9518
9519     case CA_SET_LEVEL_GEMS:
9520     {
9521       local_player->gems_still_needed = action_arg_number_new;
9522
9523       game_panel_controls[GAME_PANEL_GEMS].value =
9524         local_player->gems_still_needed;
9525
9526       DisplayGameControlValues();
9527
9528       break;
9529     }
9530
9531     case CA_SET_LEVEL_WIND:
9532     {
9533       game.wind_direction = action_arg_direction;
9534
9535       break;
9536     }
9537
9538     case CA_SET_LEVEL_RANDOM_SEED:
9539     {
9540       /* ensure that setting a new random seed while playing is predictable */
9541       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9542
9543       break;
9544     }
9545
9546     /* ---------- player actions  ------------------------------------------ */
9547
9548     case CA_MOVE_PLAYER:
9549     {
9550       /* automatically move to the next field in specified direction */
9551       for (i = 0; i < MAX_PLAYERS; i++)
9552         if (trigger_player_bits & (1 << i))
9553           stored_player[i].programmed_action = action_arg_direction;
9554
9555       break;
9556     }
9557
9558     case CA_EXIT_PLAYER:
9559     {
9560       for (i = 0; i < MAX_PLAYERS; i++)
9561         if (action_arg_player_bits & (1 << i))
9562           PlayerWins(&stored_player[i]);
9563
9564       break;
9565     }
9566
9567     case CA_KILL_PLAYER:
9568     {
9569       for (i = 0; i < MAX_PLAYERS; i++)
9570         if (action_arg_player_bits & (1 << i))
9571           KillPlayer(&stored_player[i]);
9572
9573       break;
9574     }
9575
9576     case CA_SET_PLAYER_KEYS:
9577     {
9578       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9579       int element = getSpecialActionElement(action_arg_element,
9580                                             action_arg_number, EL_KEY_1);
9581
9582       if (IS_KEY(element))
9583       {
9584         for (i = 0; i < MAX_PLAYERS; i++)
9585         {
9586           if (trigger_player_bits & (1 << i))
9587           {
9588             stored_player[i].key[KEY_NR(element)] = key_state;
9589
9590             DrawGameDoorValues();
9591           }
9592         }
9593       }
9594
9595       break;
9596     }
9597
9598     case CA_SET_PLAYER_SPEED:
9599     {
9600       for (i = 0; i < MAX_PLAYERS; i++)
9601       {
9602         if (trigger_player_bits & (1 << i))
9603         {
9604           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9605
9606           if (action_arg == CA_ARG_SPEED_FASTER &&
9607               stored_player[i].cannot_move)
9608           {
9609             action_arg_number = STEPSIZE_VERY_SLOW;
9610           }
9611           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9612                    action_arg == CA_ARG_SPEED_FASTER)
9613           {
9614             action_arg_number = 2;
9615             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9616                            CA_MODE_MULTIPLY);
9617           }
9618           else if (action_arg == CA_ARG_NUMBER_RESET)
9619           {
9620             action_arg_number = level.initial_player_stepsize[i];
9621           }
9622
9623           move_stepsize =
9624             getModifiedActionNumber(move_stepsize,
9625                                     action_mode,
9626                                     action_arg_number,
9627                                     action_arg_number_min,
9628                                     action_arg_number_max);
9629
9630           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9631         }
9632       }
9633
9634       break;
9635     }
9636
9637     case CA_SET_PLAYER_SHIELD:
9638     {
9639       for (i = 0; i < MAX_PLAYERS; i++)
9640       {
9641         if (trigger_player_bits & (1 << i))
9642         {
9643           if (action_arg == CA_ARG_SHIELD_OFF)
9644           {
9645             stored_player[i].shield_normal_time_left = 0;
9646             stored_player[i].shield_deadly_time_left = 0;
9647           }
9648           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9649           {
9650             stored_player[i].shield_normal_time_left = 999999;
9651           }
9652           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9653           {
9654             stored_player[i].shield_normal_time_left = 999999;
9655             stored_player[i].shield_deadly_time_left = 999999;
9656           }
9657         }
9658       }
9659
9660       break;
9661     }
9662
9663     case CA_SET_PLAYER_GRAVITY:
9664     {
9665       for (i = 0; i < MAX_PLAYERS; i++)
9666       {
9667         if (trigger_player_bits & (1 << i))
9668         {
9669           stored_player[i].gravity =
9670             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9671              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9672              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9673              stored_player[i].gravity);
9674         }
9675       }
9676
9677       break;
9678     }
9679
9680     case CA_SET_PLAYER_ARTWORK:
9681     {
9682       for (i = 0; i < MAX_PLAYERS; i++)
9683       {
9684         if (trigger_player_bits & (1 << i))
9685         {
9686           int artwork_element = action_arg_element;
9687
9688           if (action_arg == CA_ARG_ELEMENT_RESET)
9689             artwork_element =
9690               (level.use_artwork_element[i] ? level.artwork_element[i] :
9691                stored_player[i].element_nr);
9692
9693           if (stored_player[i].artwork_element != artwork_element)
9694             stored_player[i].Frame = 0;
9695
9696           stored_player[i].artwork_element = artwork_element;
9697
9698           SetPlayerWaiting(&stored_player[i], FALSE);
9699
9700           /* set number of special actions for bored and sleeping animation */
9701           stored_player[i].num_special_action_bored =
9702             get_num_special_action(artwork_element,
9703                                    ACTION_BORING_1, ACTION_BORING_LAST);
9704           stored_player[i].num_special_action_sleeping =
9705             get_num_special_action(artwork_element,
9706                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9707         }
9708       }
9709
9710       break;
9711     }
9712
9713     case CA_SET_PLAYER_INVENTORY:
9714     {
9715       for (i = 0; i < MAX_PLAYERS; i++)
9716       {
9717         struct PlayerInfo *player = &stored_player[i];
9718         int j, k;
9719
9720         if (trigger_player_bits & (1 << i))
9721         {
9722           int inventory_element = action_arg_element;
9723
9724           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9725               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9726               action_arg == CA_ARG_ELEMENT_ACTION)
9727           {
9728             int element = inventory_element;
9729             int collect_count = element_info[element].collect_count_initial;
9730
9731             if (!IS_CUSTOM_ELEMENT(element))
9732               collect_count = 1;
9733
9734             if (collect_count == 0)
9735               player->inventory_infinite_element = element;
9736             else
9737               for (k = 0; k < collect_count; k++)
9738                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9739                   player->inventory_element[player->inventory_size++] =
9740                     element;
9741           }
9742           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9743                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9744                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9745           {
9746             if (player->inventory_infinite_element != EL_UNDEFINED &&
9747                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9748                                      action_arg_element_raw))
9749               player->inventory_infinite_element = EL_UNDEFINED;
9750
9751             for (k = 0, j = 0; j < player->inventory_size; j++)
9752             {
9753               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9754                                         action_arg_element_raw))
9755                 player->inventory_element[k++] = player->inventory_element[j];
9756             }
9757
9758             player->inventory_size = k;
9759           }
9760           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9761           {
9762             if (player->inventory_size > 0)
9763             {
9764               for (j = 0; j < player->inventory_size - 1; j++)
9765                 player->inventory_element[j] = player->inventory_element[j + 1];
9766
9767               player->inventory_size--;
9768             }
9769           }
9770           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9771           {
9772             if (player->inventory_size > 0)
9773               player->inventory_size--;
9774           }
9775           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9776           {
9777             player->inventory_infinite_element = EL_UNDEFINED;
9778             player->inventory_size = 0;
9779           }
9780           else if (action_arg == CA_ARG_INVENTORY_RESET)
9781           {
9782             player->inventory_infinite_element = EL_UNDEFINED;
9783             player->inventory_size = 0;
9784
9785             if (level.use_initial_inventory[i])
9786             {
9787               for (j = 0; j < level.initial_inventory_size[i]; j++)
9788               {
9789                 int element = level.initial_inventory_content[i][j];
9790                 int collect_count = element_info[element].collect_count_initial;
9791
9792                 if (!IS_CUSTOM_ELEMENT(element))
9793                   collect_count = 1;
9794
9795                 if (collect_count == 0)
9796                   player->inventory_infinite_element = element;
9797                 else
9798                   for (k = 0; k < collect_count; k++)
9799                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9800                       player->inventory_element[player->inventory_size++] =
9801                         element;
9802               }
9803             }
9804           }
9805         }
9806       }
9807
9808       break;
9809     }
9810
9811     /* ---------- CE actions  ---------------------------------------------- */
9812
9813     case CA_SET_CE_VALUE:
9814     {
9815       int last_ce_value = CustomValue[x][y];
9816
9817       CustomValue[x][y] = action_arg_number_new;
9818
9819       if (CustomValue[x][y] != last_ce_value)
9820       {
9821         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9822         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9823
9824         if (CustomValue[x][y] == 0)
9825         {
9826           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9827           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9828         }
9829       }
9830
9831       break;
9832     }
9833
9834     case CA_SET_CE_SCORE:
9835     {
9836       int last_ce_score = ei->collect_score;
9837
9838       ei->collect_score = action_arg_number_new;
9839
9840       if (ei->collect_score != last_ce_score)
9841       {
9842         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9843         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9844
9845         if (ei->collect_score == 0)
9846         {
9847           int xx, yy;
9848
9849           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9850           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9851
9852           /*
9853             This is a very special case that seems to be a mixture between
9854             CheckElementChange() and CheckTriggeredElementChange(): while
9855             the first one only affects single elements that are triggered
9856             directly, the second one affects multiple elements in the playfield
9857             that are triggered indirectly by another element. This is a third
9858             case: Changing the CE score always affects multiple identical CEs,
9859             so every affected CE must be checked, not only the single CE for
9860             which the CE score was changed in the first place (as every instance
9861             of that CE shares the same CE score, and therefore also can change)!
9862           */
9863           SCAN_PLAYFIELD(xx, yy)
9864           {
9865             if (Feld[xx][yy] == element)
9866               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9867                                  CE_SCORE_GETS_ZERO);
9868           }
9869         }
9870       }
9871
9872       break;
9873     }
9874
9875     case CA_SET_CE_ARTWORK:
9876     {
9877       int artwork_element = action_arg_element;
9878       boolean reset_frame = FALSE;
9879       int xx, yy;
9880
9881       if (action_arg == CA_ARG_ELEMENT_RESET)
9882         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9883                            element);
9884
9885       if (ei->gfx_element != artwork_element)
9886         reset_frame = TRUE;
9887
9888       ei->gfx_element = artwork_element;
9889
9890       SCAN_PLAYFIELD(xx, yy)
9891       {
9892         if (Feld[xx][yy] == element)
9893         {
9894           if (reset_frame)
9895           {
9896             ResetGfxAnimation(xx, yy);
9897             ResetRandomAnimationValue(xx, yy);
9898           }
9899
9900           TEST_DrawLevelField(xx, yy);
9901         }
9902       }
9903
9904       break;
9905     }
9906
9907     /* ---------- engine actions  ------------------------------------------ */
9908
9909     case CA_SET_ENGINE_SCAN_MODE:
9910     {
9911       InitPlayfieldScanMode(action_arg);
9912
9913       break;
9914     }
9915
9916     default:
9917       break;
9918   }
9919 }
9920
9921 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9922 {
9923   int old_element = Feld[x][y];
9924   int new_element = GetElementFromGroupElement(element);
9925   int previous_move_direction = MovDir[x][y];
9926   int last_ce_value = CustomValue[x][y];
9927   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9928   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9929   boolean add_player_onto_element = (new_element_is_player &&
9930                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9931                                      IS_WALKABLE(old_element));
9932
9933   if (!add_player_onto_element)
9934   {
9935     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9936       RemoveMovingField(x, y);
9937     else
9938       RemoveField(x, y);
9939
9940     Feld[x][y] = new_element;
9941
9942     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9943       MovDir[x][y] = previous_move_direction;
9944
9945     if (element_info[new_element].use_last_ce_value)
9946       CustomValue[x][y] = last_ce_value;
9947
9948     InitField_WithBug1(x, y, FALSE);
9949
9950     new_element = Feld[x][y];   /* element may have changed */
9951
9952     ResetGfxAnimation(x, y);
9953     ResetRandomAnimationValue(x, y);
9954
9955     TEST_DrawLevelField(x, y);
9956
9957     if (GFX_CRUMBLED(new_element))
9958       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9959   }
9960
9961   /* check if element under the player changes from accessible to unaccessible
9962      (needed for special case of dropping element which then changes) */
9963   /* (must be checked after creating new element for walkable group elements) */
9964   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9965       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9966   {
9967     Bang(x, y);
9968
9969     return;
9970   }
9971
9972   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9973   if (new_element_is_player)
9974     RelocatePlayer(x, y, new_element);
9975
9976   if (is_change)
9977     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9978
9979   TestIfBadThingTouchesPlayer(x, y);
9980   TestIfPlayerTouchesCustomElement(x, y);
9981   TestIfElementTouchesCustomElement(x, y);
9982 }
9983
9984 static void CreateField(int x, int y, int element)
9985 {
9986   CreateFieldExt(x, y, element, FALSE);
9987 }
9988
9989 static void CreateElementFromChange(int x, int y, int element)
9990 {
9991   element = GET_VALID_RUNTIME_ELEMENT(element);
9992
9993   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9994   {
9995     int old_element = Feld[x][y];
9996
9997     /* prevent changed element from moving in same engine frame
9998        unless both old and new element can either fall or move */
9999     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10000         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10001       Stop[x][y] = TRUE;
10002   }
10003
10004   CreateFieldExt(x, y, element, TRUE);
10005 }
10006
10007 static boolean ChangeElement(int x, int y, int element, int page)
10008 {
10009   struct ElementInfo *ei = &element_info[element];
10010   struct ElementChangeInfo *change = &ei->change_page[page];
10011   int ce_value = CustomValue[x][y];
10012   int ce_score = ei->collect_score;
10013   int target_element;
10014   int old_element = Feld[x][y];
10015
10016   /* always use default change event to prevent running into a loop */
10017   if (ChangeEvent[x][y] == -1)
10018     ChangeEvent[x][y] = CE_DELAY;
10019
10020   if (ChangeEvent[x][y] == CE_DELAY)
10021   {
10022     /* reset actual trigger element, trigger player and action element */
10023     change->actual_trigger_element = EL_EMPTY;
10024     change->actual_trigger_player = EL_EMPTY;
10025     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10026     change->actual_trigger_side = CH_SIDE_NONE;
10027     change->actual_trigger_ce_value = 0;
10028     change->actual_trigger_ce_score = 0;
10029   }
10030
10031   /* do not change elements more than a specified maximum number of changes */
10032   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10033     return FALSE;
10034
10035   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10036
10037   if (change->explode)
10038   {
10039     Bang(x, y);
10040
10041     return TRUE;
10042   }
10043
10044   if (change->use_target_content)
10045   {
10046     boolean complete_replace = TRUE;
10047     boolean can_replace[3][3];
10048     int xx, yy;
10049
10050     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10051     {
10052       boolean is_empty;
10053       boolean is_walkable;
10054       boolean is_diggable;
10055       boolean is_collectible;
10056       boolean is_removable;
10057       boolean is_destructible;
10058       int ex = x + xx - 1;
10059       int ey = y + yy - 1;
10060       int content_element = change->target_content.e[xx][yy];
10061       int e;
10062
10063       can_replace[xx][yy] = TRUE;
10064
10065       if (ex == x && ey == y)   /* do not check changing element itself */
10066         continue;
10067
10068       if (content_element == EL_EMPTY_SPACE)
10069       {
10070         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10071
10072         continue;
10073       }
10074
10075       if (!IN_LEV_FIELD(ex, ey))
10076       {
10077         can_replace[xx][yy] = FALSE;
10078         complete_replace = FALSE;
10079
10080         continue;
10081       }
10082
10083       e = Feld[ex][ey];
10084
10085       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10086         e = MovingOrBlocked2Element(ex, ey);
10087
10088       is_empty = (IS_FREE(ex, ey) ||
10089                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10090
10091       is_walkable     = (is_empty || IS_WALKABLE(e));
10092       is_diggable     = (is_empty || IS_DIGGABLE(e));
10093       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10094       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10095       is_removable    = (is_diggable || is_collectible);
10096
10097       can_replace[xx][yy] =
10098         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10099           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10100           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10101           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10102           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10103           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10104          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10105
10106       if (!can_replace[xx][yy])
10107         complete_replace = FALSE;
10108     }
10109
10110     if (!change->only_if_complete || complete_replace)
10111     {
10112       boolean something_has_changed = FALSE;
10113
10114       if (change->only_if_complete && change->use_random_replace &&
10115           RND(100) < change->random_percentage)
10116         return FALSE;
10117
10118       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10119       {
10120         int ex = x + xx - 1;
10121         int ey = y + yy - 1;
10122         int content_element;
10123
10124         if (can_replace[xx][yy] && (!change->use_random_replace ||
10125                                     RND(100) < change->random_percentage))
10126         {
10127           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10128             RemoveMovingField(ex, ey);
10129
10130           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10131
10132           content_element = change->target_content.e[xx][yy];
10133           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10134                                               ce_value, ce_score);
10135
10136           CreateElementFromChange(ex, ey, target_element);
10137
10138           something_has_changed = TRUE;
10139
10140           /* for symmetry reasons, freeze newly created border elements */
10141           if (ex != x || ey != y)
10142             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10143         }
10144       }
10145
10146       if (something_has_changed)
10147       {
10148         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10149         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10150       }
10151     }
10152   }
10153   else
10154   {
10155     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10156                                         ce_value, ce_score);
10157
10158     if (element == EL_DIAGONAL_GROWING ||
10159         element == EL_DIAGONAL_SHRINKING)
10160     {
10161       target_element = Store[x][y];
10162
10163       Store[x][y] = EL_EMPTY;
10164     }
10165
10166     CreateElementFromChange(x, y, target_element);
10167
10168     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10169     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10170   }
10171
10172   /* this uses direct change before indirect change */
10173   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10174
10175   return TRUE;
10176 }
10177
10178 static void HandleElementChange(int x, int y, int page)
10179 {
10180   int element = MovingOrBlocked2Element(x, y);
10181   struct ElementInfo *ei = &element_info[element];
10182   struct ElementChangeInfo *change = &ei->change_page[page];
10183   boolean handle_action_before_change = FALSE;
10184
10185 #ifdef DEBUG
10186   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10187       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10188   {
10189     printf("\n\n");
10190     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10191            x, y, element, element_info[element].token_name);
10192     printf("HandleElementChange(): This should never happen!\n");
10193     printf("\n\n");
10194   }
10195 #endif
10196
10197   /* this can happen with classic bombs on walkable, changing elements */
10198   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10199   {
10200     return;
10201   }
10202
10203   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10204   {
10205     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10206
10207     if (change->can_change)
10208     {
10209       /* !!! not clear why graphic animation should be reset at all here !!! */
10210       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10211       /* when a custom element is about to change (for example by change delay),
10212          do not reset graphic animation when the custom element is moving */
10213       if (!IS_MOVING(x, y))
10214       {
10215         ResetGfxAnimation(x, y);
10216         ResetRandomAnimationValue(x, y);
10217       }
10218
10219       if (change->pre_change_function)
10220         change->pre_change_function(x, y);
10221     }
10222   }
10223
10224   ChangeDelay[x][y]--;
10225
10226   if (ChangeDelay[x][y] != 0)           /* continue element change */
10227   {
10228     if (change->can_change)
10229     {
10230       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10231
10232       if (IS_ANIMATED(graphic))
10233         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10234
10235       if (change->change_function)
10236         change->change_function(x, y);
10237     }
10238   }
10239   else                                  /* finish element change */
10240   {
10241     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10242     {
10243       page = ChangePage[x][y];
10244       ChangePage[x][y] = -1;
10245
10246       change = &ei->change_page[page];
10247     }
10248
10249     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10250     {
10251       ChangeDelay[x][y] = 1;            /* try change after next move step */
10252       ChangePage[x][y] = page;          /* remember page to use for change */
10253
10254       return;
10255     }
10256
10257     /* special case: set new level random seed before changing element */
10258     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10259       handle_action_before_change = TRUE;
10260
10261     if (change->has_action && handle_action_before_change)
10262       ExecuteCustomElementAction(x, y, element, page);
10263
10264     if (change->can_change)
10265     {
10266       if (ChangeElement(x, y, element, page))
10267       {
10268         if (change->post_change_function)
10269           change->post_change_function(x, y);
10270       }
10271     }
10272
10273     if (change->has_action && !handle_action_before_change)
10274       ExecuteCustomElementAction(x, y, element, page);
10275   }
10276 }
10277
10278 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10279                                               int trigger_element,
10280                                               int trigger_event,
10281                                               int trigger_player,
10282                                               int trigger_side,
10283                                               int trigger_page)
10284 {
10285   boolean change_done_any = FALSE;
10286   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10287   int i;
10288
10289   if (!(trigger_events[trigger_element][trigger_event]))
10290     return FALSE;
10291
10292   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10293
10294   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10295   {
10296     int element = EL_CUSTOM_START + i;
10297     boolean change_done = FALSE;
10298     int p;
10299
10300     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10301         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10302       continue;
10303
10304     for (p = 0; p < element_info[element].num_change_pages; p++)
10305     {
10306       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10307
10308       if (change->can_change_or_has_action &&
10309           change->has_event[trigger_event] &&
10310           change->trigger_side & trigger_side &&
10311           change->trigger_player & trigger_player &&
10312           change->trigger_page & trigger_page_bits &&
10313           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10314       {
10315         change->actual_trigger_element = trigger_element;
10316         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10317         change->actual_trigger_player_bits = trigger_player;
10318         change->actual_trigger_side = trigger_side;
10319         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10320         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10321
10322         if ((change->can_change && !change_done) || change->has_action)
10323         {
10324           int x, y;
10325
10326           SCAN_PLAYFIELD(x, y)
10327           {
10328             if (Feld[x][y] == element)
10329             {
10330               if (change->can_change && !change_done)
10331               {
10332                 /* if element already changed in this frame, not only prevent
10333                    another element change (checked in ChangeElement()), but
10334                    also prevent additional element actions for this element */
10335
10336                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10337                     !level.use_action_after_change_bug)
10338                   continue;
10339
10340                 ChangeDelay[x][y] = 1;
10341                 ChangeEvent[x][y] = trigger_event;
10342
10343                 HandleElementChange(x, y, p);
10344               }
10345               else if (change->has_action)
10346               {
10347                 /* if element already changed in this frame, not only prevent
10348                    another element change (checked in ChangeElement()), but
10349                    also prevent additional element actions for this element */
10350
10351                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10352                     !level.use_action_after_change_bug)
10353                   continue;
10354
10355                 ExecuteCustomElementAction(x, y, element, p);
10356                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10357               }
10358             }
10359           }
10360
10361           if (change->can_change)
10362           {
10363             change_done = TRUE;
10364             change_done_any = TRUE;
10365           }
10366         }
10367       }
10368     }
10369   }
10370
10371   RECURSION_LOOP_DETECTION_END();
10372
10373   return change_done_any;
10374 }
10375
10376 static boolean CheckElementChangeExt(int x, int y,
10377                                      int element,
10378                                      int trigger_element,
10379                                      int trigger_event,
10380                                      int trigger_player,
10381                                      int trigger_side)
10382 {
10383   boolean change_done = FALSE;
10384   int p;
10385
10386   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10387       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10388     return FALSE;
10389
10390   if (Feld[x][y] == EL_BLOCKED)
10391   {
10392     Blocked2Moving(x, y, &x, &y);
10393     element = Feld[x][y];
10394   }
10395
10396   /* check if element has already changed or is about to change after moving */
10397   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10398        Feld[x][y] != element) ||
10399
10400       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10401        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10402         ChangePage[x][y] != -1)))
10403     return FALSE;
10404
10405   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10406
10407   for (p = 0; p < element_info[element].num_change_pages; p++)
10408   {
10409     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10410
10411     /* check trigger element for all events where the element that is checked
10412        for changing interacts with a directly adjacent element -- this is
10413        different to element changes that affect other elements to change on the
10414        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10415     boolean check_trigger_element =
10416       (trigger_event == CE_TOUCHING_X ||
10417        trigger_event == CE_HITTING_X ||
10418        trigger_event == CE_HIT_BY_X ||
10419        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10420
10421     if (change->can_change_or_has_action &&
10422         change->has_event[trigger_event] &&
10423         change->trigger_side & trigger_side &&
10424         change->trigger_player & trigger_player &&
10425         (!check_trigger_element ||
10426          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10427     {
10428       change->actual_trigger_element = trigger_element;
10429       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10430       change->actual_trigger_player_bits = trigger_player;
10431       change->actual_trigger_side = trigger_side;
10432       change->actual_trigger_ce_value = CustomValue[x][y];
10433       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10434
10435       /* special case: trigger element not at (x,y) position for some events */
10436       if (check_trigger_element)
10437       {
10438         static struct
10439         {
10440           int dx, dy;
10441         } move_xy[] =
10442           {
10443             {  0,  0 },
10444             { -1,  0 },
10445             { +1,  0 },
10446             {  0,  0 },
10447             {  0, -1 },
10448             {  0,  0 }, { 0, 0 }, { 0, 0 },
10449             {  0, +1 }
10450           };
10451
10452         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10453         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10454
10455         change->actual_trigger_ce_value = CustomValue[xx][yy];
10456         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10457       }
10458
10459       if (change->can_change && !change_done)
10460       {
10461         ChangeDelay[x][y] = 1;
10462         ChangeEvent[x][y] = trigger_event;
10463
10464         HandleElementChange(x, y, p);
10465
10466         change_done = TRUE;
10467       }
10468       else if (change->has_action)
10469       {
10470         ExecuteCustomElementAction(x, y, element, p);
10471         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10472       }
10473     }
10474   }
10475
10476   RECURSION_LOOP_DETECTION_END();
10477
10478   return change_done;
10479 }
10480
10481 static void PlayPlayerSound(struct PlayerInfo *player)
10482 {
10483   int jx = player->jx, jy = player->jy;
10484   int sound_element = player->artwork_element;
10485   int last_action = player->last_action_waiting;
10486   int action = player->action_waiting;
10487
10488   if (player->is_waiting)
10489   {
10490     if (action != last_action)
10491       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10492     else
10493       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10494   }
10495   else
10496   {
10497     if (action != last_action)
10498       StopSound(element_info[sound_element].sound[last_action]);
10499
10500     if (last_action == ACTION_SLEEPING)
10501       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10502   }
10503 }
10504
10505 static void PlayAllPlayersSound()
10506 {
10507   int i;
10508
10509   for (i = 0; i < MAX_PLAYERS; i++)
10510     if (stored_player[i].active)
10511       PlayPlayerSound(&stored_player[i]);
10512 }
10513
10514 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10515 {
10516   boolean last_waiting = player->is_waiting;
10517   int move_dir = player->MovDir;
10518
10519   player->dir_waiting = move_dir;
10520   player->last_action_waiting = player->action_waiting;
10521
10522   if (is_waiting)
10523   {
10524     if (!last_waiting)          /* not waiting -> waiting */
10525     {
10526       player->is_waiting = TRUE;
10527
10528       player->frame_counter_bored =
10529         FrameCounter +
10530         game.player_boring_delay_fixed +
10531         GetSimpleRandom(game.player_boring_delay_random);
10532       player->frame_counter_sleeping =
10533         FrameCounter +
10534         game.player_sleeping_delay_fixed +
10535         GetSimpleRandom(game.player_sleeping_delay_random);
10536
10537       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10538     }
10539
10540     if (game.player_sleeping_delay_fixed +
10541         game.player_sleeping_delay_random > 0 &&
10542         player->anim_delay_counter == 0 &&
10543         player->post_delay_counter == 0 &&
10544         FrameCounter >= player->frame_counter_sleeping)
10545       player->is_sleeping = TRUE;
10546     else if (game.player_boring_delay_fixed +
10547              game.player_boring_delay_random > 0 &&
10548              FrameCounter >= player->frame_counter_bored)
10549       player->is_bored = TRUE;
10550
10551     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10552                               player->is_bored ? ACTION_BORING :
10553                               ACTION_WAITING);
10554
10555     if (player->is_sleeping && player->use_murphy)
10556     {
10557       /* special case for sleeping Murphy when leaning against non-free tile */
10558
10559       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10560           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10561            !IS_MOVING(player->jx - 1, player->jy)))
10562         move_dir = MV_LEFT;
10563       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10564                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10565                 !IS_MOVING(player->jx + 1, player->jy)))
10566         move_dir = MV_RIGHT;
10567       else
10568         player->is_sleeping = FALSE;
10569
10570       player->dir_waiting = move_dir;
10571     }
10572
10573     if (player->is_sleeping)
10574     {
10575       if (player->num_special_action_sleeping > 0)
10576       {
10577         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10578         {
10579           int last_special_action = player->special_action_sleeping;
10580           int num_special_action = player->num_special_action_sleeping;
10581           int special_action =
10582             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10583              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10584              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10585              last_special_action + 1 : ACTION_SLEEPING);
10586           int special_graphic =
10587             el_act_dir2img(player->artwork_element, special_action, move_dir);
10588
10589           player->anim_delay_counter =
10590             graphic_info[special_graphic].anim_delay_fixed +
10591             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10592           player->post_delay_counter =
10593             graphic_info[special_graphic].post_delay_fixed +
10594             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10595
10596           player->special_action_sleeping = special_action;
10597         }
10598
10599         if (player->anim_delay_counter > 0)
10600         {
10601           player->action_waiting = player->special_action_sleeping;
10602           player->anim_delay_counter--;
10603         }
10604         else if (player->post_delay_counter > 0)
10605         {
10606           player->post_delay_counter--;
10607         }
10608       }
10609     }
10610     else if (player->is_bored)
10611     {
10612       if (player->num_special_action_bored > 0)
10613       {
10614         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10615         {
10616           int special_action =
10617             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10618           int special_graphic =
10619             el_act_dir2img(player->artwork_element, special_action, move_dir);
10620
10621           player->anim_delay_counter =
10622             graphic_info[special_graphic].anim_delay_fixed +
10623             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10624           player->post_delay_counter =
10625             graphic_info[special_graphic].post_delay_fixed +
10626             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10627
10628           player->special_action_bored = special_action;
10629         }
10630
10631         if (player->anim_delay_counter > 0)
10632         {
10633           player->action_waiting = player->special_action_bored;
10634           player->anim_delay_counter--;
10635         }
10636         else if (player->post_delay_counter > 0)
10637         {
10638           player->post_delay_counter--;
10639         }
10640       }
10641     }
10642   }
10643   else if (last_waiting)        /* waiting -> not waiting */
10644   {
10645     player->is_waiting = FALSE;
10646     player->is_bored = FALSE;
10647     player->is_sleeping = FALSE;
10648
10649     player->frame_counter_bored = -1;
10650     player->frame_counter_sleeping = -1;
10651
10652     player->anim_delay_counter = 0;
10653     player->post_delay_counter = 0;
10654
10655     player->dir_waiting = player->MovDir;
10656     player->action_waiting = ACTION_DEFAULT;
10657
10658     player->special_action_bored = ACTION_DEFAULT;
10659     player->special_action_sleeping = ACTION_DEFAULT;
10660   }
10661 }
10662
10663 static void CheckSingleStepMode(struct PlayerInfo *player)
10664 {
10665   if (tape.single_step && tape.recording && !tape.pausing)
10666   {
10667     /* as it is called "single step mode", just return to pause mode when the
10668        player stopped moving after one tile (or never starts moving at all) */
10669     if (!player->is_moving && !player->is_pushing)
10670     {
10671       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10672       SnapField(player, 0, 0);                  /* stop snapping */
10673     }
10674   }
10675 }
10676
10677 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10678 {
10679   int left      = player_action & JOY_LEFT;
10680   int right     = player_action & JOY_RIGHT;
10681   int up        = player_action & JOY_UP;
10682   int down      = player_action & JOY_DOWN;
10683   int button1   = player_action & JOY_BUTTON_1;
10684   int button2   = player_action & JOY_BUTTON_2;
10685   int dx        = (left ? -1 : right ? 1 : 0);
10686   int dy        = (up   ? -1 : down  ? 1 : 0);
10687
10688   if (!player->active || tape.pausing)
10689     return 0;
10690
10691   if (player_action)
10692   {
10693     if (button1)
10694       SnapField(player, dx, dy);
10695     else
10696     {
10697       if (button2)
10698         DropElement(player);
10699
10700       MovePlayer(player, dx, dy);
10701     }
10702
10703     CheckSingleStepMode(player);
10704
10705     SetPlayerWaiting(player, FALSE);
10706
10707     return player_action;
10708   }
10709   else
10710   {
10711     /* no actions for this player (no input at player's configured device) */
10712
10713     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10714     SnapField(player, 0, 0);
10715     CheckGravityMovementWhenNotMoving(player);
10716
10717     if (player->MovPos == 0)
10718       SetPlayerWaiting(player, TRUE);
10719
10720     if (player->MovPos == 0)    /* needed for tape.playing */
10721       player->is_moving = FALSE;
10722
10723     player->is_dropping = FALSE;
10724     player->is_dropping_pressed = FALSE;
10725     player->drop_pressed_delay = 0;
10726
10727     CheckSingleStepMode(player);
10728
10729     return 0;
10730   }
10731 }
10732
10733 static void CheckLevelTime()
10734 {
10735   int i;
10736
10737   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10738   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10739   {
10740     if (level.native_em_level->lev->home == 0)  /* all players at home */
10741     {
10742       PlayerWins(local_player);
10743
10744       AllPlayersGone = TRUE;
10745
10746       level.native_em_level->lev->home = -1;
10747     }
10748
10749     if (level.native_em_level->ply[0]->alive == 0 &&
10750         level.native_em_level->ply[1]->alive == 0 &&
10751         level.native_em_level->ply[2]->alive == 0 &&
10752         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10753       AllPlayersGone = TRUE;
10754   }
10755   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10756   {
10757     if (game_sp.LevelSolved &&
10758         !game_sp.GameOver)                              /* game won */
10759     {
10760       PlayerWins(local_player);
10761
10762       game_sp.GameOver = TRUE;
10763
10764       AllPlayersGone = TRUE;
10765     }
10766
10767     if (game_sp.GameOver)                               /* game lost */
10768       AllPlayersGone = TRUE;
10769   }
10770
10771   if (TimeFrames >= FRAMES_PER_SECOND)
10772   {
10773     TimeFrames = 0;
10774     TapeTime++;
10775
10776     for (i = 0; i < MAX_PLAYERS; i++)
10777     {
10778       struct PlayerInfo *player = &stored_player[i];
10779
10780       if (SHIELD_ON(player))
10781       {
10782         player->shield_normal_time_left--;
10783
10784         if (player->shield_deadly_time_left > 0)
10785           player->shield_deadly_time_left--;
10786       }
10787     }
10788
10789     if (!local_player->LevelSolved && !level.use_step_counter)
10790     {
10791       TimePlayed++;
10792
10793       if (TimeLeft > 0)
10794       {
10795         TimeLeft--;
10796
10797         if (TimeLeft <= 10 && setup.time_limit)
10798           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10799
10800         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10801            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10802
10803         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10804
10805         if (!TimeLeft && setup.time_limit)
10806         {
10807           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10808             level.native_em_level->lev->killed_out_of_time = TRUE;
10809           else
10810             for (i = 0; i < MAX_PLAYERS; i++)
10811               KillPlayer(&stored_player[i]);
10812         }
10813       }
10814       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10815       {
10816         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10817       }
10818
10819       level.native_em_level->lev->time =
10820         (game.no_time_limit ? TimePlayed : TimeLeft);
10821     }
10822
10823     if (tape.recording || tape.playing)
10824       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10825   }
10826
10827   UpdateAndDisplayGameControlValues();
10828 }
10829
10830 void AdvanceFrameAndPlayerCounters(int player_nr)
10831 {
10832   int i;
10833
10834   /* advance frame counters (global frame counter and time frame counter) */
10835   FrameCounter++;
10836   TimeFrames++;
10837
10838   /* advance player counters (counters for move delay, move animation etc.) */
10839   for (i = 0; i < MAX_PLAYERS; i++)
10840   {
10841     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10842     int move_delay_value = stored_player[i].move_delay_value;
10843     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10844
10845     if (!advance_player_counters)       /* not all players may be affected */
10846       continue;
10847
10848     if (move_frames == 0)       /* less than one move per game frame */
10849     {
10850       int stepsize = TILEX / move_delay_value;
10851       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10852       int count = (stored_player[i].is_moving ?
10853                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10854
10855       if (count % delay == 0)
10856         move_frames = 1;
10857     }
10858
10859     stored_player[i].Frame += move_frames;
10860
10861     if (stored_player[i].MovPos != 0)
10862       stored_player[i].StepFrame += move_frames;
10863
10864     if (stored_player[i].move_delay > 0)
10865       stored_player[i].move_delay--;
10866
10867     /* due to bugs in previous versions, counter must count up, not down */
10868     if (stored_player[i].push_delay != -1)
10869       stored_player[i].push_delay++;
10870
10871     if (stored_player[i].drop_delay > 0)
10872       stored_player[i].drop_delay--;
10873
10874     if (stored_player[i].is_dropping_pressed)
10875       stored_player[i].drop_pressed_delay++;
10876   }
10877 }
10878
10879 void StartGameActions(boolean init_network_game, boolean record_tape,
10880                       int random_seed)
10881 {
10882   unsigned int new_random_seed = InitRND(random_seed);
10883
10884   if (record_tape)
10885     TapeStartRecording(new_random_seed);
10886
10887 #if defined(NETWORK_AVALIABLE)
10888   if (init_network_game)
10889   {
10890     SendToServer_StartPlaying();
10891
10892     return;
10893   }
10894 #endif
10895
10896   InitGame();
10897 }
10898
10899 void GameActions()
10900 {
10901   static unsigned int game_frame_delay = 0;
10902   unsigned int game_frame_delay_value;
10903   byte *recorded_player_action;
10904   byte summarized_player_action = 0;
10905   byte tape_action[MAX_PLAYERS];
10906   int i;
10907
10908   /* detect endless loops, caused by custom element programming */
10909   if (recursion_loop_detected && recursion_loop_depth == 0)
10910   {
10911     char *message = getStringCat3("Internal Error! Element ",
10912                                   EL_NAME(recursion_loop_element),
10913                                   " caused endless loop! Quit the game?");
10914
10915     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10916           EL_NAME(recursion_loop_element));
10917
10918     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10919
10920     recursion_loop_detected = FALSE;    /* if game should be continued */
10921
10922     free(message);
10923
10924     return;
10925   }
10926
10927   if (game.restart_level)
10928     StartGameActions(options.network, setup.autorecord, level.random_seed);
10929
10930   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10931   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10932   {
10933     if (level.native_em_level->lev->home == 0)  /* all players at home */
10934     {
10935       PlayerWins(local_player);
10936
10937       AllPlayersGone = TRUE;
10938
10939       level.native_em_level->lev->home = -1;
10940     }
10941
10942     if (level.native_em_level->ply[0]->alive == 0 &&
10943         level.native_em_level->ply[1]->alive == 0 &&
10944         level.native_em_level->ply[2]->alive == 0 &&
10945         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10946       AllPlayersGone = TRUE;
10947   }
10948   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10949   {
10950     if (game_sp.LevelSolved &&
10951         !game_sp.GameOver)                              /* game won */
10952     {
10953       PlayerWins(local_player);
10954
10955       game_sp.GameOver = TRUE;
10956
10957       AllPlayersGone = TRUE;
10958     }
10959
10960     if (game_sp.GameOver)                               /* game lost */
10961       AllPlayersGone = TRUE;
10962   }
10963
10964   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10965     GameWon();
10966
10967   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10968     TapeStop();
10969
10970   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10971     return;
10972
10973   game_frame_delay_value =
10974     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10975
10976   if (tape.playing && tape.warp_forward && !tape.pausing)
10977     game_frame_delay_value = 0;
10978
10979   /* ---------- main game synchronization point ---------- */
10980
10981   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10982
10983   if (network_playing && !network_player_action_received)
10984   {
10985     /* try to get network player actions in time */
10986
10987 #if defined(NETWORK_AVALIABLE)
10988     /* last chance to get network player actions without main loop delay */
10989     HandleNetworking();
10990 #endif
10991
10992     /* game was quit by network peer */
10993     if (game_status != GAME_MODE_PLAYING)
10994       return;
10995
10996     if (!network_player_action_received)
10997       return;           /* failed to get network player actions in time */
10998
10999     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11000   }
11001
11002   if (tape.pausing)
11003     return;
11004
11005   /* at this point we know that we really continue executing the game */
11006
11007   network_player_action_received = FALSE;
11008
11009   /* when playing tape, read previously recorded player input from tape data */
11010   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11011
11012   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11013   if (tape.pausing)
11014     return;
11015
11016   if (tape.set_centered_player)
11017   {
11018     game.centered_player_nr_next = tape.centered_player_nr_next;
11019     game.set_centered_player = TRUE;
11020   }
11021
11022   for (i = 0; i < MAX_PLAYERS; i++)
11023   {
11024     summarized_player_action |= stored_player[i].action;
11025
11026     if (!network_playing && (game.team_mode || tape.playing))
11027       stored_player[i].effective_action = stored_player[i].action;
11028   }
11029
11030 #if defined(NETWORK_AVALIABLE)
11031   if (network_playing)
11032     SendToServer_MovePlayer(summarized_player_action);
11033 #endif
11034
11035   if (!options.network && !game.team_mode)
11036     local_player->effective_action = summarized_player_action;
11037
11038   if (tape.recording &&
11039       setup.team_mode &&
11040       setup.input_on_focus &&
11041       game.centered_player_nr != -1)
11042   {
11043     for (i = 0; i < MAX_PLAYERS; i++)
11044       stored_player[i].effective_action =
11045         (i == game.centered_player_nr ? summarized_player_action : 0);
11046   }
11047
11048   if (recorded_player_action != NULL)
11049     for (i = 0; i < MAX_PLAYERS; i++)
11050       stored_player[i].effective_action = recorded_player_action[i];
11051
11052   for (i = 0; i < MAX_PLAYERS; i++)
11053   {
11054     tape_action[i] = stored_player[i].effective_action;
11055
11056     /* (this may happen in the RND game engine if a player was not present on
11057        the playfield on level start, but appeared later from a custom element */
11058     if (setup.team_mode &&
11059         tape.recording &&
11060         tape_action[i] &&
11061         !tape.player_participates[i])
11062       tape.player_participates[i] = TRUE;
11063   }
11064
11065   /* only record actions from input devices, but not programmed actions */
11066   if (tape.recording)
11067     TapeRecordAction(tape_action);
11068
11069 #if USE_NEW_PLAYER_ASSIGNMENTS
11070   // !!! also map player actions in single player mode !!!
11071   // if (game.team_mode)
11072   {
11073     byte mapped_action[MAX_PLAYERS];
11074
11075 #if DEBUG_PLAYER_ACTIONS
11076     printf(":::");
11077     for (i = 0; i < MAX_PLAYERS; i++)
11078       printf(" %d, ", stored_player[i].effective_action);
11079 #endif
11080
11081     for (i = 0; i < MAX_PLAYERS; i++)
11082       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11083
11084     for (i = 0; i < MAX_PLAYERS; i++)
11085       stored_player[i].effective_action = mapped_action[i];
11086
11087 #if DEBUG_PLAYER_ACTIONS
11088     printf(" =>");
11089     for (i = 0; i < MAX_PLAYERS; i++)
11090       printf(" %d, ", stored_player[i].effective_action);
11091     printf("\n");
11092 #endif
11093   }
11094 #if DEBUG_PLAYER_ACTIONS
11095   else
11096   {
11097     printf(":::");
11098     for (i = 0; i < MAX_PLAYERS; i++)
11099       printf(" %d, ", stored_player[i].effective_action);
11100     printf("\n");
11101   }
11102 #endif
11103 #endif
11104
11105   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11106   {
11107     GameActions_EM_Main();
11108   }
11109   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11110   {
11111     GameActions_SP_Main();
11112   }
11113   else
11114   {
11115     GameActions_RND();
11116   }
11117 }
11118
11119 void GameActions_EM_Main()
11120 {
11121   byte effective_action[MAX_PLAYERS];
11122   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11123   int i;
11124
11125   for (i = 0; i < MAX_PLAYERS; i++)
11126     effective_action[i] = stored_player[i].effective_action;
11127
11128   GameActions_EM(effective_action, warp_mode);
11129
11130   CheckLevelTime();
11131
11132   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11133 }
11134
11135 void GameActions_SP_Main()
11136 {
11137   byte effective_action[MAX_PLAYERS];
11138   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11139   int i;
11140
11141   for (i = 0; i < MAX_PLAYERS; i++)
11142     effective_action[i] = stored_player[i].effective_action;
11143
11144   GameActions_SP(effective_action, warp_mode);
11145
11146   CheckLevelTime();
11147
11148   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11149 }
11150
11151 void GameActions_RND()
11152 {
11153   int magic_wall_x = 0, magic_wall_y = 0;
11154   int i, x, y, element, graphic;
11155
11156   InitPlayfieldScanModeVars();
11157
11158   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11159   {
11160     SCAN_PLAYFIELD(x, y)
11161     {
11162       ChangeCount[x][y] = 0;
11163       ChangeEvent[x][y] = -1;
11164     }
11165   }
11166
11167   if (game.set_centered_player)
11168   {
11169     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11170
11171     /* switching to "all players" only possible if all players fit to screen */
11172     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11173     {
11174       game.centered_player_nr_next = game.centered_player_nr;
11175       game.set_centered_player = FALSE;
11176     }
11177
11178     /* do not switch focus to non-existing (or non-active) player */
11179     if (game.centered_player_nr_next >= 0 &&
11180         !stored_player[game.centered_player_nr_next].active)
11181     {
11182       game.centered_player_nr_next = game.centered_player_nr;
11183       game.set_centered_player = FALSE;
11184     }
11185   }
11186
11187   if (game.set_centered_player &&
11188       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11189   {
11190     int sx, sy;
11191
11192     if (game.centered_player_nr_next == -1)
11193     {
11194       setScreenCenteredToAllPlayers(&sx, &sy);
11195     }
11196     else
11197     {
11198       sx = stored_player[game.centered_player_nr_next].jx;
11199       sy = stored_player[game.centered_player_nr_next].jy;
11200     }
11201
11202     game.centered_player_nr = game.centered_player_nr_next;
11203     game.set_centered_player = FALSE;
11204
11205     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11206     DrawGameDoorValues();
11207   }
11208
11209   for (i = 0; i < MAX_PLAYERS; i++)
11210   {
11211     int actual_player_action = stored_player[i].effective_action;
11212
11213 #if 1
11214     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11215        - rnd_equinox_tetrachloride 048
11216        - rnd_equinox_tetrachloride_ii 096
11217        - rnd_emanuel_schmieg 002
11218        - doctor_sloan_ww 001, 020
11219     */
11220     if (stored_player[i].MovPos == 0)
11221       CheckGravityMovement(&stored_player[i]);
11222 #endif
11223
11224     /* overwrite programmed action with tape action */
11225     if (stored_player[i].programmed_action)
11226       actual_player_action = stored_player[i].programmed_action;
11227
11228     PlayerActions(&stored_player[i], actual_player_action);
11229
11230     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11231   }
11232
11233   ScrollScreen(NULL, SCROLL_GO_ON);
11234
11235   /* for backwards compatibility, the following code emulates a fixed bug that
11236      occured when pushing elements (causing elements that just made their last
11237      pushing step to already (if possible) make their first falling step in the
11238      same game frame, which is bad); this code is also needed to use the famous
11239      "spring push bug" which is used in older levels and might be wanted to be
11240      used also in newer levels, but in this case the buggy pushing code is only
11241      affecting the "spring" element and no other elements */
11242
11243   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11244   {
11245     for (i = 0; i < MAX_PLAYERS; i++)
11246     {
11247       struct PlayerInfo *player = &stored_player[i];
11248       int x = player->jx;
11249       int y = player->jy;
11250
11251       if (player->active && player->is_pushing && player->is_moving &&
11252           IS_MOVING(x, y) &&
11253           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11254            Feld[x][y] == EL_SPRING))
11255       {
11256         ContinueMoving(x, y);
11257
11258         /* continue moving after pushing (this is actually a bug) */
11259         if (!IS_MOVING(x, y))
11260           Stop[x][y] = FALSE;
11261       }
11262     }
11263   }
11264
11265   SCAN_PLAYFIELD(x, y)
11266   {
11267     ChangeCount[x][y] = 0;
11268     ChangeEvent[x][y] = -1;
11269
11270     /* this must be handled before main playfield loop */
11271     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11272     {
11273       MovDelay[x][y]--;
11274       if (MovDelay[x][y] <= 0)
11275         RemoveField(x, y);
11276     }
11277
11278     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11279     {
11280       MovDelay[x][y]--;
11281       if (MovDelay[x][y] <= 0)
11282       {
11283         RemoveField(x, y);
11284         TEST_DrawLevelField(x, y);
11285
11286         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11287       }
11288     }
11289
11290 #if DEBUG
11291     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11292     {
11293       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11294       printf("GameActions(): This should never happen!\n");
11295
11296       ChangePage[x][y] = -1;
11297     }
11298 #endif
11299
11300     Stop[x][y] = FALSE;
11301     if (WasJustMoving[x][y] > 0)
11302       WasJustMoving[x][y]--;
11303     if (WasJustFalling[x][y] > 0)
11304       WasJustFalling[x][y]--;
11305     if (CheckCollision[x][y] > 0)
11306       CheckCollision[x][y]--;
11307     if (CheckImpact[x][y] > 0)
11308       CheckImpact[x][y]--;
11309
11310     GfxFrame[x][y]++;
11311
11312     /* reset finished pushing action (not done in ContinueMoving() to allow
11313        continuous pushing animation for elements with zero push delay) */
11314     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11315     {
11316       ResetGfxAnimation(x, y);
11317       TEST_DrawLevelField(x, y);
11318     }
11319
11320 #if DEBUG
11321     if (IS_BLOCKED(x, y))
11322     {
11323       int oldx, oldy;
11324
11325       Blocked2Moving(x, y, &oldx, &oldy);
11326       if (!IS_MOVING(oldx, oldy))
11327       {
11328         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11329         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11330         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11331         printf("GameActions(): This should never happen!\n");
11332       }
11333     }
11334 #endif
11335   }
11336
11337   SCAN_PLAYFIELD(x, y)
11338   {
11339     element = Feld[x][y];
11340     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11341
11342     ResetGfxFrame(x, y, TRUE);
11343
11344     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11345         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11346       ResetRandomAnimationValue(x, y);
11347
11348     SetRandomAnimationValue(x, y);
11349
11350     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11351
11352     if (IS_INACTIVE(element))
11353     {
11354       if (IS_ANIMATED(graphic))
11355         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11356
11357       continue;
11358     }
11359
11360     /* this may take place after moving, so 'element' may have changed */
11361     if (IS_CHANGING(x, y) &&
11362         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11363     {
11364       int page = element_info[element].event_page_nr[CE_DELAY];
11365
11366       HandleElementChange(x, y, page);
11367
11368       element = Feld[x][y];
11369       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11370     }
11371
11372     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11373     {
11374       StartMoving(x, y);
11375
11376       element = Feld[x][y];
11377       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11378
11379       if (IS_ANIMATED(graphic) &&
11380           !IS_MOVING(x, y) &&
11381           !Stop[x][y])
11382         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11383
11384       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11385         TEST_DrawTwinkleOnField(x, y);
11386     }
11387     else if ((element == EL_ACID ||
11388               element == EL_EXIT_OPEN ||
11389               element == EL_EM_EXIT_OPEN ||
11390               element == EL_SP_EXIT_OPEN ||
11391               element == EL_STEEL_EXIT_OPEN ||
11392               element == EL_EM_STEEL_EXIT_OPEN ||
11393               element == EL_SP_TERMINAL ||
11394               element == EL_SP_TERMINAL_ACTIVE ||
11395               element == EL_EXTRA_TIME ||
11396               element == EL_SHIELD_NORMAL ||
11397               element == EL_SHIELD_DEADLY) &&
11398              IS_ANIMATED(graphic))
11399       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11400     else if (IS_MOVING(x, y))
11401       ContinueMoving(x, y);
11402     else if (IS_ACTIVE_BOMB(element))
11403       CheckDynamite(x, y);
11404     else if (element == EL_AMOEBA_GROWING)
11405       AmoebeWaechst(x, y);
11406     else if (element == EL_AMOEBA_SHRINKING)
11407       AmoebaDisappearing(x, y);
11408
11409 #if !USE_NEW_AMOEBA_CODE
11410     else if (IS_AMOEBALIVE(element))
11411       AmoebeAbleger(x, y);
11412 #endif
11413
11414     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11415       Life(x, y);
11416     else if (element == EL_EXIT_CLOSED)
11417       CheckExit(x, y);
11418     else if (element == EL_EM_EXIT_CLOSED)
11419       CheckExitEM(x, y);
11420     else if (element == EL_STEEL_EXIT_CLOSED)
11421       CheckExitSteel(x, y);
11422     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11423       CheckExitSteelEM(x, y);
11424     else if (element == EL_SP_EXIT_CLOSED)
11425       CheckExitSP(x, y);
11426     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11427              element == EL_EXPANDABLE_STEELWALL_GROWING)
11428       MauerWaechst(x, y);
11429     else if (element == EL_EXPANDABLE_WALL ||
11430              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11431              element == EL_EXPANDABLE_WALL_VERTICAL ||
11432              element == EL_EXPANDABLE_WALL_ANY ||
11433              element == EL_BD_EXPANDABLE_WALL)
11434       MauerAbleger(x, y);
11435     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11436              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11437              element == EL_EXPANDABLE_STEELWALL_ANY)
11438       MauerAblegerStahl(x, y);
11439     else if (element == EL_FLAMES)
11440       CheckForDragon(x, y);
11441     else if (element == EL_EXPLOSION)
11442       ; /* drawing of correct explosion animation is handled separately */
11443     else if (element == EL_ELEMENT_SNAPPING ||
11444              element == EL_DIAGONAL_SHRINKING ||
11445              element == EL_DIAGONAL_GROWING)
11446     {
11447       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11448
11449       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11450     }
11451     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11452       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11453
11454     if (IS_BELT_ACTIVE(element))
11455       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11456
11457     if (game.magic_wall_active)
11458     {
11459       int jx = local_player->jx, jy = local_player->jy;
11460
11461       /* play the element sound at the position nearest to the player */
11462       if ((element == EL_MAGIC_WALL_FULL ||
11463            element == EL_MAGIC_WALL_ACTIVE ||
11464            element == EL_MAGIC_WALL_EMPTYING ||
11465            element == EL_BD_MAGIC_WALL_FULL ||
11466            element == EL_BD_MAGIC_WALL_ACTIVE ||
11467            element == EL_BD_MAGIC_WALL_EMPTYING ||
11468            element == EL_DC_MAGIC_WALL_FULL ||
11469            element == EL_DC_MAGIC_WALL_ACTIVE ||
11470            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11471           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11472       {
11473         magic_wall_x = x;
11474         magic_wall_y = y;
11475       }
11476     }
11477   }
11478
11479 #if USE_NEW_AMOEBA_CODE
11480   /* new experimental amoeba growth stuff */
11481   if (!(FrameCounter % 8))
11482   {
11483     static unsigned int random = 1684108901;
11484
11485     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11486     {
11487       x = RND(lev_fieldx);
11488       y = RND(lev_fieldy);
11489       element = Feld[x][y];
11490
11491       if (!IS_PLAYER(x,y) &&
11492           (element == EL_EMPTY ||
11493            CAN_GROW_INTO(element) ||
11494            element == EL_QUICKSAND_EMPTY ||
11495            element == EL_QUICKSAND_FAST_EMPTY ||
11496            element == EL_ACID_SPLASH_LEFT ||
11497            element == EL_ACID_SPLASH_RIGHT))
11498       {
11499         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11500             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11501             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11502             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11503           Feld[x][y] = EL_AMOEBA_DROP;
11504       }
11505
11506       random = random * 129 + 1;
11507     }
11508   }
11509 #endif
11510
11511   game.explosions_delayed = FALSE;
11512
11513   SCAN_PLAYFIELD(x, y)
11514   {
11515     element = Feld[x][y];
11516
11517     if (ExplodeField[x][y])
11518       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11519     else if (element == EL_EXPLOSION)
11520       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11521
11522     ExplodeField[x][y] = EX_TYPE_NONE;
11523   }
11524
11525   game.explosions_delayed = TRUE;
11526
11527   if (game.magic_wall_active)
11528   {
11529     if (!(game.magic_wall_time_left % 4))
11530     {
11531       int element = Feld[magic_wall_x][magic_wall_y];
11532
11533       if (element == EL_BD_MAGIC_WALL_FULL ||
11534           element == EL_BD_MAGIC_WALL_ACTIVE ||
11535           element == EL_BD_MAGIC_WALL_EMPTYING)
11536         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11537       else if (element == EL_DC_MAGIC_WALL_FULL ||
11538                element == EL_DC_MAGIC_WALL_ACTIVE ||
11539                element == EL_DC_MAGIC_WALL_EMPTYING)
11540         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11541       else
11542         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11543     }
11544
11545     if (game.magic_wall_time_left > 0)
11546     {
11547       game.magic_wall_time_left--;
11548
11549       if (!game.magic_wall_time_left)
11550       {
11551         SCAN_PLAYFIELD(x, y)
11552         {
11553           element = Feld[x][y];
11554
11555           if (element == EL_MAGIC_WALL_ACTIVE ||
11556               element == EL_MAGIC_WALL_FULL)
11557           {
11558             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11559             TEST_DrawLevelField(x, y);
11560           }
11561           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11562                    element == EL_BD_MAGIC_WALL_FULL)
11563           {
11564             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11565             TEST_DrawLevelField(x, y);
11566           }
11567           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11568                    element == EL_DC_MAGIC_WALL_FULL)
11569           {
11570             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11571             TEST_DrawLevelField(x, y);
11572           }
11573         }
11574
11575         game.magic_wall_active = FALSE;
11576       }
11577     }
11578   }
11579
11580   if (game.light_time_left > 0)
11581   {
11582     game.light_time_left--;
11583
11584     if (game.light_time_left == 0)
11585       RedrawAllLightSwitchesAndInvisibleElements();
11586   }
11587
11588   if (game.timegate_time_left > 0)
11589   {
11590     game.timegate_time_left--;
11591
11592     if (game.timegate_time_left == 0)
11593       CloseAllOpenTimegates();
11594   }
11595
11596   if (game.lenses_time_left > 0)
11597   {
11598     game.lenses_time_left--;
11599
11600     if (game.lenses_time_left == 0)
11601       RedrawAllInvisibleElementsForLenses();
11602   }
11603
11604   if (game.magnify_time_left > 0)
11605   {
11606     game.magnify_time_left--;
11607
11608     if (game.magnify_time_left == 0)
11609       RedrawAllInvisibleElementsForMagnifier();
11610   }
11611
11612   for (i = 0; i < MAX_PLAYERS; i++)
11613   {
11614     struct PlayerInfo *player = &stored_player[i];
11615
11616     if (SHIELD_ON(player))
11617     {
11618       if (player->shield_deadly_time_left)
11619         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11620       else if (player->shield_normal_time_left)
11621         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11622     }
11623   }
11624
11625 #if USE_DELAYED_GFX_REDRAW
11626   SCAN_PLAYFIELD(x, y)
11627   {
11628     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11629     {
11630       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11631          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11632
11633       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11634         DrawLevelField(x, y);
11635
11636       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11637         DrawLevelFieldCrumbled(x, y);
11638
11639       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11640         DrawLevelFieldCrumbledNeighbours(x, y);
11641
11642       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11643         DrawTwinkleOnField(x, y);
11644     }
11645
11646     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11647   }
11648 #endif
11649
11650   CheckLevelTime();
11651
11652   DrawAllPlayers();
11653   PlayAllPlayersSound();
11654
11655   if (options.debug)                    /* calculate frames per second */
11656   {
11657     static unsigned int fps_counter = 0;
11658     static int fps_frames = 0;
11659     unsigned int fps_delay_ms = Counter() - fps_counter;
11660
11661     fps_frames++;
11662
11663     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11664     {
11665       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11666
11667       fps_frames = 0;
11668       fps_counter = Counter();
11669     }
11670
11671     redraw_mask |= REDRAW_FPS;
11672   }
11673
11674   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11675
11676   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11677   {
11678     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11679
11680     local_player->show_envelope = 0;
11681   }
11682
11683   /* use random number generator in every frame to make it less predictable */
11684   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11685     RND(1);
11686 }
11687
11688 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11689 {
11690   int min_x = x, min_y = y, max_x = x, max_y = y;
11691   int i;
11692
11693   for (i = 0; i < MAX_PLAYERS; i++)
11694   {
11695     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11696
11697     if (!stored_player[i].active || &stored_player[i] == player)
11698       continue;
11699
11700     min_x = MIN(min_x, jx);
11701     min_y = MIN(min_y, jy);
11702     max_x = MAX(max_x, jx);
11703     max_y = MAX(max_y, jy);
11704   }
11705
11706   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11707 }
11708
11709 static boolean AllPlayersInVisibleScreen()
11710 {
11711   int i;
11712
11713   for (i = 0; i < MAX_PLAYERS; i++)
11714   {
11715     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11716
11717     if (!stored_player[i].active)
11718       continue;
11719
11720     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11721       return FALSE;
11722   }
11723
11724   return TRUE;
11725 }
11726
11727 void ScrollLevel(int dx, int dy)
11728 {
11729   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
11730   int x, y;
11731
11732   BlitBitmap(drawto_field, drawto_field,
11733              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
11734              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
11735              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
11736              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
11737              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
11738              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
11739
11740   if (dx != 0)
11741   {
11742     x = (dx == 1 ? BX1 : BX2);
11743     for (y = BY1; y <= BY2; y++)
11744       DrawScreenField(x, y);
11745   }
11746
11747   if (dy != 0)
11748   {
11749     y = (dy == 1 ? BY1 : BY2);
11750     for (x = BX1; x <= BX2; x++)
11751       DrawScreenField(x, y);
11752   }
11753
11754   redraw_mask |= REDRAW_FIELD;
11755 }
11756
11757 static boolean canFallDown(struct PlayerInfo *player)
11758 {
11759   int jx = player->jx, jy = player->jy;
11760
11761   return (IN_LEV_FIELD(jx, jy + 1) &&
11762           (IS_FREE(jx, jy + 1) ||
11763            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11764           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11765           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11766 }
11767
11768 static boolean canPassField(int x, int y, int move_dir)
11769 {
11770   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11771   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11772   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11773   int nextx = x + dx;
11774   int nexty = y + dy;
11775   int element = Feld[x][y];
11776
11777   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11778           !CAN_MOVE(element) &&
11779           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11780           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11781           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11782 }
11783
11784 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11785 {
11786   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11787   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11788   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11789   int newx = x + dx;
11790   int newy = y + dy;
11791
11792   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11793           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11794           (IS_DIGGABLE(Feld[newx][newy]) ||
11795            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11796            canPassField(newx, newy, move_dir)));
11797 }
11798
11799 static void CheckGravityMovement(struct PlayerInfo *player)
11800 {
11801   if (player->gravity && !player->programmed_action)
11802   {
11803     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11804     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11805     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11806     int jx = player->jx, jy = player->jy;
11807     boolean player_is_moving_to_valid_field =
11808       (!player_is_snapping &&
11809        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11810         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11811     boolean player_can_fall_down = canFallDown(player);
11812
11813     if (player_can_fall_down &&
11814         !player_is_moving_to_valid_field)
11815       player->programmed_action = MV_DOWN;
11816   }
11817 }
11818
11819 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11820 {
11821   return CheckGravityMovement(player);
11822
11823   if (player->gravity && !player->programmed_action)
11824   {
11825     int jx = player->jx, jy = player->jy;
11826     boolean field_under_player_is_free =
11827       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11828     boolean player_is_standing_on_valid_field =
11829       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11830        (IS_WALKABLE(Feld[jx][jy]) &&
11831         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11832
11833     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11834       player->programmed_action = MV_DOWN;
11835   }
11836 }
11837
11838 /*
11839   MovePlayerOneStep()
11840   -----------------------------------------------------------------------------
11841   dx, dy:               direction (non-diagonal) to try to move the player to
11842   real_dx, real_dy:     direction as read from input device (can be diagonal)
11843 */
11844
11845 boolean MovePlayerOneStep(struct PlayerInfo *player,
11846                           int dx, int dy, int real_dx, int real_dy)
11847 {
11848   int jx = player->jx, jy = player->jy;
11849   int new_jx = jx + dx, new_jy = jy + dy;
11850   int can_move;
11851   boolean player_can_move = !player->cannot_move;
11852
11853   if (!player->active || (!dx && !dy))
11854     return MP_NO_ACTION;
11855
11856   player->MovDir = (dx < 0 ? MV_LEFT :
11857                     dx > 0 ? MV_RIGHT :
11858                     dy < 0 ? MV_UP :
11859                     dy > 0 ? MV_DOWN :  MV_NONE);
11860
11861   if (!IN_LEV_FIELD(new_jx, new_jy))
11862     return MP_NO_ACTION;
11863
11864   if (!player_can_move)
11865   {
11866     if (player->MovPos == 0)
11867     {
11868       player->is_moving = FALSE;
11869       player->is_digging = FALSE;
11870       player->is_collecting = FALSE;
11871       player->is_snapping = FALSE;
11872       player->is_pushing = FALSE;
11873     }
11874   }
11875
11876   if (!options.network && game.centered_player_nr == -1 &&
11877       !AllPlayersInSight(player, new_jx, new_jy))
11878     return MP_NO_ACTION;
11879
11880   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11881   if (can_move != MP_MOVING)
11882     return can_move;
11883
11884   /* check if DigField() has caused relocation of the player */
11885   if (player->jx != jx || player->jy != jy)
11886     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11887
11888   StorePlayer[jx][jy] = 0;
11889   player->last_jx = jx;
11890   player->last_jy = jy;
11891   player->jx = new_jx;
11892   player->jy = new_jy;
11893   StorePlayer[new_jx][new_jy] = player->element_nr;
11894
11895   if (player->move_delay_value_next != -1)
11896   {
11897     player->move_delay_value = player->move_delay_value_next;
11898     player->move_delay_value_next = -1;
11899   }
11900
11901   player->MovPos =
11902     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11903
11904   player->step_counter++;
11905
11906   PlayerVisit[jx][jy] = FrameCounter;
11907
11908   player->is_moving = TRUE;
11909
11910 #if 1
11911   /* should better be called in MovePlayer(), but this breaks some tapes */
11912   ScrollPlayer(player, SCROLL_INIT);
11913 #endif
11914
11915   return MP_MOVING;
11916 }
11917
11918 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11919 {
11920   int jx = player->jx, jy = player->jy;
11921   int old_jx = jx, old_jy = jy;
11922   int moved = MP_NO_ACTION;
11923
11924   if (!player->active)
11925     return FALSE;
11926
11927   if (!dx && !dy)
11928   {
11929     if (player->MovPos == 0)
11930     {
11931       player->is_moving = FALSE;
11932       player->is_digging = FALSE;
11933       player->is_collecting = FALSE;
11934       player->is_snapping = FALSE;
11935       player->is_pushing = FALSE;
11936     }
11937
11938     return FALSE;
11939   }
11940
11941   if (player->move_delay > 0)
11942     return FALSE;
11943
11944   player->move_delay = -1;              /* set to "uninitialized" value */
11945
11946   /* store if player is automatically moved to next field */
11947   player->is_auto_moving = (player->programmed_action != MV_NONE);
11948
11949   /* remove the last programmed player action */
11950   player->programmed_action = 0;
11951
11952   if (player->MovPos)
11953   {
11954     /* should only happen if pre-1.2 tape recordings are played */
11955     /* this is only for backward compatibility */
11956
11957     int original_move_delay_value = player->move_delay_value;
11958
11959 #if DEBUG
11960     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
11961            tape.counter);
11962 #endif
11963
11964     /* scroll remaining steps with finest movement resolution */
11965     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11966
11967     while (player->MovPos)
11968     {
11969       ScrollPlayer(player, SCROLL_GO_ON);
11970       ScrollScreen(NULL, SCROLL_GO_ON);
11971
11972       AdvanceFrameAndPlayerCounters(player->index_nr);
11973
11974       DrawAllPlayers();
11975       BackToFront();
11976     }
11977
11978     player->move_delay_value = original_move_delay_value;
11979   }
11980
11981   player->is_active = FALSE;
11982
11983   if (player->last_move_dir & MV_HORIZONTAL)
11984   {
11985     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11986       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11987   }
11988   else
11989   {
11990     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11991       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11992   }
11993
11994   if (!moved && !player->is_active)
11995   {
11996     player->is_moving = FALSE;
11997     player->is_digging = FALSE;
11998     player->is_collecting = FALSE;
11999     player->is_snapping = FALSE;
12000     player->is_pushing = FALSE;
12001   }
12002
12003   jx = player->jx;
12004   jy = player->jy;
12005
12006   if (moved & MP_MOVING && !ScreenMovPos &&
12007       (player->index_nr == game.centered_player_nr ||
12008        game.centered_player_nr == -1))
12009   {
12010     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12011     int offset = game.scroll_delay_value;
12012
12013     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12014     {
12015       /* actual player has left the screen -- scroll in that direction */
12016       if (jx != old_jx)         /* player has moved horizontally */
12017         scroll_x += (jx - old_jx);
12018       else                      /* player has moved vertically */
12019         scroll_y += (jy - old_jy);
12020     }
12021     else
12022     {
12023       if (jx != old_jx)         /* player has moved horizontally */
12024       {
12025         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12026             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12027           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12028
12029         /* don't scroll over playfield boundaries */
12030         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12031           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12032
12033         /* don't scroll more than one field at a time */
12034         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12035
12036         /* don't scroll against the player's moving direction */
12037         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12038             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12039           scroll_x = old_scroll_x;
12040       }
12041       else                      /* player has moved vertically */
12042       {
12043         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12044             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12045           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12046
12047         /* don't scroll over playfield boundaries */
12048         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12049           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12050
12051         /* don't scroll more than one field at a time */
12052         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12053
12054         /* don't scroll against the player's moving direction */
12055         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12056             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12057           scroll_y = old_scroll_y;
12058       }
12059     }
12060
12061     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12062     {
12063       if (!options.network && game.centered_player_nr == -1 &&
12064           !AllPlayersInVisibleScreen())
12065       {
12066         scroll_x = old_scroll_x;
12067         scroll_y = old_scroll_y;
12068       }
12069       else
12070       {
12071         ScrollScreen(player, SCROLL_INIT);
12072         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12073       }
12074     }
12075   }
12076
12077   player->StepFrame = 0;
12078
12079   if (moved & MP_MOVING)
12080   {
12081     if (old_jx != jx && old_jy == jy)
12082       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12083     else if (old_jx == jx && old_jy != jy)
12084       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12085
12086     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12087
12088     player->last_move_dir = player->MovDir;
12089     player->is_moving = TRUE;
12090     player->is_snapping = FALSE;
12091     player->is_switching = FALSE;
12092     player->is_dropping = FALSE;
12093     player->is_dropping_pressed = FALSE;
12094     player->drop_pressed_delay = 0;
12095
12096 #if 0
12097     /* should better be called here than above, but this breaks some tapes */
12098     ScrollPlayer(player, SCROLL_INIT);
12099 #endif
12100   }
12101   else
12102   {
12103     CheckGravityMovementWhenNotMoving(player);
12104
12105     player->is_moving = FALSE;
12106
12107     /* at this point, the player is allowed to move, but cannot move right now
12108        (e.g. because of something blocking the way) -- ensure that the player
12109        is also allowed to move in the next frame (in old versions before 3.1.1,
12110        the player was forced to wait again for eight frames before next try) */
12111
12112     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12113       player->move_delay = 0;   /* allow direct movement in the next frame */
12114   }
12115
12116   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12117     player->move_delay = player->move_delay_value;
12118
12119   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12120   {
12121     TestIfPlayerTouchesBadThing(jx, jy);
12122     TestIfPlayerTouchesCustomElement(jx, jy);
12123   }
12124
12125   if (!player->active)
12126     RemovePlayer(player);
12127
12128   return moved;
12129 }
12130
12131 void ScrollPlayer(struct PlayerInfo *player, int mode)
12132 {
12133   int jx = player->jx, jy = player->jy;
12134   int last_jx = player->last_jx, last_jy = player->last_jy;
12135   int move_stepsize = TILEX / player->move_delay_value;
12136
12137   if (!player->active)
12138     return;
12139
12140   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12141     return;
12142
12143   if (mode == SCROLL_INIT)
12144   {
12145     player->actual_frame_counter = FrameCounter;
12146     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12147
12148     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12149         Feld[last_jx][last_jy] == EL_EMPTY)
12150     {
12151       int last_field_block_delay = 0;   /* start with no blocking at all */
12152       int block_delay_adjustment = player->block_delay_adjustment;
12153
12154       /* if player blocks last field, add delay for exactly one move */
12155       if (player->block_last_field)
12156       {
12157         last_field_block_delay += player->move_delay_value;
12158
12159         /* when blocking enabled, prevent moving up despite gravity */
12160         if (player->gravity && player->MovDir == MV_UP)
12161           block_delay_adjustment = -1;
12162       }
12163
12164       /* add block delay adjustment (also possible when not blocking) */
12165       last_field_block_delay += block_delay_adjustment;
12166
12167       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12168       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12169     }
12170
12171     if (player->MovPos != 0)    /* player has not yet reached destination */
12172       return;
12173   }
12174   else if (!FrameReached(&player->actual_frame_counter, 1))
12175     return;
12176
12177   if (player->MovPos != 0)
12178   {
12179     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12180     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12181
12182     /* before DrawPlayer() to draw correct player graphic for this case */
12183     if (player->MovPos == 0)
12184       CheckGravityMovement(player);
12185   }
12186
12187   if (player->MovPos == 0)      /* player reached destination field */
12188   {
12189     if (player->move_delay_reset_counter > 0)
12190     {
12191       player->move_delay_reset_counter--;
12192
12193       if (player->move_delay_reset_counter == 0)
12194       {
12195         /* continue with normal speed after quickly moving through gate */
12196         HALVE_PLAYER_SPEED(player);
12197
12198         /* be able to make the next move without delay */
12199         player->move_delay = 0;
12200       }
12201     }
12202
12203     player->last_jx = jx;
12204     player->last_jy = jy;
12205
12206     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12207         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12208         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12209         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12210         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12211         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12212         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12213         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12214     {
12215       DrawPlayer(player);       /* needed here only to cleanup last field */
12216       RemovePlayer(player);
12217
12218       if (local_player->friends_still_needed == 0 ||
12219           IS_SP_ELEMENT(Feld[jx][jy]))
12220         PlayerWins(player);
12221     }
12222
12223     /* this breaks one level: "machine", level 000 */
12224     {
12225       int move_direction = player->MovDir;
12226       int enter_side = MV_DIR_OPPOSITE(move_direction);
12227       int leave_side = move_direction;
12228       int old_jx = last_jx;
12229       int old_jy = last_jy;
12230       int old_element = Feld[old_jx][old_jy];
12231       int new_element = Feld[jx][jy];
12232
12233       if (IS_CUSTOM_ELEMENT(old_element))
12234         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12235                                    CE_LEFT_BY_PLAYER,
12236                                    player->index_bit, leave_side);
12237
12238       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12239                                           CE_PLAYER_LEAVES_X,
12240                                           player->index_bit, leave_side);
12241
12242       if (IS_CUSTOM_ELEMENT(new_element))
12243         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12244                                    player->index_bit, enter_side);
12245
12246       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12247                                           CE_PLAYER_ENTERS_X,
12248                                           player->index_bit, enter_side);
12249
12250       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12251                                         CE_MOVE_OF_X, move_direction);
12252     }
12253
12254     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12255     {
12256       TestIfPlayerTouchesBadThing(jx, jy);
12257       TestIfPlayerTouchesCustomElement(jx, jy);
12258
12259       /* needed because pushed element has not yet reached its destination,
12260          so it would trigger a change event at its previous field location */
12261       if (!player->is_pushing)
12262         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12263
12264       if (!player->active)
12265         RemovePlayer(player);
12266     }
12267
12268     if (!local_player->LevelSolved && level.use_step_counter)
12269     {
12270       int i;
12271
12272       TimePlayed++;
12273
12274       if (TimeLeft > 0)
12275       {
12276         TimeLeft--;
12277
12278         if (TimeLeft <= 10 && setup.time_limit)
12279           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12280
12281         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12282
12283         DisplayGameControlValues();
12284
12285         if (!TimeLeft && setup.time_limit)
12286           for (i = 0; i < MAX_PLAYERS; i++)
12287             KillPlayer(&stored_player[i]);
12288       }
12289       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12290       {
12291         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12292
12293         DisplayGameControlValues();
12294       }
12295     }
12296
12297     if (tape.single_step && tape.recording && !tape.pausing &&
12298         !player->programmed_action)
12299       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12300   }
12301 }
12302
12303 void ScrollScreen(struct PlayerInfo *player, int mode)
12304 {
12305   static unsigned int screen_frame_counter = 0;
12306
12307   if (mode == SCROLL_INIT)
12308   {
12309     /* set scrolling step size according to actual player's moving speed */
12310     ScrollStepSize = TILEX / player->move_delay_value;
12311
12312     screen_frame_counter = FrameCounter;
12313     ScreenMovDir = player->MovDir;
12314     ScreenMovPos = player->MovPos;
12315     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12316     return;
12317   }
12318   else if (!FrameReached(&screen_frame_counter, 1))
12319     return;
12320
12321   if (ScreenMovPos)
12322   {
12323     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12324     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12325     redraw_mask |= REDRAW_FIELD;
12326   }
12327   else
12328     ScreenMovDir = MV_NONE;
12329 }
12330
12331 void TestIfPlayerTouchesCustomElement(int x, int y)
12332 {
12333   static int xy[4][2] =
12334   {
12335     { 0, -1 },
12336     { -1, 0 },
12337     { +1, 0 },
12338     { 0, +1 }
12339   };
12340   static int trigger_sides[4][2] =
12341   {
12342     /* center side       border side */
12343     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12344     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12345     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12346     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12347   };
12348   static int touch_dir[4] =
12349   {
12350     MV_LEFT | MV_RIGHT,
12351     MV_UP   | MV_DOWN,
12352     MV_UP   | MV_DOWN,
12353     MV_LEFT | MV_RIGHT
12354   };
12355   int center_element = Feld[x][y];      /* should always be non-moving! */
12356   int i;
12357
12358   for (i = 0; i < NUM_DIRECTIONS; i++)
12359   {
12360     int xx = x + xy[i][0];
12361     int yy = y + xy[i][1];
12362     int center_side = trigger_sides[i][0];
12363     int border_side = trigger_sides[i][1];
12364     int border_element;
12365
12366     if (!IN_LEV_FIELD(xx, yy))
12367       continue;
12368
12369     if (IS_PLAYER(x, y))                /* player found at center element */
12370     {
12371       struct PlayerInfo *player = PLAYERINFO(x, y);
12372
12373       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12374         border_element = Feld[xx][yy];          /* may be moving! */
12375       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12376         border_element = Feld[xx][yy];
12377       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12378         border_element = MovingOrBlocked2Element(xx, yy);
12379       else
12380         continue;               /* center and border element do not touch */
12381
12382       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12383                                  player->index_bit, border_side);
12384       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12385                                           CE_PLAYER_TOUCHES_X,
12386                                           player->index_bit, border_side);
12387
12388       {
12389         /* use player element that is initially defined in the level playfield,
12390            not the player element that corresponds to the runtime player number
12391            (example: a level that contains EL_PLAYER_3 as the only player would
12392            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12393         int player_element = PLAYERINFO(x, y)->initial_element;
12394
12395         CheckElementChangeBySide(xx, yy, border_element, player_element,
12396                                  CE_TOUCHING_X, border_side);
12397       }
12398     }
12399     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12400     {
12401       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12402
12403       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12404       {
12405         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12406           continue;             /* center and border element do not touch */
12407       }
12408
12409       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12410                                  player->index_bit, center_side);
12411       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12412                                           CE_PLAYER_TOUCHES_X,
12413                                           player->index_bit, center_side);
12414
12415       {
12416         /* use player element that is initially defined in the level playfield,
12417            not the player element that corresponds to the runtime player number
12418            (example: a level that contains EL_PLAYER_3 as the only player would
12419            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12420         int player_element = PLAYERINFO(xx, yy)->initial_element;
12421
12422         CheckElementChangeBySide(x, y, center_element, player_element,
12423                                  CE_TOUCHING_X, center_side);
12424       }
12425
12426       break;
12427     }
12428   }
12429 }
12430
12431 void TestIfElementTouchesCustomElement(int x, int y)
12432 {
12433   static int xy[4][2] =
12434   {
12435     { 0, -1 },
12436     { -1, 0 },
12437     { +1, 0 },
12438     { 0, +1 }
12439   };
12440   static int trigger_sides[4][2] =
12441   {
12442     /* center side      border side */
12443     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12444     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12445     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12446     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12447   };
12448   static int touch_dir[4] =
12449   {
12450     MV_LEFT | MV_RIGHT,
12451     MV_UP   | MV_DOWN,
12452     MV_UP   | MV_DOWN,
12453     MV_LEFT | MV_RIGHT
12454   };
12455   boolean change_center_element = FALSE;
12456   int center_element = Feld[x][y];      /* should always be non-moving! */
12457   int border_element_old[NUM_DIRECTIONS];
12458   int i;
12459
12460   for (i = 0; i < NUM_DIRECTIONS; i++)
12461   {
12462     int xx = x + xy[i][0];
12463     int yy = y + xy[i][1];
12464     int border_element;
12465
12466     border_element_old[i] = -1;
12467
12468     if (!IN_LEV_FIELD(xx, yy))
12469       continue;
12470
12471     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12472       border_element = Feld[xx][yy];    /* may be moving! */
12473     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12474       border_element = Feld[xx][yy];
12475     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12476       border_element = MovingOrBlocked2Element(xx, yy);
12477     else
12478       continue;                 /* center and border element do not touch */
12479
12480     border_element_old[i] = border_element;
12481   }
12482
12483   for (i = 0; i < NUM_DIRECTIONS; i++)
12484   {
12485     int xx = x + xy[i][0];
12486     int yy = y + xy[i][1];
12487     int center_side = trigger_sides[i][0];
12488     int border_element = border_element_old[i];
12489
12490     if (border_element == -1)
12491       continue;
12492
12493     /* check for change of border element */
12494     CheckElementChangeBySide(xx, yy, border_element, center_element,
12495                              CE_TOUCHING_X, center_side);
12496
12497     /* (center element cannot be player, so we dont have to check this here) */
12498   }
12499
12500   for (i = 0; i < NUM_DIRECTIONS; i++)
12501   {
12502     int xx = x + xy[i][0];
12503     int yy = y + xy[i][1];
12504     int border_side = trigger_sides[i][1];
12505     int border_element = border_element_old[i];
12506
12507     if (border_element == -1)
12508       continue;
12509
12510     /* check for change of center element (but change it only once) */
12511     if (!change_center_element)
12512       change_center_element =
12513         CheckElementChangeBySide(x, y, center_element, border_element,
12514                                  CE_TOUCHING_X, border_side);
12515
12516     if (IS_PLAYER(xx, yy))
12517     {
12518       /* use player element that is initially defined in the level playfield,
12519          not the player element that corresponds to the runtime player number
12520          (example: a level that contains EL_PLAYER_3 as the only player would
12521          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12522       int player_element = PLAYERINFO(xx, yy)->initial_element;
12523
12524       CheckElementChangeBySide(x, y, center_element, player_element,
12525                                CE_TOUCHING_X, border_side);
12526     }
12527   }
12528 }
12529
12530 void TestIfElementHitsCustomElement(int x, int y, int direction)
12531 {
12532   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12533   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12534   int hitx = x + dx, hity = y + dy;
12535   int hitting_element = Feld[x][y];
12536   int touched_element;
12537
12538   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12539     return;
12540
12541   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12542                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12543
12544   if (IN_LEV_FIELD(hitx, hity))
12545   {
12546     int opposite_direction = MV_DIR_OPPOSITE(direction);
12547     int hitting_side = direction;
12548     int touched_side = opposite_direction;
12549     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12550                           MovDir[hitx][hity] != direction ||
12551                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12552
12553     object_hit = TRUE;
12554
12555     if (object_hit)
12556     {
12557       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12558                                CE_HITTING_X, touched_side);
12559
12560       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12561                                CE_HIT_BY_X, hitting_side);
12562
12563       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12564                                CE_HIT_BY_SOMETHING, opposite_direction);
12565
12566       if (IS_PLAYER(hitx, hity))
12567       {
12568         /* use player element that is initially defined in the level playfield,
12569            not the player element that corresponds to the runtime player number
12570            (example: a level that contains EL_PLAYER_3 as the only player would
12571            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12572         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12573
12574         CheckElementChangeBySide(x, y, hitting_element, player_element,
12575                                  CE_HITTING_X, touched_side);
12576       }
12577     }
12578   }
12579
12580   /* "hitting something" is also true when hitting the playfield border */
12581   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12582                            CE_HITTING_SOMETHING, direction);
12583 }
12584
12585 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12586 {
12587   int i, kill_x = -1, kill_y = -1;
12588
12589   int bad_element = -1;
12590   static int test_xy[4][2] =
12591   {
12592     { 0, -1 },
12593     { -1, 0 },
12594     { +1, 0 },
12595     { 0, +1 }
12596   };
12597   static int test_dir[4] =
12598   {
12599     MV_UP,
12600     MV_LEFT,
12601     MV_RIGHT,
12602     MV_DOWN
12603   };
12604
12605   for (i = 0; i < NUM_DIRECTIONS; i++)
12606   {
12607     int test_x, test_y, test_move_dir, test_element;
12608
12609     test_x = good_x + test_xy[i][0];
12610     test_y = good_y + test_xy[i][1];
12611
12612     if (!IN_LEV_FIELD(test_x, test_y))
12613       continue;
12614
12615     test_move_dir =
12616       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12617
12618     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12619
12620     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12621        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12622     */
12623     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12624         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12625     {
12626       kill_x = test_x;
12627       kill_y = test_y;
12628       bad_element = test_element;
12629
12630       break;
12631     }
12632   }
12633
12634   if (kill_x != -1 || kill_y != -1)
12635   {
12636     if (IS_PLAYER(good_x, good_y))
12637     {
12638       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12639
12640       if (player->shield_deadly_time_left > 0 &&
12641           !IS_INDESTRUCTIBLE(bad_element))
12642         Bang(kill_x, kill_y);
12643       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12644         KillPlayer(player);
12645     }
12646     else
12647       Bang(good_x, good_y);
12648   }
12649 }
12650
12651 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12652 {
12653   int i, kill_x = -1, kill_y = -1;
12654   int bad_element = Feld[bad_x][bad_y];
12655   static int test_xy[4][2] =
12656   {
12657     { 0, -1 },
12658     { -1, 0 },
12659     { +1, 0 },
12660     { 0, +1 }
12661   };
12662   static int touch_dir[4] =
12663   {
12664     MV_LEFT | MV_RIGHT,
12665     MV_UP   | MV_DOWN,
12666     MV_UP   | MV_DOWN,
12667     MV_LEFT | MV_RIGHT
12668   };
12669   static int test_dir[4] =
12670   {
12671     MV_UP,
12672     MV_LEFT,
12673     MV_RIGHT,
12674     MV_DOWN
12675   };
12676
12677   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12678     return;
12679
12680   for (i = 0; i < NUM_DIRECTIONS; i++)
12681   {
12682     int test_x, test_y, test_move_dir, test_element;
12683
12684     test_x = bad_x + test_xy[i][0];
12685     test_y = bad_y + test_xy[i][1];
12686
12687     if (!IN_LEV_FIELD(test_x, test_y))
12688       continue;
12689
12690     test_move_dir =
12691       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12692
12693     test_element = Feld[test_x][test_y];
12694
12695     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12696        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12697     */
12698     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12699         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12700     {
12701       /* good thing is player or penguin that does not move away */
12702       if (IS_PLAYER(test_x, test_y))
12703       {
12704         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12705
12706         if (bad_element == EL_ROBOT && player->is_moving)
12707           continue;     /* robot does not kill player if he is moving */
12708
12709         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12710         {
12711           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12712             continue;           /* center and border element do not touch */
12713         }
12714
12715         kill_x = test_x;
12716         kill_y = test_y;
12717
12718         break;
12719       }
12720       else if (test_element == EL_PENGUIN)
12721       {
12722         kill_x = test_x;
12723         kill_y = test_y;
12724
12725         break;
12726       }
12727     }
12728   }
12729
12730   if (kill_x != -1 || kill_y != -1)
12731   {
12732     if (IS_PLAYER(kill_x, kill_y))
12733     {
12734       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12735
12736       if (player->shield_deadly_time_left > 0 &&
12737           !IS_INDESTRUCTIBLE(bad_element))
12738         Bang(bad_x, bad_y);
12739       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12740         KillPlayer(player);
12741     }
12742     else
12743       Bang(kill_x, kill_y);
12744   }
12745 }
12746
12747 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12748 {
12749   int bad_element = Feld[bad_x][bad_y];
12750   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12751   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12752   int test_x = bad_x + dx, test_y = bad_y + dy;
12753   int test_move_dir, test_element;
12754   int kill_x = -1, kill_y = -1;
12755
12756   if (!IN_LEV_FIELD(test_x, test_y))
12757     return;
12758
12759   test_move_dir =
12760     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12761
12762   test_element = Feld[test_x][test_y];
12763
12764   if (test_move_dir != bad_move_dir)
12765   {
12766     /* good thing can be player or penguin that does not move away */
12767     if (IS_PLAYER(test_x, test_y))
12768     {
12769       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12770
12771       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12772          player as being hit when he is moving towards the bad thing, because
12773          the "get hit by" condition would be lost after the player stops) */
12774       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12775         return;         /* player moves away from bad thing */
12776
12777       kill_x = test_x;
12778       kill_y = test_y;
12779     }
12780     else if (test_element == EL_PENGUIN)
12781     {
12782       kill_x = test_x;
12783       kill_y = test_y;
12784     }
12785   }
12786
12787   if (kill_x != -1 || kill_y != -1)
12788   {
12789     if (IS_PLAYER(kill_x, kill_y))
12790     {
12791       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12792
12793       if (player->shield_deadly_time_left > 0 &&
12794           !IS_INDESTRUCTIBLE(bad_element))
12795         Bang(bad_x, bad_y);
12796       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12797         KillPlayer(player);
12798     }
12799     else
12800       Bang(kill_x, kill_y);
12801   }
12802 }
12803
12804 void TestIfPlayerTouchesBadThing(int x, int y)
12805 {
12806   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12807 }
12808
12809 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12810 {
12811   TestIfGoodThingHitsBadThing(x, y, move_dir);
12812 }
12813
12814 void TestIfBadThingTouchesPlayer(int x, int y)
12815 {
12816   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12817 }
12818
12819 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12820 {
12821   TestIfBadThingHitsGoodThing(x, y, move_dir);
12822 }
12823
12824 void TestIfFriendTouchesBadThing(int x, int y)
12825 {
12826   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12827 }
12828
12829 void TestIfBadThingTouchesFriend(int x, int y)
12830 {
12831   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12832 }
12833
12834 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12835 {
12836   int i, kill_x = bad_x, kill_y = bad_y;
12837   static int xy[4][2] =
12838   {
12839     { 0, -1 },
12840     { -1, 0 },
12841     { +1, 0 },
12842     { 0, +1 }
12843   };
12844
12845   for (i = 0; i < NUM_DIRECTIONS; i++)
12846   {
12847     int x, y, element;
12848
12849     x = bad_x + xy[i][0];
12850     y = bad_y + xy[i][1];
12851     if (!IN_LEV_FIELD(x, y))
12852       continue;
12853
12854     element = Feld[x][y];
12855     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12856         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12857     {
12858       kill_x = x;
12859       kill_y = y;
12860       break;
12861     }
12862   }
12863
12864   if (kill_x != bad_x || kill_y != bad_y)
12865     Bang(bad_x, bad_y);
12866 }
12867
12868 void KillPlayer(struct PlayerInfo *player)
12869 {
12870   int jx = player->jx, jy = player->jy;
12871
12872   if (!player->active)
12873     return;
12874
12875 #if 0
12876   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12877          player->killed, player->active, player->reanimated);
12878 #endif
12879
12880   /* the following code was introduced to prevent an infinite loop when calling
12881      -> Bang()
12882      -> CheckTriggeredElementChangeExt()
12883      -> ExecuteCustomElementAction()
12884      -> KillPlayer()
12885      -> (infinitely repeating the above sequence of function calls)
12886      which occurs when killing the player while having a CE with the setting
12887      "kill player X when explosion of <player X>"; the solution using a new
12888      field "player->killed" was chosen for backwards compatibility, although
12889      clever use of the fields "player->active" etc. would probably also work */
12890 #if 1
12891   if (player->killed)
12892     return;
12893 #endif
12894
12895   player->killed = TRUE;
12896
12897   /* remove accessible field at the player's position */
12898   Feld[jx][jy] = EL_EMPTY;
12899
12900   /* deactivate shield (else Bang()/Explode() would not work right) */
12901   player->shield_normal_time_left = 0;
12902   player->shield_deadly_time_left = 0;
12903
12904 #if 0
12905   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12906          player->killed, player->active, player->reanimated);
12907 #endif
12908
12909   Bang(jx, jy);
12910
12911 #if 0
12912   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12913          player->killed, player->active, player->reanimated);
12914 #endif
12915
12916   if (player->reanimated)       /* killed player may have been reanimated */
12917     player->killed = player->reanimated = FALSE;
12918   else
12919     BuryPlayer(player);
12920 }
12921
12922 static void KillPlayerUnlessEnemyProtected(int x, int y)
12923 {
12924   if (!PLAYER_ENEMY_PROTECTED(x, y))
12925     KillPlayer(PLAYERINFO(x, y));
12926 }
12927
12928 static void KillPlayerUnlessExplosionProtected(int x, int y)
12929 {
12930   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12931     KillPlayer(PLAYERINFO(x, y));
12932 }
12933
12934 void BuryPlayer(struct PlayerInfo *player)
12935 {
12936   int jx = player->jx, jy = player->jy;
12937
12938   if (!player->active)
12939     return;
12940
12941   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12942   PlayLevelSound(jx, jy, SND_GAME_LOSING);
12943
12944   player->GameOver = TRUE;
12945   RemovePlayer(player);
12946 }
12947
12948 void RemovePlayer(struct PlayerInfo *player)
12949 {
12950   int jx = player->jx, jy = player->jy;
12951   int i, found = FALSE;
12952
12953   player->present = FALSE;
12954   player->active = FALSE;
12955
12956   if (!ExplodeField[jx][jy])
12957     StorePlayer[jx][jy] = 0;
12958
12959   if (player->is_moving)
12960     TEST_DrawLevelField(player->last_jx, player->last_jy);
12961
12962   for (i = 0; i < MAX_PLAYERS; i++)
12963     if (stored_player[i].active)
12964       found = TRUE;
12965
12966   if (!found)
12967     AllPlayersGone = TRUE;
12968
12969   ExitX = ZX = jx;
12970   ExitY = ZY = jy;
12971 }
12972
12973 static void setFieldForSnapping(int x, int y, int element, int direction)
12974 {
12975   struct ElementInfo *ei = &element_info[element];
12976   int direction_bit = MV_DIR_TO_BIT(direction);
12977   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12978   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12979                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12980
12981   Feld[x][y] = EL_ELEMENT_SNAPPING;
12982   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12983
12984   ResetGfxAnimation(x, y);
12985
12986   GfxElement[x][y] = element;
12987   GfxAction[x][y] = action;
12988   GfxDir[x][y] = direction;
12989   GfxFrame[x][y] = -1;
12990 }
12991
12992 /*
12993   =============================================================================
12994   checkDiagonalPushing()
12995   -----------------------------------------------------------------------------
12996   check if diagonal input device direction results in pushing of object
12997   (by checking if the alternative direction is walkable, diggable, ...)
12998   =============================================================================
12999 */
13000
13001 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13002                                     int x, int y, int real_dx, int real_dy)
13003 {
13004   int jx, jy, dx, dy, xx, yy;
13005
13006   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13007     return TRUE;
13008
13009   /* diagonal direction: check alternative direction */
13010   jx = player->jx;
13011   jy = player->jy;
13012   dx = x - jx;
13013   dy = y - jy;
13014   xx = jx + (dx == 0 ? real_dx : 0);
13015   yy = jy + (dy == 0 ? real_dy : 0);
13016
13017   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13018 }
13019
13020 /*
13021   =============================================================================
13022   DigField()
13023   -----------------------------------------------------------------------------
13024   x, y:                 field next to player (non-diagonal) to try to dig to
13025   real_dx, real_dy:     direction as read from input device (can be diagonal)
13026   =============================================================================
13027 */
13028
13029 static int DigField(struct PlayerInfo *player,
13030                     int oldx, int oldy, int x, int y,
13031                     int real_dx, int real_dy, int mode)
13032 {
13033   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13034   boolean player_was_pushing = player->is_pushing;
13035   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13036   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13037   int jx = oldx, jy = oldy;
13038   int dx = x - jx, dy = y - jy;
13039   int nextx = x + dx, nexty = y + dy;
13040   int move_direction = (dx == -1 ? MV_LEFT  :
13041                         dx == +1 ? MV_RIGHT :
13042                         dy == -1 ? MV_UP    :
13043                         dy == +1 ? MV_DOWN  : MV_NONE);
13044   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13045   int dig_side = MV_DIR_OPPOSITE(move_direction);
13046   int old_element = Feld[jx][jy];
13047   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13048   int collect_count;
13049
13050   if (is_player)                /* function can also be called by EL_PENGUIN */
13051   {
13052     if (player->MovPos == 0)
13053     {
13054       player->is_digging = FALSE;
13055       player->is_collecting = FALSE;
13056     }
13057
13058     if (player->MovPos == 0)    /* last pushing move finished */
13059       player->is_pushing = FALSE;
13060
13061     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13062     {
13063       player->is_switching = FALSE;
13064       player->push_delay = -1;
13065
13066       return MP_NO_ACTION;
13067     }
13068   }
13069
13070   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13071     old_element = Back[jx][jy];
13072
13073   /* in case of element dropped at player position, check background */
13074   else if (Back[jx][jy] != EL_EMPTY &&
13075            game.engine_version >= VERSION_IDENT(2,2,0,0))
13076     old_element = Back[jx][jy];
13077
13078   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13079     return MP_NO_ACTION;        /* field has no opening in this direction */
13080
13081   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13082     return MP_NO_ACTION;        /* field has no opening in this direction */
13083
13084   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13085   {
13086     SplashAcid(x, y);
13087
13088     Feld[jx][jy] = player->artwork_element;
13089     InitMovingField(jx, jy, MV_DOWN);
13090     Store[jx][jy] = EL_ACID;
13091     ContinueMoving(jx, jy);
13092     BuryPlayer(player);
13093
13094     return MP_DONT_RUN_INTO;
13095   }
13096
13097   if (player_can_move && DONT_RUN_INTO(element))
13098   {
13099     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13100
13101     return MP_DONT_RUN_INTO;
13102   }
13103
13104   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13105     return MP_NO_ACTION;
13106
13107   collect_count = element_info[element].collect_count_initial;
13108
13109   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13110     return MP_NO_ACTION;
13111
13112   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13113     player_can_move = player_can_move_or_snap;
13114
13115   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13116       game.engine_version >= VERSION_IDENT(2,2,0,0))
13117   {
13118     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13119                                player->index_bit, dig_side);
13120     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13121                                         player->index_bit, dig_side);
13122
13123     if (element == EL_DC_LANDMINE)
13124       Bang(x, y);
13125
13126     if (Feld[x][y] != element)          /* field changed by snapping */
13127       return MP_ACTION;
13128
13129     return MP_NO_ACTION;
13130   }
13131
13132   if (player->gravity && is_player && !player->is_auto_moving &&
13133       canFallDown(player) && move_direction != MV_DOWN &&
13134       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13135     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13136
13137   if (player_can_move &&
13138       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13139   {
13140     int sound_element = SND_ELEMENT(element);
13141     int sound_action = ACTION_WALKING;
13142
13143     if (IS_RND_GATE(element))
13144     {
13145       if (!player->key[RND_GATE_NR(element)])
13146         return MP_NO_ACTION;
13147     }
13148     else if (IS_RND_GATE_GRAY(element))
13149     {
13150       if (!player->key[RND_GATE_GRAY_NR(element)])
13151         return MP_NO_ACTION;
13152     }
13153     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13154     {
13155       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13156         return MP_NO_ACTION;
13157     }
13158     else if (element == EL_EXIT_OPEN ||
13159              element == EL_EM_EXIT_OPEN ||
13160              element == EL_EM_EXIT_OPENING ||
13161              element == EL_STEEL_EXIT_OPEN ||
13162              element == EL_EM_STEEL_EXIT_OPEN ||
13163              element == EL_EM_STEEL_EXIT_OPENING ||
13164              element == EL_SP_EXIT_OPEN ||
13165              element == EL_SP_EXIT_OPENING)
13166     {
13167       sound_action = ACTION_PASSING;    /* player is passing exit */
13168     }
13169     else if (element == EL_EMPTY)
13170     {
13171       sound_action = ACTION_MOVING;             /* nothing to walk on */
13172     }
13173
13174     /* play sound from background or player, whatever is available */
13175     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13176       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13177     else
13178       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13179   }
13180   else if (player_can_move &&
13181            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13182   {
13183     if (!ACCESS_FROM(element, opposite_direction))
13184       return MP_NO_ACTION;      /* field not accessible from this direction */
13185
13186     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13187       return MP_NO_ACTION;
13188
13189     if (IS_EM_GATE(element))
13190     {
13191       if (!player->key[EM_GATE_NR(element)])
13192         return MP_NO_ACTION;
13193     }
13194     else if (IS_EM_GATE_GRAY(element))
13195     {
13196       if (!player->key[EM_GATE_GRAY_NR(element)])
13197         return MP_NO_ACTION;
13198     }
13199     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13200     {
13201       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13202         return MP_NO_ACTION;
13203     }
13204     else if (IS_EMC_GATE(element))
13205     {
13206       if (!player->key[EMC_GATE_NR(element)])
13207         return MP_NO_ACTION;
13208     }
13209     else if (IS_EMC_GATE_GRAY(element))
13210     {
13211       if (!player->key[EMC_GATE_GRAY_NR(element)])
13212         return MP_NO_ACTION;
13213     }
13214     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13215     {
13216       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13217         return MP_NO_ACTION;
13218     }
13219     else if (element == EL_DC_GATE_WHITE ||
13220              element == EL_DC_GATE_WHITE_GRAY ||
13221              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13222     {
13223       if (player->num_white_keys == 0)
13224         return MP_NO_ACTION;
13225
13226       player->num_white_keys--;
13227     }
13228     else if (IS_SP_PORT(element))
13229     {
13230       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13231           element == EL_SP_GRAVITY_PORT_RIGHT ||
13232           element == EL_SP_GRAVITY_PORT_UP ||
13233           element == EL_SP_GRAVITY_PORT_DOWN)
13234         player->gravity = !player->gravity;
13235       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13236                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13237                element == EL_SP_GRAVITY_ON_PORT_UP ||
13238                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13239         player->gravity = TRUE;
13240       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13241                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13242                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13243                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13244         player->gravity = FALSE;
13245     }
13246
13247     /* automatically move to the next field with double speed */
13248     player->programmed_action = move_direction;
13249
13250     if (player->move_delay_reset_counter == 0)
13251     {
13252       player->move_delay_reset_counter = 2;     /* two double speed steps */
13253
13254       DOUBLE_PLAYER_SPEED(player);
13255     }
13256
13257     PlayLevelSoundAction(x, y, ACTION_PASSING);
13258   }
13259   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13260   {
13261     RemoveField(x, y);
13262
13263     if (mode != DF_SNAP)
13264     {
13265       GfxElement[x][y] = GFX_ELEMENT(element);
13266       player->is_digging = TRUE;
13267     }
13268
13269     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13270
13271     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13272                                         player->index_bit, dig_side);
13273
13274     if (mode == DF_SNAP)
13275     {
13276       if (level.block_snap_field)
13277         setFieldForSnapping(x, y, element, move_direction);
13278       else
13279         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13280
13281       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13282                                           player->index_bit, dig_side);
13283     }
13284   }
13285   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13286   {
13287     RemoveField(x, y);
13288
13289     if (is_player && mode != DF_SNAP)
13290     {
13291       GfxElement[x][y] = element;
13292       player->is_collecting = TRUE;
13293     }
13294
13295     if (element == EL_SPEED_PILL)
13296     {
13297       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13298     }
13299     else if (element == EL_EXTRA_TIME && level.time > 0)
13300     {
13301       TimeLeft += level.extra_time;
13302
13303       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13304
13305       DisplayGameControlValues();
13306     }
13307     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13308     {
13309       player->shield_normal_time_left += level.shield_normal_time;
13310       if (element == EL_SHIELD_DEADLY)
13311         player->shield_deadly_time_left += level.shield_deadly_time;
13312     }
13313     else if (element == EL_DYNAMITE ||
13314              element == EL_EM_DYNAMITE ||
13315              element == EL_SP_DISK_RED)
13316     {
13317       if (player->inventory_size < MAX_INVENTORY_SIZE)
13318         player->inventory_element[player->inventory_size++] = element;
13319
13320       DrawGameDoorValues();
13321     }
13322     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13323     {
13324       player->dynabomb_count++;
13325       player->dynabombs_left++;
13326     }
13327     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13328     {
13329       player->dynabomb_size++;
13330     }
13331     else if (element == EL_DYNABOMB_INCREASE_POWER)
13332     {
13333       player->dynabomb_xl = TRUE;
13334     }
13335     else if (IS_KEY(element))
13336     {
13337       player->key[KEY_NR(element)] = TRUE;
13338
13339       DrawGameDoorValues();
13340     }
13341     else if (element == EL_DC_KEY_WHITE)
13342     {
13343       player->num_white_keys++;
13344
13345       /* display white keys? */
13346       /* DrawGameDoorValues(); */
13347     }
13348     else if (IS_ENVELOPE(element))
13349     {
13350       player->show_envelope = element;
13351     }
13352     else if (element == EL_EMC_LENSES)
13353     {
13354       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13355
13356       RedrawAllInvisibleElementsForLenses();
13357     }
13358     else if (element == EL_EMC_MAGNIFIER)
13359     {
13360       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13361
13362       RedrawAllInvisibleElementsForMagnifier();
13363     }
13364     else if (IS_DROPPABLE(element) ||
13365              IS_THROWABLE(element))     /* can be collected and dropped */
13366     {
13367       int i;
13368
13369       if (collect_count == 0)
13370         player->inventory_infinite_element = element;
13371       else
13372         for (i = 0; i < collect_count; i++)
13373           if (player->inventory_size < MAX_INVENTORY_SIZE)
13374             player->inventory_element[player->inventory_size++] = element;
13375
13376       DrawGameDoorValues();
13377     }
13378     else if (collect_count > 0)
13379     {
13380       local_player->gems_still_needed -= collect_count;
13381       if (local_player->gems_still_needed < 0)
13382         local_player->gems_still_needed = 0;
13383
13384       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13385
13386       DisplayGameControlValues();
13387     }
13388
13389     RaiseScoreElement(element);
13390     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13391
13392     if (is_player)
13393       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13394                                           player->index_bit, dig_side);
13395
13396     if (mode == DF_SNAP)
13397     {
13398       if (level.block_snap_field)
13399         setFieldForSnapping(x, y, element, move_direction);
13400       else
13401         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13402
13403       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13404                                           player->index_bit, dig_side);
13405     }
13406   }
13407   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13408   {
13409     if (mode == DF_SNAP && element != EL_BD_ROCK)
13410       return MP_NO_ACTION;
13411
13412     if (CAN_FALL(element) && dy)
13413       return MP_NO_ACTION;
13414
13415     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13416         !(element == EL_SPRING && level.use_spring_bug))
13417       return MP_NO_ACTION;
13418
13419     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13420         ((move_direction & MV_VERTICAL &&
13421           ((element_info[element].move_pattern & MV_LEFT &&
13422             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13423            (element_info[element].move_pattern & MV_RIGHT &&
13424             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13425          (move_direction & MV_HORIZONTAL &&
13426           ((element_info[element].move_pattern & MV_UP &&
13427             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13428            (element_info[element].move_pattern & MV_DOWN &&
13429             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13430       return MP_NO_ACTION;
13431
13432     /* do not push elements already moving away faster than player */
13433     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13434         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13435       return MP_NO_ACTION;
13436
13437     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13438     {
13439       if (player->push_delay_value == -1 || !player_was_pushing)
13440         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13441     }
13442     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13443     {
13444       if (player->push_delay_value == -1)
13445         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13446     }
13447     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13448     {
13449       if (!player->is_pushing)
13450         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13451     }
13452
13453     player->is_pushing = TRUE;
13454     player->is_active = TRUE;
13455
13456     if (!(IN_LEV_FIELD(nextx, nexty) &&
13457           (IS_FREE(nextx, nexty) ||
13458            (IS_SB_ELEMENT(element) &&
13459             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13460            (IS_CUSTOM_ELEMENT(element) &&
13461             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13462       return MP_NO_ACTION;
13463
13464     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13465       return MP_NO_ACTION;
13466
13467     if (player->push_delay == -1)       /* new pushing; restart delay */
13468       player->push_delay = 0;
13469
13470     if (player->push_delay < player->push_delay_value &&
13471         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13472         element != EL_SPRING && element != EL_BALLOON)
13473     {
13474       /* make sure that there is no move delay before next try to push */
13475       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13476         player->move_delay = 0;
13477
13478       return MP_NO_ACTION;
13479     }
13480
13481     if (IS_CUSTOM_ELEMENT(element) &&
13482         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13483     {
13484       if (!DigFieldByCE(nextx, nexty, element))
13485         return MP_NO_ACTION;
13486     }
13487
13488     if (IS_SB_ELEMENT(element))
13489     {
13490       if (element == EL_SOKOBAN_FIELD_FULL)
13491       {
13492         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13493         local_player->sokobanfields_still_needed++;
13494       }
13495
13496       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13497       {
13498         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13499         local_player->sokobanfields_still_needed--;
13500       }
13501
13502       Feld[x][y] = EL_SOKOBAN_OBJECT;
13503
13504       if (Back[x][y] == Back[nextx][nexty])
13505         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13506       else if (Back[x][y] != 0)
13507         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13508                                     ACTION_EMPTYING);
13509       else
13510         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13511                                     ACTION_FILLING);
13512
13513       if (local_player->sokobanfields_still_needed == 0 &&
13514           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13515       {
13516         PlayerWins(player);
13517
13518         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13519       }
13520     }
13521     else
13522       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13523
13524     InitMovingField(x, y, move_direction);
13525     GfxAction[x][y] = ACTION_PUSHING;
13526
13527     if (mode == DF_SNAP)
13528       ContinueMoving(x, y);
13529     else
13530       MovPos[x][y] = (dx != 0 ? dx : dy);
13531
13532     Pushed[x][y] = TRUE;
13533     Pushed[nextx][nexty] = TRUE;
13534
13535     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13536       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13537     else
13538       player->push_delay_value = -1;    /* get new value later */
13539
13540     /* check for element change _after_ element has been pushed */
13541     if (game.use_change_when_pushing_bug)
13542     {
13543       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13544                                  player->index_bit, dig_side);
13545       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13546                                           player->index_bit, dig_side);
13547     }
13548   }
13549   else if (IS_SWITCHABLE(element))
13550   {
13551     if (PLAYER_SWITCHING(player, x, y))
13552     {
13553       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13554                                           player->index_bit, dig_side);
13555
13556       return MP_ACTION;
13557     }
13558
13559     player->is_switching = TRUE;
13560     player->switch_x = x;
13561     player->switch_y = y;
13562
13563     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13564
13565     if (element == EL_ROBOT_WHEEL)
13566     {
13567       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13568       ZX = x;
13569       ZY = y;
13570
13571       game.robot_wheel_active = TRUE;
13572
13573       TEST_DrawLevelField(x, y);
13574     }
13575     else if (element == EL_SP_TERMINAL)
13576     {
13577       int xx, yy;
13578
13579       SCAN_PLAYFIELD(xx, yy)
13580       {
13581         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13582           Bang(xx, yy);
13583         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13584           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13585       }
13586     }
13587     else if (IS_BELT_SWITCH(element))
13588     {
13589       ToggleBeltSwitch(x, y);
13590     }
13591     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13592              element == EL_SWITCHGATE_SWITCH_DOWN ||
13593              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13594              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13595     {
13596       ToggleSwitchgateSwitch(x, y);
13597     }
13598     else if (element == EL_LIGHT_SWITCH ||
13599              element == EL_LIGHT_SWITCH_ACTIVE)
13600     {
13601       ToggleLightSwitch(x, y);
13602     }
13603     else if (element == EL_TIMEGATE_SWITCH ||
13604              element == EL_DC_TIMEGATE_SWITCH)
13605     {
13606       ActivateTimegateSwitch(x, y);
13607     }
13608     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13609              element == EL_BALLOON_SWITCH_RIGHT ||
13610              element == EL_BALLOON_SWITCH_UP    ||
13611              element == EL_BALLOON_SWITCH_DOWN  ||
13612              element == EL_BALLOON_SWITCH_NONE  ||
13613              element == EL_BALLOON_SWITCH_ANY)
13614     {
13615       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13616                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13617                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13618                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13619                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13620                              move_direction);
13621     }
13622     else if (element == EL_LAMP)
13623     {
13624       Feld[x][y] = EL_LAMP_ACTIVE;
13625       local_player->lights_still_needed--;
13626
13627       ResetGfxAnimation(x, y);
13628       TEST_DrawLevelField(x, y);
13629     }
13630     else if (element == EL_TIME_ORB_FULL)
13631     {
13632       Feld[x][y] = EL_TIME_ORB_EMPTY;
13633
13634       if (level.time > 0 || level.use_time_orb_bug)
13635       {
13636         TimeLeft += level.time_orb_time;
13637         game.no_time_limit = FALSE;
13638
13639         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13640
13641         DisplayGameControlValues();
13642       }
13643
13644       ResetGfxAnimation(x, y);
13645       TEST_DrawLevelField(x, y);
13646     }
13647     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13648              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13649     {
13650       int xx, yy;
13651
13652       game.ball_state = !game.ball_state;
13653
13654       SCAN_PLAYFIELD(xx, yy)
13655       {
13656         int e = Feld[xx][yy];
13657
13658         if (game.ball_state)
13659         {
13660           if (e == EL_EMC_MAGIC_BALL)
13661             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13662           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13663             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13664         }
13665         else
13666         {
13667           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13668             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13669           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13670             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13671         }
13672       }
13673     }
13674
13675     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13676                                         player->index_bit, dig_side);
13677
13678     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13679                                         player->index_bit, dig_side);
13680
13681     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13682                                         player->index_bit, dig_side);
13683
13684     return MP_ACTION;
13685   }
13686   else
13687   {
13688     if (!PLAYER_SWITCHING(player, x, y))
13689     {
13690       player->is_switching = TRUE;
13691       player->switch_x = x;
13692       player->switch_y = y;
13693
13694       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13695                                  player->index_bit, dig_side);
13696       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13697                                           player->index_bit, dig_side);
13698
13699       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13700                                  player->index_bit, dig_side);
13701       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13702                                           player->index_bit, dig_side);
13703     }
13704
13705     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13706                                player->index_bit, dig_side);
13707     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13708                                         player->index_bit, dig_side);
13709
13710     return MP_NO_ACTION;
13711   }
13712
13713   player->push_delay = -1;
13714
13715   if (is_player)                /* function can also be called by EL_PENGUIN */
13716   {
13717     if (Feld[x][y] != element)          /* really digged/collected something */
13718     {
13719       player->is_collecting = !player->is_digging;
13720       player->is_active = TRUE;
13721     }
13722   }
13723
13724   return MP_MOVING;
13725 }
13726
13727 static boolean DigFieldByCE(int x, int y, int digging_element)
13728 {
13729   int element = Feld[x][y];
13730
13731   if (!IS_FREE(x, y))
13732   {
13733     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13734                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13735                   ACTION_BREAKING);
13736
13737     /* no element can dig solid indestructible elements */
13738     if (IS_INDESTRUCTIBLE(element) &&
13739         !IS_DIGGABLE(element) &&
13740         !IS_COLLECTIBLE(element))
13741       return FALSE;
13742
13743     if (AmoebaNr[x][y] &&
13744         (element == EL_AMOEBA_FULL ||
13745          element == EL_BD_AMOEBA ||
13746          element == EL_AMOEBA_GROWING))
13747     {
13748       AmoebaCnt[AmoebaNr[x][y]]--;
13749       AmoebaCnt2[AmoebaNr[x][y]]--;
13750     }
13751
13752     if (IS_MOVING(x, y))
13753       RemoveMovingField(x, y);
13754     else
13755     {
13756       RemoveField(x, y);
13757       TEST_DrawLevelField(x, y);
13758     }
13759
13760     /* if digged element was about to explode, prevent the explosion */
13761     ExplodeField[x][y] = EX_TYPE_NONE;
13762
13763     PlayLevelSoundAction(x, y, action);
13764   }
13765
13766   Store[x][y] = EL_EMPTY;
13767
13768   /* this makes it possible to leave the removed element again */
13769   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13770     Store[x][y] = element;
13771
13772   return TRUE;
13773 }
13774
13775 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13776 {
13777   int jx = player->jx, jy = player->jy;
13778   int x = jx + dx, y = jy + dy;
13779   int snap_direction = (dx == -1 ? MV_LEFT  :
13780                         dx == +1 ? MV_RIGHT :
13781                         dy == -1 ? MV_UP    :
13782                         dy == +1 ? MV_DOWN  : MV_NONE);
13783   boolean can_continue_snapping = (level.continuous_snapping &&
13784                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13785
13786   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13787     return FALSE;
13788
13789   if (!player->active || !IN_LEV_FIELD(x, y))
13790     return FALSE;
13791
13792   if (dx && dy)
13793     return FALSE;
13794
13795   if (!dx && !dy)
13796   {
13797     if (player->MovPos == 0)
13798       player->is_pushing = FALSE;
13799
13800     player->is_snapping = FALSE;
13801
13802     if (player->MovPos == 0)
13803     {
13804       player->is_moving = FALSE;
13805       player->is_digging = FALSE;
13806       player->is_collecting = FALSE;
13807     }
13808
13809     return FALSE;
13810   }
13811
13812   /* prevent snapping with already pressed snap key when not allowed */
13813   if (player->is_snapping && !can_continue_snapping)
13814     return FALSE;
13815
13816   player->MovDir = snap_direction;
13817
13818   if (player->MovPos == 0)
13819   {
13820     player->is_moving = FALSE;
13821     player->is_digging = FALSE;
13822     player->is_collecting = FALSE;
13823   }
13824
13825   player->is_dropping = FALSE;
13826   player->is_dropping_pressed = FALSE;
13827   player->drop_pressed_delay = 0;
13828
13829   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13830     return FALSE;
13831
13832   player->is_snapping = TRUE;
13833   player->is_active = TRUE;
13834
13835   if (player->MovPos == 0)
13836   {
13837     player->is_moving = FALSE;
13838     player->is_digging = FALSE;
13839     player->is_collecting = FALSE;
13840   }
13841
13842   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13843     TEST_DrawLevelField(player->last_jx, player->last_jy);
13844
13845   TEST_DrawLevelField(x, y);
13846
13847   return TRUE;
13848 }
13849
13850 static boolean DropElement(struct PlayerInfo *player)
13851 {
13852   int old_element, new_element;
13853   int dropx = player->jx, dropy = player->jy;
13854   int drop_direction = player->MovDir;
13855   int drop_side = drop_direction;
13856   int drop_element = get_next_dropped_element(player);
13857
13858   player->is_dropping_pressed = TRUE;
13859
13860   /* do not drop an element on top of another element; when holding drop key
13861      pressed without moving, dropped element must move away before the next
13862      element can be dropped (this is especially important if the next element
13863      is dynamite, which can be placed on background for historical reasons) */
13864   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13865     return MP_ACTION;
13866
13867   if (IS_THROWABLE(drop_element))
13868   {
13869     dropx += GET_DX_FROM_DIR(drop_direction);
13870     dropy += GET_DY_FROM_DIR(drop_direction);
13871
13872     if (!IN_LEV_FIELD(dropx, dropy))
13873       return FALSE;
13874   }
13875
13876   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13877   new_element = drop_element;           /* default: no change when dropping */
13878
13879   /* check if player is active, not moving and ready to drop */
13880   if (!player->active || player->MovPos || player->drop_delay > 0)
13881     return FALSE;
13882
13883   /* check if player has anything that can be dropped */
13884   if (new_element == EL_UNDEFINED)
13885     return FALSE;
13886
13887   /* check if drop key was pressed long enough for EM style dynamite */
13888   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13889     return FALSE;
13890
13891   /* check if anything can be dropped at the current position */
13892   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13893     return FALSE;
13894
13895   /* collected custom elements can only be dropped on empty fields */
13896   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13897     return FALSE;
13898
13899   if (old_element != EL_EMPTY)
13900     Back[dropx][dropy] = old_element;   /* store old element on this field */
13901
13902   ResetGfxAnimation(dropx, dropy);
13903   ResetRandomAnimationValue(dropx, dropy);
13904
13905   if (player->inventory_size > 0 ||
13906       player->inventory_infinite_element != EL_UNDEFINED)
13907   {
13908     if (player->inventory_size > 0)
13909     {
13910       player->inventory_size--;
13911
13912       DrawGameDoorValues();
13913
13914       if (new_element == EL_DYNAMITE)
13915         new_element = EL_DYNAMITE_ACTIVE;
13916       else if (new_element == EL_EM_DYNAMITE)
13917         new_element = EL_EM_DYNAMITE_ACTIVE;
13918       else if (new_element == EL_SP_DISK_RED)
13919         new_element = EL_SP_DISK_RED_ACTIVE;
13920     }
13921
13922     Feld[dropx][dropy] = new_element;
13923
13924     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13925       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13926                           el2img(Feld[dropx][dropy]), 0);
13927
13928     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13929
13930     /* needed if previous element just changed to "empty" in the last frame */
13931     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13932
13933     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13934                                player->index_bit, drop_side);
13935     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13936                                         CE_PLAYER_DROPS_X,
13937                                         player->index_bit, drop_side);
13938
13939     TestIfElementTouchesCustomElement(dropx, dropy);
13940   }
13941   else          /* player is dropping a dyna bomb */
13942   {
13943     player->dynabombs_left--;
13944
13945     Feld[dropx][dropy] = new_element;
13946
13947     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13948       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13949                           el2img(Feld[dropx][dropy]), 0);
13950
13951     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13952   }
13953
13954   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13955     InitField_WithBug1(dropx, dropy, FALSE);
13956
13957   new_element = Feld[dropx][dropy];     /* element might have changed */
13958
13959   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13960       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13961   {
13962     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13963       MovDir[dropx][dropy] = drop_direction;
13964
13965     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13966
13967     /* do not cause impact style collision by dropping elements that can fall */
13968     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13969   }
13970
13971   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13972   player->is_dropping = TRUE;
13973
13974   player->drop_pressed_delay = 0;
13975   player->is_dropping_pressed = FALSE;
13976
13977   player->drop_x = dropx;
13978   player->drop_y = dropy;
13979
13980   return TRUE;
13981 }
13982
13983 /* ------------------------------------------------------------------------- */
13984 /* game sound playing functions                                              */
13985 /* ------------------------------------------------------------------------- */
13986
13987 static int *loop_sound_frame = NULL;
13988 static int *loop_sound_volume = NULL;
13989
13990 void InitPlayLevelSound()
13991 {
13992   int num_sounds = getSoundListSize();
13993
13994   checked_free(loop_sound_frame);
13995   checked_free(loop_sound_volume);
13996
13997   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
13998   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13999 }
14000
14001 static void PlayLevelSound(int x, int y, int nr)
14002 {
14003   int sx = SCREENX(x), sy = SCREENY(y);
14004   int volume, stereo_position;
14005   int max_distance = 8;
14006   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14007
14008   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14009       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14010     return;
14011
14012   if (!IN_LEV_FIELD(x, y) ||
14013       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14014       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14015     return;
14016
14017   volume = SOUND_MAX_VOLUME;
14018
14019   if (!IN_SCR_FIELD(sx, sy))
14020   {
14021     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14022     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14023
14024     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14025   }
14026
14027   stereo_position = (SOUND_MAX_LEFT +
14028                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14029                      (SCR_FIELDX + 2 * max_distance));
14030
14031   if (IS_LOOP_SOUND(nr))
14032   {
14033     /* This assures that quieter loop sounds do not overwrite louder ones,
14034        while restarting sound volume comparison with each new game frame. */
14035
14036     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14037       return;
14038
14039     loop_sound_volume[nr] = volume;
14040     loop_sound_frame[nr] = FrameCounter;
14041   }
14042
14043   PlaySoundExt(nr, volume, stereo_position, type);
14044 }
14045
14046 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14047 {
14048   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14049                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14050                  y < LEVELY(BY1) ? LEVELY(BY1) :
14051                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14052                  sound_action);
14053 }
14054
14055 static void PlayLevelSoundAction(int x, int y, int action)
14056 {
14057   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14058 }
14059
14060 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14061 {
14062   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14063
14064   if (sound_effect != SND_UNDEFINED)
14065     PlayLevelSound(x, y, sound_effect);
14066 }
14067
14068 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14069                                               int action)
14070 {
14071   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14072
14073   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14074     PlayLevelSound(x, y, sound_effect);
14075 }
14076
14077 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14078 {
14079   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14080
14081   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14082     PlayLevelSound(x, y, sound_effect);
14083 }
14084
14085 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14086 {
14087   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14088
14089   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14090     StopSound(sound_effect);
14091 }
14092
14093 static void PlayLevelMusic()
14094 {
14095   if (levelset.music[level_nr] != MUS_UNDEFINED)
14096     PlayMusic(levelset.music[level_nr]);        /* from config file */
14097   else
14098     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14099 }
14100
14101 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14102 {
14103   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14104   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14105   int x = xx - 1 - offset;
14106   int y = yy - 1 - offset;
14107
14108   switch (sample)
14109   {
14110     case SAMPLE_blank:
14111       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14112       break;
14113
14114     case SAMPLE_roll:
14115       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14116       break;
14117
14118     case SAMPLE_stone:
14119       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14120       break;
14121
14122     case SAMPLE_nut:
14123       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14124       break;
14125
14126     case SAMPLE_crack:
14127       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14128       break;
14129
14130     case SAMPLE_bug:
14131       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14132       break;
14133
14134     case SAMPLE_tank:
14135       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14136       break;
14137
14138     case SAMPLE_android_clone:
14139       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14140       break;
14141
14142     case SAMPLE_android_move:
14143       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14144       break;
14145
14146     case SAMPLE_spring:
14147       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14148       break;
14149
14150     case SAMPLE_slurp:
14151       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14152       break;
14153
14154     case SAMPLE_eater:
14155       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14156       break;
14157
14158     case SAMPLE_eater_eat:
14159       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14160       break;
14161
14162     case SAMPLE_alien:
14163       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14164       break;
14165
14166     case SAMPLE_collect:
14167       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14168       break;
14169
14170     case SAMPLE_diamond:
14171       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14172       break;
14173
14174     case SAMPLE_squash:
14175       /* !!! CHECK THIS !!! */
14176 #if 1
14177       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14178 #else
14179       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14180 #endif
14181       break;
14182
14183     case SAMPLE_wonderfall:
14184       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14185       break;
14186
14187     case SAMPLE_drip:
14188       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14189       break;
14190
14191     case SAMPLE_push:
14192       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14193       break;
14194
14195     case SAMPLE_dirt:
14196       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14197       break;
14198
14199     case SAMPLE_acid:
14200       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14201       break;
14202
14203     case SAMPLE_ball:
14204       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14205       break;
14206
14207     case SAMPLE_grow:
14208       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14209       break;
14210
14211     case SAMPLE_wonder:
14212       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14213       break;
14214
14215     case SAMPLE_door:
14216       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14217       break;
14218
14219     case SAMPLE_exit_open:
14220       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14221       break;
14222
14223     case SAMPLE_exit_leave:
14224       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14225       break;
14226
14227     case SAMPLE_dynamite:
14228       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14229       break;
14230
14231     case SAMPLE_tick:
14232       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14233       break;
14234
14235     case SAMPLE_press:
14236       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14237       break;
14238
14239     case SAMPLE_wheel:
14240       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14241       break;
14242
14243     case SAMPLE_boom:
14244       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14245       break;
14246
14247     case SAMPLE_die:
14248       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14249       break;
14250
14251     case SAMPLE_time:
14252       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14253       break;
14254
14255     default:
14256       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14257       break;
14258   }
14259 }
14260
14261 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14262 {
14263   int element = map_element_SP_to_RND(element_sp);
14264   int action = map_action_SP_to_RND(action_sp);
14265   int offset = (setup.sp_show_border_elements ? 0 : 1);
14266   int x = xx - offset;
14267   int y = yy - offset;
14268
14269   PlayLevelSoundElementAction(x, y, element, action);
14270 }
14271
14272 void RaiseScore(int value)
14273 {
14274   local_player->score += value;
14275
14276   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14277
14278   DisplayGameControlValues();
14279 }
14280
14281 void RaiseScoreElement(int element)
14282 {
14283   switch (element)
14284   {
14285     case EL_EMERALD:
14286     case EL_BD_DIAMOND:
14287     case EL_EMERALD_YELLOW:
14288     case EL_EMERALD_RED:
14289     case EL_EMERALD_PURPLE:
14290     case EL_SP_INFOTRON:
14291       RaiseScore(level.score[SC_EMERALD]);
14292       break;
14293     case EL_DIAMOND:
14294       RaiseScore(level.score[SC_DIAMOND]);
14295       break;
14296     case EL_CRYSTAL:
14297       RaiseScore(level.score[SC_CRYSTAL]);
14298       break;
14299     case EL_PEARL:
14300       RaiseScore(level.score[SC_PEARL]);
14301       break;
14302     case EL_BUG:
14303     case EL_BD_BUTTERFLY:
14304     case EL_SP_ELECTRON:
14305       RaiseScore(level.score[SC_BUG]);
14306       break;
14307     case EL_SPACESHIP:
14308     case EL_BD_FIREFLY:
14309     case EL_SP_SNIKSNAK:
14310       RaiseScore(level.score[SC_SPACESHIP]);
14311       break;
14312     case EL_YAMYAM:
14313     case EL_DARK_YAMYAM:
14314       RaiseScore(level.score[SC_YAMYAM]);
14315       break;
14316     case EL_ROBOT:
14317       RaiseScore(level.score[SC_ROBOT]);
14318       break;
14319     case EL_PACMAN:
14320       RaiseScore(level.score[SC_PACMAN]);
14321       break;
14322     case EL_NUT:
14323       RaiseScore(level.score[SC_NUT]);
14324       break;
14325     case EL_DYNAMITE:
14326     case EL_EM_DYNAMITE:
14327     case EL_SP_DISK_RED:
14328     case EL_DYNABOMB_INCREASE_NUMBER:
14329     case EL_DYNABOMB_INCREASE_SIZE:
14330     case EL_DYNABOMB_INCREASE_POWER:
14331       RaiseScore(level.score[SC_DYNAMITE]);
14332       break;
14333     case EL_SHIELD_NORMAL:
14334     case EL_SHIELD_DEADLY:
14335       RaiseScore(level.score[SC_SHIELD]);
14336       break;
14337     case EL_EXTRA_TIME:
14338       RaiseScore(level.extra_time_score);
14339       break;
14340     case EL_KEY_1:
14341     case EL_KEY_2:
14342     case EL_KEY_3:
14343     case EL_KEY_4:
14344     case EL_EM_KEY_1:
14345     case EL_EM_KEY_2:
14346     case EL_EM_KEY_3:
14347     case EL_EM_KEY_4:
14348     case EL_EMC_KEY_5:
14349     case EL_EMC_KEY_6:
14350     case EL_EMC_KEY_7:
14351     case EL_EMC_KEY_8:
14352     case EL_DC_KEY_WHITE:
14353       RaiseScore(level.score[SC_KEY]);
14354       break;
14355     default:
14356       RaiseScore(element_info[element].collect_score);
14357       break;
14358   }
14359 }
14360
14361 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14362 {
14363   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14364   {
14365     /* closing door required in case of envelope style request dialogs */
14366     if (!skip_request)
14367       CloseDoor(DOOR_CLOSE_1);
14368
14369 #if defined(NETWORK_AVALIABLE)
14370     if (options.network)
14371       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14372     else
14373 #endif
14374     {
14375       if (quick_quit)
14376       {
14377         FadeSkipNextFadeIn();
14378
14379         game_status = GAME_MODE_MAIN;
14380
14381         DrawAndFadeInMainMenu(REDRAW_FIELD);
14382       }
14383       else
14384       {
14385         game_status = GAME_MODE_MAIN;
14386
14387         DrawAndFadeInMainMenu(REDRAW_FIELD);
14388       }
14389     }
14390   }
14391   else          /* continue playing the game */
14392   {
14393     if (tape.playing && tape.deactivate_display)
14394       TapeDeactivateDisplayOff(TRUE);
14395
14396     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14397
14398     if (tape.playing && tape.deactivate_display)
14399       TapeDeactivateDisplayOn();
14400   }
14401 }
14402
14403 void RequestQuitGame(boolean ask_if_really_quit)
14404 {
14405   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14406   boolean skip_request = AllPlayersGone || quick_quit;
14407
14408   RequestQuitGameExt(skip_request, quick_quit,
14409                      "Do you really want to quit the game?");
14410 }
14411
14412
14413 /* ------------------------------------------------------------------------- */
14414 /* random generator functions                                                */
14415 /* ------------------------------------------------------------------------- */
14416
14417 unsigned int InitEngineRandom_RND(int seed)
14418 {
14419   game.num_random_calls = 0;
14420
14421   return InitEngineRandom(seed);
14422 }
14423
14424 unsigned int RND(int max)
14425 {
14426   if (max > 0)
14427   {
14428     game.num_random_calls++;
14429
14430     return GetEngineRandom(max);
14431   }
14432
14433   return 0;
14434 }
14435
14436
14437 /* ------------------------------------------------------------------------- */
14438 /* game engine snapshot handling functions                                   */
14439 /* ------------------------------------------------------------------------- */
14440
14441 struct EngineSnapshotInfo
14442 {
14443   /* runtime values for custom element collect score */
14444   int collect_score[NUM_CUSTOM_ELEMENTS];
14445
14446   /* runtime values for group element choice position */
14447   int choice_pos[NUM_GROUP_ELEMENTS];
14448
14449   /* runtime values for belt position animations */
14450   int belt_graphic[4][NUM_BELT_PARTS];
14451   int belt_anim_mode[4][NUM_BELT_PARTS];
14452 };
14453
14454 static struct EngineSnapshotInfo engine_snapshot_rnd;
14455 static char *snapshot_level_identifier = NULL;
14456 static int snapshot_level_nr = -1;
14457
14458 static void SaveEngineSnapshotValues_RND()
14459 {
14460   static int belt_base_active_element[4] =
14461   {
14462     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14463     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14464     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14465     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14466   };
14467   int i, j;
14468
14469   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14470   {
14471     int element = EL_CUSTOM_START + i;
14472
14473     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14474   }
14475
14476   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14477   {
14478     int element = EL_GROUP_START + i;
14479
14480     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14481   }
14482
14483   for (i = 0; i < 4; i++)
14484   {
14485     for (j = 0; j < NUM_BELT_PARTS; j++)
14486     {
14487       int element = belt_base_active_element[i] + j;
14488       int graphic = el2img(element);
14489       int anim_mode = graphic_info[graphic].anim_mode;
14490
14491       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14492       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14493     }
14494   }
14495 }
14496
14497 static void LoadEngineSnapshotValues_RND()
14498 {
14499   unsigned int num_random_calls = game.num_random_calls;
14500   int i, j;
14501
14502   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14503   {
14504     int element = EL_CUSTOM_START + i;
14505
14506     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14507   }
14508
14509   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14510   {
14511     int element = EL_GROUP_START + i;
14512
14513     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14514   }
14515
14516   for (i = 0; i < 4; i++)
14517   {
14518     for (j = 0; j < NUM_BELT_PARTS; j++)
14519     {
14520       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14521       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14522
14523       graphic_info[graphic].anim_mode = anim_mode;
14524     }
14525   }
14526
14527   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14528   {
14529     InitRND(tape.random_seed);
14530     for (i = 0; i < num_random_calls; i++)
14531       RND(1);
14532   }
14533
14534   if (game.num_random_calls != num_random_calls)
14535   {
14536     Error(ERR_INFO, "number of random calls out of sync");
14537     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14538     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14539     Error(ERR_EXIT, "this should not happen -- please debug");
14540   }
14541 }
14542
14543 void FreeEngineSnapshot()
14544 {
14545   FreeEngineSnapshotBuffers();
14546
14547   setString(&snapshot_level_identifier, NULL);
14548   snapshot_level_nr = -1;
14549 }
14550
14551 void SaveEngineSnapshot()
14552 {
14553   /* do not save snapshots from editor */
14554   if (level_editor_test_game)
14555     return;
14556
14557   /* free previous snapshot buffers, if needed */
14558   FreeEngineSnapshotBuffers();
14559
14560   /* copy some special values to a structure better suited for the snapshot */
14561
14562   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14563     SaveEngineSnapshotValues_RND();
14564   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14565     SaveEngineSnapshotValues_EM();
14566   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14567     SaveEngineSnapshotValues_SP();
14568
14569   /* save values stored in special snapshot structure */
14570
14571   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14572     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14573   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14574     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14575   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14576     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14577
14578   /* save further RND engine values */
14579
14580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14583
14584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14588
14589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14594
14595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14598
14599   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14600
14601   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14602
14603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14604   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14605
14606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14609   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14611   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14613   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14616   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14617   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14620   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14621   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14622   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14624
14625   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14626   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14627
14628   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14629   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14630   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14631
14632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14633   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14634
14635   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14636   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14637   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14638   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14639   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14640
14641   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14642   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14643
14644   /* save level identification information */
14645
14646   setString(&snapshot_level_identifier, leveldir_current->identifier);
14647   snapshot_level_nr = level_nr;
14648
14649 #if 0
14650   ListNode *node = engine_snapshot_list_rnd;
14651   int num_bytes = 0;
14652
14653   while (node != NULL)
14654   {
14655     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14656
14657     node = node->next;
14658   }
14659
14660   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14661 #endif
14662 }
14663
14664 void LoadEngineSnapshot()
14665 {
14666   /* restore generically stored snapshot buffers */
14667
14668   LoadEngineSnapshotBuffers();
14669
14670   /* restore special values from snapshot structure */
14671
14672   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14673     LoadEngineSnapshotValues_RND();
14674   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14675     LoadEngineSnapshotValues_EM();
14676   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14677     LoadEngineSnapshotValues_SP();
14678 }
14679
14680 boolean CheckEngineSnapshot()
14681 {
14682   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14683           snapshot_level_nr == level_nr);
14684 }
14685
14686
14687 /* ---------- new game button stuff ---------------------------------------- */
14688
14689 static struct
14690 {
14691   int graphic;
14692   struct Rect *pos;
14693   int gadget_id;
14694   char *infotext;
14695 } gamebutton_info[NUM_GAME_BUTTONS] =
14696 {
14697   {
14698     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14699     GAME_CTRL_ID_STOP,                  "stop game"
14700   },
14701   {
14702     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14703     GAME_CTRL_ID_PAUSE,                 "pause game"
14704   },
14705   {
14706     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14707     GAME_CTRL_ID_PLAY,                  "play game"
14708   },
14709   {
14710     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14711     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14712   },
14713   {
14714     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14715     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14716   },
14717   {
14718     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14719     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14720   },
14721   {
14722     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14723     GAME_CTRL_ID_SAVE,                  "save game"
14724   },
14725   {
14726     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14727     GAME_CTRL_ID_LOAD,                  "load game"
14728   }
14729 };
14730
14731 void CreateGameButtons()
14732 {
14733   int i;
14734
14735   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14736   {
14737     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14738     struct Rect *pos = gamebutton_info[i].pos;
14739     struct GadgetInfo *gi;
14740     int button_type;
14741     boolean checked;
14742     unsigned int event_mask;
14743     int base_x = (tape.show_game_buttons ? VX : DX);
14744     int base_y = (tape.show_game_buttons ? VY : DY);
14745     int gd_x   = gfx->src_x;
14746     int gd_y   = gfx->src_y;
14747     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14748     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14749     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14750     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14751     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14752     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14753     int id = i;
14754
14755     if (gfx->bitmap == NULL)
14756     {
14757       game_gadget[id] = NULL;
14758
14759       continue;
14760     }
14761
14762     if (id == GAME_CTRL_ID_STOP ||
14763         id == GAME_CTRL_ID_PAUSE ||
14764         id == GAME_CTRL_ID_PLAY ||
14765         id == GAME_CTRL_ID_SAVE ||
14766         id == GAME_CTRL_ID_LOAD)
14767     {
14768       button_type = GD_TYPE_NORMAL_BUTTON;
14769       checked = FALSE;
14770       event_mask = GD_EVENT_RELEASED;
14771     }
14772     else
14773     {
14774       button_type = GD_TYPE_CHECK_BUTTON;
14775       checked =
14776         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14777          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14778          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14779       event_mask = GD_EVENT_PRESSED;
14780     }
14781
14782     gi = CreateGadget(GDI_CUSTOM_ID, id,
14783                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14784                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14785                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14786                       GDI_WIDTH, gfx->width,
14787                       GDI_HEIGHT, gfx->height,
14788                       GDI_TYPE, button_type,
14789                       GDI_STATE, GD_BUTTON_UNPRESSED,
14790                       GDI_CHECKED, checked,
14791                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14792                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14793                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14794                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14795                       GDI_DIRECT_DRAW, FALSE,
14796                       GDI_EVENT_MASK, event_mask,
14797                       GDI_CALLBACK_ACTION, HandleGameButtons,
14798                       GDI_END);
14799
14800     if (gi == NULL)
14801       Error(ERR_EXIT, "cannot create gadget");
14802
14803     game_gadget[id] = gi;
14804   }
14805 }
14806
14807 void FreeGameButtons()
14808 {
14809   int i;
14810
14811   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14812     FreeGadget(game_gadget[i]);
14813 }
14814
14815 void MapGameButtons()
14816 {
14817   int i;
14818
14819   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14820     MapGadget(game_gadget[i]);
14821 }
14822
14823 void UnmapGameButtons()
14824 {
14825   int i;
14826
14827   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14828     UnmapGadget(game_gadget[i]);
14829 }
14830
14831 void RedrawGameButtons()
14832 {
14833   int i;
14834
14835   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14836     RedrawGadget(game_gadget[i]);
14837
14838   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
14839   redraw_mask &= ~REDRAW_ALL;
14840 }
14841
14842 static void HandleGameButtonsExt(int id)
14843 {
14844   boolean handle_game_buttons =
14845     (game_status == GAME_MODE_PLAYING ||
14846      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
14847
14848   if (!handle_game_buttons)
14849     return;
14850
14851   switch (id)
14852   {
14853     case GAME_CTRL_ID_STOP:
14854       if (game_status == GAME_MODE_MAIN)
14855         break;
14856
14857       if (tape.playing)
14858         TapeStop();
14859       else
14860         RequestQuitGame(TRUE);
14861
14862       break;
14863
14864     case GAME_CTRL_ID_PAUSE:
14865       if (options.network && game_status == GAME_MODE_PLAYING)
14866       {
14867 #if defined(NETWORK_AVALIABLE)
14868         if (tape.pausing)
14869           SendToServer_ContinuePlaying();
14870         else
14871           SendToServer_PausePlaying();
14872 #endif
14873       }
14874       else
14875         TapeTogglePause(TAPE_TOGGLE_MANUAL);
14876       break;
14877
14878     case GAME_CTRL_ID_PLAY:
14879       if (game_status == GAME_MODE_MAIN)
14880       {
14881         StartGameActions(options.network, setup.autorecord, level.random_seed);
14882       }
14883       else if (tape.pausing)
14884       {
14885 #if defined(NETWORK_AVALIABLE)
14886         if (options.network)
14887           SendToServer_ContinuePlaying();
14888         else
14889 #endif
14890         {
14891           tape.pausing = FALSE;
14892           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14893         }
14894       }
14895       break;
14896
14897     case SOUND_CTRL_ID_MUSIC:
14898       if (setup.sound_music)
14899       { 
14900         setup.sound_music = FALSE;
14901
14902         FadeMusic();
14903       }
14904       else if (audio.music_available)
14905       { 
14906         setup.sound = setup.sound_music = TRUE;
14907
14908         SetAudioMode(setup.sound);
14909
14910         PlayLevelMusic();
14911       }
14912       break;
14913
14914     case SOUND_CTRL_ID_LOOPS:
14915       if (setup.sound_loops)
14916         setup.sound_loops = FALSE;
14917       else if (audio.loops_available)
14918       {
14919         setup.sound = setup.sound_loops = TRUE;
14920
14921         SetAudioMode(setup.sound);
14922       }
14923       break;
14924
14925     case SOUND_CTRL_ID_SIMPLE:
14926       if (setup.sound_simple)
14927         setup.sound_simple = FALSE;
14928       else if (audio.sound_available)
14929       {
14930         setup.sound = setup.sound_simple = TRUE;
14931
14932         SetAudioMode(setup.sound);
14933       }
14934       break;
14935
14936     case GAME_CTRL_ID_SAVE:
14937       TapeQuickSave();
14938       break;
14939
14940     case GAME_CTRL_ID_LOAD:
14941       TapeQuickLoad();
14942       break;
14943
14944     default:
14945       break;
14946   }
14947 }
14948
14949 static void HandleGameButtons(struct GadgetInfo *gi)
14950 {
14951   HandleGameButtonsExt(gi->custom_id);
14952 }
14953
14954 void HandleSoundButtonKeys(Key key)
14955 {
14956
14957   if (key == setup.shortcut.sound_simple)
14958     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
14959   else if (key == setup.shortcut.sound_loops)
14960     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
14961   else if (key == setup.shortcut.sound_music)
14962     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
14963 }