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