moved video frame synchronization to single location (screen update)
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_PAUSE2             6
978 #define GAME_CTRL_ID_LOAD               7
979 #define SOUND_CTRL_ID_MUSIC             8
980 #define SOUND_CTRL_ID_LOOPS             9
981 #define SOUND_CTRL_ID_SIMPLE            10
982
983 #define NUM_GAME_BUTTONS                11
984
985
986 /* forward declaration for internal use */
987
988 static void CreateField(int, int, int);
989
990 static void ResetGfxAnimation(int, int);
991
992 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
993 static void AdvanceFrameAndPlayerCounters(int);
994
995 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
996 static boolean MovePlayer(struct PlayerInfo *, int, int);
997 static void ScrollPlayer(struct PlayerInfo *, int);
998 static void ScrollScreen(struct PlayerInfo *, int);
999
1000 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1001 static boolean DigFieldByCE(int, int, int);
1002 static boolean SnapField(struct PlayerInfo *, int, int);
1003 static boolean DropElement(struct PlayerInfo *);
1004
1005 static void InitBeltMovement(void);
1006 static void CloseAllOpenTimegates(void);
1007 static void CheckGravityMovement(struct PlayerInfo *);
1008 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1009 static void KillPlayerUnlessEnemyProtected(int, int);
1010 static void KillPlayerUnlessExplosionProtected(int, int);
1011
1012 static void TestIfPlayerTouchesCustomElement(int, int);
1013 static void TestIfElementTouchesCustomElement(int, int);
1014 static void TestIfElementHitsCustomElement(int, int, int);
1015
1016 static void HandleElementChange(int, int, int);
1017 static void ExecuteCustomElementAction(int, int, int, int);
1018 static boolean ChangeElement(int, int, int, int);
1019
1020 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1021 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1022         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1023 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1024         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1025 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1026         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1027 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1028         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1029
1030 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1031 #define CheckElementChange(x, y, e, te, ev)                             \
1032         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1033 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1034         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1035 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1036         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1037
1038 static void PlayLevelSound(int, int, int);
1039 static void PlayLevelSoundNearest(int, int, int);
1040 static void PlayLevelSoundAction(int, int, int);
1041 static void PlayLevelSoundElementAction(int, int, int, int);
1042 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1043 static void PlayLevelSoundActionIfLoop(int, int, int);
1044 static void StopLevelSoundActionIfLoop(int, int, int);
1045 static void PlayLevelMusic();
1046
1047 static void HandleGameButtons(struct GadgetInfo *);
1048
1049 int AmoebeNachbarNr(int, int);
1050 void AmoebeUmwandeln(int, int);
1051 void ContinueMoving(int, int);
1052 void Bang(int, int);
1053 void InitMovDir(int, int);
1054 void InitAmoebaNr(int, int);
1055 int NewHiScore(void);
1056
1057 void TestIfGoodThingHitsBadThing(int, int, int);
1058 void TestIfBadThingHitsGoodThing(int, int, int);
1059 void TestIfPlayerTouchesBadThing(int, int);
1060 void TestIfPlayerRunsIntoBadThing(int, int, int);
1061 void TestIfBadThingTouchesPlayer(int, int);
1062 void TestIfBadThingRunsIntoPlayer(int, int, int);
1063 void TestIfFriendTouchesBadThing(int, int);
1064 void TestIfBadThingTouchesFriend(int, int);
1065 void TestIfBadThingTouchesOtherBadThing(int, int);
1066 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1067
1068 void KillPlayer(struct PlayerInfo *);
1069 void BuryPlayer(struct PlayerInfo *);
1070 void RemovePlayer(struct PlayerInfo *);
1071
1072 static int getInvisibleActiveFromInvisibleElement(int);
1073 static int getInvisibleFromInvisibleActiveElement(int);
1074
1075 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1076
1077 /* for detection of endless loops, caused by custom element programming */
1078 /* (using maximal playfield width x 10 is just a rough approximation) */
1079 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1080
1081 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1082 {                                                                       \
1083   if (recursion_loop_detected)                                          \
1084     return (rc);                                                        \
1085                                                                         \
1086   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1087   {                                                                     \
1088     recursion_loop_detected = TRUE;                                     \
1089     recursion_loop_element = (e);                                       \
1090   }                                                                     \
1091                                                                         \
1092   recursion_loop_depth++;                                               \
1093 }
1094
1095 #define RECURSION_LOOP_DETECTION_END()                                  \
1096 {                                                                       \
1097   recursion_loop_depth--;                                               \
1098 }
1099
1100 static int recursion_loop_depth;
1101 static boolean recursion_loop_detected;
1102 static boolean recursion_loop_element;
1103
1104 static int map_player_action[MAX_PLAYERS];
1105
1106
1107 /* ------------------------------------------------------------------------- */
1108 /* definition of elements that automatically change to other elements after  */
1109 /* a specified time, eventually calling a function when changing             */
1110 /* ------------------------------------------------------------------------- */
1111
1112 /* forward declaration for changer functions */
1113 static void InitBuggyBase(int, int);
1114 static void WarnBuggyBase(int, int);
1115
1116 static void InitTrap(int, int);
1117 static void ActivateTrap(int, int);
1118 static void ChangeActiveTrap(int, int);
1119
1120 static void InitRobotWheel(int, int);
1121 static void RunRobotWheel(int, int);
1122 static void StopRobotWheel(int, int);
1123
1124 static void InitTimegateWheel(int, int);
1125 static void RunTimegateWheel(int, int);
1126
1127 static void InitMagicBallDelay(int, int);
1128 static void ActivateMagicBall(int, int);
1129
1130 struct ChangingElementInfo
1131 {
1132   int element;
1133   int target_element;
1134   int change_delay;
1135   void (*pre_change_function)(int x, int y);
1136   void (*change_function)(int x, int y);
1137   void (*post_change_function)(int x, int y);
1138 };
1139
1140 static struct ChangingElementInfo change_delay_list[] =
1141 {
1142   {
1143     EL_NUT_BREAKING,
1144     EL_EMERALD,
1145     6,
1146     NULL,
1147     NULL,
1148     NULL
1149   },
1150   {
1151     EL_PEARL_BREAKING,
1152     EL_EMPTY,
1153     8,
1154     NULL,
1155     NULL,
1156     NULL
1157   },
1158   {
1159     EL_EXIT_OPENING,
1160     EL_EXIT_OPEN,
1161     29,
1162     NULL,
1163     NULL,
1164     NULL
1165   },
1166   {
1167     EL_EXIT_CLOSING,
1168     EL_EXIT_CLOSED,
1169     29,
1170     NULL,
1171     NULL,
1172     NULL
1173   },
1174   {
1175     EL_STEEL_EXIT_OPENING,
1176     EL_STEEL_EXIT_OPEN,
1177     29,
1178     NULL,
1179     NULL,
1180     NULL
1181   },
1182   {
1183     EL_STEEL_EXIT_CLOSING,
1184     EL_STEEL_EXIT_CLOSED,
1185     29,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_EM_EXIT_OPENING,
1192     EL_EM_EXIT_OPEN,
1193     29,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EM_EXIT_CLOSING,
1200     EL_EMPTY,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EM_STEEL_EXIT_OPENING,
1208     EL_EM_STEEL_EXIT_OPEN,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_EM_STEEL_EXIT_CLOSING,
1216     EL_STEELWALL,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_SP_EXIT_OPENING,
1224     EL_SP_EXIT_OPEN,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_SP_EXIT_CLOSING,
1232     EL_SP_EXIT_CLOSED,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_SWITCHGATE_OPENING,
1240     EL_SWITCHGATE_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_SWITCHGATE_CLOSING,
1248     EL_SWITCHGATE_CLOSED,
1249     29,
1250     NULL,
1251     NULL,
1252     NULL
1253   },
1254   {
1255     EL_TIMEGATE_OPENING,
1256     EL_TIMEGATE_OPEN,
1257     29,
1258     NULL,
1259     NULL,
1260     NULL
1261   },
1262   {
1263     EL_TIMEGATE_CLOSING,
1264     EL_TIMEGATE_CLOSED,
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270
1271   {
1272     EL_ACID_SPLASH_LEFT,
1273     EL_EMPTY,
1274     8,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_ACID_SPLASH_RIGHT,
1281     EL_EMPTY,
1282     8,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_BUGGY_BASE,
1289     EL_SP_BUGGY_BASE_ACTIVATING,
1290     0,
1291     InitBuggyBase,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SP_BUGGY_BASE_ACTIVATING,
1297     EL_SP_BUGGY_BASE_ACTIVE,
1298     0,
1299     InitBuggyBase,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_BUGGY_BASE_ACTIVE,
1305     EL_SP_BUGGY_BASE,
1306     0,
1307     InitBuggyBase,
1308     WarnBuggyBase,
1309     NULL
1310   },
1311   {
1312     EL_TRAP,
1313     EL_TRAP_ACTIVE,
1314     0,
1315     InitTrap,
1316     NULL,
1317     ActivateTrap
1318   },
1319   {
1320     EL_TRAP_ACTIVE,
1321     EL_TRAP,
1322     31,
1323     NULL,
1324     ChangeActiveTrap,
1325     NULL
1326   },
1327   {
1328     EL_ROBOT_WHEEL_ACTIVE,
1329     EL_ROBOT_WHEEL,
1330     0,
1331     InitRobotWheel,
1332     RunRobotWheel,
1333     StopRobotWheel
1334   },
1335   {
1336     EL_TIMEGATE_SWITCH_ACTIVE,
1337     EL_TIMEGATE_SWITCH,
1338     0,
1339     InitTimegateWheel,
1340     RunTimegateWheel,
1341     NULL
1342   },
1343   {
1344     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1345     EL_DC_TIMEGATE_SWITCH,
1346     0,
1347     InitTimegateWheel,
1348     RunTimegateWheel,
1349     NULL
1350   },
1351   {
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     0,
1355     InitMagicBallDelay,
1356     NULL,
1357     ActivateMagicBall
1358   },
1359   {
1360     EL_EMC_SPRING_BUMPER_ACTIVE,
1361     EL_EMC_SPRING_BUMPER,
1362     8,
1363     NULL,
1364     NULL,
1365     NULL
1366   },
1367   {
1368     EL_DIAGONAL_SHRINKING,
1369     EL_UNDEFINED,
1370     0,
1371     NULL,
1372     NULL,
1373     NULL
1374   },
1375   {
1376     EL_DIAGONAL_GROWING,
1377     EL_UNDEFINED,
1378     0,
1379     NULL,
1380     NULL,
1381     NULL,
1382   },
1383
1384   {
1385     EL_UNDEFINED,
1386     EL_UNDEFINED,
1387     -1,
1388     NULL,
1389     NULL,
1390     NULL
1391   }
1392 };
1393
1394 struct
1395 {
1396   int element;
1397   int push_delay_fixed, push_delay_random;
1398 }
1399 push_delay_list[] =
1400 {
1401   { EL_SPRING,                  0, 0 },
1402   { EL_BALLOON,                 0, 0 },
1403
1404   { EL_SOKOBAN_OBJECT,          2, 0 },
1405   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1406   { EL_SATELLITE,               2, 0 },
1407   { EL_SP_DISK_YELLOW,          2, 0 },
1408
1409   { EL_UNDEFINED,               0, 0 },
1410 };
1411
1412 struct
1413 {
1414   int element;
1415   int move_stepsize;
1416 }
1417 move_stepsize_list[] =
1418 {
1419   { EL_AMOEBA_DROP,             2 },
1420   { EL_AMOEBA_DROPPING,         2 },
1421   { EL_QUICKSAND_FILLING,       1 },
1422   { EL_QUICKSAND_EMPTYING,      1 },
1423   { EL_QUICKSAND_FAST_FILLING,  2 },
1424   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1425   { EL_MAGIC_WALL_FILLING,      2 },
1426   { EL_MAGIC_WALL_EMPTYING,     2 },
1427   { EL_BD_MAGIC_WALL_FILLING,   2 },
1428   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1429   { EL_DC_MAGIC_WALL_FILLING,   2 },
1430   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1431
1432   { EL_UNDEFINED,               0 },
1433 };
1434
1435 struct
1436 {
1437   int element;
1438   int count;
1439 }
1440 collect_count_list[] =
1441 {
1442   { EL_EMERALD,                 1 },
1443   { EL_BD_DIAMOND,              1 },
1444   { EL_EMERALD_YELLOW,          1 },
1445   { EL_EMERALD_RED,             1 },
1446   { EL_EMERALD_PURPLE,          1 },
1447   { EL_DIAMOND,                 3 },
1448   { EL_SP_INFOTRON,             1 },
1449   { EL_PEARL,                   5 },
1450   { EL_CRYSTAL,                 8 },
1451
1452   { EL_UNDEFINED,               0 },
1453 };
1454
1455 struct
1456 {
1457   int element;
1458   int direction;
1459 }
1460 access_direction_list[] =
1461 {
1462   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1463   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1464   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1465   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1466   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1467   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1468   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1469   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1470   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1471   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1472   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1473
1474   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1475   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1476   { EL_SP_PORT_UP,                                                   MV_DOWN },
1477   { EL_SP_PORT_DOWN,                                         MV_UP           },
1478   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1479   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1480   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1481   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1482   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1483   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1484   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1485   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1486   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1487   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1488   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1489   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1490   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1491   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1492   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1493
1494   { EL_UNDEFINED,                       MV_NONE                              }
1495 };
1496
1497 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1498
1499 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1500 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1501 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1502                                  IS_JUST_CHANGING(x, y))
1503
1504 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1505
1506 /* static variables for playfield scan mode (scanning forward or backward) */
1507 static int playfield_scan_start_x = 0;
1508 static int playfield_scan_start_y = 0;
1509 static int playfield_scan_delta_x = 1;
1510 static int playfield_scan_delta_y = 1;
1511
1512 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1513                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1514                                      (y) += playfield_scan_delta_y)     \
1515                                 for ((x) = playfield_scan_start_x;      \
1516                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1517                                      (x) += playfield_scan_delta_x)
1518
1519 #ifdef DEBUG
1520 void DEBUG_SetMaximumDynamite()
1521 {
1522   int i;
1523
1524   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1525     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1526       local_player->inventory_element[local_player->inventory_size++] =
1527         EL_DYNAMITE;
1528 }
1529 #endif
1530
1531 static void InitPlayfieldScanModeVars()
1532 {
1533   if (game.use_reverse_scan_direction)
1534   {
1535     playfield_scan_start_x = lev_fieldx - 1;
1536     playfield_scan_start_y = lev_fieldy - 1;
1537
1538     playfield_scan_delta_x = -1;
1539     playfield_scan_delta_y = -1;
1540   }
1541   else
1542   {
1543     playfield_scan_start_x = 0;
1544     playfield_scan_start_y = 0;
1545
1546     playfield_scan_delta_x = 1;
1547     playfield_scan_delta_y = 1;
1548   }
1549 }
1550
1551 static void InitPlayfieldScanMode(int mode)
1552 {
1553   game.use_reverse_scan_direction =
1554     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1555
1556   InitPlayfieldScanModeVars();
1557 }
1558
1559 static int get_move_delay_from_stepsize(int move_stepsize)
1560 {
1561   move_stepsize =
1562     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1563
1564   /* make sure that stepsize value is always a power of 2 */
1565   move_stepsize = (1 << log_2(move_stepsize));
1566
1567   return TILEX / move_stepsize;
1568 }
1569
1570 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1571                                boolean init_game)
1572 {
1573   int player_nr = player->index_nr;
1574   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1575   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1576
1577   /* do no immediately change move delay -- the player might just be moving */
1578   player->move_delay_value_next = move_delay;
1579
1580   /* information if player can move must be set separately */
1581   player->cannot_move = cannot_move;
1582
1583   if (init_game)
1584   {
1585     player->move_delay       = game.initial_move_delay[player_nr];
1586     player->move_delay_value = game.initial_move_delay_value[player_nr];
1587
1588     player->move_delay_value_next = -1;
1589
1590     player->move_delay_reset_counter = 0;
1591   }
1592 }
1593
1594 void GetPlayerConfig()
1595 {
1596   GameFrameDelay = setup.game_frame_delay;
1597
1598   if (!audio.sound_available)
1599     setup.sound_simple = FALSE;
1600
1601   if (!audio.loops_available)
1602     setup.sound_loops = FALSE;
1603
1604   if (!audio.music_available)
1605     setup.sound_music = FALSE;
1606
1607   if (!video.fullscreen_available)
1608     setup.fullscreen = FALSE;
1609
1610   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1611
1612   SetAudioMode(setup.sound);
1613   InitJoysticks();
1614 }
1615
1616 int GetElementFromGroupElement(int element)
1617 {
1618   if (IS_GROUP_ELEMENT(element))
1619   {
1620     struct ElementGroupInfo *group = element_info[element].group;
1621     int last_anim_random_frame = gfx.anim_random_frame;
1622     int element_pos;
1623
1624     if (group->choice_mode == ANIM_RANDOM)
1625       gfx.anim_random_frame = RND(group->num_elements_resolved);
1626
1627     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1628                                     group->choice_mode, 0,
1629                                     group->choice_pos);
1630
1631     if (group->choice_mode == ANIM_RANDOM)
1632       gfx.anim_random_frame = last_anim_random_frame;
1633
1634     group->choice_pos++;
1635
1636     element = group->element_resolved[element_pos];
1637   }
1638
1639   return element;
1640 }
1641
1642 static void InitPlayerField(int x, int y, int element, boolean init_game)
1643 {
1644   if (element == EL_SP_MURPHY)
1645   {
1646     if (init_game)
1647     {
1648       if (stored_player[0].present)
1649       {
1650         Feld[x][y] = EL_SP_MURPHY_CLONE;
1651
1652         return;
1653       }
1654       else
1655       {
1656         stored_player[0].initial_element = element;
1657         stored_player[0].use_murphy = TRUE;
1658
1659         if (!level.use_artwork_element[0])
1660           stored_player[0].artwork_element = EL_SP_MURPHY;
1661       }
1662
1663       Feld[x][y] = EL_PLAYER_1;
1664     }
1665   }
1666
1667   if (init_game)
1668   {
1669     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1670     int jx = player->jx, jy = player->jy;
1671
1672     player->present = TRUE;
1673
1674     player->block_last_field = (element == EL_SP_MURPHY ?
1675                                 level.sp_block_last_field :
1676                                 level.block_last_field);
1677
1678     /* ---------- initialize player's last field block delay --------------- */
1679
1680     /* always start with reliable default value (no adjustment needed) */
1681     player->block_delay_adjustment = 0;
1682
1683     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1684     if (player->block_last_field && element == EL_SP_MURPHY)
1685       player->block_delay_adjustment = 1;
1686
1687     /* special case 2: in game engines before 3.1.1, blocking was different */
1688     if (game.use_block_last_field_bug)
1689       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1690
1691     if (!options.network || player->connected)
1692     {
1693       player->active = TRUE;
1694
1695       /* remove potentially duplicate players */
1696       if (StorePlayer[jx][jy] == Feld[x][y])
1697         StorePlayer[jx][jy] = 0;
1698
1699       StorePlayer[x][y] = Feld[x][y];
1700
1701 #if DEBUG_INIT_PLAYER
1702       if (options.debug)
1703       {
1704         printf("- player element %d activated", player->element_nr);
1705         printf(" (local player is %d and currently %s)\n",
1706                local_player->element_nr,
1707                local_player->active ? "active" : "not active");
1708       }
1709     }
1710 #endif
1711
1712     Feld[x][y] = EL_EMPTY;
1713
1714     player->jx = player->last_jx = x;
1715     player->jy = player->last_jy = y;
1716   }
1717
1718   if (!init_game)
1719   {
1720     int player_nr = GET_PLAYER_NR(element);
1721     struct PlayerInfo *player = &stored_player[player_nr];
1722
1723     if (player->active && player->killed)
1724       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1725   }
1726 }
1727
1728 static void InitField(int x, int y, boolean init_game)
1729 {
1730   int element = Feld[x][y];
1731
1732   switch (element)
1733   {
1734     case EL_SP_MURPHY:
1735     case EL_PLAYER_1:
1736     case EL_PLAYER_2:
1737     case EL_PLAYER_3:
1738     case EL_PLAYER_4:
1739       InitPlayerField(x, y, element, init_game);
1740       break;
1741
1742     case EL_SOKOBAN_FIELD_PLAYER:
1743       element = Feld[x][y] = EL_PLAYER_1;
1744       InitField(x, y, init_game);
1745
1746       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1747       InitField(x, y, init_game);
1748       break;
1749
1750     case EL_SOKOBAN_FIELD_EMPTY:
1751       local_player->sokobanfields_still_needed++;
1752       break;
1753
1754     case EL_STONEBLOCK:
1755       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1756         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1757       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1758         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1759       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1760         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1761       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1762         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1763       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1764         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1765       break;
1766
1767     case EL_BUG:
1768     case EL_BUG_RIGHT:
1769     case EL_BUG_UP:
1770     case EL_BUG_LEFT:
1771     case EL_BUG_DOWN:
1772     case EL_SPACESHIP:
1773     case EL_SPACESHIP_RIGHT:
1774     case EL_SPACESHIP_UP:
1775     case EL_SPACESHIP_LEFT:
1776     case EL_SPACESHIP_DOWN:
1777     case EL_BD_BUTTERFLY:
1778     case EL_BD_BUTTERFLY_RIGHT:
1779     case EL_BD_BUTTERFLY_UP:
1780     case EL_BD_BUTTERFLY_LEFT:
1781     case EL_BD_BUTTERFLY_DOWN:
1782     case EL_BD_FIREFLY:
1783     case EL_BD_FIREFLY_RIGHT:
1784     case EL_BD_FIREFLY_UP:
1785     case EL_BD_FIREFLY_LEFT:
1786     case EL_BD_FIREFLY_DOWN:
1787     case EL_PACMAN_RIGHT:
1788     case EL_PACMAN_UP:
1789     case EL_PACMAN_LEFT:
1790     case EL_PACMAN_DOWN:
1791     case EL_YAMYAM:
1792     case EL_YAMYAM_LEFT:
1793     case EL_YAMYAM_RIGHT:
1794     case EL_YAMYAM_UP:
1795     case EL_YAMYAM_DOWN:
1796     case EL_DARK_YAMYAM:
1797     case EL_ROBOT:
1798     case EL_PACMAN:
1799     case EL_SP_SNIKSNAK:
1800     case EL_SP_ELECTRON:
1801     case EL_MOLE:
1802     case EL_MOLE_LEFT:
1803     case EL_MOLE_RIGHT:
1804     case EL_MOLE_UP:
1805     case EL_MOLE_DOWN:
1806       InitMovDir(x, y);
1807       break;
1808
1809     case EL_AMOEBA_FULL:
1810     case EL_BD_AMOEBA:
1811       InitAmoebaNr(x, y);
1812       break;
1813
1814     case EL_AMOEBA_DROP:
1815       if (y == lev_fieldy - 1)
1816       {
1817         Feld[x][y] = EL_AMOEBA_GROWING;
1818         Store[x][y] = EL_AMOEBA_WET;
1819       }
1820       break;
1821
1822     case EL_DYNAMITE_ACTIVE:
1823     case EL_SP_DISK_RED_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1828       MovDelay[x][y] = 96;
1829       break;
1830
1831     case EL_EM_DYNAMITE_ACTIVE:
1832       MovDelay[x][y] = 32;
1833       break;
1834
1835     case EL_LAMP:
1836       local_player->lights_still_needed++;
1837       break;
1838
1839     case EL_PENGUIN:
1840       local_player->friends_still_needed++;
1841       break;
1842
1843     case EL_PIG:
1844     case EL_DRAGON:
1845       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1846       break;
1847
1848     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1849     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1850     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1853     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1856     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1860       if (init_game)
1861       {
1862         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1865
1866         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1867         {
1868           game.belt_dir[belt_nr] = belt_dir;
1869           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1870         }
1871         else    /* more than one switch -- set it like the first switch */
1872         {
1873           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1874         }
1875       }
1876       break;
1877
1878     case EL_LIGHT_SWITCH_ACTIVE:
1879       if (init_game)
1880         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1881       break;
1882
1883     case EL_INVISIBLE_STEELWALL:
1884     case EL_INVISIBLE_WALL:
1885     case EL_INVISIBLE_SAND:
1886       if (game.light_time_left > 0 ||
1887           game.lenses_time_left > 0)
1888         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1889       break;
1890
1891     case EL_EMC_MAGIC_BALL:
1892       if (game.ball_state)
1893         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1894       break;
1895
1896     case EL_EMC_MAGIC_BALL_SWITCH:
1897       if (game.ball_state)
1898         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1899       break;
1900
1901     case EL_TRIGGER_PLAYER:
1902     case EL_TRIGGER_ELEMENT:
1903     case EL_TRIGGER_CE_VALUE:
1904     case EL_TRIGGER_CE_SCORE:
1905     case EL_SELF:
1906     case EL_ANY_ELEMENT:
1907     case EL_CURRENT_CE_VALUE:
1908     case EL_CURRENT_CE_SCORE:
1909     case EL_PREV_CE_1:
1910     case EL_PREV_CE_2:
1911     case EL_PREV_CE_3:
1912     case EL_PREV_CE_4:
1913     case EL_PREV_CE_5:
1914     case EL_PREV_CE_6:
1915     case EL_PREV_CE_7:
1916     case EL_PREV_CE_8:
1917     case EL_NEXT_CE_1:
1918     case EL_NEXT_CE_2:
1919     case EL_NEXT_CE_3:
1920     case EL_NEXT_CE_4:
1921     case EL_NEXT_CE_5:
1922     case EL_NEXT_CE_6:
1923     case EL_NEXT_CE_7:
1924     case EL_NEXT_CE_8:
1925       /* reference elements should not be used on the playfield */
1926       Feld[x][y] = EL_EMPTY;
1927       break;
1928
1929     default:
1930       if (IS_CUSTOM_ELEMENT(element))
1931       {
1932         if (CAN_MOVE(element))
1933           InitMovDir(x, y);
1934
1935         if (!element_info[element].use_last_ce_value || init_game)
1936           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1937       }
1938       else if (IS_GROUP_ELEMENT(element))
1939       {
1940         Feld[x][y] = GetElementFromGroupElement(element);
1941
1942         InitField(x, y, init_game);
1943       }
1944
1945       break;
1946   }
1947
1948   if (!init_game)
1949     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1950 }
1951
1952 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1953 {
1954   InitField(x, y, init_game);
1955
1956   /* not needed to call InitMovDir() -- already done by InitField()! */
1957   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1958       CAN_MOVE(Feld[x][y]))
1959     InitMovDir(x, y);
1960 }
1961
1962 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1963 {
1964   int old_element = Feld[x][y];
1965
1966   InitField(x, y, init_game);
1967
1968   /* not needed to call InitMovDir() -- already done by InitField()! */
1969   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1970       CAN_MOVE(old_element) &&
1971       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1972     InitMovDir(x, y);
1973
1974   /* this case is in fact a combination of not less than three bugs:
1975      first, it calls InitMovDir() for elements that can move, although this is
1976      already done by InitField(); then, it checks the element that was at this
1977      field _before_ the call to InitField() (which can change it); lastly, it
1978      was not called for "mole with direction" elements, which were treated as
1979      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1980   */
1981 }
1982
1983 static int get_key_element_from_nr(int key_nr)
1984 {
1985   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1986                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1987                           EL_EM_KEY_1 : EL_KEY_1);
1988
1989   return key_base_element + key_nr;
1990 }
1991
1992 static int get_next_dropped_element(struct PlayerInfo *player)
1993 {
1994   return (player->inventory_size > 0 ?
1995           player->inventory_element[player->inventory_size - 1] :
1996           player->inventory_infinite_element != EL_UNDEFINED ?
1997           player->inventory_infinite_element :
1998           player->dynabombs_left > 0 ?
1999           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2000           EL_UNDEFINED);
2001 }
2002
2003 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2004 {
2005   /* pos >= 0: get element from bottom of the stack;
2006      pos <  0: get element from top of the stack */
2007
2008   if (pos < 0)
2009   {
2010     int min_inventory_size = -pos;
2011     int inventory_pos = player->inventory_size - min_inventory_size;
2012     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2013
2014     return (player->inventory_size >= min_inventory_size ?
2015             player->inventory_element[inventory_pos] :
2016             player->inventory_infinite_element != EL_UNDEFINED ?
2017             player->inventory_infinite_element :
2018             player->dynabombs_left >= min_dynabombs_left ?
2019             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2020             EL_UNDEFINED);
2021   }
2022   else
2023   {
2024     int min_dynabombs_left = pos + 1;
2025     int min_inventory_size = pos + 1 - player->dynabombs_left;
2026     int inventory_pos = pos - player->dynabombs_left;
2027
2028     return (player->inventory_infinite_element != EL_UNDEFINED ?
2029             player->inventory_infinite_element :
2030             player->dynabombs_left >= min_dynabombs_left ?
2031             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032             player->inventory_size >= min_inventory_size ?
2033             player->inventory_element[inventory_pos] :
2034             EL_UNDEFINED);
2035   }
2036 }
2037
2038 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2039 {
2040   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2041   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2042   int compare_result;
2043
2044   if (gpo1->sort_priority != gpo2->sort_priority)
2045     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2046   else
2047     compare_result = gpo1->nr - gpo2->nr;
2048
2049   return compare_result;
2050 }
2051
2052 void InitGameControlValues()
2053 {
2054   int i;
2055
2056   for (i = 0; game_panel_controls[i].nr != -1; i++)
2057   {
2058     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2059     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2060     struct TextPosInfo *pos = gpc->pos;
2061     int nr = gpc->nr;
2062     int type = gpc->type;
2063
2064     if (nr != i)
2065     {
2066       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2067       Error(ERR_EXIT, "this should not happen -- please debug");
2068     }
2069
2070     /* force update of game controls after initialization */
2071     gpc->value = gpc->last_value = -1;
2072     gpc->frame = gpc->last_frame = -1;
2073     gpc->gfx_frame = -1;
2074
2075     /* determine panel value width for later calculation of alignment */
2076     if (type == TYPE_INTEGER || type == TYPE_STRING)
2077     {
2078       pos->width = pos->size * getFontWidth(pos->font);
2079       pos->height = getFontHeight(pos->font);
2080     }
2081     else if (type == TYPE_ELEMENT)
2082     {
2083       pos->width = pos->size;
2084       pos->height = pos->size;
2085     }
2086
2087     /* fill structure for game panel draw order */
2088     gpo->nr = gpc->nr;
2089     gpo->sort_priority = pos->sort_priority;
2090   }
2091
2092   /* sort game panel controls according to sort_priority and control number */
2093   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2094         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2095 }
2096
2097 void UpdatePlayfieldElementCount()
2098 {
2099   boolean use_element_count = FALSE;
2100   int i, j, x, y;
2101
2102   /* first check if it is needed at all to calculate playfield element count */
2103   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2104     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2105       use_element_count = TRUE;
2106
2107   if (!use_element_count)
2108     return;
2109
2110   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2111     element_info[i].element_count = 0;
2112
2113   SCAN_PLAYFIELD(x, y)
2114   {
2115     element_info[Feld[x][y]].element_count++;
2116   }
2117
2118   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2119     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2120       if (IS_IN_GROUP(j, i))
2121         element_info[EL_GROUP_START + i].element_count +=
2122           element_info[j].element_count;
2123 }
2124
2125 void UpdateGameControlValues()
2126 {
2127   int i, k;
2128   int time = (local_player->LevelSolved ?
2129               local_player->LevelSolved_CountingTime :
2130               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2131               level.native_em_level->lev->time :
2132               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2133               level.native_sp_level->game_sp->time_played :
2134               game.no_time_limit ? TimePlayed : TimeLeft);
2135   int score = (local_player->LevelSolved ?
2136                local_player->LevelSolved_CountingScore :
2137                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2138                level.native_em_level->lev->score :
2139                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2140                level.native_sp_level->game_sp->score :
2141                local_player->score);
2142   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2143               level.native_em_level->lev->required :
2144               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2145               level.native_sp_level->game_sp->infotrons_still_needed :
2146               local_player->gems_still_needed);
2147   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148                      level.native_em_level->lev->required > 0 :
2149                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2150                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2151                      local_player->gems_still_needed > 0 ||
2152                      local_player->sokobanfields_still_needed > 0 ||
2153                      local_player->lights_still_needed > 0);
2154
2155   UpdatePlayfieldElementCount();
2156
2157   /* update game panel control values */
2158
2159   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2160   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2161
2162   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2163   for (i = 0; i < MAX_NUM_KEYS; i++)
2164     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2167
2168   if (game.centered_player_nr == -1)
2169   {
2170     for (i = 0; i < MAX_PLAYERS; i++)
2171     {
2172       /* only one player in Supaplex game engine */
2173       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2174         break;
2175
2176       for (k = 0; k < MAX_NUM_KEYS; k++)
2177       {
2178         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179         {
2180           if (level.native_em_level->ply[i]->keys & (1 << k))
2181             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182               get_key_element_from_nr(k);
2183         }
2184         else if (stored_player[i].key[k])
2185           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2186             get_key_element_from_nr(k);
2187       }
2188
2189       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2190         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2191           level.native_em_level->ply[i]->dynamite;
2192       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2193         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194           level.native_sp_level->game_sp->red_disk_count;
2195       else
2196         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197           stored_player[i].inventory_size;
2198
2199       if (stored_player[i].num_white_keys > 0)
2200         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2201           EL_DC_KEY_WHITE;
2202
2203       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204         stored_player[i].num_white_keys;
2205     }
2206   }
2207   else
2208   {
2209     int player_nr = game.centered_player_nr;
2210
2211     for (k = 0; k < MAX_NUM_KEYS; k++)
2212     {
2213       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214       {
2215         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217             get_key_element_from_nr(k);
2218       }
2219       else if (stored_player[player_nr].key[k])
2220         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221           get_key_element_from_nr(k);
2222     }
2223
2224     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226         level.native_em_level->ply[player_nr]->dynamite;
2227     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2228       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229         level.native_sp_level->game_sp->red_disk_count;
2230     else
2231       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232         stored_player[player_nr].inventory_size;
2233
2234     if (stored_player[player_nr].num_white_keys > 0)
2235       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2236
2237     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2238       stored_player[player_nr].num_white_keys;
2239   }
2240
2241   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2242   {
2243     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2244       get_inventory_element_from_pos(local_player, i);
2245     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2246       get_inventory_element_from_pos(local_player, -i - 1);
2247   }
2248
2249   game_panel_controls[GAME_PANEL_SCORE].value = score;
2250   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2251
2252   game_panel_controls[GAME_PANEL_TIME].value = time;
2253
2254   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2255   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2256   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2257
2258   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2259
2260   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2261     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2262      EL_EMPTY);
2263   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2264     local_player->shield_normal_time_left;
2265   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2266     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2269     local_player->shield_deadly_time_left;
2270
2271   game_panel_controls[GAME_PANEL_EXIT].value =
2272     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2273
2274   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2275     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2276   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2277     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2278      EL_EMC_MAGIC_BALL_SWITCH);
2279
2280   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2281     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2282   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2283     game.light_time_left;
2284
2285   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2286     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2287   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2288     game.timegate_time_left;
2289
2290   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2291     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2292
2293   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2294     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2295   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2296     game.lenses_time_left;
2297
2298   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2299     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2301     game.magnify_time_left;
2302
2303   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2304     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2305      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2306      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2307      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2308      EL_BALLOON_SWITCH_NONE);
2309
2310   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2311     local_player->dynabomb_count;
2312   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2313     local_player->dynabomb_size;
2314   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2315     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2316
2317   game_panel_controls[GAME_PANEL_PENGUINS].value =
2318     local_player->friends_still_needed;
2319
2320   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2321     local_player->sokobanfields_still_needed;
2322   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2323     local_player->sokobanfields_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2326     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2327
2328   for (i = 0; i < NUM_BELTS; i++)
2329   {
2330     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2331       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2332        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2333     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2334       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2338     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2339   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2340     game.magic_wall_time_left;
2341
2342   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2343     local_player->gravity;
2344
2345   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2346     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2347
2348   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2349     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2350       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2351        game.panel.element[i].id : EL_UNDEFINED);
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2355       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2356        element_info[game.panel.element_count[i].id].element_count : 0);
2357
2358   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2359     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2360       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2361        element_info[game.panel.ce_score[i].id].collect_score : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2366        element_info[game.panel.ce_score_element[i].id].collect_score :
2367        EL_UNDEFINED);
2368
2369   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2372
2373   /* update game panel control frames */
2374
2375   for (i = 0; game_panel_controls[i].nr != -1; i++)
2376   {
2377     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2378
2379     if (gpc->type == TYPE_ELEMENT)
2380     {
2381       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2382       {
2383         int last_anim_random_frame = gfx.anim_random_frame;
2384         int element = gpc->value;
2385         int graphic = el2panelimg(element);
2386
2387         if (gpc->value != gpc->last_value)
2388         {
2389           gpc->gfx_frame = 0;
2390           gpc->gfx_random = INIT_GFX_RANDOM();
2391         }
2392         else
2393         {
2394           gpc->gfx_frame++;
2395
2396           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2397               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2398             gpc->gfx_random = INIT_GFX_RANDOM();
2399         }
2400
2401         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2402           gfx.anim_random_frame = gpc->gfx_random;
2403
2404         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2405           gpc->gfx_frame = element_info[element].collect_score;
2406
2407         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2408                                               gpc->gfx_frame);
2409
2410         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2411           gfx.anim_random_frame = last_anim_random_frame;
2412       }
2413     }
2414   }
2415 }
2416
2417 void DisplayGameControlValues()
2418 {
2419   boolean redraw_panel = FALSE;
2420   int i;
2421
2422   for (i = 0; game_panel_controls[i].nr != -1; i++)
2423   {
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2425
2426     if (PANEL_DEACTIVATED(gpc->pos))
2427       continue;
2428
2429     if (gpc->value == gpc->last_value &&
2430         gpc->frame == gpc->last_frame)
2431       continue;
2432
2433     redraw_panel = TRUE;
2434   }
2435
2436   if (!redraw_panel)
2437     return;
2438
2439   /* copy default game door content to main double buffer */
2440
2441   /* !!! CHECK AGAIN !!! */
2442   SetPanelBackground();
2443   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2444   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2445
2446   /* redraw game control buttons */
2447   RedrawGameButtons();
2448
2449   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2450
2451   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2452   {
2453     int nr = game_panel_order[i].nr;
2454     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2455     struct TextPosInfo *pos = gpc->pos;
2456     int type = gpc->type;
2457     int value = gpc->value;
2458     int frame = gpc->frame;
2459     int size = pos->size;
2460     int font = pos->font;
2461     boolean draw_masked = pos->draw_masked;
2462     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2463
2464     if (PANEL_DEACTIVATED(pos))
2465       continue;
2466
2467     gpc->last_value = value;
2468     gpc->last_frame = frame;
2469
2470     if (type == TYPE_INTEGER)
2471     {
2472       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2473           nr == GAME_PANEL_TIME)
2474       {
2475         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2476
2477         if (use_dynamic_size)           /* use dynamic number of digits */
2478         {
2479           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2480           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2481           int size2 = size1 + 1;
2482           int font1 = pos->font;
2483           int font2 = pos->font_alt;
2484
2485           size = (value < value_change ? size1 : size2);
2486           font = (value < value_change ? font1 : font2);
2487         }
2488       }
2489
2490       /* correct text size if "digits" is zero or less */
2491       if (size <= 0)
2492         size = strlen(int2str(value, size));
2493
2494       /* dynamically correct text alignment */
2495       pos->width = size * getFontWidth(font);
2496
2497       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2498                   int2str(value, size), font, mask_mode);
2499     }
2500     else if (type == TYPE_ELEMENT)
2501     {
2502       int element, graphic;
2503       Bitmap *src_bitmap;
2504       int src_x, src_y;
2505       int width, height;
2506       int dst_x = PANEL_XPOS(pos);
2507       int dst_y = PANEL_YPOS(pos);
2508
2509       if (value != EL_UNDEFINED && value != EL_EMPTY)
2510       {
2511         element = value;
2512         graphic = el2panelimg(value);
2513
2514         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2515
2516         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2517           size = TILESIZE;
2518
2519         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2520                               &src_x, &src_y);
2521
2522         width  = graphic_info[graphic].width  * size / TILESIZE;
2523         height = graphic_info[graphic].height * size / TILESIZE;
2524
2525         if (draw_masked)
2526           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2527                            dst_x, dst_y);
2528         else
2529           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2530                      dst_x, dst_y);
2531       }
2532     }
2533     else if (type == TYPE_STRING)
2534     {
2535       boolean active = (value != 0);
2536       char *state_normal = "off";
2537       char *state_active = "on";
2538       char *state = (active ? state_active : state_normal);
2539       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2540                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2541                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2542                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2543
2544       if (nr == GAME_PANEL_GRAVITY_STATE)
2545       {
2546         int font1 = pos->font;          /* (used for normal state) */
2547         int font2 = pos->font_alt;      /* (used for active state) */
2548
2549         font = (active ? font2 : font1);
2550       }
2551
2552       if (s != NULL)
2553       {
2554         char *s_cut;
2555
2556         if (size <= 0)
2557         {
2558           /* don't truncate output if "chars" is zero or less */
2559           size = strlen(s);
2560
2561           /* dynamically correct text alignment */
2562           pos->width = size * getFontWidth(font);
2563         }
2564
2565         s_cut = getStringCopyN(s, size);
2566
2567         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2568                     s_cut, font, mask_mode);
2569
2570         free(s_cut);
2571       }
2572     }
2573
2574     redraw_mask |= REDRAW_DOOR_1;
2575   }
2576
2577   SetGameStatus(GAME_MODE_PLAYING);
2578 }
2579
2580 void UpdateAndDisplayGameControlValues()
2581 {
2582   if (tape.deactivate_display)
2583     return;
2584
2585   UpdateGameControlValues();
2586   DisplayGameControlValues();
2587 }
2588
2589 void UpdateGameDoorValues()
2590 {
2591   UpdateGameControlValues();
2592 }
2593
2594 void DrawGameDoorValues()
2595 {
2596   DisplayGameControlValues();
2597 }
2598
2599
2600 /*
2601   =============================================================================
2602   InitGameEngine()
2603   -----------------------------------------------------------------------------
2604   initialize game engine due to level / tape version number
2605   =============================================================================
2606 */
2607
2608 static void InitGameEngine()
2609 {
2610   int i, j, k, l, x, y;
2611
2612   /* set game engine from tape file when re-playing, else from level file */
2613   game.engine_version = (tape.playing ? tape.engine_version :
2614                          level.game_version);
2615
2616   /* set single or multi-player game mode (needed for re-playing tapes) */
2617   game.team_mode = setup.team_mode;
2618
2619   if (tape.playing)
2620   {
2621     int num_players = 0;
2622
2623     for (i = 0; i < MAX_PLAYERS; i++)
2624       if (tape.player_participates[i])
2625         num_players++;
2626
2627     /* multi-player tapes contain input data for more than one player */
2628     game.team_mode = (num_players > 1);
2629   }
2630
2631   /* ---------------------------------------------------------------------- */
2632   /* set flags for bugs and changes according to active game engine version */
2633   /* ---------------------------------------------------------------------- */
2634
2635   /*
2636     Summary of bugfix/change:
2637     Fixed handling for custom elements that change when pushed by the player.
2638
2639     Fixed/changed in version:
2640     3.1.0
2641
2642     Description:
2643     Before 3.1.0, custom elements that "change when pushing" changed directly
2644     after the player started pushing them (until then handled in "DigField()").
2645     Since 3.1.0, these custom elements are not changed until the "pushing"
2646     move of the element is finished (now handled in "ContinueMoving()").
2647
2648     Affected levels/tapes:
2649     The first condition is generally needed for all levels/tapes before version
2650     3.1.0, which might use the old behaviour before it was changed; known tapes
2651     that are affected are some tapes from the level set "Walpurgis Gardens" by
2652     Jamie Cullen.
2653     The second condition is an exception from the above case and is needed for
2654     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2655     above (including some development versions of 3.1.0), but before it was
2656     known that this change would break tapes like the above and was fixed in
2657     3.1.1, so that the changed behaviour was active although the engine version
2658     while recording maybe was before 3.1.0. There is at least one tape that is
2659     affected by this exception, which is the tape for the one-level set "Bug
2660     Machine" by Juergen Bonhagen.
2661   */
2662
2663   game.use_change_when_pushing_bug =
2664     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2665      !(tape.playing &&
2666        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2667        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2668
2669   /*
2670     Summary of bugfix/change:
2671     Fixed handling for blocking the field the player leaves when moving.
2672
2673     Fixed/changed in version:
2674     3.1.1
2675
2676     Description:
2677     Before 3.1.1, when "block last field when moving" was enabled, the field
2678     the player is leaving when moving was blocked for the time of the move,
2679     and was directly unblocked afterwards. This resulted in the last field
2680     being blocked for exactly one less than the number of frames of one player
2681     move. Additionally, even when blocking was disabled, the last field was
2682     blocked for exactly one frame.
2683     Since 3.1.1, due to changes in player movement handling, the last field
2684     is not blocked at all when blocking is disabled. When blocking is enabled,
2685     the last field is blocked for exactly the number of frames of one player
2686     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2687     last field is blocked for exactly one more than the number of frames of
2688     one player move.
2689
2690     Affected levels/tapes:
2691     (!!! yet to be determined -- probably many !!!)
2692   */
2693
2694   game.use_block_last_field_bug =
2695     (game.engine_version < VERSION_IDENT(3,1,1,0));
2696
2697   /* ---------------------------------------------------------------------- */
2698
2699   /* set maximal allowed number of custom element changes per game frame */
2700   game.max_num_changes_per_frame = 1;
2701
2702   /* default scan direction: scan playfield from top/left to bottom/right */
2703   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2704
2705   /* dynamically adjust element properties according to game engine version */
2706   InitElementPropertiesEngine(game.engine_version);
2707
2708 #if 0
2709   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2710   printf("          tape version == %06d [%s] [file: %06d]\n",
2711          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2712          tape.file_version);
2713   printf("       => game.engine_version == %06d\n", game.engine_version);
2714 #endif
2715
2716   /* ---------- initialize player's initial move delay --------------------- */
2717
2718   /* dynamically adjust player properties according to level information */
2719   for (i = 0; i < MAX_PLAYERS; i++)
2720     game.initial_move_delay_value[i] =
2721       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2722
2723   /* dynamically adjust player properties according to game engine version */
2724   for (i = 0; i < MAX_PLAYERS; i++)
2725     game.initial_move_delay[i] =
2726       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2727        game.initial_move_delay_value[i] : 0);
2728
2729   /* ---------- initialize player's initial push delay --------------------- */
2730
2731   /* dynamically adjust player properties according to game engine version */
2732   game.initial_push_delay_value =
2733     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2734
2735   /* ---------- initialize changing elements ------------------------------- */
2736
2737   /* initialize changing elements information */
2738   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2739   {
2740     struct ElementInfo *ei = &element_info[i];
2741
2742     /* this pointer might have been changed in the level editor */
2743     ei->change = &ei->change_page[0];
2744
2745     if (!IS_CUSTOM_ELEMENT(i))
2746     {
2747       ei->change->target_element = EL_EMPTY_SPACE;
2748       ei->change->delay_fixed = 0;
2749       ei->change->delay_random = 0;
2750       ei->change->delay_frames = 1;
2751     }
2752
2753     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2754     {
2755       ei->has_change_event[j] = FALSE;
2756
2757       ei->event_page_nr[j] = 0;
2758       ei->event_page[j] = &ei->change_page[0];
2759     }
2760   }
2761
2762   /* add changing elements from pre-defined list */
2763   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2764   {
2765     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2766     struct ElementInfo *ei = &element_info[ch_delay->element];
2767
2768     ei->change->target_element       = ch_delay->target_element;
2769     ei->change->delay_fixed          = ch_delay->change_delay;
2770
2771     ei->change->pre_change_function  = ch_delay->pre_change_function;
2772     ei->change->change_function      = ch_delay->change_function;
2773     ei->change->post_change_function = ch_delay->post_change_function;
2774
2775     ei->change->can_change = TRUE;
2776     ei->change->can_change_or_has_action = TRUE;
2777
2778     ei->has_change_event[CE_DELAY] = TRUE;
2779
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2782   }
2783
2784   /* ---------- initialize internal run-time variables --------------------- */
2785
2786   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2787   {
2788     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2789
2790     for (j = 0; j < ei->num_change_pages; j++)
2791     {
2792       ei->change_page[j].can_change_or_has_action =
2793         (ei->change_page[j].can_change |
2794          ei->change_page[j].has_action);
2795     }
2796   }
2797
2798   /* add change events from custom element configuration */
2799   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2800   {
2801     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2802
2803     for (j = 0; j < ei->num_change_pages; j++)
2804     {
2805       if (!ei->change_page[j].can_change_or_has_action)
2806         continue;
2807
2808       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2809       {
2810         /* only add event page for the first page found with this event */
2811         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2812         {
2813           ei->has_change_event[k] = TRUE;
2814
2815           ei->event_page_nr[k] = j;
2816           ei->event_page[k] = &ei->change_page[j];
2817         }
2818       }
2819     }
2820   }
2821
2822   /* ---------- initialize reference elements in change conditions --------- */
2823
2824   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2825   {
2826     int element = EL_CUSTOM_START + i;
2827     struct ElementInfo *ei = &element_info[element];
2828
2829     for (j = 0; j < ei->num_change_pages; j++)
2830     {
2831       int trigger_element = ei->change_page[j].initial_trigger_element;
2832
2833       if (trigger_element >= EL_PREV_CE_8 &&
2834           trigger_element <= EL_NEXT_CE_8)
2835         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2836
2837       ei->change_page[j].trigger_element = trigger_element;
2838     }
2839   }
2840
2841   /* ---------- initialize run-time trigger player and element ------------- */
2842
2843   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2844   {
2845     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2846
2847     for (j = 0; j < ei->num_change_pages; j++)
2848     {
2849       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2852       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2853       ei->change_page[j].actual_trigger_ce_value = 0;
2854       ei->change_page[j].actual_trigger_ce_score = 0;
2855     }
2856   }
2857
2858   /* ---------- initialize trigger events ---------------------------------- */
2859
2860   /* initialize trigger events information */
2861   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2862     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2863       trigger_events[i][j] = FALSE;
2864
2865   /* add trigger events from element change event properties */
2866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867   {
2868     struct ElementInfo *ei = &element_info[i];
2869
2870     for (j = 0; j < ei->num_change_pages; j++)
2871     {
2872       if (!ei->change_page[j].can_change_or_has_action)
2873         continue;
2874
2875       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2876       {
2877         int trigger_element = ei->change_page[j].trigger_element;
2878
2879         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2880         {
2881           if (ei->change_page[j].has_event[k])
2882           {
2883             if (IS_GROUP_ELEMENT(trigger_element))
2884             {
2885               struct ElementGroupInfo *group =
2886                 element_info[trigger_element].group;
2887
2888               for (l = 0; l < group->num_elements_resolved; l++)
2889                 trigger_events[group->element_resolved[l]][k] = TRUE;
2890             }
2891             else if (trigger_element == EL_ANY_ELEMENT)
2892               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2893                 trigger_events[l][k] = TRUE;
2894             else
2895               trigger_events[trigger_element][k] = TRUE;
2896           }
2897         }
2898       }
2899     }
2900   }
2901
2902   /* ---------- initialize push delay -------------------------------------- */
2903
2904   /* initialize push delay values to default */
2905   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2906   {
2907     if (!IS_CUSTOM_ELEMENT(i))
2908     {
2909       /* set default push delay values (corrected since version 3.0.7-1) */
2910       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2911       {
2912         element_info[i].push_delay_fixed = 2;
2913         element_info[i].push_delay_random = 8;
2914       }
2915       else
2916       {
2917         element_info[i].push_delay_fixed = 8;
2918         element_info[i].push_delay_random = 8;
2919       }
2920     }
2921   }
2922
2923   /* set push delay value for certain elements from pre-defined list */
2924   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2925   {
2926     int e = push_delay_list[i].element;
2927
2928     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2929     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2930   }
2931
2932   /* set push delay value for Supaplex elements for newer engine versions */
2933   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2934   {
2935     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2936     {
2937       if (IS_SP_ELEMENT(i))
2938       {
2939         /* set SP push delay to just enough to push under a falling zonk */
2940         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2941
2942         element_info[i].push_delay_fixed  = delay;
2943         element_info[i].push_delay_random = 0;
2944       }
2945     }
2946   }
2947
2948   /* ---------- initialize move stepsize ----------------------------------- */
2949
2950   /* initialize move stepsize values to default */
2951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2952     if (!IS_CUSTOM_ELEMENT(i))
2953       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2954
2955   /* set move stepsize value for certain elements from pre-defined list */
2956   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2957   {
2958     int e = move_stepsize_list[i].element;
2959
2960     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2961   }
2962
2963   /* ---------- initialize collect score ----------------------------------- */
2964
2965   /* initialize collect score values for custom elements from initial value */
2966   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2967     if (IS_CUSTOM_ELEMENT(i))
2968       element_info[i].collect_score = element_info[i].collect_score_initial;
2969
2970   /* ---------- initialize collect count ----------------------------------- */
2971
2972   /* initialize collect count values for non-custom elements */
2973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974     if (!IS_CUSTOM_ELEMENT(i))
2975       element_info[i].collect_count_initial = 0;
2976
2977   /* add collect count values for all elements from pre-defined list */
2978   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2979     element_info[collect_count_list[i].element].collect_count_initial =
2980       collect_count_list[i].count;
2981
2982   /* ---------- initialize access direction -------------------------------- */
2983
2984   /* initialize access direction values to default (access from every side) */
2985   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2986     if (!IS_CUSTOM_ELEMENT(i))
2987       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2988
2989   /* set access direction value for certain elements from pre-defined list */
2990   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2991     element_info[access_direction_list[i].element].access_direction =
2992       access_direction_list[i].direction;
2993
2994   /* ---------- initialize explosion content ------------------------------- */
2995   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2996   {
2997     if (IS_CUSTOM_ELEMENT(i))
2998       continue;
2999
3000     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3001     {
3002       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3003
3004       element_info[i].content.e[x][y] =
3005         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3006          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3007          i == EL_PLAYER_3 ? EL_EMERALD :
3008          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3009          i == EL_MOLE ? EL_EMERALD_RED :
3010          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3011          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3012          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3013          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3014          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3015          i == EL_WALL_EMERALD ? EL_EMERALD :
3016          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3017          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3018          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3019          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3020          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3021          i == EL_WALL_PEARL ? EL_PEARL :
3022          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3023          EL_EMPTY);
3024     }
3025   }
3026
3027   /* ---------- initialize recursion detection ------------------------------ */
3028   recursion_loop_depth = 0;
3029   recursion_loop_detected = FALSE;
3030   recursion_loop_element = EL_UNDEFINED;
3031
3032   /* ---------- initialize graphics engine ---------------------------------- */
3033   game.scroll_delay_value =
3034     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3035      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3036   game.scroll_delay_value =
3037     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3038
3039   /* ---------- initialize game engine snapshots ---------------------------- */
3040   for (i = 0; i < MAX_PLAYERS; i++)
3041     game.snapshot.last_action[i] = 0;
3042   game.snapshot.changed_action = FALSE;
3043   game.snapshot.collected_item = FALSE;
3044   game.snapshot.mode =
3045     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3046      SNAPSHOT_MODE_EVERY_STEP :
3047      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3048      SNAPSHOT_MODE_EVERY_MOVE :
3049      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3050      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3051 }
3052
3053 int get_num_special_action(int element, int action_first, int action_last)
3054 {
3055   int num_special_action = 0;
3056   int i, j;
3057
3058   for (i = action_first; i <= action_last; i++)
3059   {
3060     boolean found = FALSE;
3061
3062     for (j = 0; j < NUM_DIRECTIONS; j++)
3063       if (el_act_dir2img(element, i, j) !=
3064           el_act_dir2img(element, ACTION_DEFAULT, j))
3065         found = TRUE;
3066
3067     if (found)
3068       num_special_action++;
3069     else
3070       break;
3071   }
3072
3073   return num_special_action;
3074 }
3075
3076
3077 /*
3078   =============================================================================
3079   InitGame()
3080   -----------------------------------------------------------------------------
3081   initialize and start new game
3082   =============================================================================
3083 */
3084
3085 void InitGame()
3086 {
3087   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3088   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3089   int fade_mask = REDRAW_FIELD;
3090
3091   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3092   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3093   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3094   int initial_move_dir = MV_DOWN;
3095   int i, j, x, y;
3096
3097   // required here to update video display before fading (FIX THIS)
3098   DrawMaskedBorder(REDRAW_DOOR_2);
3099
3100   if (!game.restart_level)
3101     CloseDoor(DOOR_CLOSE_1);
3102
3103   SetGameStatus(GAME_MODE_PLAYING);
3104
3105   /* needed if different viewport properties defined for playing */
3106   ChangeViewportPropertiesIfNeeded();
3107
3108   if (level_editor_test_game)
3109     FadeSkipNextFadeIn();
3110   else
3111     FadeSetEnterScreen();
3112
3113   if (CheckIfGlobalBorderHasChanged())
3114     fade_mask = REDRAW_ALL;
3115
3116   FadeSoundsAndMusic();
3117
3118   ExpireSoundLoops(TRUE);
3119
3120   FadeOut(fade_mask);
3121
3122   ClearField();
3123
3124   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3125
3126   DrawCompleteVideoDisplay();
3127
3128   InitGameEngine();
3129   InitGameControlValues();
3130
3131   /* don't play tapes over network */
3132   network_playing = (options.network && !tape.playing);
3133
3134   for (i = 0; i < MAX_PLAYERS; i++)
3135   {
3136     struct PlayerInfo *player = &stored_player[i];
3137
3138     player->index_nr = i;
3139     player->index_bit = (1 << i);
3140     player->element_nr = EL_PLAYER_1 + i;
3141
3142     player->present = FALSE;
3143     player->active = FALSE;
3144     player->mapped = FALSE;
3145
3146     player->killed = FALSE;
3147     player->reanimated = FALSE;
3148
3149     player->action = 0;
3150     player->effective_action = 0;
3151     player->programmed_action = 0;
3152
3153     player->score = 0;
3154     player->score_final = 0;
3155
3156     player->gems_still_needed = level.gems_needed;
3157     player->sokobanfields_still_needed = 0;
3158     player->lights_still_needed = 0;
3159     player->friends_still_needed = 0;
3160
3161     for (j = 0; j < MAX_NUM_KEYS; j++)
3162       player->key[j] = FALSE;
3163
3164     player->num_white_keys = 0;
3165
3166     player->dynabomb_count = 0;
3167     player->dynabomb_size = 1;
3168     player->dynabombs_left = 0;
3169     player->dynabomb_xl = FALSE;
3170
3171     player->MovDir = initial_move_dir;
3172     player->MovPos = 0;
3173     player->GfxPos = 0;
3174     player->GfxDir = initial_move_dir;
3175     player->GfxAction = ACTION_DEFAULT;
3176     player->Frame = 0;
3177     player->StepFrame = 0;
3178
3179     player->initial_element = player->element_nr;
3180     player->artwork_element =
3181       (level.use_artwork_element[i] ? level.artwork_element[i] :
3182        player->element_nr);
3183     player->use_murphy = FALSE;
3184
3185     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3186     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3187
3188     player->gravity = level.initial_player_gravity[i];
3189
3190     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3191
3192     player->actual_frame_counter = 0;
3193
3194     player->step_counter = 0;
3195
3196     player->last_move_dir = initial_move_dir;
3197
3198     player->is_active = FALSE;
3199
3200     player->is_waiting = FALSE;
3201     player->is_moving = FALSE;
3202     player->is_auto_moving = FALSE;
3203     player->is_digging = FALSE;
3204     player->is_snapping = FALSE;
3205     player->is_collecting = FALSE;
3206     player->is_pushing = FALSE;
3207     player->is_switching = FALSE;
3208     player->is_dropping = FALSE;
3209     player->is_dropping_pressed = FALSE;
3210
3211     player->is_bored = FALSE;
3212     player->is_sleeping = FALSE;
3213
3214     player->frame_counter_bored = -1;
3215     player->frame_counter_sleeping = -1;
3216
3217     player->anim_delay_counter = 0;
3218     player->post_delay_counter = 0;
3219
3220     player->dir_waiting = initial_move_dir;
3221     player->action_waiting = ACTION_DEFAULT;
3222     player->last_action_waiting = ACTION_DEFAULT;
3223     player->special_action_bored = ACTION_DEFAULT;
3224     player->special_action_sleeping = ACTION_DEFAULT;
3225
3226     player->switch_x = -1;
3227     player->switch_y = -1;
3228
3229     player->drop_x = -1;
3230     player->drop_y = -1;
3231
3232     player->show_envelope = 0;
3233
3234     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3235
3236     player->push_delay       = -1;      /* initialized when pushing starts */
3237     player->push_delay_value = game.initial_push_delay_value;
3238
3239     player->drop_delay = 0;
3240     player->drop_pressed_delay = 0;
3241
3242     player->last_jx = -1;
3243     player->last_jy = -1;
3244     player->jx = -1;
3245     player->jy = -1;
3246
3247     player->shield_normal_time_left = 0;
3248     player->shield_deadly_time_left = 0;
3249
3250     player->inventory_infinite_element = EL_UNDEFINED;
3251     player->inventory_size = 0;
3252
3253     if (level.use_initial_inventory[i])
3254     {
3255       for (j = 0; j < level.initial_inventory_size[i]; j++)
3256       {
3257         int element = level.initial_inventory_content[i][j];
3258         int collect_count = element_info[element].collect_count_initial;
3259         int k;
3260
3261         if (!IS_CUSTOM_ELEMENT(element))
3262           collect_count = 1;
3263
3264         if (collect_count == 0)
3265           player->inventory_infinite_element = element;
3266         else
3267           for (k = 0; k < collect_count; k++)
3268             if (player->inventory_size < MAX_INVENTORY_SIZE)
3269               player->inventory_element[player->inventory_size++] = element;
3270       }
3271     }
3272
3273     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3274     SnapField(player, 0, 0);
3275
3276     player->LevelSolved = FALSE;
3277     player->GameOver = FALSE;
3278
3279     player->LevelSolved_GameWon = FALSE;
3280     player->LevelSolved_GameEnd = FALSE;
3281     player->LevelSolved_PanelOff = FALSE;
3282     player->LevelSolved_SaveTape = FALSE;
3283     player->LevelSolved_SaveScore = FALSE;
3284     player->LevelSolved_CountingTime = 0;
3285     player->LevelSolved_CountingScore = 0;
3286
3287     map_player_action[i] = i;
3288   }
3289
3290   network_player_action_received = FALSE;
3291
3292 #if defined(NETWORK_AVALIABLE)
3293   /* initial null action */
3294   if (network_playing)
3295     SendToServer_MovePlayer(MV_NONE);
3296 #endif
3297
3298   ZX = ZY = -1;
3299   ExitX = ExitY = -1;
3300
3301   FrameCounter = 0;
3302   TimeFrames = 0;
3303   TimePlayed = 0;
3304   TimeLeft = level.time;
3305   TapeTime = 0;
3306
3307   ScreenMovDir = MV_NONE;
3308   ScreenMovPos = 0;
3309   ScreenGfxPos = 0;
3310
3311   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3312
3313   AllPlayersGone = FALSE;
3314
3315   game.no_time_limit = (level.time == 0);
3316
3317   game.yamyam_content_nr = 0;
3318   game.robot_wheel_active = FALSE;
3319   game.magic_wall_active = FALSE;
3320   game.magic_wall_time_left = 0;
3321   game.light_time_left = 0;
3322   game.timegate_time_left = 0;
3323   game.switchgate_pos = 0;
3324   game.wind_direction = level.wind_direction_initial;
3325
3326   game.lenses_time_left = 0;
3327   game.magnify_time_left = 0;
3328
3329   game.ball_state = level.ball_state_initial;
3330   game.ball_content_nr = 0;
3331
3332   game.envelope_active = FALSE;
3333
3334   /* set focus to local player for network games, else to all players */
3335   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3336   game.centered_player_nr_next = game.centered_player_nr;
3337   game.set_centered_player = FALSE;
3338
3339   if (network_playing && tape.recording)
3340   {
3341     /* store client dependent player focus when recording network games */
3342     tape.centered_player_nr_next = game.centered_player_nr_next;
3343     tape.set_centered_player = TRUE;
3344   }
3345
3346   for (i = 0; i < NUM_BELTS; i++)
3347   {
3348     game.belt_dir[i] = MV_NONE;
3349     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3350   }
3351
3352   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3353     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3354
3355 #if DEBUG_INIT_PLAYER
3356   if (options.debug)
3357   {
3358     printf("Player status at level initialization:\n");
3359   }
3360 #endif
3361
3362   SCAN_PLAYFIELD(x, y)
3363   {
3364     Feld[x][y] = level.field[x][y];
3365     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3366     ChangeDelay[x][y] = 0;
3367     ChangePage[x][y] = -1;
3368     CustomValue[x][y] = 0;              /* initialized in InitField() */
3369     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3370     AmoebaNr[x][y] = 0;
3371     WasJustMoving[x][y] = 0;
3372     WasJustFalling[x][y] = 0;
3373     CheckCollision[x][y] = 0;
3374     CheckImpact[x][y] = 0;
3375     Stop[x][y] = FALSE;
3376     Pushed[x][y] = FALSE;
3377
3378     ChangeCount[x][y] = 0;
3379     ChangeEvent[x][y] = -1;
3380
3381     ExplodePhase[x][y] = 0;
3382     ExplodeDelay[x][y] = 0;
3383     ExplodeField[x][y] = EX_TYPE_NONE;
3384
3385     RunnerVisit[x][y] = 0;
3386     PlayerVisit[x][y] = 0;
3387
3388     GfxFrame[x][y] = 0;
3389     GfxRandom[x][y] = INIT_GFX_RANDOM();
3390     GfxElement[x][y] = EL_UNDEFINED;
3391     GfxAction[x][y] = ACTION_DEFAULT;
3392     GfxDir[x][y] = MV_NONE;
3393     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3394   }
3395
3396   SCAN_PLAYFIELD(x, y)
3397   {
3398     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3399       emulate_bd = FALSE;
3400     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3401       emulate_sb = FALSE;
3402     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3403       emulate_sp = FALSE;
3404
3405     InitField(x, y, TRUE);
3406
3407     ResetGfxAnimation(x, y);
3408   }
3409
3410   InitBeltMovement();
3411
3412   for (i = 0; i < MAX_PLAYERS; i++)
3413   {
3414     struct PlayerInfo *player = &stored_player[i];
3415
3416     /* set number of special actions for bored and sleeping animation */
3417     player->num_special_action_bored =
3418       get_num_special_action(player->artwork_element,
3419                              ACTION_BORING_1, ACTION_BORING_LAST);
3420     player->num_special_action_sleeping =
3421       get_num_special_action(player->artwork_element,
3422                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3423   }
3424
3425   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3426                     emulate_sb ? EMU_SOKOBAN :
3427                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3428
3429   /* initialize type of slippery elements */
3430   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3431   {
3432     if (!IS_CUSTOM_ELEMENT(i))
3433     {
3434       /* default: elements slip down either to the left or right randomly */
3435       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3436
3437       /* SP style elements prefer to slip down on the left side */
3438       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3439         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3440
3441       /* BD style elements prefer to slip down on the left side */
3442       if (game.emulation == EMU_BOULDERDASH)
3443         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3444     }
3445   }
3446
3447   /* initialize explosion and ignition delay */
3448   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3449   {
3450     if (!IS_CUSTOM_ELEMENT(i))
3451     {
3452       int num_phase = 8;
3453       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3454                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3455                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3456       int last_phase = (num_phase + 1) * delay;
3457       int half_phase = (num_phase / 2) * delay;
3458
3459       element_info[i].explosion_delay = last_phase - 1;
3460       element_info[i].ignition_delay = half_phase;
3461
3462       if (i == EL_BLACK_ORB)
3463         element_info[i].ignition_delay = 1;
3464     }
3465   }
3466
3467   /* correct non-moving belts to start moving left */
3468   for (i = 0; i < NUM_BELTS; i++)
3469     if (game.belt_dir[i] == MV_NONE)
3470       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3471
3472 #if USE_NEW_PLAYER_ASSIGNMENTS
3473   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3474   /* choose default local player */
3475   local_player = &stored_player[0];
3476
3477   for (i = 0; i < MAX_PLAYERS; i++)
3478     stored_player[i].connected = FALSE;
3479
3480   local_player->connected = TRUE;
3481   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3482
3483   if (tape.playing)
3484   {
3485     for (i = 0; i < MAX_PLAYERS; i++)
3486       stored_player[i].connected = tape.player_participates[i];
3487   }
3488   else if (game.team_mode && !options.network)
3489   {
3490     /* try to guess locally connected team mode players (needed for correct
3491        assignment of player figures from level to locally playing players) */
3492
3493     for (i = 0; i < MAX_PLAYERS; i++)
3494       if (setup.input[i].use_joystick ||
3495           setup.input[i].key.left != KSYM_UNDEFINED)
3496         stored_player[i].connected = TRUE;
3497   }
3498
3499 #if DEBUG_INIT_PLAYER
3500   if (options.debug)
3501   {
3502     printf("Player status after level initialization:\n");
3503
3504     for (i = 0; i < MAX_PLAYERS; i++)
3505     {
3506       struct PlayerInfo *player = &stored_player[i];
3507
3508       printf("- player %d: present == %d, connected == %d, active == %d",
3509              i + 1,
3510              player->present,
3511              player->connected,
3512              player->active);
3513
3514       if (local_player == player)
3515         printf(" (local player)");
3516
3517       printf("\n");
3518     }
3519   }
3520 #endif
3521
3522 #if DEBUG_INIT_PLAYER
3523   if (options.debug)
3524     printf("Reassigning players ...\n");
3525 #endif
3526
3527   /* check if any connected player was not found in playfield */
3528   for (i = 0; i < MAX_PLAYERS; i++)
3529   {
3530     struct PlayerInfo *player = &stored_player[i];
3531
3532     if (player->connected && !player->present)
3533     {
3534       struct PlayerInfo *field_player = NULL;
3535
3536 #if DEBUG_INIT_PLAYER
3537       if (options.debug)
3538         printf("- looking for field player for player %d ...\n", i + 1);
3539 #endif
3540
3541       /* assign first free player found that is present in the playfield */
3542
3543       /* first try: look for unmapped playfield player that is not connected */
3544       for (j = 0; j < MAX_PLAYERS; j++)
3545         if (field_player == NULL &&
3546             stored_player[j].present &&
3547             !stored_player[j].mapped &&
3548             !stored_player[j].connected)
3549           field_player = &stored_player[j];
3550
3551       /* second try: look for *any* unmapped playfield player */
3552       for (j = 0; j < MAX_PLAYERS; j++)
3553         if (field_player == NULL &&
3554             stored_player[j].present &&
3555             !stored_player[j].mapped)
3556           field_player = &stored_player[j];
3557
3558       if (field_player != NULL)
3559       {
3560         int jx = field_player->jx, jy = field_player->jy;
3561
3562 #if DEBUG_INIT_PLAYER
3563         if (options.debug)
3564           printf("- found player %d\n", field_player->index_nr + 1);
3565 #endif
3566
3567         player->present = FALSE;
3568         player->active = FALSE;
3569
3570         field_player->present = TRUE;
3571         field_player->active = TRUE;
3572
3573         /*
3574         player->initial_element = field_player->initial_element;
3575         player->artwork_element = field_player->artwork_element;
3576
3577         player->block_last_field       = field_player->block_last_field;
3578         player->block_delay_adjustment = field_player->block_delay_adjustment;
3579         */
3580
3581         StorePlayer[jx][jy] = field_player->element_nr;
3582
3583         field_player->jx = field_player->last_jx = jx;
3584         field_player->jy = field_player->last_jy = jy;
3585
3586         if (local_player == player)
3587           local_player = field_player;
3588
3589         map_player_action[field_player->index_nr] = i;
3590
3591         field_player->mapped = TRUE;
3592
3593 #if DEBUG_INIT_PLAYER
3594         if (options.debug)
3595           printf("- map_player_action[%d] == %d\n",
3596                  field_player->index_nr + 1, i + 1);
3597 #endif
3598       }
3599     }
3600
3601     if (player->connected && player->present)
3602       player->mapped = TRUE;
3603   }
3604
3605 #if DEBUG_INIT_PLAYER
3606   if (options.debug)
3607   {
3608     printf("Player status after player assignment (first stage):\n");
3609
3610     for (i = 0; i < MAX_PLAYERS; i++)
3611     {
3612       struct PlayerInfo *player = &stored_player[i];
3613
3614       printf("- player %d: present == %d, connected == %d, active == %d",
3615              i + 1,
3616              player->present,
3617              player->connected,
3618              player->active);
3619
3620       if (local_player == player)
3621         printf(" (local player)");
3622
3623       printf("\n");
3624     }
3625   }
3626 #endif
3627
3628 #else
3629
3630   /* check if any connected player was not found in playfield */
3631   for (i = 0; i < MAX_PLAYERS; i++)
3632   {
3633     struct PlayerInfo *player = &stored_player[i];
3634
3635     if (player->connected && !player->present)
3636     {
3637       for (j = 0; j < MAX_PLAYERS; j++)
3638       {
3639         struct PlayerInfo *field_player = &stored_player[j];
3640         int jx = field_player->jx, jy = field_player->jy;
3641
3642         /* assign first free player found that is present in the playfield */
3643         if (field_player->present && !field_player->connected)
3644         {
3645           player->present = TRUE;
3646           player->active = TRUE;
3647
3648           field_player->present = FALSE;
3649           field_player->active = FALSE;
3650
3651           player->initial_element = field_player->initial_element;
3652           player->artwork_element = field_player->artwork_element;
3653
3654           player->block_last_field       = field_player->block_last_field;
3655           player->block_delay_adjustment = field_player->block_delay_adjustment;
3656
3657           StorePlayer[jx][jy] = player->element_nr;
3658
3659           player->jx = player->last_jx = jx;
3660           player->jy = player->last_jy = jy;
3661
3662           break;
3663         }
3664       }
3665     }
3666   }
3667 #endif
3668
3669 #if 0
3670   printf("::: local_player->present == %d\n", local_player->present);
3671 #endif
3672
3673   if (tape.playing)
3674   {
3675     /* when playing a tape, eliminate all players who do not participate */
3676
3677 #if USE_NEW_PLAYER_ASSIGNMENTS
3678
3679     if (!game.team_mode)
3680     {
3681       for (i = 0; i < MAX_PLAYERS; i++)
3682       {
3683         if (stored_player[i].active &&
3684             !tape.player_participates[map_player_action[i]])
3685         {
3686           struct PlayerInfo *player = &stored_player[i];
3687           int jx = player->jx, jy = player->jy;
3688
3689 #if DEBUG_INIT_PLAYER
3690           if (options.debug)
3691             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3692 #endif
3693
3694           player->active = FALSE;
3695           StorePlayer[jx][jy] = 0;
3696           Feld[jx][jy] = EL_EMPTY;
3697         }
3698       }
3699     }
3700
3701 #else
3702
3703     for (i = 0; i < MAX_PLAYERS; i++)
3704     {
3705       if (stored_player[i].active &&
3706           !tape.player_participates[i])
3707       {
3708         struct PlayerInfo *player = &stored_player[i];
3709         int jx = player->jx, jy = player->jy;
3710
3711         player->active = FALSE;
3712         StorePlayer[jx][jy] = 0;
3713         Feld[jx][jy] = EL_EMPTY;
3714       }
3715     }
3716 #endif
3717   }
3718   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3719   {
3720     /* when in single player mode, eliminate all but the first active player */
3721
3722     for (i = 0; i < MAX_PLAYERS; i++)
3723     {
3724       if (stored_player[i].active)
3725       {
3726         for (j = i + 1; j < MAX_PLAYERS; j++)
3727         {
3728           if (stored_player[j].active)
3729           {
3730             struct PlayerInfo *player = &stored_player[j];
3731             int jx = player->jx, jy = player->jy;
3732
3733             player->active = FALSE;
3734             player->present = FALSE;
3735
3736             StorePlayer[jx][jy] = 0;
3737             Feld[jx][jy] = EL_EMPTY;
3738           }
3739         }
3740       }
3741     }
3742   }
3743
3744   /* when recording the game, store which players take part in the game */
3745   if (tape.recording)
3746   {
3747 #if USE_NEW_PLAYER_ASSIGNMENTS
3748     for (i = 0; i < MAX_PLAYERS; i++)
3749       if (stored_player[i].connected)
3750         tape.player_participates[i] = TRUE;
3751 #else
3752     for (i = 0; i < MAX_PLAYERS; i++)
3753       if (stored_player[i].active)
3754         tape.player_participates[i] = TRUE;
3755 #endif
3756   }
3757
3758 #if DEBUG_INIT_PLAYER
3759   if (options.debug)
3760   {
3761     printf("Player status after player assignment (final stage):\n");
3762
3763     for (i = 0; i < MAX_PLAYERS; i++)
3764     {
3765       struct PlayerInfo *player = &stored_player[i];
3766
3767       printf("- player %d: present == %d, connected == %d, active == %d",
3768              i + 1,
3769              player->present,
3770              player->connected,
3771              player->active);
3772
3773       if (local_player == player)
3774         printf(" (local player)");
3775
3776       printf("\n");
3777     }
3778   }
3779 #endif
3780
3781   if (BorderElement == EL_EMPTY)
3782   {
3783     SBX_Left = 0;
3784     SBX_Right = lev_fieldx - SCR_FIELDX;
3785     SBY_Upper = 0;
3786     SBY_Lower = lev_fieldy - SCR_FIELDY;
3787   }
3788   else
3789   {
3790     SBX_Left = -1;
3791     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3792     SBY_Upper = -1;
3793     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3794   }
3795
3796   if (full_lev_fieldx <= SCR_FIELDX)
3797     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3798   if (full_lev_fieldy <= SCR_FIELDY)
3799     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3800
3801   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3802     SBX_Left--;
3803   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3804     SBY_Upper--;
3805
3806   /* if local player not found, look for custom element that might create
3807      the player (make some assumptions about the right custom element) */
3808   if (!local_player->present)
3809   {
3810     int start_x = 0, start_y = 0;
3811     int found_rating = 0;
3812     int found_element = EL_UNDEFINED;
3813     int player_nr = local_player->index_nr;
3814
3815     SCAN_PLAYFIELD(x, y)
3816     {
3817       int element = Feld[x][y];
3818       int content;
3819       int xx, yy;
3820       boolean is_player;
3821
3822       if (level.use_start_element[player_nr] &&
3823           level.start_element[player_nr] == element &&
3824           found_rating < 4)
3825       {
3826         start_x = x;
3827         start_y = y;
3828
3829         found_rating = 4;
3830         found_element = element;
3831       }
3832
3833       if (!IS_CUSTOM_ELEMENT(element))
3834         continue;
3835
3836       if (CAN_CHANGE(element))
3837       {
3838         for (i = 0; i < element_info[element].num_change_pages; i++)
3839         {
3840           /* check for player created from custom element as single target */
3841           content = element_info[element].change_page[i].target_element;
3842           is_player = ELEM_IS_PLAYER(content);
3843
3844           if (is_player && (found_rating < 3 ||
3845                             (found_rating == 3 && element < found_element)))
3846           {
3847             start_x = x;
3848             start_y = y;
3849
3850             found_rating = 3;
3851             found_element = element;
3852           }
3853         }
3854       }
3855
3856       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3857       {
3858         /* check for player created from custom element as explosion content */
3859         content = element_info[element].content.e[xx][yy];
3860         is_player = ELEM_IS_PLAYER(content);
3861
3862         if (is_player && (found_rating < 2 ||
3863                           (found_rating == 2 && element < found_element)))
3864         {
3865           start_x = x + xx - 1;
3866           start_y = y + yy - 1;
3867
3868           found_rating = 2;
3869           found_element = element;
3870         }
3871
3872         if (!CAN_CHANGE(element))
3873           continue;
3874
3875         for (i = 0; i < element_info[element].num_change_pages; i++)
3876         {
3877           /* check for player created from custom element as extended target */
3878           content =
3879             element_info[element].change_page[i].target_content.e[xx][yy];
3880
3881           is_player = ELEM_IS_PLAYER(content);
3882
3883           if (is_player && (found_rating < 1 ||
3884                             (found_rating == 1 && element < found_element)))
3885           {
3886             start_x = x + xx - 1;
3887             start_y = y + yy - 1;
3888
3889             found_rating = 1;
3890             found_element = element;
3891           }
3892         }
3893       }
3894     }
3895
3896     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3897                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3898                 start_x - MIDPOSX);
3899
3900     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3901                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3902                 start_y - MIDPOSY);
3903   }
3904   else
3905   {
3906     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3907                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3908                 local_player->jx - MIDPOSX);
3909
3910     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3911                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3912                 local_player->jy - MIDPOSY);
3913   }
3914
3915   /* !!! FIX THIS (START) !!! */
3916   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3917   {
3918     InitGameEngine_EM();
3919   }
3920   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3921   {
3922     InitGameEngine_SP();
3923   }
3924   else
3925   {
3926     DrawLevel(REDRAW_FIELD);
3927     DrawAllPlayers();
3928
3929     /* after drawing the level, correct some elements */
3930     if (game.timegate_time_left == 0)
3931       CloseAllOpenTimegates();
3932   }
3933
3934   /* blit playfield from scroll buffer to normal back buffer for fading in */
3935   BlitScreenToBitmap(backbuffer);
3936   /* !!! FIX THIS (END) !!! */
3937
3938   DrawMaskedBorder(fade_mask);
3939
3940   FadeIn(fade_mask);
3941
3942 #if 1
3943   // full screen redraw is required at this point in the following cases:
3944   // - special editor door undrawn when game was started from level editor
3945   // - drawing area (playfield) was changed and has to be removed completely
3946   redraw_mask = REDRAW_ALL;
3947   BackToFront();
3948 #endif
3949
3950   if (!game.restart_level)
3951   {
3952     /* copy default game door content to main double buffer */
3953
3954     /* !!! CHECK AGAIN !!! */
3955     SetPanelBackground();
3956     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3957     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3958   }
3959
3960   SetPanelBackground();
3961   SetDrawBackgroundMask(REDRAW_DOOR_1);
3962
3963   UpdateAndDisplayGameControlValues();
3964
3965   if (!game.restart_level)
3966   {
3967     UnmapGameButtons();
3968     UnmapTapeButtons();
3969
3970     FreeGameButtons();
3971     CreateGameButtons();
3972
3973     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3974     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3975     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3976
3977     MapGameButtons();
3978     MapTapeButtons();
3979
3980     /* copy actual game door content to door double buffer for OpenDoor() */
3981     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3982
3983     OpenDoor(DOOR_OPEN_ALL);
3984
3985     PlaySound(SND_GAME_STARTING);
3986
3987     if (setup.sound_music)
3988       PlayLevelMusic();
3989
3990     KeyboardAutoRepeatOffUnlessAutoplay();
3991
3992 #if DEBUG_INIT_PLAYER
3993     if (options.debug)
3994     {
3995       printf("Player status (final):\n");
3996
3997       for (i = 0; i < MAX_PLAYERS; i++)
3998       {
3999         struct PlayerInfo *player = &stored_player[i];
4000
4001         printf("- player %d: present == %d, connected == %d, active == %d",
4002                i + 1,
4003                player->present,
4004                player->connected,
4005                player->active);
4006
4007         if (local_player == player)
4008           printf(" (local player)");
4009
4010         printf("\n");
4011       }
4012     }
4013 #endif
4014   }
4015
4016   UnmapAllGadgets();
4017
4018   MapGameButtons();
4019   MapTapeButtons();
4020
4021   if (!game.restart_level && !tape.playing)
4022   {
4023     LevelStats_incPlayed(level_nr);
4024
4025     SaveLevelSetup_SeriesInfo();
4026   }
4027
4028   game.restart_level = FALSE;
4029
4030   SaveEngineSnapshotToListInitial();
4031 }
4032
4033 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4034 {
4035   /* this is used for non-R'n'D game engines to update certain engine values */
4036
4037   /* needed to determine if sounds are played within the visible screen area */
4038   scroll_x = actual_scroll_x;
4039   scroll_y = actual_scroll_y;
4040 }
4041
4042 void InitMovDir(int x, int y)
4043 {
4044   int i, element = Feld[x][y];
4045   static int xy[4][2] =
4046   {
4047     {  0, +1 },
4048     { +1,  0 },
4049     {  0, -1 },
4050     { -1,  0 }
4051   };
4052   static int direction[3][4] =
4053   {
4054     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4055     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4056     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4057   };
4058
4059   switch (element)
4060   {
4061     case EL_BUG_RIGHT:
4062     case EL_BUG_UP:
4063     case EL_BUG_LEFT:
4064     case EL_BUG_DOWN:
4065       Feld[x][y] = EL_BUG;
4066       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4067       break;
4068
4069     case EL_SPACESHIP_RIGHT:
4070     case EL_SPACESHIP_UP:
4071     case EL_SPACESHIP_LEFT:
4072     case EL_SPACESHIP_DOWN:
4073       Feld[x][y] = EL_SPACESHIP;
4074       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4075       break;
4076
4077     case EL_BD_BUTTERFLY_RIGHT:
4078     case EL_BD_BUTTERFLY_UP:
4079     case EL_BD_BUTTERFLY_LEFT:
4080     case EL_BD_BUTTERFLY_DOWN:
4081       Feld[x][y] = EL_BD_BUTTERFLY;
4082       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4083       break;
4084
4085     case EL_BD_FIREFLY_RIGHT:
4086     case EL_BD_FIREFLY_UP:
4087     case EL_BD_FIREFLY_LEFT:
4088     case EL_BD_FIREFLY_DOWN:
4089       Feld[x][y] = EL_BD_FIREFLY;
4090       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4091       break;
4092
4093     case EL_PACMAN_RIGHT:
4094     case EL_PACMAN_UP:
4095     case EL_PACMAN_LEFT:
4096     case EL_PACMAN_DOWN:
4097       Feld[x][y] = EL_PACMAN;
4098       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4099       break;
4100
4101     case EL_YAMYAM_LEFT:
4102     case EL_YAMYAM_RIGHT:
4103     case EL_YAMYAM_UP:
4104     case EL_YAMYAM_DOWN:
4105       Feld[x][y] = EL_YAMYAM;
4106       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4107       break;
4108
4109     case EL_SP_SNIKSNAK:
4110       MovDir[x][y] = MV_UP;
4111       break;
4112
4113     case EL_SP_ELECTRON:
4114       MovDir[x][y] = MV_LEFT;
4115       break;
4116
4117     case EL_MOLE_LEFT:
4118     case EL_MOLE_RIGHT:
4119     case EL_MOLE_UP:
4120     case EL_MOLE_DOWN:
4121       Feld[x][y] = EL_MOLE;
4122       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4123       break;
4124
4125     default:
4126       if (IS_CUSTOM_ELEMENT(element))
4127       {
4128         struct ElementInfo *ei = &element_info[element];
4129         int move_direction_initial = ei->move_direction_initial;
4130         int move_pattern = ei->move_pattern;
4131
4132         if (move_direction_initial == MV_START_PREVIOUS)
4133         {
4134           if (MovDir[x][y] != MV_NONE)
4135             return;
4136
4137           move_direction_initial = MV_START_AUTOMATIC;
4138         }
4139
4140         if (move_direction_initial == MV_START_RANDOM)
4141           MovDir[x][y] = 1 << RND(4);
4142         else if (move_direction_initial & MV_ANY_DIRECTION)
4143           MovDir[x][y] = move_direction_initial;
4144         else if (move_pattern == MV_ALL_DIRECTIONS ||
4145                  move_pattern == MV_TURNING_LEFT ||
4146                  move_pattern == MV_TURNING_RIGHT ||
4147                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4148                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4149                  move_pattern == MV_TURNING_RANDOM)
4150           MovDir[x][y] = 1 << RND(4);
4151         else if (move_pattern == MV_HORIZONTAL)
4152           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4153         else if (move_pattern == MV_VERTICAL)
4154           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4155         else if (move_pattern & MV_ANY_DIRECTION)
4156           MovDir[x][y] = element_info[element].move_pattern;
4157         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4158                  move_pattern == MV_ALONG_RIGHT_SIDE)
4159         {
4160           /* use random direction as default start direction */
4161           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4162             MovDir[x][y] = 1 << RND(4);
4163
4164           for (i = 0; i < NUM_DIRECTIONS; i++)
4165           {
4166             int x1 = x + xy[i][0];
4167             int y1 = y + xy[i][1];
4168
4169             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4170             {
4171               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4172                 MovDir[x][y] = direction[0][i];
4173               else
4174                 MovDir[x][y] = direction[1][i];
4175
4176               break;
4177             }
4178           }
4179         }                
4180       }
4181       else
4182       {
4183         MovDir[x][y] = 1 << RND(4);
4184
4185         if (element != EL_BUG &&
4186             element != EL_SPACESHIP &&
4187             element != EL_BD_BUTTERFLY &&
4188             element != EL_BD_FIREFLY)
4189           break;
4190
4191         for (i = 0; i < NUM_DIRECTIONS; i++)
4192         {
4193           int x1 = x + xy[i][0];
4194           int y1 = y + xy[i][1];
4195
4196           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4197           {
4198             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4199             {
4200               MovDir[x][y] = direction[0][i];
4201               break;
4202             }
4203             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4204                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4205             {
4206               MovDir[x][y] = direction[1][i];
4207               break;
4208             }
4209           }
4210         }
4211       }
4212       break;
4213   }
4214
4215   GfxDir[x][y] = MovDir[x][y];
4216 }
4217
4218 void InitAmoebaNr(int x, int y)
4219 {
4220   int i;
4221   int group_nr = AmoebeNachbarNr(x, y);
4222
4223   if (group_nr == 0)
4224   {
4225     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4226     {
4227       if (AmoebaCnt[i] == 0)
4228       {
4229         group_nr = i;
4230         break;
4231       }
4232     }
4233   }
4234
4235   AmoebaNr[x][y] = group_nr;
4236   AmoebaCnt[group_nr]++;
4237   AmoebaCnt2[group_nr]++;
4238 }
4239
4240 static void PlayerWins(struct PlayerInfo *player)
4241 {
4242   player->LevelSolved = TRUE;
4243   player->GameOver = TRUE;
4244
4245   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4246                          level.native_em_level->lev->score : player->score);
4247
4248   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4249                                       TimeLeft);
4250   player->LevelSolved_CountingScore = player->score_final;
4251 }
4252
4253 void GameWon()
4254 {
4255   static int time, time_final;
4256   static int score, score_final;
4257   static int game_over_delay_1 = 0;
4258   static int game_over_delay_2 = 0;
4259   int game_over_delay_value_1 = 50;
4260   int game_over_delay_value_2 = 50;
4261
4262   if (!local_player->LevelSolved_GameWon)
4263   {
4264     int i;
4265
4266     /* do not start end game actions before the player stops moving (to exit) */
4267     if (local_player->MovPos)
4268       return;
4269
4270     local_player->LevelSolved_GameWon = TRUE;
4271     local_player->LevelSolved_SaveTape = tape.recording;
4272     local_player->LevelSolved_SaveScore = !tape.playing;
4273
4274     if (!tape.playing)
4275     {
4276       LevelStats_incSolved(level_nr);
4277
4278       SaveLevelSetup_SeriesInfo();
4279     }
4280
4281     if (tape.auto_play)         /* tape might already be stopped here */
4282       tape.auto_play_level_solved = TRUE;
4283
4284     TapeStop();
4285
4286     game_over_delay_1 = game_over_delay_value_1;
4287     game_over_delay_2 = game_over_delay_value_2;
4288
4289     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4290     score = score_final = local_player->score_final;
4291
4292     if (TimeLeft > 0)
4293     {
4294       time_final = 0;
4295       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4296     }
4297     else if (game.no_time_limit && TimePlayed < 999)
4298     {
4299       time_final = 999;
4300       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4301     }
4302
4303     local_player->score_final = score_final;
4304
4305     if (level_editor_test_game)
4306     {
4307       time = time_final;
4308       score = score_final;
4309
4310       local_player->LevelSolved_CountingTime = time;
4311       local_player->LevelSolved_CountingScore = score;
4312
4313       game_panel_controls[GAME_PANEL_TIME].value = time;
4314       game_panel_controls[GAME_PANEL_SCORE].value = score;
4315
4316       DisplayGameControlValues();
4317     }
4318
4319     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4320     {
4321       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4322       {
4323         /* close exit door after last player */
4324         if ((AllPlayersGone &&
4325              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4326               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4327               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4328             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4329             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4330         {
4331           int element = Feld[ExitX][ExitY];
4332
4333           Feld[ExitX][ExitY] =
4334             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4335              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4336              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4337              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4338              EL_EM_STEEL_EXIT_CLOSING);
4339
4340           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4341         }
4342
4343         /* player disappears */
4344         DrawLevelField(ExitX, ExitY);
4345       }
4346
4347       for (i = 0; i < MAX_PLAYERS; i++)
4348       {
4349         struct PlayerInfo *player = &stored_player[i];
4350
4351         if (player->present)
4352         {
4353           RemovePlayer(player);
4354
4355           /* player disappears */
4356           DrawLevelField(player->jx, player->jy);
4357         }
4358       }
4359     }
4360
4361     PlaySound(SND_GAME_WINNING);
4362   }
4363
4364   if (game_over_delay_1 > 0)
4365   {
4366     game_over_delay_1--;
4367
4368     return;
4369   }
4370
4371   if (time != time_final)
4372   {
4373     int time_to_go = ABS(time_final - time);
4374     int time_count_dir = (time < time_final ? +1 : -1);
4375     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4376
4377     time  += time_count_steps * time_count_dir;
4378     score += time_count_steps * level.score[SC_TIME_BONUS];
4379
4380     local_player->LevelSolved_CountingTime = time;
4381     local_player->LevelSolved_CountingScore = score;
4382
4383     game_panel_controls[GAME_PANEL_TIME].value = time;
4384     game_panel_controls[GAME_PANEL_SCORE].value = score;
4385
4386     DisplayGameControlValues();
4387
4388     if (time == time_final)
4389       StopSound(SND_GAME_LEVELTIME_BONUS);
4390     else if (setup.sound_loops)
4391       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4392     else
4393       PlaySound(SND_GAME_LEVELTIME_BONUS);
4394
4395     return;
4396   }
4397
4398   local_player->LevelSolved_PanelOff = TRUE;
4399
4400   if (game_over_delay_2 > 0)
4401   {
4402     game_over_delay_2--;
4403
4404     return;
4405   }
4406
4407   GameEnd();
4408 }
4409
4410 void GameEnd()
4411 {
4412   int hi_pos;
4413   boolean raise_level = FALSE;
4414
4415   local_player->LevelSolved_GameEnd = TRUE;
4416
4417   if (!global.use_envelope_request)
4418     CloseDoor(DOOR_CLOSE_1);
4419
4420   if (local_player->LevelSolved_SaveTape)
4421   {
4422     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4423   }
4424
4425   CloseDoor(DOOR_CLOSE_ALL);
4426
4427   if (level_editor_test_game)
4428   {
4429     SetGameStatus(GAME_MODE_MAIN);
4430
4431     DrawMainMenu();
4432
4433     return;
4434   }
4435
4436   if (!local_player->LevelSolved_SaveScore)
4437   {
4438     SetGameStatus(GAME_MODE_MAIN);
4439
4440     DrawMainMenu();
4441
4442     return;
4443   }
4444
4445   if (level_nr == leveldir_current->handicap_level)
4446   {
4447     leveldir_current->handicap_level++;
4448
4449     SaveLevelSetup_SeriesInfo();
4450   }
4451
4452   if (level_nr < leveldir_current->last_level)
4453     raise_level = TRUE;                 /* advance to next level */
4454
4455   if ((hi_pos = NewHiScore()) >= 0) 
4456   {
4457     SetGameStatus(GAME_MODE_SCORES);
4458
4459     DrawHallOfFame(hi_pos);
4460
4461     if (raise_level)
4462     {
4463       level_nr++;
4464       TapeErase();
4465     }
4466   }
4467   else
4468   {
4469     SetGameStatus(GAME_MODE_MAIN);
4470
4471     if (raise_level)
4472     {
4473       level_nr++;
4474       TapeErase();
4475     }
4476
4477     DrawMainMenu();
4478   }
4479 }
4480
4481 int NewHiScore()
4482 {
4483   int k, l;
4484   int position = -1;
4485
4486   LoadScore(level_nr);
4487
4488   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4489       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4490     return -1;
4491
4492   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4493   {
4494     if (local_player->score_final > highscore[k].Score)
4495     {
4496       /* player has made it to the hall of fame */
4497
4498       if (k < MAX_SCORE_ENTRIES - 1)
4499       {
4500         int m = MAX_SCORE_ENTRIES - 1;
4501
4502 #ifdef ONE_PER_NAME
4503         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4504           if (strEqual(setup.player_name, highscore[l].Name))
4505             m = l;
4506         if (m == k)     /* player's new highscore overwrites his old one */
4507           goto put_into_list;
4508 #endif
4509
4510         for (l = m; l > k; l--)
4511         {
4512           strcpy(highscore[l].Name, highscore[l - 1].Name);
4513           highscore[l].Score = highscore[l - 1].Score;
4514         }
4515       }
4516
4517 #ifdef ONE_PER_NAME
4518       put_into_list:
4519 #endif
4520       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4521       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4522       highscore[k].Score = local_player->score_final; 
4523       position = k;
4524       break;
4525     }
4526
4527 #ifdef ONE_PER_NAME
4528     else if (!strncmp(setup.player_name, highscore[k].Name,
4529                       MAX_PLAYER_NAME_LEN))
4530       break;    /* player already there with a higher score */
4531 #endif
4532
4533   }
4534
4535   if (position >= 0) 
4536     SaveScore(level_nr);
4537
4538   return position;
4539 }
4540
4541 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4542 {
4543   int element = Feld[x][y];
4544   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4545   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4546   int horiz_move = (dx != 0);
4547   int sign = (horiz_move ? dx : dy);
4548   int step = sign * element_info[element].move_stepsize;
4549
4550   /* special values for move stepsize for spring and things on conveyor belt */
4551   if (horiz_move)
4552   {
4553     if (CAN_FALL(element) &&
4554         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4555       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4556     else if (element == EL_SPRING)
4557       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4558   }
4559
4560   return step;
4561 }
4562
4563 inline static int getElementMoveStepsize(int x, int y)
4564 {
4565   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4566 }
4567
4568 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4569 {
4570   if (player->GfxAction != action || player->GfxDir != dir)
4571   {
4572     player->GfxAction = action;
4573     player->GfxDir = dir;
4574     player->Frame = 0;
4575     player->StepFrame = 0;
4576   }
4577 }
4578
4579 static void ResetGfxFrame(int x, int y, boolean redraw)
4580 {
4581   int element = Feld[x][y];
4582   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4583   int last_gfx_frame = GfxFrame[x][y];
4584
4585   if (graphic_info[graphic].anim_global_sync)
4586     GfxFrame[x][y] = FrameCounter;
4587   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4588     GfxFrame[x][y] = CustomValue[x][y];
4589   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4590     GfxFrame[x][y] = element_info[element].collect_score;
4591   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4592     GfxFrame[x][y] = ChangeDelay[x][y];
4593
4594   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4595     DrawLevelGraphicAnimation(x, y, graphic);
4596 }
4597
4598 static void ResetGfxAnimation(int x, int y)
4599 {
4600   GfxAction[x][y] = ACTION_DEFAULT;
4601   GfxDir[x][y] = MovDir[x][y];
4602   GfxFrame[x][y] = 0;
4603
4604   ResetGfxFrame(x, y, FALSE);
4605 }
4606
4607 static void ResetRandomAnimationValue(int x, int y)
4608 {
4609   GfxRandom[x][y] = INIT_GFX_RANDOM();
4610 }
4611
4612 void InitMovingField(int x, int y, int direction)
4613 {
4614   int element = Feld[x][y];
4615   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4616   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4617   int newx = x + dx;
4618   int newy = y + dy;
4619   boolean is_moving_before, is_moving_after;
4620
4621   /* check if element was/is moving or being moved before/after mode change */
4622   is_moving_before = (WasJustMoving[x][y] != 0);
4623   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4624
4625   /* reset animation only for moving elements which change direction of moving
4626      or which just started or stopped moving
4627      (else CEs with property "can move" / "not moving" are reset each frame) */
4628   if (is_moving_before != is_moving_after ||
4629       direction != MovDir[x][y])
4630     ResetGfxAnimation(x, y);
4631
4632   MovDir[x][y] = direction;
4633   GfxDir[x][y] = direction;
4634
4635   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4636                      direction == MV_DOWN && CAN_FALL(element) ?
4637                      ACTION_FALLING : ACTION_MOVING);
4638
4639   /* this is needed for CEs with property "can move" / "not moving" */
4640
4641   if (is_moving_after)
4642   {
4643     if (Feld[newx][newy] == EL_EMPTY)
4644       Feld[newx][newy] = EL_BLOCKED;
4645
4646     MovDir[newx][newy] = MovDir[x][y];
4647
4648     CustomValue[newx][newy] = CustomValue[x][y];
4649
4650     GfxFrame[newx][newy] = GfxFrame[x][y];
4651     GfxRandom[newx][newy] = GfxRandom[x][y];
4652     GfxAction[newx][newy] = GfxAction[x][y];
4653     GfxDir[newx][newy] = GfxDir[x][y];
4654   }
4655 }
4656
4657 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4658 {
4659   int direction = MovDir[x][y];
4660   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4661   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4662
4663   *goes_to_x = newx;
4664   *goes_to_y = newy;
4665 }
4666
4667 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4668 {
4669   int oldx = x, oldy = y;
4670   int direction = MovDir[x][y];
4671
4672   if (direction == MV_LEFT)
4673     oldx++;
4674   else if (direction == MV_RIGHT)
4675     oldx--;
4676   else if (direction == MV_UP)
4677     oldy++;
4678   else if (direction == MV_DOWN)
4679     oldy--;
4680
4681   *comes_from_x = oldx;
4682   *comes_from_y = oldy;
4683 }
4684
4685 int MovingOrBlocked2Element(int x, int y)
4686 {
4687   int element = Feld[x][y];
4688
4689   if (element == EL_BLOCKED)
4690   {
4691     int oldx, oldy;
4692
4693     Blocked2Moving(x, y, &oldx, &oldy);
4694     return Feld[oldx][oldy];
4695   }
4696   else
4697     return element;
4698 }
4699
4700 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4701 {
4702   /* like MovingOrBlocked2Element(), but if element is moving
4703      and (x,y) is the field the moving element is just leaving,
4704      return EL_BLOCKED instead of the element value */
4705   int element = Feld[x][y];
4706
4707   if (IS_MOVING(x, y))
4708   {
4709     if (element == EL_BLOCKED)
4710     {
4711       int oldx, oldy;
4712
4713       Blocked2Moving(x, y, &oldx, &oldy);
4714       return Feld[oldx][oldy];
4715     }
4716     else
4717       return EL_BLOCKED;
4718   }
4719   else
4720     return element;
4721 }
4722
4723 static void RemoveField(int x, int y)
4724 {
4725   Feld[x][y] = EL_EMPTY;
4726
4727   MovPos[x][y] = 0;
4728   MovDir[x][y] = 0;
4729   MovDelay[x][y] = 0;
4730
4731   CustomValue[x][y] = 0;
4732
4733   AmoebaNr[x][y] = 0;
4734   ChangeDelay[x][y] = 0;
4735   ChangePage[x][y] = -1;
4736   Pushed[x][y] = FALSE;
4737
4738   GfxElement[x][y] = EL_UNDEFINED;
4739   GfxAction[x][y] = ACTION_DEFAULT;
4740   GfxDir[x][y] = MV_NONE;
4741 }
4742
4743 void RemoveMovingField(int x, int y)
4744 {
4745   int oldx = x, oldy = y, newx = x, newy = y;
4746   int element = Feld[x][y];
4747   int next_element = EL_UNDEFINED;
4748
4749   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4750     return;
4751
4752   if (IS_MOVING(x, y))
4753   {
4754     Moving2Blocked(x, y, &newx, &newy);
4755
4756     if (Feld[newx][newy] != EL_BLOCKED)
4757     {
4758       /* element is moving, but target field is not free (blocked), but
4759          already occupied by something different (example: acid pool);
4760          in this case, only remove the moving field, but not the target */
4761
4762       RemoveField(oldx, oldy);
4763
4764       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4765
4766       TEST_DrawLevelField(oldx, oldy);
4767
4768       return;
4769     }
4770   }
4771   else if (element == EL_BLOCKED)
4772   {
4773     Blocked2Moving(x, y, &oldx, &oldy);
4774     if (!IS_MOVING(oldx, oldy))
4775       return;
4776   }
4777
4778   if (element == EL_BLOCKED &&
4779       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4780        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4781        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4782        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4783        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4784        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4785     next_element = get_next_element(Feld[oldx][oldy]);
4786
4787   RemoveField(oldx, oldy);
4788   RemoveField(newx, newy);
4789
4790   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4791
4792   if (next_element != EL_UNDEFINED)
4793     Feld[oldx][oldy] = next_element;
4794
4795   TEST_DrawLevelField(oldx, oldy);
4796   TEST_DrawLevelField(newx, newy);
4797 }
4798
4799 void DrawDynamite(int x, int y)
4800 {
4801   int sx = SCREENX(x), sy = SCREENY(y);
4802   int graphic = el2img(Feld[x][y]);
4803   int frame;
4804
4805   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4806     return;
4807
4808   if (IS_WALKABLE_INSIDE(Back[x][y]))
4809     return;
4810
4811   if (Back[x][y])
4812     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4813   else if (Store[x][y])
4814     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4815
4816   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4817
4818   if (Back[x][y] || Store[x][y])
4819     DrawGraphicThruMask(sx, sy, graphic, frame);
4820   else
4821     DrawGraphic(sx, sy, graphic, frame);
4822 }
4823
4824 void CheckDynamite(int x, int y)
4825 {
4826   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4827   {
4828     MovDelay[x][y]--;
4829
4830     if (MovDelay[x][y] != 0)
4831     {
4832       DrawDynamite(x, y);
4833       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4834
4835       return;
4836     }
4837   }
4838
4839   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4840
4841   Bang(x, y);
4842 }
4843
4844 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4845 {
4846   boolean num_checked_players = 0;
4847   int i;
4848
4849   for (i = 0; i < MAX_PLAYERS; i++)
4850   {
4851     if (stored_player[i].active)
4852     {
4853       int sx = stored_player[i].jx;
4854       int sy = stored_player[i].jy;
4855
4856       if (num_checked_players == 0)
4857       {
4858         *sx1 = *sx2 = sx;
4859         *sy1 = *sy2 = sy;
4860       }
4861       else
4862       {
4863         *sx1 = MIN(*sx1, sx);
4864         *sy1 = MIN(*sy1, sy);
4865         *sx2 = MAX(*sx2, sx);
4866         *sy2 = MAX(*sy2, sy);
4867       }
4868
4869       num_checked_players++;
4870     }
4871   }
4872 }
4873
4874 static boolean checkIfAllPlayersFitToScreen_RND()
4875 {
4876   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4877
4878   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4879
4880   return (sx2 - sx1 < SCR_FIELDX &&
4881           sy2 - sy1 < SCR_FIELDY);
4882 }
4883
4884 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4885 {
4886   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4887
4888   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4889
4890   *sx = (sx1 + sx2) / 2;
4891   *sy = (sy1 + sy2) / 2;
4892 }
4893
4894 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4895                         boolean center_screen, boolean quick_relocation)
4896 {
4897   unsigned int frame_delay_value_old = GetVideoFrameDelay();
4898   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4899   boolean no_delay = (tape.warp_forward);
4900   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4901   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4902   int new_scroll_x, new_scroll_y;
4903
4904   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4905   {
4906     /* case 1: quick relocation inside visible screen (without scrolling) */
4907
4908     RedrawPlayfield();
4909
4910     return;
4911   }
4912
4913   if (!level.shifted_relocation || center_screen)
4914   {
4915     /* relocation _with_ centering of screen */
4916
4917     new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4918                     x > SBX_Right + MIDPOSX ? SBX_Right :
4919                     x - MIDPOSX);
4920
4921     new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4922                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4923                     y - MIDPOSY);
4924   }
4925   else
4926   {
4927     /* relocation _without_ centering of screen */
4928
4929     int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4930                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
4931                            old_x - MIDPOSX);
4932
4933     int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4934                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4935                            old_y - MIDPOSY);
4936
4937     int offset_x = x + (scroll_x - center_scroll_x);
4938     int offset_y = y + (scroll_y - center_scroll_y);
4939
4940     new_scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4941                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4942                     offset_x - MIDPOSX);
4943
4944     new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4945                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4946                     offset_y - MIDPOSY);
4947   }
4948
4949   if (quick_relocation)
4950   {
4951     /* case 2: quick relocation (redraw without visible scrolling) */
4952
4953     scroll_x = new_scroll_x;
4954     scroll_y = new_scroll_y;
4955
4956     RedrawPlayfield();
4957
4958     return;
4959   }
4960
4961   /* case 3: visible relocation (with scrolling to new position) */
4962
4963   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4964
4965   SetVideoFrameDelay(wait_delay_value);
4966
4967   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4968   {
4969     int dx = 0, dy = 0;
4970     int fx = FX, fy = FY;
4971
4972     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4973     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4974
4975     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4976       break;
4977
4978     scroll_x -= dx;
4979     scroll_y -= dy;
4980
4981     fx += dx * TILEX / 2;
4982     fy += dy * TILEY / 2;
4983
4984     ScrollLevel(dx, dy);
4985     DrawAllPlayers();
4986
4987     /* scroll in two steps of half tile size to make things smoother */
4988     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4989
4990     /* scroll second step to align at full tile size */
4991     BlitScreenToBitmap(window);
4992   }
4993
4994   DrawAllPlayers();
4995   BackToFront();
4996
4997   SetVideoFrameDelay(frame_delay_value_old);
4998 }
4999
5000 void RelocatePlayer(int jx, int jy, int el_player_raw)
5001 {
5002   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5003   int player_nr = GET_PLAYER_NR(el_player);
5004   struct PlayerInfo *player = &stored_player[player_nr];
5005   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5006   boolean no_delay = (tape.warp_forward);
5007   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5008   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5009   int old_jx = player->jx;
5010   int old_jy = player->jy;
5011   int old_element = Feld[old_jx][old_jy];
5012   int element = Feld[jx][jy];
5013   boolean player_relocated = (old_jx != jx || old_jy != jy);
5014
5015   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5016   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5017   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5018   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5019   int leave_side_horiz = move_dir_horiz;
5020   int leave_side_vert  = move_dir_vert;
5021   int enter_side = enter_side_horiz | enter_side_vert;
5022   int leave_side = leave_side_horiz | leave_side_vert;
5023
5024   if (player->GameOver)         /* do not reanimate dead player */
5025     return;
5026
5027   if (!player_relocated)        /* no need to relocate the player */
5028     return;
5029
5030   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5031   {
5032     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5033     DrawLevelField(jx, jy);
5034   }
5035
5036   if (player->present)
5037   {
5038     while (player->MovPos)
5039     {
5040       ScrollPlayer(player, SCROLL_GO_ON);
5041       ScrollScreen(NULL, SCROLL_GO_ON);
5042
5043       AdvanceFrameAndPlayerCounters(player->index_nr);
5044
5045       DrawPlayer(player);
5046
5047       BackToFront_WithFrameDelay(wait_delay_value);
5048     }
5049
5050     DrawPlayer(player);         /* needed here only to cleanup last field */
5051     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5052
5053     player->is_moving = FALSE;
5054   }
5055
5056   if (IS_CUSTOM_ELEMENT(old_element))
5057     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5058                                CE_LEFT_BY_PLAYER,
5059                                player->index_bit, leave_side);
5060
5061   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5062                                       CE_PLAYER_LEAVES_X,
5063                                       player->index_bit, leave_side);
5064
5065   Feld[jx][jy] = el_player;
5066   InitPlayerField(jx, jy, el_player, TRUE);
5067
5068   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5069      possible that the relocation target field did not contain a player element,
5070      but a walkable element, to which the new player was relocated -- in this
5071      case, restore that (already initialized!) element on the player field */
5072   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5073   {
5074     Feld[jx][jy] = element;     /* restore previously existing element */
5075   }
5076
5077   /* only visually relocate centered player */
5078   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5079                      FALSE, level.instant_relocation);
5080
5081   TestIfPlayerTouchesBadThing(jx, jy);
5082   TestIfPlayerTouchesCustomElement(jx, jy);
5083
5084   if (IS_CUSTOM_ELEMENT(element))
5085     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5086                                player->index_bit, enter_side);
5087
5088   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5089                                       player->index_bit, enter_side);
5090
5091   if (player->is_switching)
5092   {
5093     /* ensure that relocation while still switching an element does not cause
5094        a new element to be treated as also switched directly after relocation
5095        (this is important for teleporter switches that teleport the player to
5096        a place where another teleporter switch is in the same direction, which
5097        would then incorrectly be treated as immediately switched before the
5098        direction key that caused the switch was released) */
5099
5100     player->switch_x += jx - old_jx;
5101     player->switch_y += jy - old_jy;
5102   }
5103 }
5104
5105 void Explode(int ex, int ey, int phase, int mode)
5106 {
5107   int x, y;
5108   int last_phase;
5109   int border_element;
5110
5111   /* !!! eliminate this variable !!! */
5112   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5113
5114   if (game.explosions_delayed)
5115   {
5116     ExplodeField[ex][ey] = mode;
5117     return;
5118   }
5119
5120   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5121   {
5122     int center_element = Feld[ex][ey];
5123     int artwork_element, explosion_element;     /* set these values later */
5124
5125     /* remove things displayed in background while burning dynamite */
5126     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5127       Back[ex][ey] = 0;
5128
5129     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5130     {
5131       /* put moving element to center field (and let it explode there) */
5132       center_element = MovingOrBlocked2Element(ex, ey);
5133       RemoveMovingField(ex, ey);
5134       Feld[ex][ey] = center_element;
5135     }
5136
5137     /* now "center_element" is finally determined -- set related values now */
5138     artwork_element = center_element;           /* for custom player artwork */
5139     explosion_element = center_element;         /* for custom player artwork */
5140
5141     if (IS_PLAYER(ex, ey))
5142     {
5143       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5144
5145       artwork_element = stored_player[player_nr].artwork_element;
5146
5147       if (level.use_explosion_element[player_nr])
5148       {
5149         explosion_element = level.explosion_element[player_nr];
5150         artwork_element = explosion_element;
5151       }
5152     }
5153
5154     if (mode == EX_TYPE_NORMAL ||
5155         mode == EX_TYPE_CENTER ||
5156         mode == EX_TYPE_CROSS)
5157       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5158
5159     last_phase = element_info[explosion_element].explosion_delay + 1;
5160
5161     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5162     {
5163       int xx = x - ex + 1;
5164       int yy = y - ey + 1;
5165       int element;
5166
5167       if (!IN_LEV_FIELD(x, y) ||
5168           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5169           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5170         continue;
5171
5172       element = Feld[x][y];
5173
5174       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5175       {
5176         element = MovingOrBlocked2Element(x, y);
5177
5178         if (!IS_EXPLOSION_PROOF(element))
5179           RemoveMovingField(x, y);
5180       }
5181
5182       /* indestructible elements can only explode in center (but not flames) */
5183       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5184                                            mode == EX_TYPE_BORDER)) ||
5185           element == EL_FLAMES)
5186         continue;
5187
5188       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5189          behaviour, for example when touching a yamyam that explodes to rocks
5190          with active deadly shield, a rock is created under the player !!! */
5191       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5192 #if 0
5193       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5194           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5195            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5196 #else
5197       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5198 #endif
5199       {
5200         if (IS_ACTIVE_BOMB(element))
5201         {
5202           /* re-activate things under the bomb like gate or penguin */
5203           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5204           Back[x][y] = 0;
5205         }
5206
5207         continue;
5208       }
5209
5210       /* save walkable background elements while explosion on same tile */
5211       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5212           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5213         Back[x][y] = element;
5214
5215       /* ignite explodable elements reached by other explosion */
5216       if (element == EL_EXPLOSION)
5217         element = Store2[x][y];
5218
5219       if (AmoebaNr[x][y] &&
5220           (element == EL_AMOEBA_FULL ||
5221            element == EL_BD_AMOEBA ||
5222            element == EL_AMOEBA_GROWING))
5223       {
5224         AmoebaCnt[AmoebaNr[x][y]]--;
5225         AmoebaCnt2[AmoebaNr[x][y]]--;
5226       }
5227
5228       RemoveField(x, y);
5229
5230       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5231       {
5232         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5233
5234         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5235
5236         if (PLAYERINFO(ex, ey)->use_murphy)
5237           Store[x][y] = EL_EMPTY;
5238       }
5239
5240       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5241          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5242       else if (ELEM_IS_PLAYER(center_element))
5243         Store[x][y] = EL_EMPTY;
5244       else if (center_element == EL_YAMYAM)
5245         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5246       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5247         Store[x][y] = element_info[center_element].content.e[xx][yy];
5248 #if 1
5249       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5250          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5251          otherwise) -- FIX THIS !!! */
5252       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5253         Store[x][y] = element_info[element].content.e[1][1];
5254 #else
5255       else if (!CAN_EXPLODE(element))
5256         Store[x][y] = element_info[element].content.e[1][1];
5257 #endif
5258       else
5259         Store[x][y] = EL_EMPTY;
5260
5261       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5262           center_element == EL_AMOEBA_TO_DIAMOND)
5263         Store2[x][y] = element;
5264
5265       Feld[x][y] = EL_EXPLOSION;
5266       GfxElement[x][y] = artwork_element;
5267
5268       ExplodePhase[x][y] = 1;
5269       ExplodeDelay[x][y] = last_phase;
5270
5271       Stop[x][y] = TRUE;
5272     }
5273
5274     if (center_element == EL_YAMYAM)
5275       game.yamyam_content_nr =
5276         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5277
5278     return;
5279   }
5280
5281   if (Stop[ex][ey])
5282     return;
5283
5284   x = ex;
5285   y = ey;
5286
5287   if (phase == 1)
5288     GfxFrame[x][y] = 0;         /* restart explosion animation */
5289
5290   last_phase = ExplodeDelay[x][y];
5291
5292   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5293
5294   /* this can happen if the player leaves an explosion just in time */
5295   if (GfxElement[x][y] == EL_UNDEFINED)
5296     GfxElement[x][y] = EL_EMPTY;
5297
5298   border_element = Store2[x][y];
5299   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5300     border_element = StorePlayer[x][y];
5301
5302   if (phase == element_info[border_element].ignition_delay ||
5303       phase == last_phase)
5304   {
5305     boolean border_explosion = FALSE;
5306
5307     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5308         !PLAYER_EXPLOSION_PROTECTED(x, y))
5309     {
5310       KillPlayerUnlessExplosionProtected(x, y);
5311       border_explosion = TRUE;
5312     }
5313     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5314     {
5315       Feld[x][y] = Store2[x][y];
5316       Store2[x][y] = 0;
5317       Bang(x, y);
5318       border_explosion = TRUE;
5319     }
5320     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5321     {
5322       AmoebeUmwandeln(x, y);
5323       Store2[x][y] = 0;
5324       border_explosion = TRUE;
5325     }
5326
5327     /* if an element just explodes due to another explosion (chain-reaction),
5328        do not immediately end the new explosion when it was the last frame of
5329        the explosion (as it would be done in the following "if"-statement!) */
5330     if (border_explosion && phase == last_phase)
5331       return;
5332   }
5333
5334   if (phase == last_phase)
5335   {
5336     int element;
5337
5338     element = Feld[x][y] = Store[x][y];
5339     Store[x][y] = Store2[x][y] = 0;
5340     GfxElement[x][y] = EL_UNDEFINED;
5341
5342     /* player can escape from explosions and might therefore be still alive */
5343     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5344         element <= EL_PLAYER_IS_EXPLODING_4)
5345     {
5346       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5347       int explosion_element = EL_PLAYER_1 + player_nr;
5348       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5349       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5350
5351       if (level.use_explosion_element[player_nr])
5352         explosion_element = level.explosion_element[player_nr];
5353
5354       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5355                     element_info[explosion_element].content.e[xx][yy]);
5356     }
5357
5358     /* restore probably existing indestructible background element */
5359     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5360       element = Feld[x][y] = Back[x][y];
5361     Back[x][y] = 0;
5362
5363     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5364     GfxDir[x][y] = MV_NONE;
5365     ChangeDelay[x][y] = 0;
5366     ChangePage[x][y] = -1;
5367
5368     CustomValue[x][y] = 0;
5369
5370     InitField_WithBug2(x, y, FALSE);
5371
5372     TEST_DrawLevelField(x, y);
5373
5374     TestIfElementTouchesCustomElement(x, y);
5375
5376     if (GFX_CRUMBLED(element))
5377       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5378
5379     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5380       StorePlayer[x][y] = 0;
5381
5382     if (ELEM_IS_PLAYER(element))
5383       RelocatePlayer(x, y, element);
5384   }
5385   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5386   {
5387     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5388     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5389
5390     if (phase == delay)
5391       TEST_DrawLevelFieldCrumbled(x, y);
5392
5393     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5394     {
5395       DrawLevelElement(x, y, Back[x][y]);
5396       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5397     }
5398     else if (IS_WALKABLE_UNDER(Back[x][y]))
5399     {
5400       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5401       DrawLevelElementThruMask(x, y, Back[x][y]);
5402     }
5403     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5404       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5405   }
5406 }
5407
5408 void DynaExplode(int ex, int ey)
5409 {
5410   int i, j;
5411   int dynabomb_element = Feld[ex][ey];
5412   int dynabomb_size = 1;
5413   boolean dynabomb_xl = FALSE;
5414   struct PlayerInfo *player;
5415   static int xy[4][2] =
5416   {
5417     { 0, -1 },
5418     { -1, 0 },
5419     { +1, 0 },
5420     { 0, +1 }
5421   };
5422
5423   if (IS_ACTIVE_BOMB(dynabomb_element))
5424   {
5425     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5426     dynabomb_size = player->dynabomb_size;
5427     dynabomb_xl = player->dynabomb_xl;
5428     player->dynabombs_left++;
5429   }
5430
5431   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5432
5433   for (i = 0; i < NUM_DIRECTIONS; i++)
5434   {
5435     for (j = 1; j <= dynabomb_size; j++)
5436     {
5437       int x = ex + j * xy[i][0];
5438       int y = ey + j * xy[i][1];
5439       int element;
5440
5441       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5442         break;
5443
5444       element = Feld[x][y];
5445
5446       /* do not restart explosions of fields with active bombs */
5447       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5448         continue;
5449
5450       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5451
5452       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5453           !IS_DIGGABLE(element) && !dynabomb_xl)
5454         break;
5455     }
5456   }
5457 }
5458
5459 void Bang(int x, int y)
5460 {
5461   int element = MovingOrBlocked2Element(x, y);
5462   int explosion_type = EX_TYPE_NORMAL;
5463
5464   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5465   {
5466     struct PlayerInfo *player = PLAYERINFO(x, y);
5467
5468     element = Feld[x][y] = player->initial_element;
5469
5470     if (level.use_explosion_element[player->index_nr])
5471     {
5472       int explosion_element = level.explosion_element[player->index_nr];
5473
5474       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5475         explosion_type = EX_TYPE_CROSS;
5476       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5477         explosion_type = EX_TYPE_CENTER;
5478     }
5479   }
5480
5481   switch (element)
5482   {
5483     case EL_BUG:
5484     case EL_SPACESHIP:
5485     case EL_BD_BUTTERFLY:
5486     case EL_BD_FIREFLY:
5487     case EL_YAMYAM:
5488     case EL_DARK_YAMYAM:
5489     case EL_ROBOT:
5490     case EL_PACMAN:
5491     case EL_MOLE:
5492       RaiseScoreElement(element);
5493       break;
5494
5495     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5496     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5497     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5498     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5499     case EL_DYNABOMB_INCREASE_NUMBER:
5500     case EL_DYNABOMB_INCREASE_SIZE:
5501     case EL_DYNABOMB_INCREASE_POWER:
5502       explosion_type = EX_TYPE_DYNA;
5503       break;
5504
5505     case EL_DC_LANDMINE:
5506       explosion_type = EX_TYPE_CENTER;
5507       break;
5508
5509     case EL_PENGUIN:
5510     case EL_LAMP:
5511     case EL_LAMP_ACTIVE:
5512     case EL_AMOEBA_TO_DIAMOND:
5513       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5514         explosion_type = EX_TYPE_CENTER;
5515       break;
5516
5517     default:
5518       if (element_info[element].explosion_type == EXPLODES_CROSS)
5519         explosion_type = EX_TYPE_CROSS;
5520       else if (element_info[element].explosion_type == EXPLODES_1X1)
5521         explosion_type = EX_TYPE_CENTER;
5522       break;
5523   }
5524
5525   if (explosion_type == EX_TYPE_DYNA)
5526     DynaExplode(x, y);
5527   else
5528     Explode(x, y, EX_PHASE_START, explosion_type);
5529
5530   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5531 }
5532
5533 void SplashAcid(int x, int y)
5534 {
5535   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5536       (!IN_LEV_FIELD(x - 1, y - 2) ||
5537        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5538     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5539
5540   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5541       (!IN_LEV_FIELD(x + 1, y - 2) ||
5542        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5543     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5544
5545   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5546 }
5547
5548 static void InitBeltMovement()
5549 {
5550   static int belt_base_element[4] =
5551   {
5552     EL_CONVEYOR_BELT_1_LEFT,
5553     EL_CONVEYOR_BELT_2_LEFT,
5554     EL_CONVEYOR_BELT_3_LEFT,
5555     EL_CONVEYOR_BELT_4_LEFT
5556   };
5557   static int belt_base_active_element[4] =
5558   {
5559     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5560     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5561     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5562     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5563   };
5564
5565   int x, y, i, j;
5566
5567   /* set frame order for belt animation graphic according to belt direction */
5568   for (i = 0; i < NUM_BELTS; i++)
5569   {
5570     int belt_nr = i;
5571
5572     for (j = 0; j < NUM_BELT_PARTS; j++)
5573     {
5574       int element = belt_base_active_element[belt_nr] + j;
5575       int graphic_1 = el2img(element);
5576       int graphic_2 = el2panelimg(element);
5577
5578       if (game.belt_dir[i] == MV_LEFT)
5579       {
5580         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5581         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5582       }
5583       else
5584       {
5585         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5586         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5587       }
5588     }
5589   }
5590
5591   SCAN_PLAYFIELD(x, y)
5592   {
5593     int element = Feld[x][y];
5594
5595     for (i = 0; i < NUM_BELTS; i++)
5596     {
5597       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5598       {
5599         int e_belt_nr = getBeltNrFromBeltElement(element);
5600         int belt_nr = i;
5601
5602         if (e_belt_nr == belt_nr)
5603         {
5604           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5605
5606           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5607         }
5608       }
5609     }
5610   }
5611 }
5612
5613 static void ToggleBeltSwitch(int x, int y)
5614 {
5615   static int belt_base_element[4] =
5616   {
5617     EL_CONVEYOR_BELT_1_LEFT,
5618     EL_CONVEYOR_BELT_2_LEFT,
5619     EL_CONVEYOR_BELT_3_LEFT,
5620     EL_CONVEYOR_BELT_4_LEFT
5621   };
5622   static int belt_base_active_element[4] =
5623   {
5624     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5625     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5626     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5627     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5628   };
5629   static int belt_base_switch_element[4] =
5630   {
5631     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5632     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5633     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5634     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5635   };
5636   static int belt_move_dir[4] =
5637   {
5638     MV_LEFT,
5639     MV_NONE,
5640     MV_RIGHT,
5641     MV_NONE,
5642   };
5643
5644   int element = Feld[x][y];
5645   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5646   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5647   int belt_dir = belt_move_dir[belt_dir_nr];
5648   int xx, yy, i;
5649
5650   if (!IS_BELT_SWITCH(element))
5651     return;
5652
5653   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5654   game.belt_dir[belt_nr] = belt_dir;
5655
5656   if (belt_dir_nr == 3)
5657     belt_dir_nr = 1;
5658
5659   /* set frame order for belt animation graphic according to belt direction */
5660   for (i = 0; i < NUM_BELT_PARTS; i++)
5661   {
5662     int element = belt_base_active_element[belt_nr] + i;
5663     int graphic_1 = el2img(element);
5664     int graphic_2 = el2panelimg(element);
5665
5666     if (belt_dir == MV_LEFT)
5667     {
5668       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5669       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5670     }
5671     else
5672     {
5673       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5674       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5675     }
5676   }
5677
5678   SCAN_PLAYFIELD(xx, yy)
5679   {
5680     int element = Feld[xx][yy];
5681
5682     if (IS_BELT_SWITCH(element))
5683     {
5684       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5685
5686       if (e_belt_nr == belt_nr)
5687       {
5688         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5689         TEST_DrawLevelField(xx, yy);
5690       }
5691     }
5692     else if (IS_BELT(element) && belt_dir != MV_NONE)
5693     {
5694       int e_belt_nr = getBeltNrFromBeltElement(element);
5695
5696       if (e_belt_nr == belt_nr)
5697       {
5698         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5699
5700         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5701         TEST_DrawLevelField(xx, yy);
5702       }
5703     }
5704     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5705     {
5706       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5707
5708       if (e_belt_nr == belt_nr)
5709       {
5710         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5711
5712         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5713         TEST_DrawLevelField(xx, yy);
5714       }
5715     }
5716   }
5717 }
5718
5719 static void ToggleSwitchgateSwitch(int x, int y)
5720 {
5721   int xx, yy;
5722
5723   game.switchgate_pos = !game.switchgate_pos;
5724
5725   SCAN_PLAYFIELD(xx, yy)
5726   {
5727     int element = Feld[xx][yy];
5728
5729     if (element == EL_SWITCHGATE_SWITCH_UP)
5730     {
5731       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5732       TEST_DrawLevelField(xx, yy);
5733     }
5734     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5735     {
5736       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5737       TEST_DrawLevelField(xx, yy);
5738     }
5739     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5740     {
5741       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5742       TEST_DrawLevelField(xx, yy);
5743     }
5744     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5745     {
5746       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5747       TEST_DrawLevelField(xx, yy);
5748     }
5749     else if (element == EL_SWITCHGATE_OPEN ||
5750              element == EL_SWITCHGATE_OPENING)
5751     {
5752       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5753
5754       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5755     }
5756     else if (element == EL_SWITCHGATE_CLOSED ||
5757              element == EL_SWITCHGATE_CLOSING)
5758     {
5759       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5760
5761       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5762     }
5763   }
5764 }
5765
5766 static int getInvisibleActiveFromInvisibleElement(int element)
5767 {
5768   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5769           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5770           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5771           element);
5772 }
5773
5774 static int getInvisibleFromInvisibleActiveElement(int element)
5775 {
5776   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5777           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5778           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5779           element);
5780 }
5781
5782 static void RedrawAllLightSwitchesAndInvisibleElements()
5783 {
5784   int x, y;
5785
5786   SCAN_PLAYFIELD(x, y)
5787   {
5788     int element = Feld[x][y];
5789
5790     if (element == EL_LIGHT_SWITCH &&
5791         game.light_time_left > 0)
5792     {
5793       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5794       TEST_DrawLevelField(x, y);
5795     }
5796     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5797              game.light_time_left == 0)
5798     {
5799       Feld[x][y] = EL_LIGHT_SWITCH;
5800       TEST_DrawLevelField(x, y);
5801     }
5802     else if (element == EL_EMC_DRIPPER &&
5803              game.light_time_left > 0)
5804     {
5805       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5806       TEST_DrawLevelField(x, y);
5807     }
5808     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5809              game.light_time_left == 0)
5810     {
5811       Feld[x][y] = EL_EMC_DRIPPER;
5812       TEST_DrawLevelField(x, y);
5813     }
5814     else if (element == EL_INVISIBLE_STEELWALL ||
5815              element == EL_INVISIBLE_WALL ||
5816              element == EL_INVISIBLE_SAND)
5817     {
5818       if (game.light_time_left > 0)
5819         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5820
5821       TEST_DrawLevelField(x, y);
5822
5823       /* uncrumble neighbour fields, if needed */
5824       if (element == EL_INVISIBLE_SAND)
5825         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5826     }
5827     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5828              element == EL_INVISIBLE_WALL_ACTIVE ||
5829              element == EL_INVISIBLE_SAND_ACTIVE)
5830     {
5831       if (game.light_time_left == 0)
5832         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5833
5834       TEST_DrawLevelField(x, y);
5835
5836       /* re-crumble neighbour fields, if needed */
5837       if (element == EL_INVISIBLE_SAND)
5838         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5839     }
5840   }
5841 }
5842
5843 static void RedrawAllInvisibleElementsForLenses()
5844 {
5845   int x, y;
5846
5847   SCAN_PLAYFIELD(x, y)
5848   {
5849     int element = Feld[x][y];
5850
5851     if (element == EL_EMC_DRIPPER &&
5852         game.lenses_time_left > 0)
5853     {
5854       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5855       TEST_DrawLevelField(x, y);
5856     }
5857     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5858              game.lenses_time_left == 0)
5859     {
5860       Feld[x][y] = EL_EMC_DRIPPER;
5861       TEST_DrawLevelField(x, y);
5862     }
5863     else if (element == EL_INVISIBLE_STEELWALL ||
5864              element == EL_INVISIBLE_WALL ||
5865              element == EL_INVISIBLE_SAND)
5866     {
5867       if (game.lenses_time_left > 0)
5868         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5869
5870       TEST_DrawLevelField(x, y);
5871
5872       /* uncrumble neighbour fields, if needed */
5873       if (element == EL_INVISIBLE_SAND)
5874         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5875     }
5876     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5877              element == EL_INVISIBLE_WALL_ACTIVE ||
5878              element == EL_INVISIBLE_SAND_ACTIVE)
5879     {
5880       if (game.lenses_time_left == 0)
5881         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5882
5883       TEST_DrawLevelField(x, y);
5884
5885       /* re-crumble neighbour fields, if needed */
5886       if (element == EL_INVISIBLE_SAND)
5887         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5888     }
5889   }
5890 }
5891
5892 static void RedrawAllInvisibleElementsForMagnifier()
5893 {
5894   int x, y;
5895
5896   SCAN_PLAYFIELD(x, y)
5897   {
5898     int element = Feld[x][y];
5899
5900     if (element == EL_EMC_FAKE_GRASS &&
5901         game.magnify_time_left > 0)
5902     {
5903       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5904       TEST_DrawLevelField(x, y);
5905     }
5906     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5907              game.magnify_time_left == 0)
5908     {
5909       Feld[x][y] = EL_EMC_FAKE_GRASS;
5910       TEST_DrawLevelField(x, y);
5911     }
5912     else if (IS_GATE_GRAY(element) &&
5913              game.magnify_time_left > 0)
5914     {
5915       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5916                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5917                     IS_EM_GATE_GRAY(element) ?
5918                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5919                     IS_EMC_GATE_GRAY(element) ?
5920                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5921                     IS_DC_GATE_GRAY(element) ?
5922                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5923                     element);
5924       TEST_DrawLevelField(x, y);
5925     }
5926     else if (IS_GATE_GRAY_ACTIVE(element) &&
5927              game.magnify_time_left == 0)
5928     {
5929       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5930                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5931                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5932                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5933                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5934                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5935                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5936                     EL_DC_GATE_WHITE_GRAY :
5937                     element);
5938       TEST_DrawLevelField(x, y);
5939     }
5940   }
5941 }
5942
5943 static void ToggleLightSwitch(int x, int y)
5944 {
5945   int element = Feld[x][y];
5946
5947   game.light_time_left =
5948     (element == EL_LIGHT_SWITCH ?
5949      level.time_light * FRAMES_PER_SECOND : 0);
5950
5951   RedrawAllLightSwitchesAndInvisibleElements();
5952 }
5953
5954 static void ActivateTimegateSwitch(int x, int y)
5955 {
5956   int xx, yy;
5957
5958   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5959
5960   SCAN_PLAYFIELD(xx, yy)
5961   {
5962     int element = Feld[xx][yy];
5963
5964     if (element == EL_TIMEGATE_CLOSED ||
5965         element == EL_TIMEGATE_CLOSING)
5966     {
5967       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5968       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5969     }
5970
5971     /*
5972     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5973     {
5974       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5975       TEST_DrawLevelField(xx, yy);
5976     }
5977     */
5978
5979   }
5980
5981   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5982                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5983 }
5984
5985 void Impact(int x, int y)
5986 {
5987   boolean last_line = (y == lev_fieldy - 1);
5988   boolean object_hit = FALSE;
5989   boolean impact = (last_line || object_hit);
5990   int element = Feld[x][y];
5991   int smashed = EL_STEELWALL;
5992
5993   if (!last_line)       /* check if element below was hit */
5994   {
5995     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5996       return;
5997
5998     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5999                                          MovDir[x][y + 1] != MV_DOWN ||
6000                                          MovPos[x][y + 1] <= TILEY / 2));
6001
6002     /* do not smash moving elements that left the smashed field in time */
6003     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6004         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6005       object_hit = FALSE;
6006
6007 #if USE_QUICKSAND_IMPACT_BUGFIX
6008     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6009     {
6010       RemoveMovingField(x, y + 1);
6011       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6012       Feld[x][y + 2] = EL_ROCK;
6013       TEST_DrawLevelField(x, y + 2);
6014
6015       object_hit = TRUE;
6016     }
6017
6018     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6019     {
6020       RemoveMovingField(x, y + 1);
6021       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6022       Feld[x][y + 2] = EL_ROCK;
6023       TEST_DrawLevelField(x, y + 2);
6024
6025       object_hit = TRUE;
6026     }
6027 #endif
6028
6029     if (object_hit)
6030       smashed = MovingOrBlocked2Element(x, y + 1);
6031
6032     impact = (last_line || object_hit);
6033   }
6034
6035   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6036   {
6037     SplashAcid(x, y + 1);
6038     return;
6039   }
6040
6041   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6042   /* only reset graphic animation if graphic really changes after impact */
6043   if (impact &&
6044       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6045   {
6046     ResetGfxAnimation(x, y);
6047     TEST_DrawLevelField(x, y);
6048   }
6049
6050   if (impact && CAN_EXPLODE_IMPACT(element))
6051   {
6052     Bang(x, y);
6053     return;
6054   }
6055   else if (impact && element == EL_PEARL &&
6056            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6057   {
6058     ResetGfxAnimation(x, y);
6059
6060     Feld[x][y] = EL_PEARL_BREAKING;
6061     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6062     return;
6063   }
6064   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6065   {
6066     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6067
6068     return;
6069   }
6070
6071   if (impact && element == EL_AMOEBA_DROP)
6072   {
6073     if (object_hit && IS_PLAYER(x, y + 1))
6074       KillPlayerUnlessEnemyProtected(x, y + 1);
6075     else if (object_hit && smashed == EL_PENGUIN)
6076       Bang(x, y + 1);
6077     else
6078     {
6079       Feld[x][y] = EL_AMOEBA_GROWING;
6080       Store[x][y] = EL_AMOEBA_WET;
6081
6082       ResetRandomAnimationValue(x, y);
6083     }
6084     return;
6085   }
6086
6087   if (object_hit)               /* check which object was hit */
6088   {
6089     if ((CAN_PASS_MAGIC_WALL(element) && 
6090          (smashed == EL_MAGIC_WALL ||
6091           smashed == EL_BD_MAGIC_WALL)) ||
6092         (CAN_PASS_DC_MAGIC_WALL(element) &&
6093          smashed == EL_DC_MAGIC_WALL))
6094     {
6095       int xx, yy;
6096       int activated_magic_wall =
6097         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6098          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6099          EL_DC_MAGIC_WALL_ACTIVE);
6100
6101       /* activate magic wall / mill */
6102       SCAN_PLAYFIELD(xx, yy)
6103       {
6104         if (Feld[xx][yy] == smashed)
6105           Feld[xx][yy] = activated_magic_wall;
6106       }
6107
6108       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6109       game.magic_wall_active = TRUE;
6110
6111       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6112                             SND_MAGIC_WALL_ACTIVATING :
6113                             smashed == EL_BD_MAGIC_WALL ?
6114                             SND_BD_MAGIC_WALL_ACTIVATING :
6115                             SND_DC_MAGIC_WALL_ACTIVATING));
6116     }
6117
6118     if (IS_PLAYER(x, y + 1))
6119     {
6120       if (CAN_SMASH_PLAYER(element))
6121       {
6122         KillPlayerUnlessEnemyProtected(x, y + 1);
6123         return;
6124       }
6125     }
6126     else if (smashed == EL_PENGUIN)
6127     {
6128       if (CAN_SMASH_PLAYER(element))
6129       {
6130         Bang(x, y + 1);
6131         return;
6132       }
6133     }
6134     else if (element == EL_BD_DIAMOND)
6135     {
6136       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6137       {
6138         Bang(x, y + 1);
6139         return;
6140       }
6141     }
6142     else if (((element == EL_SP_INFOTRON ||
6143                element == EL_SP_ZONK) &&
6144               (smashed == EL_SP_SNIKSNAK ||
6145                smashed == EL_SP_ELECTRON ||
6146                smashed == EL_SP_DISK_ORANGE)) ||
6147              (element == EL_SP_INFOTRON &&
6148               smashed == EL_SP_DISK_YELLOW))
6149     {
6150       Bang(x, y + 1);
6151       return;
6152     }
6153     else if (CAN_SMASH_EVERYTHING(element))
6154     {
6155       if (IS_CLASSIC_ENEMY(smashed) ||
6156           CAN_EXPLODE_SMASHED(smashed))
6157       {
6158         Bang(x, y + 1);
6159         return;
6160       }
6161       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6162       {
6163         if (smashed == EL_LAMP ||
6164             smashed == EL_LAMP_ACTIVE)
6165         {
6166           Bang(x, y + 1);
6167           return;
6168         }
6169         else if (smashed == EL_NUT)
6170         {
6171           Feld[x][y + 1] = EL_NUT_BREAKING;
6172           PlayLevelSound(x, y, SND_NUT_BREAKING);
6173           RaiseScoreElement(EL_NUT);
6174           return;
6175         }
6176         else if (smashed == EL_PEARL)
6177         {
6178           ResetGfxAnimation(x, y);
6179
6180           Feld[x][y + 1] = EL_PEARL_BREAKING;
6181           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6182           return;
6183         }
6184         else if (smashed == EL_DIAMOND)
6185         {
6186           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6187           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6188           return;
6189         }
6190         else if (IS_BELT_SWITCH(smashed))
6191         {
6192           ToggleBeltSwitch(x, y + 1);
6193         }
6194         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6195                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6196                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6197                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6198         {
6199           ToggleSwitchgateSwitch(x, y + 1);
6200         }
6201         else if (smashed == EL_LIGHT_SWITCH ||
6202                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6203         {
6204           ToggleLightSwitch(x, y + 1);
6205         }
6206         else
6207         {
6208           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6209
6210           CheckElementChangeBySide(x, y + 1, smashed, element,
6211                                    CE_SWITCHED, CH_SIDE_TOP);
6212           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6213                                             CH_SIDE_TOP);
6214         }
6215       }
6216       else
6217       {
6218         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6219       }
6220     }
6221   }
6222
6223   /* play sound of magic wall / mill */
6224   if (!last_line &&
6225       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6226        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6227        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6228   {
6229     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6230       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6231     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6232       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6233     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6234       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6235
6236     return;
6237   }
6238
6239   /* play sound of object that hits the ground */
6240   if (last_line || object_hit)
6241     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6242 }
6243
6244 inline static void TurnRoundExt(int x, int y)
6245 {
6246   static struct
6247   {
6248     int dx, dy;
6249   } move_xy[] =
6250   {
6251     {  0,  0 },
6252     { -1,  0 },
6253     { +1,  0 },
6254     {  0,  0 },
6255     {  0, -1 },
6256     {  0,  0 }, { 0, 0 }, { 0, 0 },
6257     {  0, +1 }
6258   };
6259   static struct
6260   {
6261     int left, right, back;
6262   } turn[] =
6263   {
6264     { 0,        0,              0        },
6265     { MV_DOWN,  MV_UP,          MV_RIGHT },
6266     { MV_UP,    MV_DOWN,        MV_LEFT  },
6267     { 0,        0,              0        },
6268     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6269     { 0,        0,              0        },
6270     { 0,        0,              0        },
6271     { 0,        0,              0        },
6272     { MV_RIGHT, MV_LEFT,        MV_UP    }
6273   };
6274
6275   int element = Feld[x][y];
6276   int move_pattern = element_info[element].move_pattern;
6277
6278   int old_move_dir = MovDir[x][y];
6279   int left_dir  = turn[old_move_dir].left;
6280   int right_dir = turn[old_move_dir].right;
6281   int back_dir  = turn[old_move_dir].back;
6282
6283   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6284   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6285   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6286   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6287
6288   int left_x  = x + left_dx,  left_y  = y + left_dy;
6289   int right_x = x + right_dx, right_y = y + right_dy;
6290   int move_x  = x + move_dx,  move_y  = y + move_dy;
6291
6292   int xx, yy;
6293
6294   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6295   {
6296     TestIfBadThingTouchesOtherBadThing(x, y);
6297
6298     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6299       MovDir[x][y] = right_dir;
6300     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6301       MovDir[x][y] = left_dir;
6302
6303     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6304       MovDelay[x][y] = 9;
6305     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6306       MovDelay[x][y] = 1;
6307   }
6308   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6309   {
6310     TestIfBadThingTouchesOtherBadThing(x, y);
6311
6312     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6313       MovDir[x][y] = left_dir;
6314     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6315       MovDir[x][y] = right_dir;
6316
6317     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6318       MovDelay[x][y] = 9;
6319     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6320       MovDelay[x][y] = 1;
6321   }
6322   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6323   {
6324     TestIfBadThingTouchesOtherBadThing(x, y);
6325
6326     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6327       MovDir[x][y] = left_dir;
6328     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6329       MovDir[x][y] = right_dir;
6330
6331     if (MovDir[x][y] != old_move_dir)
6332       MovDelay[x][y] = 9;
6333   }
6334   else if (element == EL_YAMYAM)
6335   {
6336     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6337     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6338
6339     if (can_turn_left && can_turn_right)
6340       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6341     else if (can_turn_left)
6342       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6343     else if (can_turn_right)
6344       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6345     else
6346       MovDir[x][y] = back_dir;
6347
6348     MovDelay[x][y] = 16 + 16 * RND(3);
6349   }
6350   else if (element == EL_DARK_YAMYAM)
6351   {
6352     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6353                                                          left_x, left_y);
6354     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6355                                                          right_x, right_y);
6356
6357     if (can_turn_left && can_turn_right)
6358       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6359     else if (can_turn_left)
6360       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6361     else if (can_turn_right)
6362       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6363     else
6364       MovDir[x][y] = back_dir;
6365
6366     MovDelay[x][y] = 16 + 16 * RND(3);
6367   }
6368   else if (element == EL_PACMAN)
6369   {
6370     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6371     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6372
6373     if (can_turn_left && can_turn_right)
6374       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6375     else if (can_turn_left)
6376       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6377     else if (can_turn_right)
6378       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6379     else
6380       MovDir[x][y] = back_dir;
6381
6382     MovDelay[x][y] = 6 + RND(40);
6383   }
6384   else if (element == EL_PIG)
6385   {
6386     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6387     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6388     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6389     boolean should_turn_left, should_turn_right, should_move_on;
6390     int rnd_value = 24;
6391     int rnd = RND(rnd_value);
6392
6393     should_turn_left = (can_turn_left &&
6394                         (!can_move_on ||
6395                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6396                                                    y + back_dy + left_dy)));
6397     should_turn_right = (can_turn_right &&
6398                          (!can_move_on ||
6399                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6400                                                     y + back_dy + right_dy)));
6401     should_move_on = (can_move_on &&
6402                       (!can_turn_left ||
6403                        !can_turn_right ||
6404                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6405                                                  y + move_dy + left_dy) ||
6406                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6407                                                  y + move_dy + right_dy)));
6408
6409     if (should_turn_left || should_turn_right || should_move_on)
6410     {
6411       if (should_turn_left && should_turn_right && should_move_on)
6412         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6413                         rnd < 2 * rnd_value / 3 ? right_dir :
6414                         old_move_dir);
6415       else if (should_turn_left && should_turn_right)
6416         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6417       else if (should_turn_left && should_move_on)
6418         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6419       else if (should_turn_right && should_move_on)
6420         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6421       else if (should_turn_left)
6422         MovDir[x][y] = left_dir;
6423       else if (should_turn_right)
6424         MovDir[x][y] = right_dir;
6425       else if (should_move_on)
6426         MovDir[x][y] = old_move_dir;
6427     }
6428     else if (can_move_on && rnd > rnd_value / 8)
6429       MovDir[x][y] = old_move_dir;
6430     else if (can_turn_left && can_turn_right)
6431       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6432     else if (can_turn_left && rnd > rnd_value / 8)
6433       MovDir[x][y] = left_dir;
6434     else if (can_turn_right && rnd > rnd_value/8)
6435       MovDir[x][y] = right_dir;
6436     else
6437       MovDir[x][y] = back_dir;
6438
6439     xx = x + move_xy[MovDir[x][y]].dx;
6440     yy = y + move_xy[MovDir[x][y]].dy;
6441
6442     if (!IN_LEV_FIELD(xx, yy) ||
6443         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6444       MovDir[x][y] = old_move_dir;
6445
6446     MovDelay[x][y] = 0;
6447   }
6448   else if (element == EL_DRAGON)
6449   {
6450     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6451     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6452     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6453     int rnd_value = 24;
6454     int rnd = RND(rnd_value);
6455
6456     if (can_move_on && rnd > rnd_value / 8)
6457       MovDir[x][y] = old_move_dir;
6458     else if (can_turn_left && can_turn_right)
6459       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6460     else if (can_turn_left && rnd > rnd_value / 8)
6461       MovDir[x][y] = left_dir;
6462     else if (can_turn_right && rnd > rnd_value / 8)
6463       MovDir[x][y] = right_dir;
6464     else
6465       MovDir[x][y] = back_dir;
6466
6467     xx = x + move_xy[MovDir[x][y]].dx;
6468     yy = y + move_xy[MovDir[x][y]].dy;
6469
6470     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6471       MovDir[x][y] = old_move_dir;
6472
6473     MovDelay[x][y] = 0;
6474   }
6475   else if (element == EL_MOLE)
6476   {
6477     boolean can_move_on =
6478       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6479                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6480                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6481     if (!can_move_on)
6482     {
6483       boolean can_turn_left =
6484         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6485                               IS_AMOEBOID(Feld[left_x][left_y])));
6486
6487       boolean can_turn_right =
6488         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6489                               IS_AMOEBOID(Feld[right_x][right_y])));
6490
6491       if (can_turn_left && can_turn_right)
6492         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6493       else if (can_turn_left)
6494         MovDir[x][y] = left_dir;
6495       else
6496         MovDir[x][y] = right_dir;
6497     }
6498
6499     if (MovDir[x][y] != old_move_dir)
6500       MovDelay[x][y] = 9;
6501   }
6502   else if (element == EL_BALLOON)
6503   {
6504     MovDir[x][y] = game.wind_direction;
6505     MovDelay[x][y] = 0;
6506   }
6507   else if (element == EL_SPRING)
6508   {
6509     if (MovDir[x][y] & MV_HORIZONTAL)
6510     {
6511       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6512           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6513       {
6514         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6515         ResetGfxAnimation(move_x, move_y);
6516         TEST_DrawLevelField(move_x, move_y);
6517
6518         MovDir[x][y] = back_dir;
6519       }
6520       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6521                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6522         MovDir[x][y] = MV_NONE;
6523     }
6524
6525     MovDelay[x][y] = 0;
6526   }
6527   else if (element == EL_ROBOT ||
6528            element == EL_SATELLITE ||
6529            element == EL_PENGUIN ||
6530            element == EL_EMC_ANDROID)
6531   {
6532     int attr_x = -1, attr_y = -1;
6533
6534     if (AllPlayersGone)
6535     {
6536       attr_x = ExitX;
6537       attr_y = ExitY;
6538     }
6539     else
6540     {
6541       int i;
6542
6543       for (i = 0; i < MAX_PLAYERS; i++)
6544       {
6545         struct PlayerInfo *player = &stored_player[i];
6546         int jx = player->jx, jy = player->jy;
6547
6548         if (!player->active)
6549           continue;
6550
6551         if (attr_x == -1 ||
6552             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6553         {
6554           attr_x = jx;
6555           attr_y = jy;
6556         }
6557       }
6558     }
6559
6560     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6561         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6562          game.engine_version < VERSION_IDENT(3,1,0,0)))
6563     {
6564       attr_x = ZX;
6565       attr_y = ZY;
6566     }
6567
6568     if (element == EL_PENGUIN)
6569     {
6570       int i;
6571       static int xy[4][2] =
6572       {
6573         { 0, -1 },
6574         { -1, 0 },
6575         { +1, 0 },
6576         { 0, +1 }
6577       };
6578
6579       for (i = 0; i < NUM_DIRECTIONS; i++)
6580       {
6581         int ex = x + xy[i][0];
6582         int ey = y + xy[i][1];
6583
6584         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6585                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6586                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6587                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6588         {
6589           attr_x = ex;
6590           attr_y = ey;
6591           break;
6592         }
6593       }
6594     }
6595
6596     MovDir[x][y] = MV_NONE;
6597     if (attr_x < x)
6598       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6599     else if (attr_x > x)
6600       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6601     if (attr_y < y)
6602       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6603     else if (attr_y > y)
6604       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6605
6606     if (element == EL_ROBOT)
6607     {
6608       int newx, newy;
6609
6610       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6611         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6612       Moving2Blocked(x, y, &newx, &newy);
6613
6614       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6615         MovDelay[x][y] = 8 + 8 * !RND(3);
6616       else
6617         MovDelay[x][y] = 16;
6618     }
6619     else if (element == EL_PENGUIN)
6620     {
6621       int newx, newy;
6622
6623       MovDelay[x][y] = 1;
6624
6625       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6626       {
6627         boolean first_horiz = RND(2);
6628         int new_move_dir = MovDir[x][y];
6629
6630         MovDir[x][y] =
6631           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6632         Moving2Blocked(x, y, &newx, &newy);
6633
6634         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6635           return;
6636
6637         MovDir[x][y] =
6638           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6639         Moving2Blocked(x, y, &newx, &newy);
6640
6641         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6642           return;
6643
6644         MovDir[x][y] = old_move_dir;
6645         return;
6646       }
6647     }
6648     else if (element == EL_SATELLITE)
6649     {
6650       int newx, newy;
6651
6652       MovDelay[x][y] = 1;
6653
6654       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6655       {
6656         boolean first_horiz = RND(2);
6657         int new_move_dir = MovDir[x][y];
6658
6659         MovDir[x][y] =
6660           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6661         Moving2Blocked(x, y, &newx, &newy);
6662
6663         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6664           return;
6665
6666         MovDir[x][y] =
6667           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6668         Moving2Blocked(x, y, &newx, &newy);
6669
6670         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6671           return;
6672
6673         MovDir[x][y] = old_move_dir;
6674         return;
6675       }
6676     }
6677     else if (element == EL_EMC_ANDROID)
6678     {
6679       static int check_pos[16] =
6680       {
6681         -1,             /*  0 => (invalid)          */
6682         7,              /*  1 => MV_LEFT            */
6683         3,              /*  2 => MV_RIGHT           */
6684         -1,             /*  3 => (invalid)          */
6685         1,              /*  4 =>            MV_UP   */
6686         0,              /*  5 => MV_LEFT  | MV_UP   */
6687         2,              /*  6 => MV_RIGHT | MV_UP   */
6688         -1,             /*  7 => (invalid)          */
6689         5,              /*  8 =>            MV_DOWN */
6690         6,              /*  9 => MV_LEFT  | MV_DOWN */
6691         4,              /* 10 => MV_RIGHT | MV_DOWN */
6692         -1,             /* 11 => (invalid)          */
6693         -1,             /* 12 => (invalid)          */
6694         -1,             /* 13 => (invalid)          */
6695         -1,             /* 14 => (invalid)          */
6696         -1,             /* 15 => (invalid)          */
6697       };
6698       static struct
6699       {
6700         int dx, dy;
6701         int dir;
6702       } check_xy[8] =
6703       {
6704         { -1, -1,       MV_LEFT  | MV_UP   },
6705         {  0, -1,                  MV_UP   },
6706         { +1, -1,       MV_RIGHT | MV_UP   },
6707         { +1,  0,       MV_RIGHT           },
6708         { +1, +1,       MV_RIGHT | MV_DOWN },
6709         {  0, +1,                  MV_DOWN },
6710         { -1, +1,       MV_LEFT  | MV_DOWN },
6711         { -1,  0,       MV_LEFT            },
6712       };
6713       int start_pos, check_order;
6714       boolean can_clone = FALSE;
6715       int i;
6716
6717       /* check if there is any free field around current position */
6718       for (i = 0; i < 8; i++)
6719       {
6720         int newx = x + check_xy[i].dx;
6721         int newy = y + check_xy[i].dy;
6722
6723         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6724         {
6725           can_clone = TRUE;
6726
6727           break;
6728         }
6729       }
6730
6731       if (can_clone)            /* randomly find an element to clone */
6732       {
6733         can_clone = FALSE;
6734
6735         start_pos = check_pos[RND(8)];
6736         check_order = (RND(2) ? -1 : +1);
6737
6738         for (i = 0; i < 8; i++)
6739         {
6740           int pos_raw = start_pos + i * check_order;
6741           int pos = (pos_raw + 8) % 8;
6742           int newx = x + check_xy[pos].dx;
6743           int newy = y + check_xy[pos].dy;
6744
6745           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6746           {
6747             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6748             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6749
6750             Store[x][y] = Feld[newx][newy];
6751
6752             can_clone = TRUE;
6753
6754             break;
6755           }
6756         }
6757       }
6758
6759       if (can_clone)            /* randomly find a direction to move */
6760       {
6761         can_clone = FALSE;
6762
6763         start_pos = check_pos[RND(8)];
6764         check_order = (RND(2) ? -1 : +1);
6765
6766         for (i = 0; i < 8; i++)
6767         {
6768           int pos_raw = start_pos + i * check_order;
6769           int pos = (pos_raw + 8) % 8;
6770           int newx = x + check_xy[pos].dx;
6771           int newy = y + check_xy[pos].dy;
6772           int new_move_dir = check_xy[pos].dir;
6773
6774           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6775           {
6776             MovDir[x][y] = new_move_dir;
6777             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6778
6779             can_clone = TRUE;
6780
6781             break;
6782           }
6783         }
6784       }
6785
6786       if (can_clone)            /* cloning and moving successful */
6787         return;
6788
6789       /* cannot clone -- try to move towards player */
6790
6791       start_pos = check_pos[MovDir[x][y] & 0x0f];
6792       check_order = (RND(2) ? -1 : +1);
6793
6794       for (i = 0; i < 3; i++)
6795       {
6796         /* first check start_pos, then previous/next or (next/previous) pos */
6797         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6798         int pos = (pos_raw + 8) % 8;
6799         int newx = x + check_xy[pos].dx;
6800         int newy = y + check_xy[pos].dy;
6801         int new_move_dir = check_xy[pos].dir;
6802
6803         if (IS_PLAYER(newx, newy))
6804           break;
6805
6806         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6807         {
6808           MovDir[x][y] = new_move_dir;
6809           MovDelay[x][y] = level.android_move_time * 8 + 1;
6810
6811           break;
6812         }
6813       }
6814     }
6815   }
6816   else if (move_pattern == MV_TURNING_LEFT ||
6817            move_pattern == MV_TURNING_RIGHT ||
6818            move_pattern == MV_TURNING_LEFT_RIGHT ||
6819            move_pattern == MV_TURNING_RIGHT_LEFT ||
6820            move_pattern == MV_TURNING_RANDOM ||
6821            move_pattern == MV_ALL_DIRECTIONS)
6822   {
6823     boolean can_turn_left =
6824       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6825     boolean can_turn_right =
6826       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6827
6828     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6829       return;
6830
6831     if (move_pattern == MV_TURNING_LEFT)
6832       MovDir[x][y] = left_dir;
6833     else if (move_pattern == MV_TURNING_RIGHT)
6834       MovDir[x][y] = right_dir;
6835     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6836       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6837     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6838       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6839     else if (move_pattern == MV_TURNING_RANDOM)
6840       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6841                       can_turn_right && !can_turn_left ? right_dir :
6842                       RND(2) ? left_dir : right_dir);
6843     else if (can_turn_left && can_turn_right)
6844       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6845     else if (can_turn_left)
6846       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6847     else if (can_turn_right)
6848       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6849     else
6850       MovDir[x][y] = back_dir;
6851
6852     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6853   }
6854   else if (move_pattern == MV_HORIZONTAL ||
6855            move_pattern == MV_VERTICAL)
6856   {
6857     if (move_pattern & old_move_dir)
6858       MovDir[x][y] = back_dir;
6859     else if (move_pattern == MV_HORIZONTAL)
6860       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6861     else if (move_pattern == MV_VERTICAL)
6862       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6863
6864     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6865   }
6866   else if (move_pattern & MV_ANY_DIRECTION)
6867   {
6868     MovDir[x][y] = move_pattern;
6869     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6870   }
6871   else if (move_pattern & MV_WIND_DIRECTION)
6872   {
6873     MovDir[x][y] = game.wind_direction;
6874     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6875   }
6876   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6877   {
6878     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6879       MovDir[x][y] = left_dir;
6880     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6881       MovDir[x][y] = right_dir;
6882
6883     if (MovDir[x][y] != old_move_dir)
6884       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6885   }
6886   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6887   {
6888     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6889       MovDir[x][y] = right_dir;
6890     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6891       MovDir[x][y] = left_dir;
6892
6893     if (MovDir[x][y] != old_move_dir)
6894       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6895   }
6896   else if (move_pattern == MV_TOWARDS_PLAYER ||
6897            move_pattern == MV_AWAY_FROM_PLAYER)
6898   {
6899     int attr_x = -1, attr_y = -1;
6900     int newx, newy;
6901     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6902
6903     if (AllPlayersGone)
6904     {
6905       attr_x = ExitX;
6906       attr_y = ExitY;
6907     }
6908     else
6909     {
6910       int i;
6911
6912       for (i = 0; i < MAX_PLAYERS; i++)
6913       {
6914         struct PlayerInfo *player = &stored_player[i];
6915         int jx = player->jx, jy = player->jy;
6916
6917         if (!player->active)
6918           continue;
6919
6920         if (attr_x == -1 ||
6921             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6922         {
6923           attr_x = jx;
6924           attr_y = jy;
6925         }
6926       }
6927     }
6928
6929     MovDir[x][y] = MV_NONE;
6930     if (attr_x < x)
6931       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6932     else if (attr_x > x)
6933       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6934     if (attr_y < y)
6935       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6936     else if (attr_y > y)
6937       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6938
6939     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6940
6941     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6942     {
6943       boolean first_horiz = RND(2);
6944       int new_move_dir = MovDir[x][y];
6945
6946       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6947       {
6948         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6949         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6950
6951         return;
6952       }
6953
6954       MovDir[x][y] =
6955         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6956       Moving2Blocked(x, y, &newx, &newy);
6957
6958       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6959         return;
6960
6961       MovDir[x][y] =
6962         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6963       Moving2Blocked(x, y, &newx, &newy);
6964
6965       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6966         return;
6967
6968       MovDir[x][y] = old_move_dir;
6969     }
6970   }
6971   else if (move_pattern == MV_WHEN_PUSHED ||
6972            move_pattern == MV_WHEN_DROPPED)
6973   {
6974     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6975       MovDir[x][y] = MV_NONE;
6976
6977     MovDelay[x][y] = 0;
6978   }
6979   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6980   {
6981     static int test_xy[7][2] =
6982     {
6983       { 0, -1 },
6984       { -1, 0 },
6985       { +1, 0 },
6986       { 0, +1 },
6987       { 0, -1 },
6988       { -1, 0 },
6989       { +1, 0 },
6990     };
6991     static int test_dir[7] =
6992     {
6993       MV_UP,
6994       MV_LEFT,
6995       MV_RIGHT,
6996       MV_DOWN,
6997       MV_UP,
6998       MV_LEFT,
6999       MV_RIGHT,
7000     };
7001     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7002     int move_preference = -1000000;     /* start with very low preference */
7003     int new_move_dir = MV_NONE;
7004     int start_test = RND(4);
7005     int i;
7006
7007     for (i = 0; i < NUM_DIRECTIONS; i++)
7008     {
7009       int move_dir = test_dir[start_test + i];
7010       int move_dir_preference;
7011
7012       xx = x + test_xy[start_test + i][0];
7013       yy = y + test_xy[start_test + i][1];
7014
7015       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7016           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7017       {
7018         new_move_dir = move_dir;
7019
7020         break;
7021       }
7022
7023       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7024         continue;
7025
7026       move_dir_preference = -1 * RunnerVisit[xx][yy];
7027       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7028         move_dir_preference = PlayerVisit[xx][yy];
7029
7030       if (move_dir_preference > move_preference)
7031       {
7032         /* prefer field that has not been visited for the longest time */
7033         move_preference = move_dir_preference;
7034         new_move_dir = move_dir;
7035       }
7036       else if (move_dir_preference == move_preference &&
7037                move_dir == old_move_dir)
7038       {
7039         /* prefer last direction when all directions are preferred equally */
7040         move_preference = move_dir_preference;
7041         new_move_dir = move_dir;
7042       }
7043     }
7044
7045     MovDir[x][y] = new_move_dir;
7046     if (old_move_dir != new_move_dir)
7047       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7048   }
7049 }
7050
7051 static void TurnRound(int x, int y)
7052 {
7053   int direction = MovDir[x][y];
7054
7055   TurnRoundExt(x, y);
7056
7057   GfxDir[x][y] = MovDir[x][y];
7058
7059   if (direction != MovDir[x][y])
7060     GfxFrame[x][y] = 0;
7061
7062   if (MovDelay[x][y])
7063     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7064
7065   ResetGfxFrame(x, y, FALSE);
7066 }
7067
7068 static boolean JustBeingPushed(int x, int y)
7069 {
7070   int i;
7071
7072   for (i = 0; i < MAX_PLAYERS; i++)
7073   {
7074     struct PlayerInfo *player = &stored_player[i];
7075
7076     if (player->active && player->is_pushing && player->MovPos)
7077     {
7078       int next_jx = player->jx + (player->jx - player->last_jx);
7079       int next_jy = player->jy + (player->jy - player->last_jy);
7080
7081       if (x == next_jx && y == next_jy)
7082         return TRUE;
7083     }
7084   }
7085
7086   return FALSE;
7087 }
7088
7089 void StartMoving(int x, int y)
7090 {
7091   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7092   int element = Feld[x][y];
7093
7094   if (Stop[x][y])
7095     return;
7096
7097   if (MovDelay[x][y] == 0)
7098     GfxAction[x][y] = ACTION_DEFAULT;
7099
7100   if (CAN_FALL(element) && y < lev_fieldy - 1)
7101   {
7102     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7103         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7104       if (JustBeingPushed(x, y))
7105         return;
7106
7107     if (element == EL_QUICKSAND_FULL)
7108     {
7109       if (IS_FREE(x, y + 1))
7110       {
7111         InitMovingField(x, y, MV_DOWN);
7112         started_moving = TRUE;
7113
7114         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7115 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7116         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7117           Store[x][y] = EL_ROCK;
7118 #else
7119         Store[x][y] = EL_ROCK;
7120 #endif
7121
7122         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7123       }
7124       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7125       {
7126         if (!MovDelay[x][y])
7127         {
7128           MovDelay[x][y] = TILEY + 1;
7129
7130           ResetGfxAnimation(x, y);
7131           ResetGfxAnimation(x, y + 1);
7132         }
7133
7134         if (MovDelay[x][y])
7135         {
7136           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7137           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7138
7139           MovDelay[x][y]--;
7140           if (MovDelay[x][y])
7141             return;
7142         }
7143
7144         Feld[x][y] = EL_QUICKSAND_EMPTY;
7145         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7146         Store[x][y + 1] = Store[x][y];
7147         Store[x][y] = 0;
7148
7149         PlayLevelSoundAction(x, y, ACTION_FILLING);
7150       }
7151       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7152       {
7153         if (!MovDelay[x][y])
7154         {
7155           MovDelay[x][y] = TILEY + 1;
7156
7157           ResetGfxAnimation(x, y);
7158           ResetGfxAnimation(x, y + 1);
7159         }
7160
7161         if (MovDelay[x][y])
7162         {
7163           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7164           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7165
7166           MovDelay[x][y]--;
7167           if (MovDelay[x][y])
7168             return;
7169         }
7170
7171         Feld[x][y] = EL_QUICKSAND_EMPTY;
7172         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7173         Store[x][y + 1] = Store[x][y];
7174         Store[x][y] = 0;
7175
7176         PlayLevelSoundAction(x, y, ACTION_FILLING);
7177       }
7178     }
7179     else if (element == EL_QUICKSAND_FAST_FULL)
7180     {
7181       if (IS_FREE(x, y + 1))
7182       {
7183         InitMovingField(x, y, MV_DOWN);
7184         started_moving = TRUE;
7185
7186         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7187 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7188         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7189           Store[x][y] = EL_ROCK;
7190 #else
7191         Store[x][y] = EL_ROCK;
7192 #endif
7193
7194         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7195       }
7196       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7197       {
7198         if (!MovDelay[x][y])
7199         {
7200           MovDelay[x][y] = TILEY + 1;
7201
7202           ResetGfxAnimation(x, y);
7203           ResetGfxAnimation(x, y + 1);
7204         }
7205
7206         if (MovDelay[x][y])
7207         {
7208           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7209           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7210
7211           MovDelay[x][y]--;
7212           if (MovDelay[x][y])
7213             return;
7214         }
7215
7216         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7217         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7218         Store[x][y + 1] = Store[x][y];
7219         Store[x][y] = 0;
7220
7221         PlayLevelSoundAction(x, y, ACTION_FILLING);
7222       }
7223       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7224       {
7225         if (!MovDelay[x][y])
7226         {
7227           MovDelay[x][y] = TILEY + 1;
7228
7229           ResetGfxAnimation(x, y);
7230           ResetGfxAnimation(x, y + 1);
7231         }
7232
7233         if (MovDelay[x][y])
7234         {
7235           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7236           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7237
7238           MovDelay[x][y]--;
7239           if (MovDelay[x][y])
7240             return;
7241         }
7242
7243         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7244         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7245         Store[x][y + 1] = Store[x][y];
7246         Store[x][y] = 0;
7247
7248         PlayLevelSoundAction(x, y, ACTION_FILLING);
7249       }
7250     }
7251     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7252              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7253     {
7254       InitMovingField(x, y, MV_DOWN);
7255       started_moving = TRUE;
7256
7257       Feld[x][y] = EL_QUICKSAND_FILLING;
7258       Store[x][y] = element;
7259
7260       PlayLevelSoundAction(x, y, ACTION_FILLING);
7261     }
7262     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7263              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7264     {
7265       InitMovingField(x, y, MV_DOWN);
7266       started_moving = TRUE;
7267
7268       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7269       Store[x][y] = element;
7270
7271       PlayLevelSoundAction(x, y, ACTION_FILLING);
7272     }
7273     else if (element == EL_MAGIC_WALL_FULL)
7274     {
7275       if (IS_FREE(x, y + 1))
7276       {
7277         InitMovingField(x, y, MV_DOWN);
7278         started_moving = TRUE;
7279
7280         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7281         Store[x][y] = EL_CHANGED(Store[x][y]);
7282       }
7283       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7284       {
7285         if (!MovDelay[x][y])
7286           MovDelay[x][y] = TILEY / 4 + 1;
7287
7288         if (MovDelay[x][y])
7289         {
7290           MovDelay[x][y]--;
7291           if (MovDelay[x][y])
7292             return;
7293         }
7294
7295         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7296         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7297         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7298         Store[x][y] = 0;
7299       }
7300     }
7301     else if (element == EL_BD_MAGIC_WALL_FULL)
7302     {
7303       if (IS_FREE(x, y + 1))
7304       {
7305         InitMovingField(x, y, MV_DOWN);
7306         started_moving = TRUE;
7307
7308         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7309         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7310       }
7311       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7312       {
7313         if (!MovDelay[x][y])
7314           MovDelay[x][y] = TILEY / 4 + 1;
7315
7316         if (MovDelay[x][y])
7317         {
7318           MovDelay[x][y]--;
7319           if (MovDelay[x][y])
7320             return;
7321         }
7322
7323         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7324         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7325         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7326         Store[x][y] = 0;
7327       }
7328     }
7329     else if (element == EL_DC_MAGIC_WALL_FULL)
7330     {
7331       if (IS_FREE(x, y + 1))
7332       {
7333         InitMovingField(x, y, MV_DOWN);
7334         started_moving = TRUE;
7335
7336         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7337         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7338       }
7339       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7340       {
7341         if (!MovDelay[x][y])
7342           MovDelay[x][y] = TILEY / 4 + 1;
7343
7344         if (MovDelay[x][y])
7345         {
7346           MovDelay[x][y]--;
7347           if (MovDelay[x][y])
7348             return;
7349         }
7350
7351         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7352         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7353         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7354         Store[x][y] = 0;
7355       }
7356     }
7357     else if ((CAN_PASS_MAGIC_WALL(element) &&
7358               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7359                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7360              (CAN_PASS_DC_MAGIC_WALL(element) &&
7361               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7362
7363     {
7364       InitMovingField(x, y, MV_DOWN);
7365       started_moving = TRUE;
7366
7367       Feld[x][y] =
7368         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7369          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7370          EL_DC_MAGIC_WALL_FILLING);
7371       Store[x][y] = element;
7372     }
7373     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7374     {
7375       SplashAcid(x, y + 1);
7376
7377       InitMovingField(x, y, MV_DOWN);
7378       started_moving = TRUE;
7379
7380       Store[x][y] = EL_ACID;
7381     }
7382     else if (
7383              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7384               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7385              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7386               CAN_FALL(element) && WasJustFalling[x][y] &&
7387               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7388
7389              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7390               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7391               (Feld[x][y + 1] == EL_BLOCKED)))
7392     {
7393       /* this is needed for a special case not covered by calling "Impact()"
7394          from "ContinueMoving()": if an element moves to a tile directly below
7395          another element which was just falling on that tile (which was empty
7396          in the previous frame), the falling element above would just stop
7397          instead of smashing the element below (in previous version, the above
7398          element was just checked for "moving" instead of "falling", resulting
7399          in incorrect smashes caused by horizontal movement of the above
7400          element; also, the case of the player being the element to smash was
7401          simply not covered here... :-/ ) */
7402
7403       CheckCollision[x][y] = 0;
7404       CheckImpact[x][y] = 0;
7405
7406       Impact(x, y);
7407     }
7408     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7409     {
7410       if (MovDir[x][y] == MV_NONE)
7411       {
7412         InitMovingField(x, y, MV_DOWN);
7413         started_moving = TRUE;
7414       }
7415     }
7416     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7417     {
7418       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7419         MovDir[x][y] = MV_DOWN;
7420
7421       InitMovingField(x, y, MV_DOWN);
7422       started_moving = TRUE;
7423     }
7424     else if (element == EL_AMOEBA_DROP)
7425     {
7426       Feld[x][y] = EL_AMOEBA_GROWING;
7427       Store[x][y] = EL_AMOEBA_WET;
7428     }
7429     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7430               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7431              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7432              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7433     {
7434       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7435                                 (IS_FREE(x - 1, y + 1) ||
7436                                  Feld[x - 1][y + 1] == EL_ACID));
7437       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7438                                 (IS_FREE(x + 1, y + 1) ||
7439                                  Feld[x + 1][y + 1] == EL_ACID));
7440       boolean can_fall_any  = (can_fall_left || can_fall_right);
7441       boolean can_fall_both = (can_fall_left && can_fall_right);
7442       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7443
7444       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7445       {
7446         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7447           can_fall_right = FALSE;
7448         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7449           can_fall_left = FALSE;
7450         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7451           can_fall_right = FALSE;
7452         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7453           can_fall_left = FALSE;
7454
7455         can_fall_any  = (can_fall_left || can_fall_right);
7456         can_fall_both = FALSE;
7457       }
7458
7459       if (can_fall_both)
7460       {
7461         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7462           can_fall_right = FALSE;       /* slip down on left side */
7463         else
7464           can_fall_left = !(can_fall_right = RND(2));
7465
7466         can_fall_both = FALSE;
7467       }
7468
7469       if (can_fall_any)
7470       {
7471         /* if not determined otherwise, prefer left side for slipping down */
7472         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7473         started_moving = TRUE;
7474       }
7475     }
7476     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7477     {
7478       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7479       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7480       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7481       int belt_dir = game.belt_dir[belt_nr];
7482
7483       if ((belt_dir == MV_LEFT  && left_is_free) ||
7484           (belt_dir == MV_RIGHT && right_is_free))
7485       {
7486         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7487
7488         InitMovingField(x, y, belt_dir);
7489         started_moving = TRUE;
7490
7491         Pushed[x][y] = TRUE;
7492         Pushed[nextx][y] = TRUE;
7493
7494         GfxAction[x][y] = ACTION_DEFAULT;
7495       }
7496       else
7497       {
7498         MovDir[x][y] = 0;       /* if element was moving, stop it */
7499       }
7500     }
7501   }
7502
7503   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7504   if (CAN_MOVE(element) && !started_moving)
7505   {
7506     int move_pattern = element_info[element].move_pattern;
7507     int newx, newy;
7508
7509     Moving2Blocked(x, y, &newx, &newy);
7510
7511     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7512       return;
7513
7514     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7515         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7516     {
7517       WasJustMoving[x][y] = 0;
7518       CheckCollision[x][y] = 0;
7519
7520       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7521
7522       if (Feld[x][y] != element)        /* element has changed */
7523         return;
7524     }
7525
7526     if (!MovDelay[x][y])        /* start new movement phase */
7527     {
7528       /* all objects that can change their move direction after each step
7529          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7530
7531       if (element != EL_YAMYAM &&
7532           element != EL_DARK_YAMYAM &&
7533           element != EL_PACMAN &&
7534           !(move_pattern & MV_ANY_DIRECTION) &&
7535           move_pattern != MV_TURNING_LEFT &&
7536           move_pattern != MV_TURNING_RIGHT &&
7537           move_pattern != MV_TURNING_LEFT_RIGHT &&
7538           move_pattern != MV_TURNING_RIGHT_LEFT &&
7539           move_pattern != MV_TURNING_RANDOM)
7540       {
7541         TurnRound(x, y);
7542
7543         if (MovDelay[x][y] && (element == EL_BUG ||
7544                                element == EL_SPACESHIP ||
7545                                element == EL_SP_SNIKSNAK ||
7546                                element == EL_SP_ELECTRON ||
7547                                element == EL_MOLE))
7548           TEST_DrawLevelField(x, y);
7549       }
7550     }
7551
7552     if (MovDelay[x][y])         /* wait some time before next movement */
7553     {
7554       MovDelay[x][y]--;
7555
7556       if (element == EL_ROBOT ||
7557           element == EL_YAMYAM ||
7558           element == EL_DARK_YAMYAM)
7559       {
7560         DrawLevelElementAnimationIfNeeded(x, y, element);
7561         PlayLevelSoundAction(x, y, ACTION_WAITING);
7562       }
7563       else if (element == EL_SP_ELECTRON)
7564         DrawLevelElementAnimationIfNeeded(x, y, element);
7565       else if (element == EL_DRAGON)
7566       {
7567         int i;
7568         int dir = MovDir[x][y];
7569         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7570         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7571         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7572                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7573                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7574                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7575         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7576
7577         GfxAction[x][y] = ACTION_ATTACKING;
7578
7579         if (IS_PLAYER(x, y))
7580           DrawPlayerField(x, y);
7581         else
7582           TEST_DrawLevelField(x, y);
7583
7584         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7585
7586         for (i = 1; i <= 3; i++)
7587         {
7588           int xx = x + i * dx;
7589           int yy = y + i * dy;
7590           int sx = SCREENX(xx);
7591           int sy = SCREENY(yy);
7592           int flame_graphic = graphic + (i - 1);
7593
7594           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7595             break;
7596
7597           if (MovDelay[x][y])
7598           {
7599             int flamed = MovingOrBlocked2Element(xx, yy);
7600
7601             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7602               Bang(xx, yy);
7603             else
7604               RemoveMovingField(xx, yy);
7605
7606             ChangeDelay[xx][yy] = 0;
7607
7608             Feld[xx][yy] = EL_FLAMES;
7609
7610             if (IN_SCR_FIELD(sx, sy))
7611             {
7612               TEST_DrawLevelFieldCrumbled(xx, yy);
7613               DrawGraphic(sx, sy, flame_graphic, frame);
7614             }
7615           }
7616           else
7617           {
7618             if (Feld[xx][yy] == EL_FLAMES)
7619               Feld[xx][yy] = EL_EMPTY;
7620             TEST_DrawLevelField(xx, yy);
7621           }
7622         }
7623       }
7624
7625       if (MovDelay[x][y])       /* element still has to wait some time */
7626       {
7627         PlayLevelSoundAction(x, y, ACTION_WAITING);
7628
7629         return;
7630       }
7631     }
7632
7633     /* now make next step */
7634
7635     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7636
7637     if (DONT_COLLIDE_WITH(element) &&
7638         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7639         !PLAYER_ENEMY_PROTECTED(newx, newy))
7640     {
7641       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7642
7643       return;
7644     }
7645
7646     else if (CAN_MOVE_INTO_ACID(element) &&
7647              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7648              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7649              (MovDir[x][y] == MV_DOWN ||
7650               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7651     {
7652       SplashAcid(newx, newy);
7653       Store[x][y] = EL_ACID;
7654     }
7655     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7656     {
7657       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7658           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7659           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7660           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7661       {
7662         RemoveField(x, y);
7663         TEST_DrawLevelField(x, y);
7664
7665         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7666         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7667           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7668
7669         local_player->friends_still_needed--;
7670         if (!local_player->friends_still_needed &&
7671             !local_player->GameOver && AllPlayersGone)
7672           PlayerWins(local_player);
7673
7674         return;
7675       }
7676       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7677       {
7678         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7679           TEST_DrawLevelField(newx, newy);
7680         else
7681           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7682       }
7683       else if (!IS_FREE(newx, newy))
7684       {
7685         GfxAction[x][y] = ACTION_WAITING;
7686
7687         if (IS_PLAYER(x, y))
7688           DrawPlayerField(x, y);
7689         else
7690           TEST_DrawLevelField(x, y);
7691
7692         return;
7693       }
7694     }
7695     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7696     {
7697       if (IS_FOOD_PIG(Feld[newx][newy]))
7698       {
7699         if (IS_MOVING(newx, newy))
7700           RemoveMovingField(newx, newy);
7701         else
7702         {
7703           Feld[newx][newy] = EL_EMPTY;
7704           TEST_DrawLevelField(newx, newy);
7705         }
7706
7707         PlayLevelSound(x, y, SND_PIG_DIGGING);
7708       }
7709       else if (!IS_FREE(newx, newy))
7710       {
7711         if (IS_PLAYER(x, y))
7712           DrawPlayerField(x, y);
7713         else
7714           TEST_DrawLevelField(x, y);
7715
7716         return;
7717       }
7718     }
7719     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7720     {
7721       if (Store[x][y] != EL_EMPTY)
7722       {
7723         boolean can_clone = FALSE;
7724         int xx, yy;
7725
7726         /* check if element to clone is still there */
7727         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7728         {
7729           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7730           {
7731             can_clone = TRUE;
7732
7733             break;
7734           }
7735         }
7736
7737         /* cannot clone or target field not free anymore -- do not clone */
7738         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7739           Store[x][y] = EL_EMPTY;
7740       }
7741
7742       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7743       {
7744         if (IS_MV_DIAGONAL(MovDir[x][y]))
7745         {
7746           int diagonal_move_dir = MovDir[x][y];
7747           int stored = Store[x][y];
7748           int change_delay = 8;
7749           int graphic;
7750
7751           /* android is moving diagonally */
7752
7753           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7754
7755           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7756           GfxElement[x][y] = EL_EMC_ANDROID;
7757           GfxAction[x][y] = ACTION_SHRINKING;
7758           GfxDir[x][y] = diagonal_move_dir;
7759           ChangeDelay[x][y] = change_delay;
7760
7761           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7762                                    GfxDir[x][y]);
7763
7764           DrawLevelGraphicAnimation(x, y, graphic);
7765           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7766
7767           if (Feld[newx][newy] == EL_ACID)
7768           {
7769             SplashAcid(newx, newy);
7770
7771             return;
7772           }
7773
7774           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7775
7776           Store[newx][newy] = EL_EMC_ANDROID;
7777           GfxElement[newx][newy] = EL_EMC_ANDROID;
7778           GfxAction[newx][newy] = ACTION_GROWING;
7779           GfxDir[newx][newy] = diagonal_move_dir;
7780           ChangeDelay[newx][newy] = change_delay;
7781
7782           graphic = el_act_dir2img(GfxElement[newx][newy],
7783                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7784
7785           DrawLevelGraphicAnimation(newx, newy, graphic);
7786           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7787
7788           return;
7789         }
7790         else
7791         {
7792           Feld[newx][newy] = EL_EMPTY;
7793           TEST_DrawLevelField(newx, newy);
7794
7795           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7796         }
7797       }
7798       else if (!IS_FREE(newx, newy))
7799       {
7800         return;
7801       }
7802     }
7803     else if (IS_CUSTOM_ELEMENT(element) &&
7804              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7805     {
7806       if (!DigFieldByCE(newx, newy, element))
7807         return;
7808
7809       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7810       {
7811         RunnerVisit[x][y] = FrameCounter;
7812         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7813       }
7814     }
7815     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7816     {
7817       if (!IS_FREE(newx, newy))
7818       {
7819         if (IS_PLAYER(x, y))
7820           DrawPlayerField(x, y);
7821         else
7822           TEST_DrawLevelField(x, y);
7823
7824         return;
7825       }
7826       else
7827       {
7828         boolean wanna_flame = !RND(10);
7829         int dx = newx - x, dy = newy - y;
7830         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7831         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7832         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7833                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7834         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7835                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7836
7837         if ((wanna_flame ||
7838              IS_CLASSIC_ENEMY(element1) ||
7839              IS_CLASSIC_ENEMY(element2)) &&
7840             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7841             element1 != EL_FLAMES && element2 != EL_FLAMES)
7842         {
7843           ResetGfxAnimation(x, y);
7844           GfxAction[x][y] = ACTION_ATTACKING;
7845
7846           if (IS_PLAYER(x, y))
7847             DrawPlayerField(x, y);
7848           else
7849             TEST_DrawLevelField(x, y);
7850
7851           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7852
7853           MovDelay[x][y] = 50;
7854
7855           Feld[newx][newy] = EL_FLAMES;
7856           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7857             Feld[newx1][newy1] = EL_FLAMES;
7858           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7859             Feld[newx2][newy2] = EL_FLAMES;
7860
7861           return;
7862         }
7863       }
7864     }
7865     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7866              Feld[newx][newy] == EL_DIAMOND)
7867     {
7868       if (IS_MOVING(newx, newy))
7869         RemoveMovingField(newx, newy);
7870       else
7871       {
7872         Feld[newx][newy] = EL_EMPTY;
7873         TEST_DrawLevelField(newx, newy);
7874       }
7875
7876       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7877     }
7878     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7879              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7880     {
7881       if (AmoebaNr[newx][newy])
7882       {
7883         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7884         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7885             Feld[newx][newy] == EL_BD_AMOEBA)
7886           AmoebaCnt[AmoebaNr[newx][newy]]--;
7887       }
7888
7889       if (IS_MOVING(newx, newy))
7890       {
7891         RemoveMovingField(newx, newy);
7892       }
7893       else
7894       {
7895         Feld[newx][newy] = EL_EMPTY;
7896         TEST_DrawLevelField(newx, newy);
7897       }
7898
7899       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7900     }
7901     else if ((element == EL_PACMAN || element == EL_MOLE)
7902              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7903     {
7904       if (AmoebaNr[newx][newy])
7905       {
7906         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7907         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7908             Feld[newx][newy] == EL_BD_AMOEBA)
7909           AmoebaCnt[AmoebaNr[newx][newy]]--;
7910       }
7911
7912       if (element == EL_MOLE)
7913       {
7914         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7915         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7916
7917         ResetGfxAnimation(x, y);
7918         GfxAction[x][y] = ACTION_DIGGING;
7919         TEST_DrawLevelField(x, y);
7920
7921         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7922
7923         return;                         /* wait for shrinking amoeba */
7924       }
7925       else      /* element == EL_PACMAN */
7926       {
7927         Feld[newx][newy] = EL_EMPTY;
7928         TEST_DrawLevelField(newx, newy);
7929         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7930       }
7931     }
7932     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7933              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7934               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7935     {
7936       /* wait for shrinking amoeba to completely disappear */
7937       return;
7938     }
7939     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7940     {
7941       /* object was running against a wall */
7942
7943       TurnRound(x, y);
7944
7945       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7946         DrawLevelElementAnimation(x, y, element);
7947
7948       if (DONT_TOUCH(element))
7949         TestIfBadThingTouchesPlayer(x, y);
7950
7951       return;
7952     }
7953
7954     InitMovingField(x, y, MovDir[x][y]);
7955
7956     PlayLevelSoundAction(x, y, ACTION_MOVING);
7957   }
7958
7959   if (MovDir[x][y])
7960     ContinueMoving(x, y);
7961 }
7962
7963 void ContinueMoving(int x, int y)
7964 {
7965   int element = Feld[x][y];
7966   struct ElementInfo *ei = &element_info[element];
7967   int direction = MovDir[x][y];
7968   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7969   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7970   int newx = x + dx, newy = y + dy;
7971   int stored = Store[x][y];
7972   int stored_new = Store[newx][newy];
7973   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7974   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7975   boolean last_line = (newy == lev_fieldy - 1);
7976
7977   MovPos[x][y] += getElementMoveStepsize(x, y);
7978
7979   if (pushed_by_player) /* special case: moving object pushed by player */
7980     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7981
7982   if (ABS(MovPos[x][y]) < TILEX)
7983   {
7984     TEST_DrawLevelField(x, y);
7985
7986     return;     /* element is still moving */
7987   }
7988
7989   /* element reached destination field */
7990
7991   Feld[x][y] = EL_EMPTY;
7992   Feld[newx][newy] = element;
7993   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7994
7995   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7996   {
7997     element = Feld[newx][newy] = EL_ACID;
7998   }
7999   else if (element == EL_MOLE)
8000   {
8001     Feld[x][y] = EL_SAND;
8002
8003     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8004   }
8005   else if (element == EL_QUICKSAND_FILLING)
8006   {
8007     element = Feld[newx][newy] = get_next_element(element);
8008     Store[newx][newy] = Store[x][y];
8009   }
8010   else if (element == EL_QUICKSAND_EMPTYING)
8011   {
8012     Feld[x][y] = get_next_element(element);
8013     element = Feld[newx][newy] = Store[x][y];
8014   }
8015   else if (element == EL_QUICKSAND_FAST_FILLING)
8016   {
8017     element = Feld[newx][newy] = get_next_element(element);
8018     Store[newx][newy] = Store[x][y];
8019   }
8020   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8021   {
8022     Feld[x][y] = get_next_element(element);
8023     element = Feld[newx][newy] = Store[x][y];
8024   }
8025   else if (element == EL_MAGIC_WALL_FILLING)
8026   {
8027     element = Feld[newx][newy] = get_next_element(element);
8028     if (!game.magic_wall_active)
8029       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8030     Store[newx][newy] = Store[x][y];
8031   }
8032   else if (element == EL_MAGIC_WALL_EMPTYING)
8033   {
8034     Feld[x][y] = get_next_element(element);
8035     if (!game.magic_wall_active)
8036       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8037     element = Feld[newx][newy] = Store[x][y];
8038
8039     InitField(newx, newy, FALSE);
8040   }
8041   else if (element == EL_BD_MAGIC_WALL_FILLING)
8042   {
8043     element = Feld[newx][newy] = get_next_element(element);
8044     if (!game.magic_wall_active)
8045       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8046     Store[newx][newy] = Store[x][y];
8047   }
8048   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8049   {
8050     Feld[x][y] = get_next_element(element);
8051     if (!game.magic_wall_active)
8052       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8053     element = Feld[newx][newy] = Store[x][y];
8054
8055     InitField(newx, newy, FALSE);
8056   }
8057   else if (element == EL_DC_MAGIC_WALL_FILLING)
8058   {
8059     element = Feld[newx][newy] = get_next_element(element);
8060     if (!game.magic_wall_active)
8061       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8062     Store[newx][newy] = Store[x][y];
8063   }
8064   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8065   {
8066     Feld[x][y] = get_next_element(element);
8067     if (!game.magic_wall_active)
8068       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8069     element = Feld[newx][newy] = Store[x][y];
8070
8071     InitField(newx, newy, FALSE);
8072   }
8073   else if (element == EL_AMOEBA_DROPPING)
8074   {
8075     Feld[x][y] = get_next_element(element);
8076     element = Feld[newx][newy] = Store[x][y];
8077   }
8078   else if (element == EL_SOKOBAN_OBJECT)
8079   {
8080     if (Back[x][y])
8081       Feld[x][y] = Back[x][y];
8082
8083     if (Back[newx][newy])
8084       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8085
8086     Back[x][y] = Back[newx][newy] = 0;
8087   }
8088
8089   Store[x][y] = EL_EMPTY;
8090   MovPos[x][y] = 0;
8091   MovDir[x][y] = 0;
8092   MovDelay[x][y] = 0;
8093
8094   MovDelay[newx][newy] = 0;
8095
8096   if (CAN_CHANGE_OR_HAS_ACTION(element))
8097   {
8098     /* copy element change control values to new field */
8099     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8100     ChangePage[newx][newy]  = ChangePage[x][y];
8101     ChangeCount[newx][newy] = ChangeCount[x][y];
8102     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8103   }
8104
8105   CustomValue[newx][newy] = CustomValue[x][y];
8106
8107   ChangeDelay[x][y] = 0;
8108   ChangePage[x][y] = -1;
8109   ChangeCount[x][y] = 0;
8110   ChangeEvent[x][y] = -1;
8111
8112   CustomValue[x][y] = 0;
8113
8114   /* copy animation control values to new field */
8115   GfxFrame[newx][newy]  = GfxFrame[x][y];
8116   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8117   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8118   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8119
8120   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8121
8122   /* some elements can leave other elements behind after moving */
8123   if (ei->move_leave_element != EL_EMPTY &&
8124       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8125       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8126   {
8127     int move_leave_element = ei->move_leave_element;
8128
8129     /* this makes it possible to leave the removed element again */
8130     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8131       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8132
8133     Feld[x][y] = move_leave_element;
8134
8135     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8136       MovDir[x][y] = direction;
8137
8138     InitField(x, y, FALSE);
8139
8140     if (GFX_CRUMBLED(Feld[x][y]))
8141       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8142
8143     if (ELEM_IS_PLAYER(move_leave_element))
8144       RelocatePlayer(x, y, move_leave_element);
8145   }
8146
8147   /* do this after checking for left-behind element */
8148   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8149
8150   if (!CAN_MOVE(element) ||
8151       (CAN_FALL(element) && direction == MV_DOWN &&
8152        (element == EL_SPRING ||
8153         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8154         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8155     GfxDir[x][y] = MovDir[newx][newy] = 0;
8156
8157   TEST_DrawLevelField(x, y);
8158   TEST_DrawLevelField(newx, newy);
8159
8160   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8161
8162   /* prevent pushed element from moving on in pushed direction */
8163   if (pushed_by_player && CAN_MOVE(element) &&
8164       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8165       !(element_info[element].move_pattern & direction))
8166     TurnRound(newx, newy);
8167
8168   /* prevent elements on conveyor belt from moving on in last direction */
8169   if (pushed_by_conveyor && CAN_FALL(element) &&
8170       direction & MV_HORIZONTAL)
8171     MovDir[newx][newy] = 0;
8172
8173   if (!pushed_by_player)
8174   {
8175     int nextx = newx + dx, nexty = newy + dy;
8176     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8177
8178     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8179
8180     if (CAN_FALL(element) && direction == MV_DOWN)
8181       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8182
8183     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8184       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8185
8186     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8187       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8188   }
8189
8190   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8191   {
8192     TestIfBadThingTouchesPlayer(newx, newy);
8193     TestIfBadThingTouchesFriend(newx, newy);
8194
8195     if (!IS_CUSTOM_ELEMENT(element))
8196       TestIfBadThingTouchesOtherBadThing(newx, newy);
8197   }
8198   else if (element == EL_PENGUIN)
8199     TestIfFriendTouchesBadThing(newx, newy);
8200
8201   if (DONT_GET_HIT_BY(element))
8202   {
8203     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8204   }
8205
8206   /* give the player one last chance (one more frame) to move away */
8207   if (CAN_FALL(element) && direction == MV_DOWN &&
8208       (last_line || (!IS_FREE(x, newy + 1) &&
8209                      (!IS_PLAYER(x, newy + 1) ||
8210                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8211     Impact(x, newy);
8212
8213   if (pushed_by_player && !game.use_change_when_pushing_bug)
8214   {
8215     int push_side = MV_DIR_OPPOSITE(direction);
8216     struct PlayerInfo *player = PLAYERINFO(x, y);
8217
8218     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8219                                player->index_bit, push_side);
8220     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8221                                         player->index_bit, push_side);
8222   }
8223
8224   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8225     MovDelay[newx][newy] = 1;
8226
8227   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8228
8229   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8230   TestIfElementHitsCustomElement(newx, newy, direction);
8231   TestIfPlayerTouchesCustomElement(newx, newy);
8232   TestIfElementTouchesCustomElement(newx, newy);
8233
8234   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8235       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8236     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8237                              MV_DIR_OPPOSITE(direction));
8238 }
8239
8240 int AmoebeNachbarNr(int ax, int ay)
8241 {
8242   int i;
8243   int element = Feld[ax][ay];
8244   int group_nr = 0;
8245   static int xy[4][2] =
8246   {
8247     { 0, -1 },
8248     { -1, 0 },
8249     { +1, 0 },
8250     { 0, +1 }
8251   };
8252
8253   for (i = 0; i < NUM_DIRECTIONS; i++)
8254   {
8255     int x = ax + xy[i][0];
8256     int y = ay + xy[i][1];
8257
8258     if (!IN_LEV_FIELD(x, y))
8259       continue;
8260
8261     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8262       group_nr = AmoebaNr[x][y];
8263   }
8264
8265   return group_nr;
8266 }
8267
8268 void AmoebenVereinigen(int ax, int ay)
8269 {
8270   int i, x, y, xx, yy;
8271   int new_group_nr = AmoebaNr[ax][ay];
8272   static int xy[4][2] =
8273   {
8274     { 0, -1 },
8275     { -1, 0 },
8276     { +1, 0 },
8277     { 0, +1 }
8278   };
8279
8280   if (new_group_nr == 0)
8281     return;
8282
8283   for (i = 0; i < NUM_DIRECTIONS; i++)
8284   {
8285     x = ax + xy[i][0];
8286     y = ay + xy[i][1];
8287
8288     if (!IN_LEV_FIELD(x, y))
8289       continue;
8290
8291     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8292          Feld[x][y] == EL_BD_AMOEBA ||
8293          Feld[x][y] == EL_AMOEBA_DEAD) &&
8294         AmoebaNr[x][y] != new_group_nr)
8295     {
8296       int old_group_nr = AmoebaNr[x][y];
8297
8298       if (old_group_nr == 0)
8299         return;
8300
8301       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8302       AmoebaCnt[old_group_nr] = 0;
8303       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8304       AmoebaCnt2[old_group_nr] = 0;
8305
8306       SCAN_PLAYFIELD(xx, yy)
8307       {
8308         if (AmoebaNr[xx][yy] == old_group_nr)
8309           AmoebaNr[xx][yy] = new_group_nr;
8310       }
8311     }
8312   }
8313 }
8314
8315 void AmoebeUmwandeln(int ax, int ay)
8316 {
8317   int i, x, y;
8318
8319   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8320   {
8321     int group_nr = AmoebaNr[ax][ay];
8322
8323 #ifdef DEBUG
8324     if (group_nr == 0)
8325     {
8326       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8327       printf("AmoebeUmwandeln(): This should never happen!\n");
8328       return;
8329     }
8330 #endif
8331
8332     SCAN_PLAYFIELD(x, y)
8333     {
8334       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8335       {
8336         AmoebaNr[x][y] = 0;
8337         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8338       }
8339     }
8340
8341     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8342                             SND_AMOEBA_TURNING_TO_GEM :
8343                             SND_AMOEBA_TURNING_TO_ROCK));
8344     Bang(ax, ay);
8345   }
8346   else
8347   {
8348     static int xy[4][2] =
8349     {
8350       { 0, -1 },
8351       { -1, 0 },
8352       { +1, 0 },
8353       { 0, +1 }
8354     };
8355
8356     for (i = 0; i < NUM_DIRECTIONS; i++)
8357     {
8358       x = ax + xy[i][0];
8359       y = ay + xy[i][1];
8360
8361       if (!IN_LEV_FIELD(x, y))
8362         continue;
8363
8364       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8365       {
8366         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8367                               SND_AMOEBA_TURNING_TO_GEM :
8368                               SND_AMOEBA_TURNING_TO_ROCK));
8369         Bang(x, y);
8370       }
8371     }
8372   }
8373 }
8374
8375 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8376 {
8377   int x, y;
8378   int group_nr = AmoebaNr[ax][ay];
8379   boolean done = FALSE;
8380
8381 #ifdef DEBUG
8382   if (group_nr == 0)
8383   {
8384     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8385     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8386     return;
8387   }
8388 #endif
8389
8390   SCAN_PLAYFIELD(x, y)
8391   {
8392     if (AmoebaNr[x][y] == group_nr &&
8393         (Feld[x][y] == EL_AMOEBA_DEAD ||
8394          Feld[x][y] == EL_BD_AMOEBA ||
8395          Feld[x][y] == EL_AMOEBA_GROWING))
8396     {
8397       AmoebaNr[x][y] = 0;
8398       Feld[x][y] = new_element;
8399       InitField(x, y, FALSE);
8400       TEST_DrawLevelField(x, y);
8401       done = TRUE;
8402     }
8403   }
8404
8405   if (done)
8406     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8407                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8408                             SND_BD_AMOEBA_TURNING_TO_GEM));
8409 }
8410
8411 void AmoebeWaechst(int x, int y)
8412 {
8413   static unsigned int sound_delay = 0;
8414   static unsigned int sound_delay_value = 0;
8415
8416   if (!MovDelay[x][y])          /* start new growing cycle */
8417   {
8418     MovDelay[x][y] = 7;
8419
8420     if (DelayReached(&sound_delay, sound_delay_value))
8421     {
8422       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8423       sound_delay_value = 30;
8424     }
8425   }
8426
8427   if (MovDelay[x][y])           /* wait some time before growing bigger */
8428   {
8429     MovDelay[x][y]--;
8430     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8431     {
8432       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8433                                            6 - MovDelay[x][y]);
8434
8435       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8436     }
8437
8438     if (!MovDelay[x][y])
8439     {
8440       Feld[x][y] = Store[x][y];
8441       Store[x][y] = 0;
8442       TEST_DrawLevelField(x, y);
8443     }
8444   }
8445 }
8446
8447 void AmoebaDisappearing(int x, int y)
8448 {
8449   static unsigned int sound_delay = 0;
8450   static unsigned int sound_delay_value = 0;
8451
8452   if (!MovDelay[x][y])          /* start new shrinking cycle */
8453   {
8454     MovDelay[x][y] = 7;
8455
8456     if (DelayReached(&sound_delay, sound_delay_value))
8457       sound_delay_value = 30;
8458   }
8459
8460   if (MovDelay[x][y])           /* wait some time before shrinking */
8461   {
8462     MovDelay[x][y]--;
8463     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8464     {
8465       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8466                                            6 - MovDelay[x][y]);
8467
8468       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8469     }
8470
8471     if (!MovDelay[x][y])
8472     {
8473       Feld[x][y] = EL_EMPTY;
8474       TEST_DrawLevelField(x, y);
8475
8476       /* don't let mole enter this field in this cycle;
8477          (give priority to objects falling to this field from above) */
8478       Stop[x][y] = TRUE;
8479     }
8480   }
8481 }
8482
8483 void AmoebeAbleger(int ax, int ay)
8484 {
8485   int i;
8486   int element = Feld[ax][ay];
8487   int graphic = el2img(element);
8488   int newax = ax, neway = ay;
8489   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8490   static int xy[4][2] =
8491   {
8492     { 0, -1 },
8493     { -1, 0 },
8494     { +1, 0 },
8495     { 0, +1 }
8496   };
8497
8498   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8499   {
8500     Feld[ax][ay] = EL_AMOEBA_DEAD;
8501     TEST_DrawLevelField(ax, ay);
8502     return;
8503   }
8504
8505   if (IS_ANIMATED(graphic))
8506     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8507
8508   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8509     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8510
8511   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8512   {
8513     MovDelay[ax][ay]--;
8514     if (MovDelay[ax][ay])
8515       return;
8516   }
8517
8518   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8519   {
8520     int start = RND(4);
8521     int x = ax + xy[start][0];
8522     int y = ay + xy[start][1];
8523
8524     if (!IN_LEV_FIELD(x, y))
8525       return;
8526
8527     if (IS_FREE(x, y) ||
8528         CAN_GROW_INTO(Feld[x][y]) ||
8529         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8530         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8531     {
8532       newax = x;
8533       neway = y;
8534     }
8535
8536     if (newax == ax && neway == ay)
8537       return;
8538   }
8539   else                          /* normal or "filled" (BD style) amoeba */
8540   {
8541     int start = RND(4);
8542     boolean waiting_for_player = FALSE;
8543
8544     for (i = 0; i < NUM_DIRECTIONS; i++)
8545     {
8546       int j = (start + i) % 4;
8547       int x = ax + xy[j][0];
8548       int y = ay + xy[j][1];
8549
8550       if (!IN_LEV_FIELD(x, y))
8551         continue;
8552
8553       if (IS_FREE(x, y) ||
8554           CAN_GROW_INTO(Feld[x][y]) ||
8555           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8556           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8557       {
8558         newax = x;
8559         neway = y;
8560         break;
8561       }
8562       else if (IS_PLAYER(x, y))
8563         waiting_for_player = TRUE;
8564     }
8565
8566     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8567     {
8568       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8569       {
8570         Feld[ax][ay] = EL_AMOEBA_DEAD;
8571         TEST_DrawLevelField(ax, ay);
8572         AmoebaCnt[AmoebaNr[ax][ay]]--;
8573
8574         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8575         {
8576           if (element == EL_AMOEBA_FULL)
8577             AmoebeUmwandeln(ax, ay);
8578           else if (element == EL_BD_AMOEBA)
8579             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8580         }
8581       }
8582       return;
8583     }
8584     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8585     {
8586       /* amoeba gets larger by growing in some direction */
8587
8588       int new_group_nr = AmoebaNr[ax][ay];
8589
8590 #ifdef DEBUG
8591   if (new_group_nr == 0)
8592   {
8593     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8594     printf("AmoebeAbleger(): This should never happen!\n");
8595     return;
8596   }
8597 #endif
8598
8599       AmoebaNr[newax][neway] = new_group_nr;
8600       AmoebaCnt[new_group_nr]++;
8601       AmoebaCnt2[new_group_nr]++;
8602
8603       /* if amoeba touches other amoeba(s) after growing, unify them */
8604       AmoebenVereinigen(newax, neway);
8605
8606       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8607       {
8608         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8609         return;
8610       }
8611     }
8612   }
8613
8614   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8615       (neway == lev_fieldy - 1 && newax != ax))
8616   {
8617     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8618     Store[newax][neway] = element;
8619   }
8620   else if (neway == ay || element == EL_EMC_DRIPPER)
8621   {
8622     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8623
8624     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8625   }
8626   else
8627   {
8628     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8629     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8630     Store[ax][ay] = EL_AMOEBA_DROP;
8631     ContinueMoving(ax, ay);
8632     return;
8633   }
8634
8635   TEST_DrawLevelField(newax, neway);
8636 }
8637
8638 void Life(int ax, int ay)
8639 {
8640   int x1, y1, x2, y2;
8641   int life_time = 40;
8642   int element = Feld[ax][ay];
8643   int graphic = el2img(element);
8644   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8645                          level.biomaze);
8646   boolean changed = FALSE;
8647
8648   if (IS_ANIMATED(graphic))
8649     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8650
8651   if (Stop[ax][ay])
8652     return;
8653
8654   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8655     MovDelay[ax][ay] = life_time;
8656
8657   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8658   {
8659     MovDelay[ax][ay]--;
8660     if (MovDelay[ax][ay])
8661       return;
8662   }
8663
8664   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8665   {
8666     int xx = ax+x1, yy = ay+y1;
8667     int nachbarn = 0;
8668
8669     if (!IN_LEV_FIELD(xx, yy))
8670       continue;
8671
8672     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8673     {
8674       int x = xx+x2, y = yy+y2;
8675
8676       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8677         continue;
8678
8679       if (((Feld[x][y] == element ||
8680             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8681            !Stop[x][y]) ||
8682           (IS_FREE(x, y) && Stop[x][y]))
8683         nachbarn++;
8684     }
8685
8686     if (xx == ax && yy == ay)           /* field in the middle */
8687     {
8688       if (nachbarn < life_parameter[0] ||
8689           nachbarn > life_parameter[1])
8690       {
8691         Feld[xx][yy] = EL_EMPTY;
8692         if (!Stop[xx][yy])
8693           TEST_DrawLevelField(xx, yy);
8694         Stop[xx][yy] = TRUE;
8695         changed = TRUE;
8696       }
8697     }
8698     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8699     {                                   /* free border field */
8700       if (nachbarn >= life_parameter[2] &&
8701           nachbarn <= life_parameter[3])
8702       {
8703         Feld[xx][yy] = element;
8704         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8705         if (!Stop[xx][yy])
8706           TEST_DrawLevelField(xx, yy);
8707         Stop[xx][yy] = TRUE;
8708         changed = TRUE;
8709       }
8710     }
8711   }
8712
8713   if (changed)
8714     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8715                    SND_GAME_OF_LIFE_GROWING);
8716 }
8717
8718 static void InitRobotWheel(int x, int y)
8719 {
8720   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8721 }
8722
8723 static void RunRobotWheel(int x, int y)
8724 {
8725   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8726 }
8727
8728 static void StopRobotWheel(int x, int y)
8729 {
8730   if (ZX == x && ZY == y)
8731   {
8732     ZX = ZY = -1;
8733
8734     game.robot_wheel_active = FALSE;
8735   }
8736 }
8737
8738 static void InitTimegateWheel(int x, int y)
8739 {
8740   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8741 }
8742
8743 static void RunTimegateWheel(int x, int y)
8744 {
8745   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8746 }
8747
8748 static void InitMagicBallDelay(int x, int y)
8749 {
8750   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8751 }
8752
8753 static void ActivateMagicBall(int bx, int by)
8754 {
8755   int x, y;
8756
8757   if (level.ball_random)
8758   {
8759     int pos_border = RND(8);    /* select one of the eight border elements */
8760     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8761     int xx = pos_content % 3;
8762     int yy = pos_content / 3;
8763
8764     x = bx - 1 + xx;
8765     y = by - 1 + yy;
8766
8767     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8768       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8769   }
8770   else
8771   {
8772     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8773     {
8774       int xx = x - bx + 1;
8775       int yy = y - by + 1;
8776
8777       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8778         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8779     }
8780   }
8781
8782   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8783 }
8784
8785 void CheckExit(int x, int y)
8786 {
8787   if (local_player->gems_still_needed > 0 ||
8788       local_player->sokobanfields_still_needed > 0 ||
8789       local_player->lights_still_needed > 0)
8790   {
8791     int element = Feld[x][y];
8792     int graphic = el2img(element);
8793
8794     if (IS_ANIMATED(graphic))
8795       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8796
8797     return;
8798   }
8799
8800   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8801     return;
8802
8803   Feld[x][y] = EL_EXIT_OPENING;
8804
8805   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8806 }
8807
8808 void CheckExitEM(int x, int y)
8809 {
8810   if (local_player->gems_still_needed > 0 ||
8811       local_player->sokobanfields_still_needed > 0 ||
8812       local_player->lights_still_needed > 0)
8813   {
8814     int element = Feld[x][y];
8815     int graphic = el2img(element);
8816
8817     if (IS_ANIMATED(graphic))
8818       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8819
8820     return;
8821   }
8822
8823   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8824     return;
8825
8826   Feld[x][y] = EL_EM_EXIT_OPENING;
8827
8828   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8829 }
8830
8831 void CheckExitSteel(int x, int y)
8832 {
8833   if (local_player->gems_still_needed > 0 ||
8834       local_player->sokobanfields_still_needed > 0 ||
8835       local_player->lights_still_needed > 0)
8836   {
8837     int element = Feld[x][y];
8838     int graphic = el2img(element);
8839
8840     if (IS_ANIMATED(graphic))
8841       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8842
8843     return;
8844   }
8845
8846   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8847     return;
8848
8849   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8850
8851   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8852 }
8853
8854 void CheckExitSteelEM(int x, int y)
8855 {
8856   if (local_player->gems_still_needed > 0 ||
8857       local_player->sokobanfields_still_needed > 0 ||
8858       local_player->lights_still_needed > 0)
8859   {
8860     int element = Feld[x][y];
8861     int graphic = el2img(element);
8862
8863     if (IS_ANIMATED(graphic))
8864       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8865
8866     return;
8867   }
8868
8869   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8870     return;
8871
8872   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8873
8874   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8875 }
8876
8877 void CheckExitSP(int x, int y)
8878 {
8879   if (local_player->gems_still_needed > 0)
8880   {
8881     int element = Feld[x][y];
8882     int graphic = el2img(element);
8883
8884     if (IS_ANIMATED(graphic))
8885       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8886
8887     return;
8888   }
8889
8890   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8891     return;
8892
8893   Feld[x][y] = EL_SP_EXIT_OPENING;
8894
8895   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8896 }
8897
8898 static void CloseAllOpenTimegates()
8899 {
8900   int x, y;
8901
8902   SCAN_PLAYFIELD(x, y)
8903   {
8904     int element = Feld[x][y];
8905
8906     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8907     {
8908       Feld[x][y] = EL_TIMEGATE_CLOSING;
8909
8910       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8911     }
8912   }
8913 }
8914
8915 void DrawTwinkleOnField(int x, int y)
8916 {
8917   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8918     return;
8919
8920   if (Feld[x][y] == EL_BD_DIAMOND)
8921     return;
8922
8923   if (MovDelay[x][y] == 0)      /* next animation frame */
8924     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8925
8926   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8927   {
8928     MovDelay[x][y]--;
8929
8930     DrawLevelElementAnimation(x, y, Feld[x][y]);
8931
8932     if (MovDelay[x][y] != 0)
8933     {
8934       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8935                                            10 - MovDelay[x][y]);
8936
8937       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8938     }
8939   }
8940 }
8941
8942 void MauerWaechst(int x, int y)
8943 {
8944   int delay = 6;
8945
8946   if (!MovDelay[x][y])          /* next animation frame */
8947     MovDelay[x][y] = 3 * delay;
8948
8949   if (MovDelay[x][y])           /* wait some time before next frame */
8950   {
8951     MovDelay[x][y]--;
8952
8953     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8954     {
8955       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8956       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8957
8958       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8959     }
8960
8961     if (!MovDelay[x][y])
8962     {
8963       if (MovDir[x][y] == MV_LEFT)
8964       {
8965         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8966           TEST_DrawLevelField(x - 1, y);
8967       }
8968       else if (MovDir[x][y] == MV_RIGHT)
8969       {
8970         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8971           TEST_DrawLevelField(x + 1, y);
8972       }
8973       else if (MovDir[x][y] == MV_UP)
8974       {
8975         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8976           TEST_DrawLevelField(x, y - 1);
8977       }
8978       else
8979       {
8980         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8981           TEST_DrawLevelField(x, y + 1);
8982       }
8983
8984       Feld[x][y] = Store[x][y];
8985       Store[x][y] = 0;
8986       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8987       TEST_DrawLevelField(x, y);
8988     }
8989   }
8990 }
8991
8992 void MauerAbleger(int ax, int ay)
8993 {
8994   int element = Feld[ax][ay];
8995   int graphic = el2img(element);
8996   boolean oben_frei = FALSE, unten_frei = FALSE;
8997   boolean links_frei = FALSE, rechts_frei = FALSE;
8998   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8999   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9000   boolean new_wall = FALSE;
9001
9002   if (IS_ANIMATED(graphic))
9003     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9004
9005   if (!MovDelay[ax][ay])        /* start building new wall */
9006     MovDelay[ax][ay] = 6;
9007
9008   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9009   {
9010     MovDelay[ax][ay]--;
9011     if (MovDelay[ax][ay])
9012       return;
9013   }
9014
9015   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9016     oben_frei = TRUE;
9017   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9018     unten_frei = TRUE;
9019   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9020     links_frei = TRUE;
9021   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9022     rechts_frei = TRUE;
9023
9024   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9025       element == EL_EXPANDABLE_WALL_ANY)
9026   {
9027     if (oben_frei)
9028     {
9029       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9030       Store[ax][ay-1] = element;
9031       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9032       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9033         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9034                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9035       new_wall = TRUE;
9036     }
9037     if (unten_frei)
9038     {
9039       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9040       Store[ax][ay+1] = element;
9041       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9042       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9043         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9044                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9045       new_wall = TRUE;
9046     }
9047   }
9048
9049   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9050       element == EL_EXPANDABLE_WALL_ANY ||
9051       element == EL_EXPANDABLE_WALL ||
9052       element == EL_BD_EXPANDABLE_WALL)
9053   {
9054     if (links_frei)
9055     {
9056       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9057       Store[ax-1][ay] = element;
9058       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9059       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9060         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9061                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9062       new_wall = TRUE;
9063     }
9064
9065     if (rechts_frei)
9066     {
9067       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9068       Store[ax+1][ay] = element;
9069       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9070       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9071         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9072                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9073       new_wall = TRUE;
9074     }
9075   }
9076
9077   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9078     TEST_DrawLevelField(ax, ay);
9079
9080   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9081     oben_massiv = TRUE;
9082   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9083     unten_massiv = TRUE;
9084   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9085     links_massiv = TRUE;
9086   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9087     rechts_massiv = TRUE;
9088
9089   if (((oben_massiv && unten_massiv) ||
9090        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9091        element == EL_EXPANDABLE_WALL) &&
9092       ((links_massiv && rechts_massiv) ||
9093        element == EL_EXPANDABLE_WALL_VERTICAL))
9094     Feld[ax][ay] = EL_WALL;
9095
9096   if (new_wall)
9097     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9098 }
9099
9100 void MauerAblegerStahl(int ax, int ay)
9101 {
9102   int element = Feld[ax][ay];
9103   int graphic = el2img(element);
9104   boolean oben_frei = FALSE, unten_frei = FALSE;
9105   boolean links_frei = FALSE, rechts_frei = FALSE;
9106   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9107   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9108   boolean new_wall = FALSE;
9109
9110   if (IS_ANIMATED(graphic))
9111     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9112
9113   if (!MovDelay[ax][ay])        /* start building new wall */
9114     MovDelay[ax][ay] = 6;
9115
9116   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9117   {
9118     MovDelay[ax][ay]--;
9119     if (MovDelay[ax][ay])
9120       return;
9121   }
9122
9123   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9124     oben_frei = TRUE;
9125   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9126     unten_frei = TRUE;
9127   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9128     links_frei = TRUE;
9129   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9130     rechts_frei = TRUE;
9131
9132   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9133       element == EL_EXPANDABLE_STEELWALL_ANY)
9134   {
9135     if (oben_frei)
9136     {
9137       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9138       Store[ax][ay-1] = element;
9139       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9140       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9141         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9142                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9143       new_wall = TRUE;
9144     }
9145     if (unten_frei)
9146     {
9147       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9148       Store[ax][ay+1] = element;
9149       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9150       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9151         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9152                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9153       new_wall = TRUE;
9154     }
9155   }
9156
9157   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9158       element == EL_EXPANDABLE_STEELWALL_ANY)
9159   {
9160     if (links_frei)
9161     {
9162       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9163       Store[ax-1][ay] = element;
9164       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9165       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9166         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9167                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9168       new_wall = TRUE;
9169     }
9170
9171     if (rechts_frei)
9172     {
9173       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9174       Store[ax+1][ay] = element;
9175       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9176       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9177         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9178                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9179       new_wall = TRUE;
9180     }
9181   }
9182
9183   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9184     oben_massiv = TRUE;
9185   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9186     unten_massiv = TRUE;
9187   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9188     links_massiv = TRUE;
9189   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9190     rechts_massiv = TRUE;
9191
9192   if (((oben_massiv && unten_massiv) ||
9193        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9194       ((links_massiv && rechts_massiv) ||
9195        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9196     Feld[ax][ay] = EL_STEELWALL;
9197
9198   if (new_wall)
9199     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9200 }
9201
9202 void CheckForDragon(int x, int y)
9203 {
9204   int i, j;
9205   boolean dragon_found = FALSE;
9206   static int xy[4][2] =
9207   {
9208     { 0, -1 },
9209     { -1, 0 },
9210     { +1, 0 },
9211     { 0, +1 }
9212   };
9213
9214   for (i = 0; i < NUM_DIRECTIONS; i++)
9215   {
9216     for (j = 0; j < 4; j++)
9217     {
9218       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9219
9220       if (IN_LEV_FIELD(xx, yy) &&
9221           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9222       {
9223         if (Feld[xx][yy] == EL_DRAGON)
9224           dragon_found = TRUE;
9225       }
9226       else
9227         break;
9228     }
9229   }
9230
9231   if (!dragon_found)
9232   {
9233     for (i = 0; i < NUM_DIRECTIONS; i++)
9234     {
9235       for (j = 0; j < 3; j++)
9236       {
9237         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9238   
9239         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9240         {
9241           Feld[xx][yy] = EL_EMPTY;
9242           TEST_DrawLevelField(xx, yy);
9243         }
9244         else
9245           break;
9246       }
9247     }
9248   }
9249 }
9250
9251 static void InitBuggyBase(int x, int y)
9252 {
9253   int element = Feld[x][y];
9254   int activating_delay = FRAMES_PER_SECOND / 4;
9255
9256   ChangeDelay[x][y] =
9257     (element == EL_SP_BUGGY_BASE ?
9258      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9259      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9260      activating_delay :
9261      element == EL_SP_BUGGY_BASE_ACTIVE ?
9262      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9263 }
9264
9265 static void WarnBuggyBase(int x, int y)
9266 {
9267   int i;
9268   static int xy[4][2] =
9269   {
9270     { 0, -1 },
9271     { -1, 0 },
9272     { +1, 0 },
9273     { 0, +1 }
9274   };
9275
9276   for (i = 0; i < NUM_DIRECTIONS; i++)
9277   {
9278     int xx = x + xy[i][0];
9279     int yy = y + xy[i][1];
9280
9281     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9282     {
9283       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9284
9285       break;
9286     }
9287   }
9288 }
9289
9290 static void InitTrap(int x, int y)
9291 {
9292   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9293 }
9294
9295 static void ActivateTrap(int x, int y)
9296 {
9297   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9298 }
9299
9300 static void ChangeActiveTrap(int x, int y)
9301 {
9302   int graphic = IMG_TRAP_ACTIVE;
9303
9304   /* if new animation frame was drawn, correct crumbled sand border */
9305   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9306     TEST_DrawLevelFieldCrumbled(x, y);
9307 }
9308
9309 static int getSpecialActionElement(int element, int number, int base_element)
9310 {
9311   return (element != EL_EMPTY ? element :
9312           number != -1 ? base_element + number - 1 :
9313           EL_EMPTY);
9314 }
9315
9316 static int getModifiedActionNumber(int value_old, int operator, int operand,
9317                                    int value_min, int value_max)
9318 {
9319   int value_new = (operator == CA_MODE_SET      ? operand :
9320                    operator == CA_MODE_ADD      ? value_old + operand :
9321                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9322                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9323                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9324                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9325                    value_old);
9326
9327   return (value_new < value_min ? value_min :
9328           value_new > value_max ? value_max :
9329           value_new);
9330 }
9331
9332 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9333 {
9334   struct ElementInfo *ei = &element_info[element];
9335   struct ElementChangeInfo *change = &ei->change_page[page];
9336   int target_element = change->target_element;
9337   int action_type = change->action_type;
9338   int action_mode = change->action_mode;
9339   int action_arg = change->action_arg;
9340   int action_element = change->action_element;
9341   int i;
9342
9343   if (!change->has_action)
9344     return;
9345
9346   /* ---------- determine action paramater values -------------------------- */
9347
9348   int level_time_value =
9349     (level.time > 0 ? TimeLeft :
9350      TimePlayed);
9351
9352   int action_arg_element_raw =
9353     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9354      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9355      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9356      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9357      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9358      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9359      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9360      EL_EMPTY);
9361   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9362
9363   int action_arg_direction =
9364     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9365      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9366      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9367      change->actual_trigger_side :
9368      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9369      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9370      MV_NONE);
9371
9372   int action_arg_number_min =
9373     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9374      CA_ARG_MIN);
9375
9376   int action_arg_number_max =
9377     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9378      action_type == CA_SET_LEVEL_GEMS ? 999 :
9379      action_type == CA_SET_LEVEL_TIME ? 9999 :
9380      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9381      action_type == CA_SET_CE_VALUE ? 9999 :
9382      action_type == CA_SET_CE_SCORE ? 9999 :
9383      CA_ARG_MAX);
9384
9385   int action_arg_number_reset =
9386     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9387      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9388      action_type == CA_SET_LEVEL_TIME ? level.time :
9389      action_type == CA_SET_LEVEL_SCORE ? 0 :
9390      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9391      action_type == CA_SET_CE_SCORE ? 0 :
9392      0);
9393
9394   int action_arg_number =
9395     (action_arg <= CA_ARG_MAX ? action_arg :
9396      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9397      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9398      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9399      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9400      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9401      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9402      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9403      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9404      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9405      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9406      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9407      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9408      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9409      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9410      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9411      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9412      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9413      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9414      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9415      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9416      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9417      -1);
9418
9419   int action_arg_number_old =
9420     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9421      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9422      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9423      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9424      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9425      0);
9426
9427   int action_arg_number_new =
9428     getModifiedActionNumber(action_arg_number_old,
9429                             action_mode, action_arg_number,
9430                             action_arg_number_min, action_arg_number_max);
9431
9432   int trigger_player_bits =
9433     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9434      change->actual_trigger_player_bits : change->trigger_player);
9435
9436   int action_arg_player_bits =
9437     (action_arg >= CA_ARG_PLAYER_1 &&
9438      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9439      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9440      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9441      PLAYER_BITS_ANY);
9442
9443   /* ---------- execute action  -------------------------------------------- */
9444
9445   switch (action_type)
9446   {
9447     case CA_NO_ACTION:
9448     {
9449       return;
9450     }
9451
9452     /* ---------- level actions  ------------------------------------------- */
9453
9454     case CA_RESTART_LEVEL:
9455     {
9456       game.restart_level = TRUE;
9457
9458       break;
9459     }
9460
9461     case CA_SHOW_ENVELOPE:
9462     {
9463       int element = getSpecialActionElement(action_arg_element,
9464                                             action_arg_number, EL_ENVELOPE_1);
9465
9466       if (IS_ENVELOPE(element))
9467         local_player->show_envelope = element;
9468
9469       break;
9470     }
9471
9472     case CA_SET_LEVEL_TIME:
9473     {
9474       if (level.time > 0)       /* only modify limited time value */
9475       {
9476         TimeLeft = action_arg_number_new;
9477
9478         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9479
9480         DisplayGameControlValues();
9481
9482         if (!TimeLeft && setup.time_limit)
9483           for (i = 0; i < MAX_PLAYERS; i++)
9484             KillPlayer(&stored_player[i]);
9485       }
9486
9487       break;
9488     }
9489
9490     case CA_SET_LEVEL_SCORE:
9491     {
9492       local_player->score = action_arg_number_new;
9493
9494       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9495
9496       DisplayGameControlValues();
9497
9498       break;
9499     }
9500
9501     case CA_SET_LEVEL_GEMS:
9502     {
9503       local_player->gems_still_needed = action_arg_number_new;
9504
9505       game.snapshot.collected_item = TRUE;
9506
9507       game_panel_controls[GAME_PANEL_GEMS].value =
9508         local_player->gems_still_needed;
9509
9510       DisplayGameControlValues();
9511
9512       break;
9513     }
9514
9515     case CA_SET_LEVEL_WIND:
9516     {
9517       game.wind_direction = action_arg_direction;
9518
9519       break;
9520     }
9521
9522     case CA_SET_LEVEL_RANDOM_SEED:
9523     {
9524       /* ensure that setting a new random seed while playing is predictable */
9525       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9526
9527       break;
9528     }
9529
9530     /* ---------- player actions  ------------------------------------------ */
9531
9532     case CA_MOVE_PLAYER:
9533     {
9534       /* automatically move to the next field in specified direction */
9535       for (i = 0; i < MAX_PLAYERS; i++)
9536         if (trigger_player_bits & (1 << i))
9537           stored_player[i].programmed_action = action_arg_direction;
9538
9539       break;
9540     }
9541
9542     case CA_EXIT_PLAYER:
9543     {
9544       for (i = 0; i < MAX_PLAYERS; i++)
9545         if (action_arg_player_bits & (1 << i))
9546           PlayerWins(&stored_player[i]);
9547
9548       break;
9549     }
9550
9551     case CA_KILL_PLAYER:
9552     {
9553       for (i = 0; i < MAX_PLAYERS; i++)
9554         if (action_arg_player_bits & (1 << i))
9555           KillPlayer(&stored_player[i]);
9556
9557       break;
9558     }
9559
9560     case CA_SET_PLAYER_KEYS:
9561     {
9562       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9563       int element = getSpecialActionElement(action_arg_element,
9564                                             action_arg_number, EL_KEY_1);
9565
9566       if (IS_KEY(element))
9567       {
9568         for (i = 0; i < MAX_PLAYERS; i++)
9569         {
9570           if (trigger_player_bits & (1 << i))
9571           {
9572             stored_player[i].key[KEY_NR(element)] = key_state;
9573
9574             DrawGameDoorValues();
9575           }
9576         }
9577       }
9578
9579       break;
9580     }
9581
9582     case CA_SET_PLAYER_SPEED:
9583     {
9584       for (i = 0; i < MAX_PLAYERS; i++)
9585       {
9586         if (trigger_player_bits & (1 << i))
9587         {
9588           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9589
9590           if (action_arg == CA_ARG_SPEED_FASTER &&
9591               stored_player[i].cannot_move)
9592           {
9593             action_arg_number = STEPSIZE_VERY_SLOW;
9594           }
9595           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9596                    action_arg == CA_ARG_SPEED_FASTER)
9597           {
9598             action_arg_number = 2;
9599             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9600                            CA_MODE_MULTIPLY);
9601           }
9602           else if (action_arg == CA_ARG_NUMBER_RESET)
9603           {
9604             action_arg_number = level.initial_player_stepsize[i];
9605           }
9606
9607           move_stepsize =
9608             getModifiedActionNumber(move_stepsize,
9609                                     action_mode,
9610                                     action_arg_number,
9611                                     action_arg_number_min,
9612                                     action_arg_number_max);
9613
9614           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9615         }
9616       }
9617
9618       break;
9619     }
9620
9621     case CA_SET_PLAYER_SHIELD:
9622     {
9623       for (i = 0; i < MAX_PLAYERS; i++)
9624       {
9625         if (trigger_player_bits & (1 << i))
9626         {
9627           if (action_arg == CA_ARG_SHIELD_OFF)
9628           {
9629             stored_player[i].shield_normal_time_left = 0;
9630             stored_player[i].shield_deadly_time_left = 0;
9631           }
9632           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9633           {
9634             stored_player[i].shield_normal_time_left = 999999;
9635           }
9636           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9637           {
9638             stored_player[i].shield_normal_time_left = 999999;
9639             stored_player[i].shield_deadly_time_left = 999999;
9640           }
9641         }
9642       }
9643
9644       break;
9645     }
9646
9647     case CA_SET_PLAYER_GRAVITY:
9648     {
9649       for (i = 0; i < MAX_PLAYERS; i++)
9650       {
9651         if (trigger_player_bits & (1 << i))
9652         {
9653           stored_player[i].gravity =
9654             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9655              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9656              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9657              stored_player[i].gravity);
9658         }
9659       }
9660
9661       break;
9662     }
9663
9664     case CA_SET_PLAYER_ARTWORK:
9665     {
9666       for (i = 0; i < MAX_PLAYERS; i++)
9667       {
9668         if (trigger_player_bits & (1 << i))
9669         {
9670           int artwork_element = action_arg_element;
9671
9672           if (action_arg == CA_ARG_ELEMENT_RESET)
9673             artwork_element =
9674               (level.use_artwork_element[i] ? level.artwork_element[i] :
9675                stored_player[i].element_nr);
9676
9677           if (stored_player[i].artwork_element != artwork_element)
9678             stored_player[i].Frame = 0;
9679
9680           stored_player[i].artwork_element = artwork_element;
9681
9682           SetPlayerWaiting(&stored_player[i], FALSE);
9683
9684           /* set number of special actions for bored and sleeping animation */
9685           stored_player[i].num_special_action_bored =
9686             get_num_special_action(artwork_element,
9687                                    ACTION_BORING_1, ACTION_BORING_LAST);
9688           stored_player[i].num_special_action_sleeping =
9689             get_num_special_action(artwork_element,
9690                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9691         }
9692       }
9693
9694       break;
9695     }
9696
9697     case CA_SET_PLAYER_INVENTORY:
9698     {
9699       for (i = 0; i < MAX_PLAYERS; i++)
9700       {
9701         struct PlayerInfo *player = &stored_player[i];
9702         int j, k;
9703
9704         if (trigger_player_bits & (1 << i))
9705         {
9706           int inventory_element = action_arg_element;
9707
9708           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9709               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9710               action_arg == CA_ARG_ELEMENT_ACTION)
9711           {
9712             int element = inventory_element;
9713             int collect_count = element_info[element].collect_count_initial;
9714
9715             if (!IS_CUSTOM_ELEMENT(element))
9716               collect_count = 1;
9717
9718             if (collect_count == 0)
9719               player->inventory_infinite_element = element;
9720             else
9721               for (k = 0; k < collect_count; k++)
9722                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9723                   player->inventory_element[player->inventory_size++] =
9724                     element;
9725           }
9726           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9727                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9728                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9729           {
9730             if (player->inventory_infinite_element != EL_UNDEFINED &&
9731                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9732                                      action_arg_element_raw))
9733               player->inventory_infinite_element = EL_UNDEFINED;
9734
9735             for (k = 0, j = 0; j < player->inventory_size; j++)
9736             {
9737               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9738                                         action_arg_element_raw))
9739                 player->inventory_element[k++] = player->inventory_element[j];
9740             }
9741
9742             player->inventory_size = k;
9743           }
9744           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9745           {
9746             if (player->inventory_size > 0)
9747             {
9748               for (j = 0; j < player->inventory_size - 1; j++)
9749                 player->inventory_element[j] = player->inventory_element[j + 1];
9750
9751               player->inventory_size--;
9752             }
9753           }
9754           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9755           {
9756             if (player->inventory_size > 0)
9757               player->inventory_size--;
9758           }
9759           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9760           {
9761             player->inventory_infinite_element = EL_UNDEFINED;
9762             player->inventory_size = 0;
9763           }
9764           else if (action_arg == CA_ARG_INVENTORY_RESET)
9765           {
9766             player->inventory_infinite_element = EL_UNDEFINED;
9767             player->inventory_size = 0;
9768
9769             if (level.use_initial_inventory[i])
9770             {
9771               for (j = 0; j < level.initial_inventory_size[i]; j++)
9772               {
9773                 int element = level.initial_inventory_content[i][j];
9774                 int collect_count = element_info[element].collect_count_initial;
9775
9776                 if (!IS_CUSTOM_ELEMENT(element))
9777                   collect_count = 1;
9778
9779                 if (collect_count == 0)
9780                   player->inventory_infinite_element = element;
9781                 else
9782                   for (k = 0; k < collect_count; k++)
9783                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9784                       player->inventory_element[player->inventory_size++] =
9785                         element;
9786               }
9787             }
9788           }
9789         }
9790       }
9791
9792       break;
9793     }
9794
9795     /* ---------- CE actions  ---------------------------------------------- */
9796
9797     case CA_SET_CE_VALUE:
9798     {
9799       int last_ce_value = CustomValue[x][y];
9800
9801       CustomValue[x][y] = action_arg_number_new;
9802
9803       if (CustomValue[x][y] != last_ce_value)
9804       {
9805         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9806         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9807
9808         if (CustomValue[x][y] == 0)
9809         {
9810           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9811           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9812         }
9813       }
9814
9815       break;
9816     }
9817
9818     case CA_SET_CE_SCORE:
9819     {
9820       int last_ce_score = ei->collect_score;
9821
9822       ei->collect_score = action_arg_number_new;
9823
9824       if (ei->collect_score != last_ce_score)
9825       {
9826         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9827         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9828
9829         if (ei->collect_score == 0)
9830         {
9831           int xx, yy;
9832
9833           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9834           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9835
9836           /*
9837             This is a very special case that seems to be a mixture between
9838             CheckElementChange() and CheckTriggeredElementChange(): while
9839             the first one only affects single elements that are triggered
9840             directly, the second one affects multiple elements in the playfield
9841             that are triggered indirectly by another element. This is a third
9842             case: Changing the CE score always affects multiple identical CEs,
9843             so every affected CE must be checked, not only the single CE for
9844             which the CE score was changed in the first place (as every instance
9845             of that CE shares the same CE score, and therefore also can change)!
9846           */
9847           SCAN_PLAYFIELD(xx, yy)
9848           {
9849             if (Feld[xx][yy] == element)
9850               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9851                                  CE_SCORE_GETS_ZERO);
9852           }
9853         }
9854       }
9855
9856       break;
9857     }
9858
9859     case CA_SET_CE_ARTWORK:
9860     {
9861       int artwork_element = action_arg_element;
9862       boolean reset_frame = FALSE;
9863       int xx, yy;
9864
9865       if (action_arg == CA_ARG_ELEMENT_RESET)
9866         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9867                            element);
9868
9869       if (ei->gfx_element != artwork_element)
9870         reset_frame = TRUE;
9871
9872       ei->gfx_element = artwork_element;
9873
9874       SCAN_PLAYFIELD(xx, yy)
9875       {
9876         if (Feld[xx][yy] == element)
9877         {
9878           if (reset_frame)
9879           {
9880             ResetGfxAnimation(xx, yy);
9881             ResetRandomAnimationValue(xx, yy);
9882           }
9883
9884           TEST_DrawLevelField(xx, yy);
9885         }
9886       }
9887
9888       break;
9889     }
9890
9891     /* ---------- engine actions  ------------------------------------------ */
9892
9893     case CA_SET_ENGINE_SCAN_MODE:
9894     {
9895       InitPlayfieldScanMode(action_arg);
9896
9897       break;
9898     }
9899
9900     default:
9901       break;
9902   }
9903 }
9904
9905 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9906 {
9907   int old_element = Feld[x][y];
9908   int new_element = GetElementFromGroupElement(element);
9909   int previous_move_direction = MovDir[x][y];
9910   int last_ce_value = CustomValue[x][y];
9911   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9912   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9913   boolean add_player_onto_element = (new_element_is_player &&
9914                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9915                                      IS_WALKABLE(old_element));
9916
9917   if (!add_player_onto_element)
9918   {
9919     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9920       RemoveMovingField(x, y);
9921     else
9922       RemoveField(x, y);
9923
9924     Feld[x][y] = new_element;
9925
9926     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9927       MovDir[x][y] = previous_move_direction;
9928
9929     if (element_info[new_element].use_last_ce_value)
9930       CustomValue[x][y] = last_ce_value;
9931
9932     InitField_WithBug1(x, y, FALSE);
9933
9934     new_element = Feld[x][y];   /* element may have changed */
9935
9936     ResetGfxAnimation(x, y);
9937     ResetRandomAnimationValue(x, y);
9938
9939     TEST_DrawLevelField(x, y);
9940
9941     if (GFX_CRUMBLED(new_element))
9942       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9943   }
9944
9945   /* check if element under the player changes from accessible to unaccessible
9946      (needed for special case of dropping element which then changes) */
9947   /* (must be checked after creating new element for walkable group elements) */
9948   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9949       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9950   {
9951     Bang(x, y);
9952
9953     return;
9954   }
9955
9956   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9957   if (new_element_is_player)
9958     RelocatePlayer(x, y, new_element);
9959
9960   if (is_change)
9961     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9962
9963   TestIfBadThingTouchesPlayer(x, y);
9964   TestIfPlayerTouchesCustomElement(x, y);
9965   TestIfElementTouchesCustomElement(x, y);
9966 }
9967
9968 static void CreateField(int x, int y, int element)
9969 {
9970   CreateFieldExt(x, y, element, FALSE);
9971 }
9972
9973 static void CreateElementFromChange(int x, int y, int element)
9974 {
9975   element = GET_VALID_RUNTIME_ELEMENT(element);
9976
9977   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9978   {
9979     int old_element = Feld[x][y];
9980
9981     /* prevent changed element from moving in same engine frame
9982        unless both old and new element can either fall or move */
9983     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9984         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9985       Stop[x][y] = TRUE;
9986   }
9987
9988   CreateFieldExt(x, y, element, TRUE);
9989 }
9990
9991 static boolean ChangeElement(int x, int y, int element, int page)
9992 {
9993   struct ElementInfo *ei = &element_info[element];
9994   struct ElementChangeInfo *change = &ei->change_page[page];
9995   int ce_value = CustomValue[x][y];
9996   int ce_score = ei->collect_score;
9997   int target_element;
9998   int old_element = Feld[x][y];
9999
10000   /* always use default change event to prevent running into a loop */
10001   if (ChangeEvent[x][y] == -1)
10002     ChangeEvent[x][y] = CE_DELAY;
10003
10004   if (ChangeEvent[x][y] == CE_DELAY)
10005   {
10006     /* reset actual trigger element, trigger player and action element */
10007     change->actual_trigger_element = EL_EMPTY;
10008     change->actual_trigger_player = EL_EMPTY;
10009     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10010     change->actual_trigger_side = CH_SIDE_NONE;
10011     change->actual_trigger_ce_value = 0;
10012     change->actual_trigger_ce_score = 0;
10013   }
10014
10015   /* do not change elements more than a specified maximum number of changes */
10016   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10017     return FALSE;
10018
10019   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10020
10021   if (change->explode)
10022   {
10023     Bang(x, y);
10024
10025     return TRUE;
10026   }
10027
10028   if (change->use_target_content)
10029   {
10030     boolean complete_replace = TRUE;
10031     boolean can_replace[3][3];
10032     int xx, yy;
10033
10034     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10035     {
10036       boolean is_empty;
10037       boolean is_walkable;
10038       boolean is_diggable;
10039       boolean is_collectible;
10040       boolean is_removable;
10041       boolean is_destructible;
10042       int ex = x + xx - 1;
10043       int ey = y + yy - 1;
10044       int content_element = change->target_content.e[xx][yy];
10045       int e;
10046
10047       can_replace[xx][yy] = TRUE;
10048
10049       if (ex == x && ey == y)   /* do not check changing element itself */
10050         continue;
10051
10052       if (content_element == EL_EMPTY_SPACE)
10053       {
10054         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10055
10056         continue;
10057       }
10058
10059       if (!IN_LEV_FIELD(ex, ey))
10060       {
10061         can_replace[xx][yy] = FALSE;
10062         complete_replace = FALSE;
10063
10064         continue;
10065       }
10066
10067       e = Feld[ex][ey];
10068
10069       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10070         e = MovingOrBlocked2Element(ex, ey);
10071
10072       is_empty = (IS_FREE(ex, ey) ||
10073                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10074
10075       is_walkable     = (is_empty || IS_WALKABLE(e));
10076       is_diggable     = (is_empty || IS_DIGGABLE(e));
10077       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10078       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10079       is_removable    = (is_diggable || is_collectible);
10080
10081       can_replace[xx][yy] =
10082         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10083           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10084           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10085           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10086           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10087           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10088          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10089
10090       if (!can_replace[xx][yy])
10091         complete_replace = FALSE;
10092     }
10093
10094     if (!change->only_if_complete || complete_replace)
10095     {
10096       boolean something_has_changed = FALSE;
10097
10098       if (change->only_if_complete && change->use_random_replace &&
10099           RND(100) < change->random_percentage)
10100         return FALSE;
10101
10102       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10103       {
10104         int ex = x + xx - 1;
10105         int ey = y + yy - 1;
10106         int content_element;
10107
10108         if (can_replace[xx][yy] && (!change->use_random_replace ||
10109                                     RND(100) < change->random_percentage))
10110         {
10111           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10112             RemoveMovingField(ex, ey);
10113
10114           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10115
10116           content_element = change->target_content.e[xx][yy];
10117           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10118                                               ce_value, ce_score);
10119
10120           CreateElementFromChange(ex, ey, target_element);
10121
10122           something_has_changed = TRUE;
10123
10124           /* for symmetry reasons, freeze newly created border elements */
10125           if (ex != x || ey != y)
10126             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10127         }
10128       }
10129
10130       if (something_has_changed)
10131       {
10132         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10133         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10134       }
10135     }
10136   }
10137   else
10138   {
10139     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10140                                         ce_value, ce_score);
10141
10142     if (element == EL_DIAGONAL_GROWING ||
10143         element == EL_DIAGONAL_SHRINKING)
10144     {
10145       target_element = Store[x][y];
10146
10147       Store[x][y] = EL_EMPTY;
10148     }
10149
10150     CreateElementFromChange(x, y, target_element);
10151
10152     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10153     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10154   }
10155
10156   /* this uses direct change before indirect change */
10157   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10158
10159   return TRUE;
10160 }
10161
10162 static void HandleElementChange(int x, int y, int page)
10163 {
10164   int element = MovingOrBlocked2Element(x, y);
10165   struct ElementInfo *ei = &element_info[element];
10166   struct ElementChangeInfo *change = &ei->change_page[page];
10167   boolean handle_action_before_change = FALSE;
10168
10169 #ifdef DEBUG
10170   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10171       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10172   {
10173     printf("\n\n");
10174     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10175            x, y, element, element_info[element].token_name);
10176     printf("HandleElementChange(): This should never happen!\n");
10177     printf("\n\n");
10178   }
10179 #endif
10180
10181   /* this can happen with classic bombs on walkable, changing elements */
10182   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10183   {
10184     return;
10185   }
10186
10187   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10188   {
10189     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10190
10191     if (change->can_change)
10192     {
10193       /* !!! not clear why graphic animation should be reset at all here !!! */
10194       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10195       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10196
10197       /*
10198         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10199
10200         When using an animation frame delay of 1 (this only happens with
10201         "sp_zonk.moving.left/right" in the classic graphics), the default
10202         (non-moving) animation shows wrong animation frames (while the
10203         moving animation, like "sp_zonk.moving.left/right", is correct,
10204         so this graphical bug never shows up with the classic graphics).
10205         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10206         be drawn instead of the correct frames 0,1,2,3. This is caused by
10207         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10208         an element change: First when the change delay ("ChangeDelay[][]")
10209         counter has reached zero after decrementing, then a second time in
10210         the next frame (after "GfxFrame[][]" was already incremented) when
10211         "ChangeDelay[][]" is reset to the initial delay value again.
10212
10213         This causes frame 0 to be drawn twice, while the last frame won't
10214         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10215
10216         As some animations may already be cleverly designed around this bug
10217         (at least the "Snake Bite" snake tail animation does this), it cannot
10218         simply be fixed here without breaking such existing animations.
10219         Unfortunately, it cannot easily be detected if a graphics set was
10220         designed "before" or "after" the bug was fixed. As a workaround,
10221         a new graphics set option "game.graphics_engine_version" was added
10222         to be able to specify the game's major release version for which the
10223         graphics set was designed, which can then be used to decide if the
10224         bugfix should be used (version 4 and above) or not (version 3 or
10225         below, or if no version was specified at all, as with old sets).
10226
10227         (The wrong/fixed animation frames can be tested with the test level set
10228         "test_gfxframe" and level "000", which contains a specially prepared
10229         custom element at level position (x/y) == (11/9) which uses the zonk
10230         animation mentioned above. Using "game.graphics_engine_version: 4"
10231         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10232         This can also be seen from the debug output for this test element.)
10233       */
10234
10235       /* when a custom element is about to change (for example by change delay),
10236          do not reset graphic animation when the custom element is moving */
10237       if (game.graphics_engine_version < 4 &&
10238           !IS_MOVING(x, y))
10239       {
10240         ResetGfxAnimation(x, y);
10241         ResetRandomAnimationValue(x, y);
10242       }
10243
10244       if (change->pre_change_function)
10245         change->pre_change_function(x, y);
10246     }
10247   }
10248
10249   ChangeDelay[x][y]--;
10250
10251   if (ChangeDelay[x][y] != 0)           /* continue element change */
10252   {
10253     if (change->can_change)
10254     {
10255       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10256
10257       if (IS_ANIMATED(graphic))
10258         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10259
10260       if (change->change_function)
10261         change->change_function(x, y);
10262     }
10263   }
10264   else                                  /* finish element change */
10265   {
10266     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10267     {
10268       page = ChangePage[x][y];
10269       ChangePage[x][y] = -1;
10270
10271       change = &ei->change_page[page];
10272     }
10273
10274     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10275     {
10276       ChangeDelay[x][y] = 1;            /* try change after next move step */
10277       ChangePage[x][y] = page;          /* remember page to use for change */
10278
10279       return;
10280     }
10281
10282     /* special case: set new level random seed before changing element */
10283     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10284       handle_action_before_change = TRUE;
10285
10286     if (change->has_action && handle_action_before_change)
10287       ExecuteCustomElementAction(x, y, element, page);
10288
10289     if (change->can_change)
10290     {
10291       if (ChangeElement(x, y, element, page))
10292       {
10293         if (change->post_change_function)
10294           change->post_change_function(x, y);
10295       }
10296     }
10297
10298     if (change->has_action && !handle_action_before_change)
10299       ExecuteCustomElementAction(x, y, element, page);
10300   }
10301 }
10302
10303 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10304                                               int trigger_element,
10305                                               int trigger_event,
10306                                               int trigger_player,
10307                                               int trigger_side,
10308                                               int trigger_page)
10309 {
10310   boolean change_done_any = FALSE;
10311   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10312   int i;
10313
10314   if (!(trigger_events[trigger_element][trigger_event]))
10315     return FALSE;
10316
10317   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10318
10319   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10320   {
10321     int element = EL_CUSTOM_START + i;
10322     boolean change_done = FALSE;
10323     int p;
10324
10325     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10326         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10327       continue;
10328
10329     for (p = 0; p < element_info[element].num_change_pages; p++)
10330     {
10331       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10332
10333       if (change->can_change_or_has_action &&
10334           change->has_event[trigger_event] &&
10335           change->trigger_side & trigger_side &&
10336           change->trigger_player & trigger_player &&
10337           change->trigger_page & trigger_page_bits &&
10338           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10339       {
10340         change->actual_trigger_element = trigger_element;
10341         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10342         change->actual_trigger_player_bits = trigger_player;
10343         change->actual_trigger_side = trigger_side;
10344         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10345         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10346
10347         if ((change->can_change && !change_done) || change->has_action)
10348         {
10349           int x, y;
10350
10351           SCAN_PLAYFIELD(x, y)
10352           {
10353             if (Feld[x][y] == element)
10354             {
10355               if (change->can_change && !change_done)
10356               {
10357                 /* if element already changed in this frame, not only prevent
10358                    another element change (checked in ChangeElement()), but
10359                    also prevent additional element actions for this element */
10360
10361                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10362                     !level.use_action_after_change_bug)
10363                   continue;
10364
10365                 ChangeDelay[x][y] = 1;
10366                 ChangeEvent[x][y] = trigger_event;
10367
10368                 HandleElementChange(x, y, p);
10369               }
10370               else if (change->has_action)
10371               {
10372                 /* if element already changed in this frame, not only prevent
10373                    another element change (checked in ChangeElement()), but
10374                    also prevent additional element actions for this element */
10375
10376                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10377                     !level.use_action_after_change_bug)
10378                   continue;
10379
10380                 ExecuteCustomElementAction(x, y, element, p);
10381                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10382               }
10383             }
10384           }
10385
10386           if (change->can_change)
10387           {
10388             change_done = TRUE;
10389             change_done_any = TRUE;
10390           }
10391         }
10392       }
10393     }
10394   }
10395
10396   RECURSION_LOOP_DETECTION_END();
10397
10398   return change_done_any;
10399 }
10400
10401 static boolean CheckElementChangeExt(int x, int y,
10402                                      int element,
10403                                      int trigger_element,
10404                                      int trigger_event,
10405                                      int trigger_player,
10406                                      int trigger_side)
10407 {
10408   boolean change_done = FALSE;
10409   int p;
10410
10411   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10412       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10413     return FALSE;
10414
10415   if (Feld[x][y] == EL_BLOCKED)
10416   {
10417     Blocked2Moving(x, y, &x, &y);
10418     element = Feld[x][y];
10419   }
10420
10421   /* check if element has already changed or is about to change after moving */
10422   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10423        Feld[x][y] != element) ||
10424
10425       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10426        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10427         ChangePage[x][y] != -1)))
10428     return FALSE;
10429
10430   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10431
10432   for (p = 0; p < element_info[element].num_change_pages; p++)
10433   {
10434     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10435
10436     /* check trigger element for all events where the element that is checked
10437        for changing interacts with a directly adjacent element -- this is
10438        different to element changes that affect other elements to change on the
10439        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10440     boolean check_trigger_element =
10441       (trigger_event == CE_TOUCHING_X ||
10442        trigger_event == CE_HITTING_X ||
10443        trigger_event == CE_HIT_BY_X ||
10444        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10445
10446     if (change->can_change_or_has_action &&
10447         change->has_event[trigger_event] &&
10448         change->trigger_side & trigger_side &&
10449         change->trigger_player & trigger_player &&
10450         (!check_trigger_element ||
10451          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10452     {
10453       change->actual_trigger_element = trigger_element;
10454       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10455       change->actual_trigger_player_bits = trigger_player;
10456       change->actual_trigger_side = trigger_side;
10457       change->actual_trigger_ce_value = CustomValue[x][y];
10458       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10459
10460       /* special case: trigger element not at (x,y) position for some events */
10461       if (check_trigger_element)
10462       {
10463         static struct
10464         {
10465           int dx, dy;
10466         } move_xy[] =
10467           {
10468             {  0,  0 },
10469             { -1,  0 },
10470             { +1,  0 },
10471             {  0,  0 },
10472             {  0, -1 },
10473             {  0,  0 }, { 0, 0 }, { 0, 0 },
10474             {  0, +1 }
10475           };
10476
10477         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10478         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10479
10480         change->actual_trigger_ce_value = CustomValue[xx][yy];
10481         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10482       }
10483
10484       if (change->can_change && !change_done)
10485       {
10486         ChangeDelay[x][y] = 1;
10487         ChangeEvent[x][y] = trigger_event;
10488
10489         HandleElementChange(x, y, p);
10490
10491         change_done = TRUE;
10492       }
10493       else if (change->has_action)
10494       {
10495         ExecuteCustomElementAction(x, y, element, p);
10496         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10497       }
10498     }
10499   }
10500
10501   RECURSION_LOOP_DETECTION_END();
10502
10503   return change_done;
10504 }
10505
10506 static void PlayPlayerSound(struct PlayerInfo *player)
10507 {
10508   int jx = player->jx, jy = player->jy;
10509   int sound_element = player->artwork_element;
10510   int last_action = player->last_action_waiting;
10511   int action = player->action_waiting;
10512
10513   if (player->is_waiting)
10514   {
10515     if (action != last_action)
10516       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10517     else
10518       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10519   }
10520   else
10521   {
10522     if (action != last_action)
10523       StopSound(element_info[sound_element].sound[last_action]);
10524
10525     if (last_action == ACTION_SLEEPING)
10526       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10527   }
10528 }
10529
10530 static void PlayAllPlayersSound()
10531 {
10532   int i;
10533
10534   for (i = 0; i < MAX_PLAYERS; i++)
10535     if (stored_player[i].active)
10536       PlayPlayerSound(&stored_player[i]);
10537 }
10538
10539 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10540 {
10541   boolean last_waiting = player->is_waiting;
10542   int move_dir = player->MovDir;
10543
10544   player->dir_waiting = move_dir;
10545   player->last_action_waiting = player->action_waiting;
10546
10547   if (is_waiting)
10548   {
10549     if (!last_waiting)          /* not waiting -> waiting */
10550     {
10551       player->is_waiting = TRUE;
10552
10553       player->frame_counter_bored =
10554         FrameCounter +
10555         game.player_boring_delay_fixed +
10556         GetSimpleRandom(game.player_boring_delay_random);
10557       player->frame_counter_sleeping =
10558         FrameCounter +
10559         game.player_sleeping_delay_fixed +
10560         GetSimpleRandom(game.player_sleeping_delay_random);
10561
10562       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10563     }
10564
10565     if (game.player_sleeping_delay_fixed +
10566         game.player_sleeping_delay_random > 0 &&
10567         player->anim_delay_counter == 0 &&
10568         player->post_delay_counter == 0 &&
10569         FrameCounter >= player->frame_counter_sleeping)
10570       player->is_sleeping = TRUE;
10571     else if (game.player_boring_delay_fixed +
10572              game.player_boring_delay_random > 0 &&
10573              FrameCounter >= player->frame_counter_bored)
10574       player->is_bored = TRUE;
10575
10576     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10577                               player->is_bored ? ACTION_BORING :
10578                               ACTION_WAITING);
10579
10580     if (player->is_sleeping && player->use_murphy)
10581     {
10582       /* special case for sleeping Murphy when leaning against non-free tile */
10583
10584       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10585           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10586            !IS_MOVING(player->jx - 1, player->jy)))
10587         move_dir = MV_LEFT;
10588       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10589                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10590                 !IS_MOVING(player->jx + 1, player->jy)))
10591         move_dir = MV_RIGHT;
10592       else
10593         player->is_sleeping = FALSE;
10594
10595       player->dir_waiting = move_dir;
10596     }
10597
10598     if (player->is_sleeping)
10599     {
10600       if (player->num_special_action_sleeping > 0)
10601       {
10602         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10603         {
10604           int last_special_action = player->special_action_sleeping;
10605           int num_special_action = player->num_special_action_sleeping;
10606           int special_action =
10607             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10608              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10609              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10610              last_special_action + 1 : ACTION_SLEEPING);
10611           int special_graphic =
10612             el_act_dir2img(player->artwork_element, special_action, move_dir);
10613
10614           player->anim_delay_counter =
10615             graphic_info[special_graphic].anim_delay_fixed +
10616             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10617           player->post_delay_counter =
10618             graphic_info[special_graphic].post_delay_fixed +
10619             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10620
10621           player->special_action_sleeping = special_action;
10622         }
10623
10624         if (player->anim_delay_counter > 0)
10625         {
10626           player->action_waiting = player->special_action_sleeping;
10627           player->anim_delay_counter--;
10628         }
10629         else if (player->post_delay_counter > 0)
10630         {
10631           player->post_delay_counter--;
10632         }
10633       }
10634     }
10635     else if (player->is_bored)
10636     {
10637       if (player->num_special_action_bored > 0)
10638       {
10639         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10640         {
10641           int special_action =
10642             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10643           int special_graphic =
10644             el_act_dir2img(player->artwork_element, special_action, move_dir);
10645
10646           player->anim_delay_counter =
10647             graphic_info[special_graphic].anim_delay_fixed +
10648             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10649           player->post_delay_counter =
10650             graphic_info[special_graphic].post_delay_fixed +
10651             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10652
10653           player->special_action_bored = special_action;
10654         }
10655
10656         if (player->anim_delay_counter > 0)
10657         {
10658           player->action_waiting = player->special_action_bored;
10659           player->anim_delay_counter--;
10660         }
10661         else if (player->post_delay_counter > 0)
10662         {
10663           player->post_delay_counter--;
10664         }
10665       }
10666     }
10667   }
10668   else if (last_waiting)        /* waiting -> not waiting */
10669   {
10670     player->is_waiting = FALSE;
10671     player->is_bored = FALSE;
10672     player->is_sleeping = FALSE;
10673
10674     player->frame_counter_bored = -1;
10675     player->frame_counter_sleeping = -1;
10676
10677     player->anim_delay_counter = 0;
10678     player->post_delay_counter = 0;
10679
10680     player->dir_waiting = player->MovDir;
10681     player->action_waiting = ACTION_DEFAULT;
10682
10683     player->special_action_bored = ACTION_DEFAULT;
10684     player->special_action_sleeping = ACTION_DEFAULT;
10685   }
10686 }
10687
10688 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10689 {
10690   static boolean player_was_moving = FALSE;
10691   static boolean player_was_snapping = FALSE;
10692   static boolean player_was_dropping = FALSE;
10693
10694   if ((!player->is_moving  && player_was_moving) ||
10695       (player->MovPos == 0 && player_was_moving) ||
10696       (player->is_snapping && !player_was_snapping) ||
10697       (player->is_dropping && !player_was_dropping))
10698   {
10699     if (!SaveEngineSnapshotToList())
10700       return;
10701
10702     player_was_moving = FALSE;
10703     player_was_snapping = TRUE;
10704     player_was_dropping = TRUE;
10705   }
10706   else
10707   {
10708     if (player->is_moving)
10709       player_was_moving = TRUE;
10710
10711     if (!player->is_snapping)
10712       player_was_snapping = FALSE;
10713
10714     if (!player->is_dropping)
10715       player_was_dropping = FALSE;
10716   }
10717 }
10718
10719 static void CheckSingleStepMode(struct PlayerInfo *player)
10720 {
10721   if (tape.single_step && tape.recording && !tape.pausing)
10722   {
10723     /* as it is called "single step mode", just return to pause mode when the
10724        player stopped moving after one tile (or never starts moving at all) */
10725     if (!player->is_moving && !player->is_pushing)
10726     {
10727       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10728       SnapField(player, 0, 0);                  /* stop snapping */
10729     }
10730   }
10731
10732   CheckSaveEngineSnapshot(player);
10733 }
10734
10735 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10736 {
10737   int left      = player_action & JOY_LEFT;
10738   int right     = player_action & JOY_RIGHT;
10739   int up        = player_action & JOY_UP;
10740   int down      = player_action & JOY_DOWN;
10741   int button1   = player_action & JOY_BUTTON_1;
10742   int button2   = player_action & JOY_BUTTON_2;
10743   int dx        = (left ? -1 : right ? 1 : 0);
10744   int dy        = (up   ? -1 : down  ? 1 : 0);
10745
10746   if (!player->active || tape.pausing)
10747     return 0;
10748
10749   if (player_action)
10750   {
10751     if (button1)
10752       SnapField(player, dx, dy);
10753     else
10754     {
10755       if (button2)
10756         DropElement(player);
10757
10758       MovePlayer(player, dx, dy);
10759     }
10760
10761     CheckSingleStepMode(player);
10762
10763     SetPlayerWaiting(player, FALSE);
10764
10765     return player_action;
10766   }
10767   else
10768   {
10769     /* no actions for this player (no input at player's configured device) */
10770
10771     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10772     SnapField(player, 0, 0);
10773     CheckGravityMovementWhenNotMoving(player);
10774
10775     if (player->MovPos == 0)
10776       SetPlayerWaiting(player, TRUE);
10777
10778     if (player->MovPos == 0)    /* needed for tape.playing */
10779       player->is_moving = FALSE;
10780
10781     player->is_dropping = FALSE;
10782     player->is_dropping_pressed = FALSE;
10783     player->drop_pressed_delay = 0;
10784
10785     CheckSingleStepMode(player);
10786
10787     return 0;
10788   }
10789 }
10790
10791 static void CheckLevelTime()
10792 {
10793   int i;
10794
10795   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10796   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10797   {
10798     if (level.native_em_level->lev->home == 0)  /* all players at home */
10799     {
10800       PlayerWins(local_player);
10801
10802       AllPlayersGone = TRUE;
10803
10804       level.native_em_level->lev->home = -1;
10805     }
10806
10807     if (level.native_em_level->ply[0]->alive == 0 &&
10808         level.native_em_level->ply[1]->alive == 0 &&
10809         level.native_em_level->ply[2]->alive == 0 &&
10810         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10811       AllPlayersGone = TRUE;
10812   }
10813   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10814   {
10815     if (game_sp.LevelSolved &&
10816         !game_sp.GameOver)                              /* game won */
10817     {
10818       PlayerWins(local_player);
10819
10820       game_sp.GameOver = TRUE;
10821
10822       AllPlayersGone = TRUE;
10823     }
10824
10825     if (game_sp.GameOver)                               /* game lost */
10826       AllPlayersGone = TRUE;
10827   }
10828
10829   if (TimeFrames >= FRAMES_PER_SECOND)
10830   {
10831     TimeFrames = 0;
10832     TapeTime++;
10833
10834     for (i = 0; i < MAX_PLAYERS; i++)
10835     {
10836       struct PlayerInfo *player = &stored_player[i];
10837
10838       if (SHIELD_ON(player))
10839       {
10840         player->shield_normal_time_left--;
10841
10842         if (player->shield_deadly_time_left > 0)
10843           player->shield_deadly_time_left--;
10844       }
10845     }
10846
10847     if (!local_player->LevelSolved && !level.use_step_counter)
10848     {
10849       TimePlayed++;
10850
10851       if (TimeLeft > 0)
10852       {
10853         TimeLeft--;
10854
10855         if (TimeLeft <= 10 && setup.time_limit)
10856           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10857
10858         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10859            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10860
10861         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10862
10863         if (!TimeLeft && setup.time_limit)
10864         {
10865           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10866             level.native_em_level->lev->killed_out_of_time = TRUE;
10867           else
10868             for (i = 0; i < MAX_PLAYERS; i++)
10869               KillPlayer(&stored_player[i]);
10870         }
10871       }
10872       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10873       {
10874         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10875       }
10876
10877       level.native_em_level->lev->time =
10878         (game.no_time_limit ? TimePlayed : TimeLeft);
10879     }
10880
10881     if (tape.recording || tape.playing)
10882       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10883   }
10884
10885   if (tape.recording || tape.playing)
10886     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10887
10888   UpdateAndDisplayGameControlValues();
10889 }
10890
10891 void AdvanceFrameAndPlayerCounters(int player_nr)
10892 {
10893   int i;
10894
10895   /* advance frame counters (global frame counter and time frame counter) */
10896   FrameCounter++;
10897   TimeFrames++;
10898
10899   /* advance player counters (counters for move delay, move animation etc.) */
10900   for (i = 0; i < MAX_PLAYERS; i++)
10901   {
10902     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10903     int move_delay_value = stored_player[i].move_delay_value;
10904     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10905
10906     if (!advance_player_counters)       /* not all players may be affected */
10907       continue;
10908
10909     if (move_frames == 0)       /* less than one move per game frame */
10910     {
10911       int stepsize = TILEX / move_delay_value;
10912       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10913       int count = (stored_player[i].is_moving ?
10914                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10915
10916       if (count % delay == 0)
10917         move_frames = 1;
10918     }
10919
10920     stored_player[i].Frame += move_frames;
10921
10922     if (stored_player[i].MovPos != 0)
10923       stored_player[i].StepFrame += move_frames;
10924
10925     if (stored_player[i].move_delay > 0)
10926       stored_player[i].move_delay--;
10927
10928     /* due to bugs in previous versions, counter must count up, not down */
10929     if (stored_player[i].push_delay != -1)
10930       stored_player[i].push_delay++;
10931
10932     if (stored_player[i].drop_delay > 0)
10933       stored_player[i].drop_delay--;
10934
10935     if (stored_player[i].is_dropping_pressed)
10936       stored_player[i].drop_pressed_delay++;
10937   }
10938 }
10939
10940 void StartGameActions(boolean init_network_game, boolean record_tape,
10941                       int random_seed)
10942 {
10943   unsigned int new_random_seed = InitRND(random_seed);
10944
10945   if (record_tape)
10946     TapeStartRecording(new_random_seed);
10947
10948 #if defined(NETWORK_AVALIABLE)
10949   if (init_network_game)
10950   {
10951     SendToServer_StartPlaying();
10952
10953     return;
10954   }
10955 #endif
10956
10957   InitGame();
10958 }
10959
10960 void GameActions()
10961 {
10962 #if 0
10963   static unsigned int game_frame_delay = 0;
10964 #endif
10965   unsigned int game_frame_delay_value;
10966   byte *recorded_player_action;
10967   byte summarized_player_action = 0;
10968   byte tape_action[MAX_PLAYERS];
10969   int i;
10970
10971   /* detect endless loops, caused by custom element programming */
10972   if (recursion_loop_detected && recursion_loop_depth == 0)
10973   {
10974     char *message = getStringCat3("Internal Error! Element ",
10975                                   EL_NAME(recursion_loop_element),
10976                                   " caused endless loop! Quit the game?");
10977
10978     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10979           EL_NAME(recursion_loop_element));
10980
10981     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10982
10983     recursion_loop_detected = FALSE;    /* if game should be continued */
10984
10985     free(message);
10986
10987     return;
10988   }
10989
10990   if (game.restart_level)
10991     StartGameActions(options.network, setup.autorecord, level.random_seed);
10992
10993   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10994   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10995   {
10996     if (level.native_em_level->lev->home == 0)  /* all players at home */
10997     {
10998       PlayerWins(local_player);
10999
11000       AllPlayersGone = TRUE;
11001
11002       level.native_em_level->lev->home = -1;
11003     }
11004
11005     if (level.native_em_level->ply[0]->alive == 0 &&
11006         level.native_em_level->ply[1]->alive == 0 &&
11007         level.native_em_level->ply[2]->alive == 0 &&
11008         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11009       AllPlayersGone = TRUE;
11010   }
11011   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11012   {
11013     if (game_sp.LevelSolved &&
11014         !game_sp.GameOver)                              /* game won */
11015     {
11016       PlayerWins(local_player);
11017
11018       game_sp.GameOver = TRUE;
11019
11020       AllPlayersGone = TRUE;
11021     }
11022
11023     if (game_sp.GameOver)                               /* game lost */
11024       AllPlayersGone = TRUE;
11025   }
11026
11027   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11028     GameWon();
11029
11030   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11031     TapeStop();
11032
11033   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11034     return;
11035
11036   game_frame_delay_value =
11037     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11038
11039   if (tape.playing && tape.warp_forward && !tape.pausing)
11040     game_frame_delay_value = 0;
11041
11042   SetVideoFrameDelay(game_frame_delay_value);
11043
11044 #if 0
11045 #if 0
11046   /* ---------- main game synchronization point ---------- */
11047
11048   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11049
11050   printf("::: skip == %d\n", skip);
11051
11052 #else
11053   /* ---------- main game synchronization point ---------- */
11054
11055   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11056 #endif
11057 #endif
11058
11059   if (network_playing && !network_player_action_received)
11060   {
11061     /* try to get network player actions in time */
11062
11063 #if defined(NETWORK_AVALIABLE)
11064     /* last chance to get network player actions without main loop delay */
11065     HandleNetworking();
11066 #endif
11067
11068     /* game was quit by network peer */
11069     if (game_status != GAME_MODE_PLAYING)
11070       return;
11071
11072     if (!network_player_action_received)
11073       return;           /* failed to get network player actions in time */
11074
11075     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11076   }
11077
11078   if (tape.pausing)
11079     return;
11080
11081   /* at this point we know that we really continue executing the game */
11082
11083   network_player_action_received = FALSE;
11084
11085   /* when playing tape, read previously recorded player input from tape data */
11086   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11087
11088   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11089   if (tape.pausing)
11090     return;
11091
11092   if (tape.set_centered_player)
11093   {
11094     game.centered_player_nr_next = tape.centered_player_nr_next;
11095     game.set_centered_player = TRUE;
11096   }
11097
11098   for (i = 0; i < MAX_PLAYERS; i++)
11099   {
11100     summarized_player_action |= stored_player[i].action;
11101
11102     if (!network_playing && (game.team_mode || tape.playing))
11103       stored_player[i].effective_action = stored_player[i].action;
11104   }
11105
11106 #if defined(NETWORK_AVALIABLE)
11107   if (network_playing)
11108     SendToServer_MovePlayer(summarized_player_action);
11109 #endif
11110
11111   // summarize all actions at local players mapped input device position
11112   // (this allows using different input devices in single player mode)
11113   if (!options.network && !game.team_mode)
11114     stored_player[map_player_action[local_player->index_nr]].effective_action =
11115       summarized_player_action;
11116
11117   if (tape.recording &&
11118       setup.team_mode &&
11119       setup.input_on_focus &&
11120       game.centered_player_nr != -1)
11121   {
11122     for (i = 0; i < MAX_PLAYERS; i++)
11123       stored_player[i].effective_action =
11124         (i == game.centered_player_nr ? summarized_player_action : 0);
11125   }
11126
11127   if (recorded_player_action != NULL)
11128     for (i = 0; i < MAX_PLAYERS; i++)
11129       stored_player[i].effective_action = recorded_player_action[i];
11130
11131   for (i = 0; i < MAX_PLAYERS; i++)
11132   {
11133     tape_action[i] = stored_player[i].effective_action;
11134
11135     /* (this may happen in the RND game engine if a player was not present on
11136        the playfield on level start, but appeared later from a custom element */
11137     if (setup.team_mode &&
11138         tape.recording &&
11139         tape_action[i] &&
11140         !tape.player_participates[i])
11141       tape.player_participates[i] = TRUE;
11142   }
11143
11144   /* only record actions from input devices, but not programmed actions */
11145   if (tape.recording)
11146     TapeRecordAction(tape_action);
11147
11148 #if USE_NEW_PLAYER_ASSIGNMENTS
11149   // !!! also map player actions in single player mode !!!
11150   // if (game.team_mode)
11151   if (1)
11152   {
11153     byte mapped_action[MAX_PLAYERS];
11154
11155 #if DEBUG_PLAYER_ACTIONS
11156     printf(":::");
11157     for (i = 0; i < MAX_PLAYERS; i++)
11158       printf(" %d, ", stored_player[i].effective_action);
11159 #endif
11160
11161     for (i = 0; i < MAX_PLAYERS; i++)
11162       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11163
11164     for (i = 0; i < MAX_PLAYERS; i++)
11165       stored_player[i].effective_action = mapped_action[i];
11166
11167 #if DEBUG_PLAYER_ACTIONS
11168     printf(" =>");
11169     for (i = 0; i < MAX_PLAYERS; i++)
11170       printf(" %d, ", stored_player[i].effective_action);
11171     printf("\n");
11172 #endif
11173   }
11174 #if DEBUG_PLAYER_ACTIONS
11175   else
11176   {
11177     printf(":::");
11178     for (i = 0; i < MAX_PLAYERS; i++)
11179       printf(" %d, ", stored_player[i].effective_action);
11180     printf("\n");
11181   }
11182 #endif
11183 #endif
11184
11185   for (i = 0; i < MAX_PLAYERS; i++)
11186   {
11187     // allow engine snapshot in case of changed movement attempt
11188     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11189         (stored_player[i].effective_action & KEY_MOTION))
11190       game.snapshot.changed_action = TRUE;
11191
11192     // allow engine snapshot in case of snapping/dropping attempt
11193     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11194         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11195       game.snapshot.changed_action = TRUE;
11196
11197     game.snapshot.last_action[i] = stored_player[i].effective_action;
11198   }
11199
11200   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11201   {
11202     GameActions_EM_Main();
11203   }
11204   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11205   {
11206     GameActions_SP_Main();
11207   }
11208   else
11209   {
11210     GameActions_RND_Main();
11211   }
11212
11213   BlitScreenToBitmap(backbuffer);
11214
11215   CheckLevelTime();
11216
11217   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11218
11219   if (options.debug)                    /* calculate frames per second */
11220   {
11221     static unsigned int fps_counter = 0;
11222     static int fps_frames = 0;
11223     unsigned int fps_delay_ms = Counter() - fps_counter;
11224
11225     fps_frames++;
11226
11227     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11228     {
11229       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11230
11231       fps_frames = 0;
11232       fps_counter = Counter();
11233     }
11234
11235     redraw_mask |= REDRAW_FPS;
11236   }
11237 }
11238
11239 void GameActions_EM_Main()
11240 {
11241   byte effective_action[MAX_PLAYERS];
11242   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11243   int i;
11244
11245   for (i = 0; i < MAX_PLAYERS; i++)
11246     effective_action[i] = stored_player[i].effective_action;
11247
11248   GameActions_EM(effective_action, warp_mode);
11249 }
11250
11251 void GameActions_SP_Main()
11252 {
11253   byte effective_action[MAX_PLAYERS];
11254   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11255   int i;
11256
11257   for (i = 0; i < MAX_PLAYERS; i++)
11258     effective_action[i] = stored_player[i].effective_action;
11259
11260   GameActions_SP(effective_action, warp_mode);
11261 }
11262
11263 void GameActions_RND_Main()
11264 {
11265   GameActions_RND();
11266 }
11267
11268 void GameActions_RND()
11269 {
11270   int magic_wall_x = 0, magic_wall_y = 0;
11271   int i, x, y, element, graphic;
11272
11273   InitPlayfieldScanModeVars();
11274
11275   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11276   {
11277     SCAN_PLAYFIELD(x, y)
11278     {
11279       ChangeCount[x][y] = 0;
11280       ChangeEvent[x][y] = -1;
11281     }
11282   }
11283
11284   if (game.set_centered_player)
11285   {
11286     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11287
11288     /* switching to "all players" only possible if all players fit to screen */
11289     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11290     {
11291       game.centered_player_nr_next = game.centered_player_nr;
11292       game.set_centered_player = FALSE;
11293     }
11294
11295     /* do not switch focus to non-existing (or non-active) player */
11296     if (game.centered_player_nr_next >= 0 &&
11297         !stored_player[game.centered_player_nr_next].active)
11298     {
11299       game.centered_player_nr_next = game.centered_player_nr;
11300       game.set_centered_player = FALSE;
11301     }
11302   }
11303
11304   if (game.set_centered_player &&
11305       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11306   {
11307     int sx, sy;
11308
11309     if (game.centered_player_nr_next == -1)
11310     {
11311       setScreenCenteredToAllPlayers(&sx, &sy);
11312     }
11313     else
11314     {
11315       sx = stored_player[game.centered_player_nr_next].jx;
11316       sy = stored_player[game.centered_player_nr_next].jy;
11317     }
11318
11319     game.centered_player_nr = game.centered_player_nr_next;
11320     game.set_centered_player = FALSE;
11321
11322     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11323     DrawGameDoorValues();
11324   }
11325
11326   for (i = 0; i < MAX_PLAYERS; i++)
11327   {
11328     int actual_player_action = stored_player[i].effective_action;
11329
11330 #if 1
11331     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11332        - rnd_equinox_tetrachloride 048
11333        - rnd_equinox_tetrachloride_ii 096
11334        - rnd_emanuel_schmieg 002
11335        - doctor_sloan_ww 001, 020
11336     */
11337     if (stored_player[i].MovPos == 0)
11338       CheckGravityMovement(&stored_player[i]);
11339 #endif
11340
11341     /* overwrite programmed action with tape action */
11342     if (stored_player[i].programmed_action)
11343       actual_player_action = stored_player[i].programmed_action;
11344
11345     PlayerActions(&stored_player[i], actual_player_action);
11346
11347     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11348   }
11349
11350   ScrollScreen(NULL, SCROLL_GO_ON);
11351
11352   /* for backwards compatibility, the following code emulates a fixed bug that
11353      occured when pushing elements (causing elements that just made their last
11354      pushing step to already (if possible) make their first falling step in the
11355      same game frame, which is bad); this code is also needed to use the famous
11356      "spring push bug" which is used in older levels and might be wanted to be
11357      used also in newer levels, but in this case the buggy pushing code is only
11358      affecting the "spring" element and no other elements */
11359
11360   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11361   {
11362     for (i = 0; i < MAX_PLAYERS; i++)
11363     {
11364       struct PlayerInfo *player = &stored_player[i];
11365       int x = player->jx;
11366       int y = player->jy;
11367
11368       if (player->active && player->is_pushing && player->is_moving &&
11369           IS_MOVING(x, y) &&
11370           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11371            Feld[x][y] == EL_SPRING))
11372       {
11373         ContinueMoving(x, y);
11374
11375         /* continue moving after pushing (this is actually a bug) */
11376         if (!IS_MOVING(x, y))
11377           Stop[x][y] = FALSE;
11378       }
11379     }
11380   }
11381
11382   SCAN_PLAYFIELD(x, y)
11383   {
11384     ChangeCount[x][y] = 0;
11385     ChangeEvent[x][y] = -1;
11386
11387     /* this must be handled before main playfield loop */
11388     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11389     {
11390       MovDelay[x][y]--;
11391       if (MovDelay[x][y] <= 0)
11392         RemoveField(x, y);
11393     }
11394
11395     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11396     {
11397       MovDelay[x][y]--;
11398       if (MovDelay[x][y] <= 0)
11399       {
11400         RemoveField(x, y);
11401         TEST_DrawLevelField(x, y);
11402
11403         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11404       }
11405     }
11406
11407 #if DEBUG
11408     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11409     {
11410       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11411       printf("GameActions(): This should never happen!\n");
11412
11413       ChangePage[x][y] = -1;
11414     }
11415 #endif
11416
11417     Stop[x][y] = FALSE;
11418     if (WasJustMoving[x][y] > 0)
11419       WasJustMoving[x][y]--;
11420     if (WasJustFalling[x][y] > 0)
11421       WasJustFalling[x][y]--;
11422     if (CheckCollision[x][y] > 0)
11423       CheckCollision[x][y]--;
11424     if (CheckImpact[x][y] > 0)
11425       CheckImpact[x][y]--;
11426
11427     GfxFrame[x][y]++;
11428
11429     /* reset finished pushing action (not done in ContinueMoving() to allow
11430        continuous pushing animation for elements with zero push delay) */
11431     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11432     {
11433       ResetGfxAnimation(x, y);
11434       TEST_DrawLevelField(x, y);
11435     }
11436
11437 #if DEBUG
11438     if (IS_BLOCKED(x, y))
11439     {
11440       int oldx, oldy;
11441
11442       Blocked2Moving(x, y, &oldx, &oldy);
11443       if (!IS_MOVING(oldx, oldy))
11444       {
11445         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11446         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11447         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11448         printf("GameActions(): This should never happen!\n");
11449       }
11450     }
11451 #endif
11452   }
11453
11454   SCAN_PLAYFIELD(x, y)
11455   {
11456     element = Feld[x][y];
11457     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11458
11459     ResetGfxFrame(x, y, TRUE);
11460
11461     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11462         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11463       ResetRandomAnimationValue(x, y);
11464
11465     SetRandomAnimationValue(x, y);
11466
11467     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11468
11469     if (IS_INACTIVE(element))
11470     {
11471       if (IS_ANIMATED(graphic))
11472         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11473
11474       continue;
11475     }
11476
11477     /* this may take place after moving, so 'element' may have changed */
11478     if (IS_CHANGING(x, y) &&
11479         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11480     {
11481       int page = element_info[element].event_page_nr[CE_DELAY];
11482
11483       HandleElementChange(x, y, page);
11484
11485       element = Feld[x][y];
11486       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11487     }
11488
11489     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11490     {
11491       StartMoving(x, y);
11492
11493       element = Feld[x][y];
11494       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11495
11496       if (IS_ANIMATED(graphic) &&
11497           !IS_MOVING(x, y) &&
11498           !Stop[x][y])
11499         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11500
11501       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11502         TEST_DrawTwinkleOnField(x, y);
11503     }
11504     else if ((element == EL_ACID ||
11505               element == EL_EXIT_OPEN ||
11506               element == EL_EM_EXIT_OPEN ||
11507               element == EL_SP_EXIT_OPEN ||
11508               element == EL_STEEL_EXIT_OPEN ||
11509               element == EL_EM_STEEL_EXIT_OPEN ||
11510               element == EL_SP_TERMINAL ||
11511               element == EL_SP_TERMINAL_ACTIVE ||
11512               element == EL_EXTRA_TIME ||
11513               element == EL_SHIELD_NORMAL ||
11514               element == EL_SHIELD_DEADLY) &&
11515              IS_ANIMATED(graphic))
11516       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11517     else if (IS_MOVING(x, y))
11518       ContinueMoving(x, y);
11519     else if (IS_ACTIVE_BOMB(element))
11520       CheckDynamite(x, y);
11521     else if (element == EL_AMOEBA_GROWING)
11522       AmoebeWaechst(x, y);
11523     else if (element == EL_AMOEBA_SHRINKING)
11524       AmoebaDisappearing(x, y);
11525
11526 #if !USE_NEW_AMOEBA_CODE
11527     else if (IS_AMOEBALIVE(element))
11528       AmoebeAbleger(x, y);
11529 #endif
11530
11531     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11532       Life(x, y);
11533     else if (element == EL_EXIT_CLOSED)
11534       CheckExit(x, y);
11535     else if (element == EL_EM_EXIT_CLOSED)
11536       CheckExitEM(x, y);
11537     else if (element == EL_STEEL_EXIT_CLOSED)
11538       CheckExitSteel(x, y);
11539     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11540       CheckExitSteelEM(x, y);
11541     else if (element == EL_SP_EXIT_CLOSED)
11542       CheckExitSP(x, y);
11543     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11544              element == EL_EXPANDABLE_STEELWALL_GROWING)
11545       MauerWaechst(x, y);
11546     else if (element == EL_EXPANDABLE_WALL ||
11547              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11548              element == EL_EXPANDABLE_WALL_VERTICAL ||
11549              element == EL_EXPANDABLE_WALL_ANY ||
11550              element == EL_BD_EXPANDABLE_WALL)
11551       MauerAbleger(x, y);
11552     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11553              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11554              element == EL_EXPANDABLE_STEELWALL_ANY)
11555       MauerAblegerStahl(x, y);
11556     else if (element == EL_FLAMES)
11557       CheckForDragon(x, y);
11558     else if (element == EL_EXPLOSION)
11559       ; /* drawing of correct explosion animation is handled separately */
11560     else if (element == EL_ELEMENT_SNAPPING ||
11561              element == EL_DIAGONAL_SHRINKING ||
11562              element == EL_DIAGONAL_GROWING)
11563     {
11564       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11565
11566       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11567     }
11568     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11569       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11570
11571     if (IS_BELT_ACTIVE(element))
11572       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11573
11574     if (game.magic_wall_active)
11575     {
11576       int jx = local_player->jx, jy = local_player->jy;
11577
11578       /* play the element sound at the position nearest to the player */
11579       if ((element == EL_MAGIC_WALL_FULL ||
11580            element == EL_MAGIC_WALL_ACTIVE ||
11581            element == EL_MAGIC_WALL_EMPTYING ||
11582            element == EL_BD_MAGIC_WALL_FULL ||
11583            element == EL_BD_MAGIC_WALL_ACTIVE ||
11584            element == EL_BD_MAGIC_WALL_EMPTYING ||
11585            element == EL_DC_MAGIC_WALL_FULL ||
11586            element == EL_DC_MAGIC_WALL_ACTIVE ||
11587            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11588           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11589       {
11590         magic_wall_x = x;
11591         magic_wall_y = y;
11592       }
11593     }
11594   }
11595
11596 #if USE_NEW_AMOEBA_CODE
11597   /* new experimental amoeba growth stuff */
11598   if (!(FrameCounter % 8))
11599   {
11600     static unsigned int random = 1684108901;
11601
11602     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11603     {
11604       x = RND(lev_fieldx);
11605       y = RND(lev_fieldy);
11606       element = Feld[x][y];
11607
11608       if (!IS_PLAYER(x,y) &&
11609           (element == EL_EMPTY ||
11610            CAN_GROW_INTO(element) ||
11611            element == EL_QUICKSAND_EMPTY ||
11612            element == EL_QUICKSAND_FAST_EMPTY ||
11613            element == EL_ACID_SPLASH_LEFT ||
11614            element == EL_ACID_SPLASH_RIGHT))
11615       {
11616         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11617             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11618             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11619             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11620           Feld[x][y] = EL_AMOEBA_DROP;
11621       }
11622
11623       random = random * 129 + 1;
11624     }
11625   }
11626 #endif
11627
11628   game.explosions_delayed = FALSE;
11629
11630   SCAN_PLAYFIELD(x, y)
11631   {
11632     element = Feld[x][y];
11633
11634     if (ExplodeField[x][y])
11635       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11636     else if (element == EL_EXPLOSION)
11637       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11638
11639     ExplodeField[x][y] = EX_TYPE_NONE;
11640   }
11641
11642   game.explosions_delayed = TRUE;
11643
11644   if (game.magic_wall_active)
11645   {
11646     if (!(game.magic_wall_time_left % 4))
11647     {
11648       int element = Feld[magic_wall_x][magic_wall_y];
11649
11650       if (element == EL_BD_MAGIC_WALL_FULL ||
11651           element == EL_BD_MAGIC_WALL_ACTIVE ||
11652           element == EL_BD_MAGIC_WALL_EMPTYING)
11653         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11654       else if (element == EL_DC_MAGIC_WALL_FULL ||
11655                element == EL_DC_MAGIC_WALL_ACTIVE ||
11656                element == EL_DC_MAGIC_WALL_EMPTYING)
11657         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11658       else
11659         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11660     }
11661
11662     if (game.magic_wall_time_left > 0)
11663     {
11664       game.magic_wall_time_left--;
11665
11666       if (!game.magic_wall_time_left)
11667       {
11668         SCAN_PLAYFIELD(x, y)
11669         {
11670           element = Feld[x][y];
11671
11672           if (element == EL_MAGIC_WALL_ACTIVE ||
11673               element == EL_MAGIC_WALL_FULL)
11674           {
11675             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11676             TEST_DrawLevelField(x, y);
11677           }
11678           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11679                    element == EL_BD_MAGIC_WALL_FULL)
11680           {
11681             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11682             TEST_DrawLevelField(x, y);
11683           }
11684           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11685                    element == EL_DC_MAGIC_WALL_FULL)
11686           {
11687             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11688             TEST_DrawLevelField(x, y);
11689           }
11690         }
11691
11692         game.magic_wall_active = FALSE;
11693       }
11694     }
11695   }
11696
11697   if (game.light_time_left > 0)
11698   {
11699     game.light_time_left--;
11700
11701     if (game.light_time_left == 0)
11702       RedrawAllLightSwitchesAndInvisibleElements();
11703   }
11704
11705   if (game.timegate_time_left > 0)
11706   {
11707     game.timegate_time_left--;
11708
11709     if (game.timegate_time_left == 0)
11710       CloseAllOpenTimegates();
11711   }
11712
11713   if (game.lenses_time_left > 0)
11714   {
11715     game.lenses_time_left--;
11716
11717     if (game.lenses_time_left == 0)
11718       RedrawAllInvisibleElementsForLenses();
11719   }
11720
11721   if (game.magnify_time_left > 0)
11722   {
11723     game.magnify_time_left--;
11724
11725     if (game.magnify_time_left == 0)
11726       RedrawAllInvisibleElementsForMagnifier();
11727   }
11728
11729   for (i = 0; i < MAX_PLAYERS; i++)
11730   {
11731     struct PlayerInfo *player = &stored_player[i];
11732
11733     if (SHIELD_ON(player))
11734     {
11735       if (player->shield_deadly_time_left)
11736         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11737       else if (player->shield_normal_time_left)
11738         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11739     }
11740   }
11741
11742 #if USE_DELAYED_GFX_REDRAW
11743   SCAN_PLAYFIELD(x, y)
11744   {
11745     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11746     {
11747       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11748          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11749
11750       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11751         DrawLevelField(x, y);
11752
11753       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11754         DrawLevelFieldCrumbled(x, y);
11755
11756       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11757         DrawLevelFieldCrumbledNeighbours(x, y);
11758
11759       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11760         DrawTwinkleOnField(x, y);
11761     }
11762
11763     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11764   }
11765 #endif
11766
11767   DrawAllPlayers();
11768   PlayAllPlayersSound();
11769
11770   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11771   {
11772     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11773
11774     local_player->show_envelope = 0;
11775   }
11776
11777   /* use random number generator in every frame to make it less predictable */
11778   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11779     RND(1);
11780 }
11781
11782 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11783 {
11784   int min_x = x, min_y = y, max_x = x, max_y = y;
11785   int i;
11786
11787   for (i = 0; i < MAX_PLAYERS; i++)
11788   {
11789     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11790
11791     if (!stored_player[i].active || &stored_player[i] == player)
11792       continue;
11793
11794     min_x = MIN(min_x, jx);
11795     min_y = MIN(min_y, jy);
11796     max_x = MAX(max_x, jx);
11797     max_y = MAX(max_y, jy);
11798   }
11799
11800   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11801 }
11802
11803 static boolean AllPlayersInVisibleScreen()
11804 {
11805   int i;
11806
11807   for (i = 0; i < MAX_PLAYERS; i++)
11808   {
11809     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11810
11811     if (!stored_player[i].active)
11812       continue;
11813
11814     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11815       return FALSE;
11816   }
11817
11818   return TRUE;
11819 }
11820
11821 void ScrollLevel(int dx, int dy)
11822 {
11823   int scroll_offset = 2 * TILEX_VAR;
11824   int x, y;
11825
11826   BlitBitmap(drawto_field, drawto_field,
11827              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11828              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11829              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11830              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11831              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11832              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11833
11834   if (dx != 0)
11835   {
11836     x = (dx == 1 ? BX1 : BX2);
11837     for (y = BY1; y <= BY2; y++)
11838       DrawScreenField(x, y);
11839   }
11840
11841   if (dy != 0)
11842   {
11843     y = (dy == 1 ? BY1 : BY2);
11844     for (x = BX1; x <= BX2; x++)
11845       DrawScreenField(x, y);
11846   }
11847
11848   redraw_mask |= REDRAW_FIELD;
11849 }
11850
11851 static boolean canFallDown(struct PlayerInfo *player)
11852 {
11853   int jx = player->jx, jy = player->jy;
11854
11855   return (IN_LEV_FIELD(jx, jy + 1) &&
11856           (IS_FREE(jx, jy + 1) ||
11857            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11858           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11859           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11860 }
11861
11862 static boolean canPassField(int x, int y, int move_dir)
11863 {
11864   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11865   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11866   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11867   int nextx = x + dx;
11868   int nexty = y + dy;
11869   int element = Feld[x][y];
11870
11871   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11872           !CAN_MOVE(element) &&
11873           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11874           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11875           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11876 }
11877
11878 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11879 {
11880   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11881   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11882   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11883   int newx = x + dx;
11884   int newy = y + dy;
11885
11886   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11887           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11888           (IS_DIGGABLE(Feld[newx][newy]) ||
11889            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11890            canPassField(newx, newy, move_dir)));
11891 }
11892
11893 static void CheckGravityMovement(struct PlayerInfo *player)
11894 {
11895   if (player->gravity && !player->programmed_action)
11896   {
11897     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11898     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11899     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11900     int jx = player->jx, jy = player->jy;
11901     boolean player_is_moving_to_valid_field =
11902       (!player_is_snapping &&
11903        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11904         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11905     boolean player_can_fall_down = canFallDown(player);
11906
11907     if (player_can_fall_down &&
11908         !player_is_moving_to_valid_field)
11909       player->programmed_action = MV_DOWN;
11910   }
11911 }
11912
11913 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11914 {
11915   return CheckGravityMovement(player);
11916
11917   if (player->gravity && !player->programmed_action)
11918   {
11919     int jx = player->jx, jy = player->jy;
11920     boolean field_under_player_is_free =
11921       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11922     boolean player_is_standing_on_valid_field =
11923       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11924        (IS_WALKABLE(Feld[jx][jy]) &&
11925         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11926
11927     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11928       player->programmed_action = MV_DOWN;
11929   }
11930 }
11931
11932 /*
11933   MovePlayerOneStep()
11934   -----------------------------------------------------------------------------
11935   dx, dy:               direction (non-diagonal) to try to move the player to
11936   real_dx, real_dy:     direction as read from input device (can be diagonal)
11937 */
11938
11939 boolean MovePlayerOneStep(struct PlayerInfo *player,
11940                           int dx, int dy, int real_dx, int real_dy)
11941 {
11942   int jx = player->jx, jy = player->jy;
11943   int new_jx = jx + dx, new_jy = jy + dy;
11944   int can_move;
11945   boolean player_can_move = !player->cannot_move;
11946
11947   if (!player->active || (!dx && !dy))
11948     return MP_NO_ACTION;
11949
11950   player->MovDir = (dx < 0 ? MV_LEFT :
11951                     dx > 0 ? MV_RIGHT :
11952                     dy < 0 ? MV_UP :
11953                     dy > 0 ? MV_DOWN :  MV_NONE);
11954
11955   if (!IN_LEV_FIELD(new_jx, new_jy))
11956     return MP_NO_ACTION;
11957
11958   if (!player_can_move)
11959   {
11960     if (player->MovPos == 0)
11961     {
11962       player->is_moving = FALSE;
11963       player->is_digging = FALSE;
11964       player->is_collecting = FALSE;
11965       player->is_snapping = FALSE;
11966       player->is_pushing = FALSE;
11967     }
11968   }
11969
11970   if (!options.network && game.centered_player_nr == -1 &&
11971       !AllPlayersInSight(player, new_jx, new_jy))
11972     return MP_NO_ACTION;
11973
11974   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11975   if (can_move != MP_MOVING)
11976     return can_move;
11977
11978   /* check if DigField() has caused relocation of the player */
11979   if (player->jx != jx || player->jy != jy)
11980     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11981
11982   StorePlayer[jx][jy] = 0;
11983   player->last_jx = jx;
11984   player->last_jy = jy;
11985   player->jx = new_jx;
11986   player->jy = new_jy;
11987   StorePlayer[new_jx][new_jy] = player->element_nr;
11988
11989   if (player->move_delay_value_next != -1)
11990   {
11991     player->move_delay_value = player->move_delay_value_next;
11992     player->move_delay_value_next = -1;
11993   }
11994
11995   player->MovPos =
11996     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11997
11998   player->step_counter++;
11999
12000   PlayerVisit[jx][jy] = FrameCounter;
12001
12002   player->is_moving = TRUE;
12003
12004 #if 1
12005   /* should better be called in MovePlayer(), but this breaks some tapes */
12006   ScrollPlayer(player, SCROLL_INIT);
12007 #endif
12008
12009   return MP_MOVING;
12010 }
12011
12012 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12013 {
12014   int jx = player->jx, jy = player->jy;
12015   int old_jx = jx, old_jy = jy;
12016   int moved = MP_NO_ACTION;
12017
12018   if (!player->active)
12019     return FALSE;
12020
12021   if (!dx && !dy)
12022   {
12023     if (player->MovPos == 0)
12024     {
12025       player->is_moving = FALSE;
12026       player->is_digging = FALSE;
12027       player->is_collecting = FALSE;
12028       player->is_snapping = FALSE;
12029       player->is_pushing = FALSE;
12030     }
12031
12032     return FALSE;
12033   }
12034
12035   if (player->move_delay > 0)
12036     return FALSE;
12037
12038   player->move_delay = -1;              /* set to "uninitialized" value */
12039
12040   /* store if player is automatically moved to next field */
12041   player->is_auto_moving = (player->programmed_action != MV_NONE);
12042
12043   /* remove the last programmed player action */
12044   player->programmed_action = 0;
12045
12046   if (player->MovPos)
12047   {
12048     /* should only happen if pre-1.2 tape recordings are played */
12049     /* this is only for backward compatibility */
12050
12051     int original_move_delay_value = player->move_delay_value;
12052
12053 #if DEBUG
12054     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12055            tape.counter);
12056 #endif
12057
12058     /* scroll remaining steps with finest movement resolution */
12059     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12060
12061     while (player->MovPos)
12062     {
12063       ScrollPlayer(player, SCROLL_GO_ON);
12064       ScrollScreen(NULL, SCROLL_GO_ON);
12065
12066       AdvanceFrameAndPlayerCounters(player->index_nr);
12067
12068       DrawAllPlayers();
12069       BackToFront_WithFrameDelay(0);
12070     }
12071
12072     player->move_delay_value = original_move_delay_value;
12073   }
12074
12075   player->is_active = FALSE;
12076
12077   if (player->last_move_dir & MV_HORIZONTAL)
12078   {
12079     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12080       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12081   }
12082   else
12083   {
12084     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12085       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12086   }
12087
12088   if (!moved && !player->is_active)
12089   {
12090     player->is_moving = FALSE;
12091     player->is_digging = FALSE;
12092     player->is_collecting = FALSE;
12093     player->is_snapping = FALSE;
12094     player->is_pushing = FALSE;
12095   }
12096
12097   jx = player->jx;
12098   jy = player->jy;
12099
12100   if (moved & MP_MOVING && !ScreenMovPos &&
12101       (player->index_nr == game.centered_player_nr ||
12102        game.centered_player_nr == -1))
12103   {
12104     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12105     int offset = game.scroll_delay_value;
12106
12107     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12108     {
12109       /* actual player has left the screen -- scroll in that direction */
12110       if (jx != old_jx)         /* player has moved horizontally */
12111         scroll_x += (jx - old_jx);
12112       else                      /* player has moved vertically */
12113         scroll_y += (jy - old_jy);
12114     }
12115     else
12116     {
12117       if (jx != old_jx)         /* player has moved horizontally */
12118       {
12119         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12120             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12121           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12122
12123         /* don't scroll over playfield boundaries */
12124         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12125           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12126
12127         /* don't scroll more than one field at a time */
12128         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12129
12130         /* don't scroll against the player's moving direction */
12131         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12132             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12133           scroll_x = old_scroll_x;
12134       }
12135       else                      /* player has moved vertically */
12136       {
12137         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12138             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12139           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12140
12141         /* don't scroll over playfield boundaries */
12142         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12143           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12144
12145         /* don't scroll more than one field at a time */
12146         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12147
12148         /* don't scroll against the player's moving direction */
12149         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12150             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12151           scroll_y = old_scroll_y;
12152       }
12153     }
12154
12155     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12156     {
12157       if (!options.network && game.centered_player_nr == -1 &&
12158           !AllPlayersInVisibleScreen())
12159       {
12160         scroll_x = old_scroll_x;
12161         scroll_y = old_scroll_y;
12162       }
12163       else
12164       {
12165         ScrollScreen(player, SCROLL_INIT);
12166         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12167       }
12168     }
12169   }
12170
12171   player->StepFrame = 0;
12172
12173   if (moved & MP_MOVING)
12174   {
12175     if (old_jx != jx && old_jy == jy)
12176       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12177     else if (old_jx == jx && old_jy != jy)
12178       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12179
12180     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12181
12182     player->last_move_dir = player->MovDir;
12183     player->is_moving = TRUE;
12184     player->is_snapping = FALSE;
12185     player->is_switching = FALSE;
12186     player->is_dropping = FALSE;
12187     player->is_dropping_pressed = FALSE;
12188     player->drop_pressed_delay = 0;
12189
12190 #if 0
12191     /* should better be called here than above, but this breaks some tapes */
12192     ScrollPlayer(player, SCROLL_INIT);
12193 #endif
12194   }
12195   else
12196   {
12197     CheckGravityMovementWhenNotMoving(player);
12198
12199     player->is_moving = FALSE;
12200
12201     /* at this point, the player is allowed to move, but cannot move right now
12202        (e.g. because of something blocking the way) -- ensure that the player
12203        is also allowed to move in the next frame (in old versions before 3.1.1,
12204        the player was forced to wait again for eight frames before next try) */
12205
12206     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12207       player->move_delay = 0;   /* allow direct movement in the next frame */
12208   }
12209
12210   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12211     player->move_delay = player->move_delay_value;
12212
12213   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12214   {
12215     TestIfPlayerTouchesBadThing(jx, jy);
12216     TestIfPlayerTouchesCustomElement(jx, jy);
12217   }
12218
12219   if (!player->active)
12220     RemovePlayer(player);
12221
12222   return moved;
12223 }
12224
12225 void ScrollPlayer(struct PlayerInfo *player, int mode)
12226 {
12227   int jx = player->jx, jy = player->jy;
12228   int last_jx = player->last_jx, last_jy = player->last_jy;
12229   int move_stepsize = TILEX / player->move_delay_value;
12230
12231   if (!player->active)
12232     return;
12233
12234   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12235     return;
12236
12237   if (mode == SCROLL_INIT)
12238   {
12239     player->actual_frame_counter = FrameCounter;
12240     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12241
12242     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12243         Feld[last_jx][last_jy] == EL_EMPTY)
12244     {
12245       int last_field_block_delay = 0;   /* start with no blocking at all */
12246       int block_delay_adjustment = player->block_delay_adjustment;
12247
12248       /* if player blocks last field, add delay for exactly one move */
12249       if (player->block_last_field)
12250       {
12251         last_field_block_delay += player->move_delay_value;
12252
12253         /* when blocking enabled, prevent moving up despite gravity */
12254         if (player->gravity && player->MovDir == MV_UP)
12255           block_delay_adjustment = -1;
12256       }
12257
12258       /* add block delay adjustment (also possible when not blocking) */
12259       last_field_block_delay += block_delay_adjustment;
12260
12261       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12262       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12263     }
12264
12265     if (player->MovPos != 0)    /* player has not yet reached destination */
12266       return;
12267   }
12268   else if (!FrameReached(&player->actual_frame_counter, 1))
12269     return;
12270
12271   if (player->MovPos != 0)
12272   {
12273     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12274     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12275
12276     /* before DrawPlayer() to draw correct player graphic for this case */
12277     if (player->MovPos == 0)
12278       CheckGravityMovement(player);
12279   }
12280
12281   if (player->MovPos == 0)      /* player reached destination field */
12282   {
12283     if (player->move_delay_reset_counter > 0)
12284     {
12285       player->move_delay_reset_counter--;
12286
12287       if (player->move_delay_reset_counter == 0)
12288       {
12289         /* continue with normal speed after quickly moving through gate */
12290         HALVE_PLAYER_SPEED(player);
12291
12292         /* be able to make the next move without delay */
12293         player->move_delay = 0;
12294       }
12295     }
12296
12297     player->last_jx = jx;
12298     player->last_jy = jy;
12299
12300     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12301         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12302         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12303         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12304         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12305         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12306         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12307         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12308     {
12309       DrawPlayer(player);       /* needed here only to cleanup last field */
12310       RemovePlayer(player);
12311
12312       if (local_player->friends_still_needed == 0 ||
12313           IS_SP_ELEMENT(Feld[jx][jy]))
12314         PlayerWins(player);
12315     }
12316
12317     /* this breaks one level: "machine", level 000 */
12318     {
12319       int move_direction = player->MovDir;
12320       int enter_side = MV_DIR_OPPOSITE(move_direction);
12321       int leave_side = move_direction;
12322       int old_jx = last_jx;
12323       int old_jy = last_jy;
12324       int old_element = Feld[old_jx][old_jy];
12325       int new_element = Feld[jx][jy];
12326
12327       if (IS_CUSTOM_ELEMENT(old_element))
12328         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12329                                    CE_LEFT_BY_PLAYER,
12330                                    player->index_bit, leave_side);
12331
12332       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12333                                           CE_PLAYER_LEAVES_X,
12334                                           player->index_bit, leave_side);
12335
12336       if (IS_CUSTOM_ELEMENT(new_element))
12337         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12338                                    player->index_bit, enter_side);
12339
12340       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12341                                           CE_PLAYER_ENTERS_X,
12342                                           player->index_bit, enter_side);
12343
12344       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12345                                         CE_MOVE_OF_X, move_direction);
12346     }
12347
12348     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12349     {
12350       TestIfPlayerTouchesBadThing(jx, jy);
12351       TestIfPlayerTouchesCustomElement(jx, jy);
12352
12353       /* needed because pushed element has not yet reached its destination,
12354          so it would trigger a change event at its previous field location */
12355       if (!player->is_pushing)
12356         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12357
12358       if (!player->active)
12359         RemovePlayer(player);
12360     }
12361
12362     if (!local_player->LevelSolved && level.use_step_counter)
12363     {
12364       int i;
12365
12366       TimePlayed++;
12367
12368       if (TimeLeft > 0)
12369       {
12370         TimeLeft--;
12371
12372         if (TimeLeft <= 10 && setup.time_limit)
12373           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12374
12375         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12376
12377         DisplayGameControlValues();
12378
12379         if (!TimeLeft && setup.time_limit)
12380           for (i = 0; i < MAX_PLAYERS; i++)
12381             KillPlayer(&stored_player[i]);
12382       }
12383       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12384       {
12385         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12386
12387         DisplayGameControlValues();
12388       }
12389     }
12390
12391     if (tape.single_step && tape.recording && !tape.pausing &&
12392         !player->programmed_action)
12393       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12394
12395     if (!player->programmed_action)
12396       CheckSaveEngineSnapshot(player);
12397   }
12398 }
12399
12400 void ScrollScreen(struct PlayerInfo *player, int mode)
12401 {
12402   static unsigned int screen_frame_counter = 0;
12403
12404   if (mode == SCROLL_INIT)
12405   {
12406     /* set scrolling step size according to actual player's moving speed */
12407     ScrollStepSize = TILEX / player->move_delay_value;
12408
12409     screen_frame_counter = FrameCounter;
12410     ScreenMovDir = player->MovDir;
12411     ScreenMovPos = player->MovPos;
12412     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12413     return;
12414   }
12415   else if (!FrameReached(&screen_frame_counter, 1))
12416     return;
12417
12418   if (ScreenMovPos)
12419   {
12420     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12421     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12422     redraw_mask |= REDRAW_FIELD;
12423   }
12424   else
12425     ScreenMovDir = MV_NONE;
12426 }
12427
12428 void TestIfPlayerTouchesCustomElement(int x, int y)
12429 {
12430   static int xy[4][2] =
12431   {
12432     { 0, -1 },
12433     { -1, 0 },
12434     { +1, 0 },
12435     { 0, +1 }
12436   };
12437   static int trigger_sides[4][2] =
12438   {
12439     /* center side       border side */
12440     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12441     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12442     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12443     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12444   };
12445   static int touch_dir[4] =
12446   {
12447     MV_LEFT | MV_RIGHT,
12448     MV_UP   | MV_DOWN,
12449     MV_UP   | MV_DOWN,
12450     MV_LEFT | MV_RIGHT
12451   };
12452   int center_element = Feld[x][y];      /* should always be non-moving! */
12453   int i;
12454
12455   for (i = 0; i < NUM_DIRECTIONS; i++)
12456   {
12457     int xx = x + xy[i][0];
12458     int yy = y + xy[i][1];
12459     int center_side = trigger_sides[i][0];
12460     int border_side = trigger_sides[i][1];
12461     int border_element;
12462
12463     if (!IN_LEV_FIELD(xx, yy))
12464       continue;
12465
12466     if (IS_PLAYER(x, y))                /* player found at center element */
12467     {
12468       struct PlayerInfo *player = PLAYERINFO(x, y);
12469
12470       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12471         border_element = Feld[xx][yy];          /* may be moving! */
12472       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12473         border_element = Feld[xx][yy];
12474       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12475         border_element = MovingOrBlocked2Element(xx, yy);
12476       else
12477         continue;               /* center and border element do not touch */
12478
12479       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12480                                  player->index_bit, border_side);
12481       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12482                                           CE_PLAYER_TOUCHES_X,
12483                                           player->index_bit, border_side);
12484
12485       {
12486         /* use player element that is initially defined in the level playfield,
12487            not the player element that corresponds to the runtime player number
12488            (example: a level that contains EL_PLAYER_3 as the only player would
12489            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12490         int player_element = PLAYERINFO(x, y)->initial_element;
12491
12492         CheckElementChangeBySide(xx, yy, border_element, player_element,
12493                                  CE_TOUCHING_X, border_side);
12494       }
12495     }
12496     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12497     {
12498       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12499
12500       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12501       {
12502         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12503           continue;             /* center and border element do not touch */
12504       }
12505
12506       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12507                                  player->index_bit, center_side);
12508       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12509                                           CE_PLAYER_TOUCHES_X,
12510                                           player->index_bit, center_side);
12511
12512       {
12513         /* use player element that is initially defined in the level playfield,
12514            not the player element that corresponds to the runtime player number
12515            (example: a level that contains EL_PLAYER_3 as the only player would
12516            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12517         int player_element = PLAYERINFO(xx, yy)->initial_element;
12518
12519         CheckElementChangeBySide(x, y, center_element, player_element,
12520                                  CE_TOUCHING_X, center_side);
12521       }
12522
12523       break;
12524     }
12525   }
12526 }
12527
12528 void TestIfElementTouchesCustomElement(int x, int y)
12529 {
12530   static int xy[4][2] =
12531   {
12532     { 0, -1 },
12533     { -1, 0 },
12534     { +1, 0 },
12535     { 0, +1 }
12536   };
12537   static int trigger_sides[4][2] =
12538   {
12539     /* center side      border side */
12540     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12541     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12542     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12543     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12544   };
12545   static int touch_dir[4] =
12546   {
12547     MV_LEFT | MV_RIGHT,
12548     MV_UP   | MV_DOWN,
12549     MV_UP   | MV_DOWN,
12550     MV_LEFT | MV_RIGHT
12551   };
12552   boolean change_center_element = FALSE;
12553   int center_element = Feld[x][y];      /* should always be non-moving! */
12554   int border_element_old[NUM_DIRECTIONS];
12555   int i;
12556
12557   for (i = 0; i < NUM_DIRECTIONS; i++)
12558   {
12559     int xx = x + xy[i][0];
12560     int yy = y + xy[i][1];
12561     int border_element;
12562
12563     border_element_old[i] = -1;
12564
12565     if (!IN_LEV_FIELD(xx, yy))
12566       continue;
12567
12568     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12569       border_element = Feld[xx][yy];    /* may be moving! */
12570     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12571       border_element = Feld[xx][yy];
12572     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12573       border_element = MovingOrBlocked2Element(xx, yy);
12574     else
12575       continue;                 /* center and border element do not touch */
12576
12577     border_element_old[i] = border_element;
12578   }
12579
12580   for (i = 0; i < NUM_DIRECTIONS; i++)
12581   {
12582     int xx = x + xy[i][0];
12583     int yy = y + xy[i][1];
12584     int center_side = trigger_sides[i][0];
12585     int border_element = border_element_old[i];
12586
12587     if (border_element == -1)
12588       continue;
12589
12590     /* check for change of border element */
12591     CheckElementChangeBySide(xx, yy, border_element, center_element,
12592                              CE_TOUCHING_X, center_side);
12593
12594     /* (center element cannot be player, so we dont have to check this here) */
12595   }
12596
12597   for (i = 0; i < NUM_DIRECTIONS; i++)
12598   {
12599     int xx = x + xy[i][0];
12600     int yy = y + xy[i][1];
12601     int border_side = trigger_sides[i][1];
12602     int border_element = border_element_old[i];
12603
12604     if (border_element == -1)
12605       continue;
12606
12607     /* check for change of center element (but change it only once) */
12608     if (!change_center_element)
12609       change_center_element =
12610         CheckElementChangeBySide(x, y, center_element, border_element,
12611                                  CE_TOUCHING_X, border_side);
12612
12613     if (IS_PLAYER(xx, yy))
12614     {
12615       /* use player element that is initially defined in the level playfield,
12616          not the player element that corresponds to the runtime player number
12617          (example: a level that contains EL_PLAYER_3 as the only player would
12618          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12619       int player_element = PLAYERINFO(xx, yy)->initial_element;
12620
12621       CheckElementChangeBySide(x, y, center_element, player_element,
12622                                CE_TOUCHING_X, border_side);
12623     }
12624   }
12625 }
12626
12627 void TestIfElementHitsCustomElement(int x, int y, int direction)
12628 {
12629   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12630   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12631   int hitx = x + dx, hity = y + dy;
12632   int hitting_element = Feld[x][y];
12633   int touched_element;
12634
12635   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12636     return;
12637
12638   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12639                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12640
12641   if (IN_LEV_FIELD(hitx, hity))
12642   {
12643     int opposite_direction = MV_DIR_OPPOSITE(direction);
12644     int hitting_side = direction;
12645     int touched_side = opposite_direction;
12646     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12647                           MovDir[hitx][hity] != direction ||
12648                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12649
12650     object_hit = TRUE;
12651
12652     if (object_hit)
12653     {
12654       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12655                                CE_HITTING_X, touched_side);
12656
12657       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12658                                CE_HIT_BY_X, hitting_side);
12659
12660       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12661                                CE_HIT_BY_SOMETHING, opposite_direction);
12662
12663       if (IS_PLAYER(hitx, hity))
12664       {
12665         /* use player element that is initially defined in the level playfield,
12666            not the player element that corresponds to the runtime player number
12667            (example: a level that contains EL_PLAYER_3 as the only player would
12668            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12669         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12670
12671         CheckElementChangeBySide(x, y, hitting_element, player_element,
12672                                  CE_HITTING_X, touched_side);
12673       }
12674     }
12675   }
12676
12677   /* "hitting something" is also true when hitting the playfield border */
12678   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12679                            CE_HITTING_SOMETHING, direction);
12680 }
12681
12682 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12683 {
12684   int i, kill_x = -1, kill_y = -1;
12685
12686   int bad_element = -1;
12687   static int test_xy[4][2] =
12688   {
12689     { 0, -1 },
12690     { -1, 0 },
12691     { +1, 0 },
12692     { 0, +1 }
12693   };
12694   static int test_dir[4] =
12695   {
12696     MV_UP,
12697     MV_LEFT,
12698     MV_RIGHT,
12699     MV_DOWN
12700   };
12701
12702   for (i = 0; i < NUM_DIRECTIONS; i++)
12703   {
12704     int test_x, test_y, test_move_dir, test_element;
12705
12706     test_x = good_x + test_xy[i][0];
12707     test_y = good_y + test_xy[i][1];
12708
12709     if (!IN_LEV_FIELD(test_x, test_y))
12710       continue;
12711
12712     test_move_dir =
12713       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12714
12715     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12716
12717     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12718        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12719     */
12720     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12721         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12722     {
12723       kill_x = test_x;
12724       kill_y = test_y;
12725       bad_element = test_element;
12726
12727       break;
12728     }
12729   }
12730
12731   if (kill_x != -1 || kill_y != -1)
12732   {
12733     if (IS_PLAYER(good_x, good_y))
12734     {
12735       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12736
12737       if (player->shield_deadly_time_left > 0 &&
12738           !IS_INDESTRUCTIBLE(bad_element))
12739         Bang(kill_x, kill_y);
12740       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12741         KillPlayer(player);
12742     }
12743     else
12744       Bang(good_x, good_y);
12745   }
12746 }
12747
12748 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12749 {
12750   int i, kill_x = -1, kill_y = -1;
12751   int bad_element = Feld[bad_x][bad_y];
12752   static int test_xy[4][2] =
12753   {
12754     { 0, -1 },
12755     { -1, 0 },
12756     { +1, 0 },
12757     { 0, +1 }
12758   };
12759   static int touch_dir[4] =
12760   {
12761     MV_LEFT | MV_RIGHT,
12762     MV_UP   | MV_DOWN,
12763     MV_UP   | MV_DOWN,
12764     MV_LEFT | MV_RIGHT
12765   };
12766   static int test_dir[4] =
12767   {
12768     MV_UP,
12769     MV_LEFT,
12770     MV_RIGHT,
12771     MV_DOWN
12772   };
12773
12774   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12775     return;
12776
12777   for (i = 0; i < NUM_DIRECTIONS; i++)
12778   {
12779     int test_x, test_y, test_move_dir, test_element;
12780
12781     test_x = bad_x + test_xy[i][0];
12782     test_y = bad_y + test_xy[i][1];
12783
12784     if (!IN_LEV_FIELD(test_x, test_y))
12785       continue;
12786
12787     test_move_dir =
12788       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12789
12790     test_element = Feld[test_x][test_y];
12791
12792     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12793        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12794     */
12795     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12796         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12797     {
12798       /* good thing is player or penguin that does not move away */
12799       if (IS_PLAYER(test_x, test_y))
12800       {
12801         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12802
12803         if (bad_element == EL_ROBOT && player->is_moving)
12804           continue;     /* robot does not kill player if he is moving */
12805
12806         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12807         {
12808           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12809             continue;           /* center and border element do not touch */
12810         }
12811
12812         kill_x = test_x;
12813         kill_y = test_y;
12814
12815         break;
12816       }
12817       else if (test_element == EL_PENGUIN)
12818       {
12819         kill_x = test_x;
12820         kill_y = test_y;
12821
12822         break;
12823       }
12824     }
12825   }
12826
12827   if (kill_x != -1 || kill_y != -1)
12828   {
12829     if (IS_PLAYER(kill_x, kill_y))
12830     {
12831       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12832
12833       if (player->shield_deadly_time_left > 0 &&
12834           !IS_INDESTRUCTIBLE(bad_element))
12835         Bang(bad_x, bad_y);
12836       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12837         KillPlayer(player);
12838     }
12839     else
12840       Bang(kill_x, kill_y);
12841   }
12842 }
12843
12844 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12845 {
12846   int bad_element = Feld[bad_x][bad_y];
12847   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12848   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12849   int test_x = bad_x + dx, test_y = bad_y + dy;
12850   int test_move_dir, test_element;
12851   int kill_x = -1, kill_y = -1;
12852
12853   if (!IN_LEV_FIELD(test_x, test_y))
12854     return;
12855
12856   test_move_dir =
12857     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12858
12859   test_element = Feld[test_x][test_y];
12860
12861   if (test_move_dir != bad_move_dir)
12862   {
12863     /* good thing can be player or penguin that does not move away */
12864     if (IS_PLAYER(test_x, test_y))
12865     {
12866       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12867
12868       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12869          player as being hit when he is moving towards the bad thing, because
12870          the "get hit by" condition would be lost after the player stops) */
12871       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12872         return;         /* player moves away from bad thing */
12873
12874       kill_x = test_x;
12875       kill_y = test_y;
12876     }
12877     else if (test_element == EL_PENGUIN)
12878     {
12879       kill_x = test_x;
12880       kill_y = test_y;
12881     }
12882   }
12883
12884   if (kill_x != -1 || kill_y != -1)
12885   {
12886     if (IS_PLAYER(kill_x, kill_y))
12887     {
12888       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12889
12890       if (player->shield_deadly_time_left > 0 &&
12891           !IS_INDESTRUCTIBLE(bad_element))
12892         Bang(bad_x, bad_y);
12893       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12894         KillPlayer(player);
12895     }
12896     else
12897       Bang(kill_x, kill_y);
12898   }
12899 }
12900
12901 void TestIfPlayerTouchesBadThing(int x, int y)
12902 {
12903   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12904 }
12905
12906 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12907 {
12908   TestIfGoodThingHitsBadThing(x, y, move_dir);
12909 }
12910
12911 void TestIfBadThingTouchesPlayer(int x, int y)
12912 {
12913   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12914 }
12915
12916 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12917 {
12918   TestIfBadThingHitsGoodThing(x, y, move_dir);
12919 }
12920
12921 void TestIfFriendTouchesBadThing(int x, int y)
12922 {
12923   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12924 }
12925
12926 void TestIfBadThingTouchesFriend(int x, int y)
12927 {
12928   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12929 }
12930
12931 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12932 {
12933   int i, kill_x = bad_x, kill_y = bad_y;
12934   static int xy[4][2] =
12935   {
12936     { 0, -1 },
12937     { -1, 0 },
12938     { +1, 0 },
12939     { 0, +1 }
12940   };
12941
12942   for (i = 0; i < NUM_DIRECTIONS; i++)
12943   {
12944     int x, y, element;
12945
12946     x = bad_x + xy[i][0];
12947     y = bad_y + xy[i][1];
12948     if (!IN_LEV_FIELD(x, y))
12949       continue;
12950
12951     element = Feld[x][y];
12952     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12953         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12954     {
12955       kill_x = x;
12956       kill_y = y;
12957       break;
12958     }
12959   }
12960
12961   if (kill_x != bad_x || kill_y != bad_y)
12962     Bang(bad_x, bad_y);
12963 }
12964
12965 void KillPlayer(struct PlayerInfo *player)
12966 {
12967   int jx = player->jx, jy = player->jy;
12968
12969   if (!player->active)
12970     return;
12971
12972 #if 0
12973   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12974          player->killed, player->active, player->reanimated);
12975 #endif
12976
12977   /* the following code was introduced to prevent an infinite loop when calling
12978      -> Bang()
12979      -> CheckTriggeredElementChangeExt()
12980      -> ExecuteCustomElementAction()
12981      -> KillPlayer()
12982      -> (infinitely repeating the above sequence of function calls)
12983      which occurs when killing the player while having a CE with the setting
12984      "kill player X when explosion of <player X>"; the solution using a new
12985      field "player->killed" was chosen for backwards compatibility, although
12986      clever use of the fields "player->active" etc. would probably also work */
12987 #if 1
12988   if (player->killed)
12989     return;
12990 #endif
12991
12992   player->killed = TRUE;
12993
12994   /* remove accessible field at the player's position */
12995   Feld[jx][jy] = EL_EMPTY;
12996
12997   /* deactivate shield (else Bang()/Explode() would not work right) */
12998   player->shield_normal_time_left = 0;
12999   player->shield_deadly_time_left = 0;
13000
13001 #if 0
13002   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13003          player->killed, player->active, player->reanimated);
13004 #endif
13005
13006   Bang(jx, jy);
13007
13008 #if 0
13009   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13010          player->killed, player->active, player->reanimated);
13011 #endif
13012
13013   if (player->reanimated)       /* killed player may have been reanimated */
13014     player->killed = player->reanimated = FALSE;
13015   else
13016     BuryPlayer(player);
13017 }
13018
13019 static void KillPlayerUnlessEnemyProtected(int x, int y)
13020 {
13021   if (!PLAYER_ENEMY_PROTECTED(x, y))
13022     KillPlayer(PLAYERINFO(x, y));
13023 }
13024
13025 static void KillPlayerUnlessExplosionProtected(int x, int y)
13026 {
13027   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13028     KillPlayer(PLAYERINFO(x, y));
13029 }
13030
13031 void BuryPlayer(struct PlayerInfo *player)
13032 {
13033   int jx = player->jx, jy = player->jy;
13034
13035   if (!player->active)
13036     return;
13037
13038   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13039   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13040
13041   player->GameOver = TRUE;
13042   RemovePlayer(player);
13043 }
13044
13045 void RemovePlayer(struct PlayerInfo *player)
13046 {
13047   int jx = player->jx, jy = player->jy;
13048   int i, found = FALSE;
13049
13050   player->present = FALSE;
13051   player->active = FALSE;
13052
13053   if (!ExplodeField[jx][jy])
13054     StorePlayer[jx][jy] = 0;
13055
13056   if (player->is_moving)
13057     TEST_DrawLevelField(player->last_jx, player->last_jy);
13058
13059   for (i = 0; i < MAX_PLAYERS; i++)
13060     if (stored_player[i].active)
13061       found = TRUE;
13062
13063   if (!found)
13064     AllPlayersGone = TRUE;
13065
13066   ExitX = ZX = jx;
13067   ExitY = ZY = jy;
13068 }
13069
13070 static void setFieldForSnapping(int x, int y, int element, int direction)
13071 {
13072   struct ElementInfo *ei = &element_info[element];
13073   int direction_bit = MV_DIR_TO_BIT(direction);
13074   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13075   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13076                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13077
13078   Feld[x][y] = EL_ELEMENT_SNAPPING;
13079   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13080
13081   ResetGfxAnimation(x, y);
13082
13083   GfxElement[x][y] = element;
13084   GfxAction[x][y] = action;
13085   GfxDir[x][y] = direction;
13086   GfxFrame[x][y] = -1;
13087 }
13088
13089 /*
13090   =============================================================================
13091   checkDiagonalPushing()
13092   -----------------------------------------------------------------------------
13093   check if diagonal input device direction results in pushing of object
13094   (by checking if the alternative direction is walkable, diggable, ...)
13095   =============================================================================
13096 */
13097
13098 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13099                                     int x, int y, int real_dx, int real_dy)
13100 {
13101   int jx, jy, dx, dy, xx, yy;
13102
13103   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13104     return TRUE;
13105
13106   /* diagonal direction: check alternative direction */
13107   jx = player->jx;
13108   jy = player->jy;
13109   dx = x - jx;
13110   dy = y - jy;
13111   xx = jx + (dx == 0 ? real_dx : 0);
13112   yy = jy + (dy == 0 ? real_dy : 0);
13113
13114   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13115 }
13116
13117 /*
13118   =============================================================================
13119   DigField()
13120   -----------------------------------------------------------------------------
13121   x, y:                 field next to player (non-diagonal) to try to dig to
13122   real_dx, real_dy:     direction as read from input device (can be diagonal)
13123   =============================================================================
13124 */
13125
13126 static int DigField(struct PlayerInfo *player,
13127                     int oldx, int oldy, int x, int y,
13128                     int real_dx, int real_dy, int mode)
13129 {
13130   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13131   boolean player_was_pushing = player->is_pushing;
13132   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13133   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13134   int jx = oldx, jy = oldy;
13135   int dx = x - jx, dy = y - jy;
13136   int nextx = x + dx, nexty = y + dy;
13137   int move_direction = (dx == -1 ? MV_LEFT  :
13138                         dx == +1 ? MV_RIGHT :
13139                         dy == -1 ? MV_UP    :
13140                         dy == +1 ? MV_DOWN  : MV_NONE);
13141   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13142   int dig_side = MV_DIR_OPPOSITE(move_direction);
13143   int old_element = Feld[jx][jy];
13144   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13145   int collect_count;
13146
13147   if (is_player)                /* function can also be called by EL_PENGUIN */
13148   {
13149     if (player->MovPos == 0)
13150     {
13151       player->is_digging = FALSE;
13152       player->is_collecting = FALSE;
13153     }
13154
13155     if (player->MovPos == 0)    /* last pushing move finished */
13156       player->is_pushing = FALSE;
13157
13158     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13159     {
13160       player->is_switching = FALSE;
13161       player->push_delay = -1;
13162
13163       return MP_NO_ACTION;
13164     }
13165   }
13166
13167   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13168     old_element = Back[jx][jy];
13169
13170   /* in case of element dropped at player position, check background */
13171   else if (Back[jx][jy] != EL_EMPTY &&
13172            game.engine_version >= VERSION_IDENT(2,2,0,0))
13173     old_element = Back[jx][jy];
13174
13175   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13176     return MP_NO_ACTION;        /* field has no opening in this direction */
13177
13178   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13179     return MP_NO_ACTION;        /* field has no opening in this direction */
13180
13181   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13182   {
13183     SplashAcid(x, y);
13184
13185     Feld[jx][jy] = player->artwork_element;
13186     InitMovingField(jx, jy, MV_DOWN);
13187     Store[jx][jy] = EL_ACID;
13188     ContinueMoving(jx, jy);
13189     BuryPlayer(player);
13190
13191     return MP_DONT_RUN_INTO;
13192   }
13193
13194   if (player_can_move && DONT_RUN_INTO(element))
13195   {
13196     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13197
13198     return MP_DONT_RUN_INTO;
13199   }
13200
13201   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13202     return MP_NO_ACTION;
13203
13204   collect_count = element_info[element].collect_count_initial;
13205
13206   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13207     return MP_NO_ACTION;
13208
13209   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13210     player_can_move = player_can_move_or_snap;
13211
13212   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13213       game.engine_version >= VERSION_IDENT(2,2,0,0))
13214   {
13215     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13216                                player->index_bit, dig_side);
13217     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13218                                         player->index_bit, dig_side);
13219
13220     if (element == EL_DC_LANDMINE)
13221       Bang(x, y);
13222
13223     if (Feld[x][y] != element)          /* field changed by snapping */
13224       return MP_ACTION;
13225
13226     return MP_NO_ACTION;
13227   }
13228
13229   if (player->gravity && is_player && !player->is_auto_moving &&
13230       canFallDown(player) && move_direction != MV_DOWN &&
13231       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13232     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13233
13234   if (player_can_move &&
13235       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13236   {
13237     int sound_element = SND_ELEMENT(element);
13238     int sound_action = ACTION_WALKING;
13239
13240     if (IS_RND_GATE(element))
13241     {
13242       if (!player->key[RND_GATE_NR(element)])
13243         return MP_NO_ACTION;
13244     }
13245     else if (IS_RND_GATE_GRAY(element))
13246     {
13247       if (!player->key[RND_GATE_GRAY_NR(element)])
13248         return MP_NO_ACTION;
13249     }
13250     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13251     {
13252       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13253         return MP_NO_ACTION;
13254     }
13255     else if (element == EL_EXIT_OPEN ||
13256              element == EL_EM_EXIT_OPEN ||
13257              element == EL_EM_EXIT_OPENING ||
13258              element == EL_STEEL_EXIT_OPEN ||
13259              element == EL_EM_STEEL_EXIT_OPEN ||
13260              element == EL_EM_STEEL_EXIT_OPENING ||
13261              element == EL_SP_EXIT_OPEN ||
13262              element == EL_SP_EXIT_OPENING)
13263     {
13264       sound_action = ACTION_PASSING;    /* player is passing exit */
13265     }
13266     else if (element == EL_EMPTY)
13267     {
13268       sound_action = ACTION_MOVING;             /* nothing to walk on */
13269     }
13270
13271     /* play sound from background or player, whatever is available */
13272     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13273       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13274     else
13275       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13276   }
13277   else if (player_can_move &&
13278            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13279   {
13280     if (!ACCESS_FROM(element, opposite_direction))
13281       return MP_NO_ACTION;      /* field not accessible from this direction */
13282
13283     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13284       return MP_NO_ACTION;
13285
13286     if (IS_EM_GATE(element))
13287     {
13288       if (!player->key[EM_GATE_NR(element)])
13289         return MP_NO_ACTION;
13290     }
13291     else if (IS_EM_GATE_GRAY(element))
13292     {
13293       if (!player->key[EM_GATE_GRAY_NR(element)])
13294         return MP_NO_ACTION;
13295     }
13296     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13297     {
13298       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13299         return MP_NO_ACTION;
13300     }
13301     else if (IS_EMC_GATE(element))
13302     {
13303       if (!player->key[EMC_GATE_NR(element)])
13304         return MP_NO_ACTION;
13305     }
13306     else if (IS_EMC_GATE_GRAY(element))
13307     {
13308       if (!player->key[EMC_GATE_GRAY_NR(element)])
13309         return MP_NO_ACTION;
13310     }
13311     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13312     {
13313       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13314         return MP_NO_ACTION;
13315     }
13316     else if (element == EL_DC_GATE_WHITE ||
13317              element == EL_DC_GATE_WHITE_GRAY ||
13318              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13319     {
13320       if (player->num_white_keys == 0)
13321         return MP_NO_ACTION;
13322
13323       player->num_white_keys--;
13324     }
13325     else if (IS_SP_PORT(element))
13326     {
13327       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13328           element == EL_SP_GRAVITY_PORT_RIGHT ||
13329           element == EL_SP_GRAVITY_PORT_UP ||
13330           element == EL_SP_GRAVITY_PORT_DOWN)
13331         player->gravity = !player->gravity;
13332       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13333                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13334                element == EL_SP_GRAVITY_ON_PORT_UP ||
13335                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13336         player->gravity = TRUE;
13337       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13338                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13339                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13340                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13341         player->gravity = FALSE;
13342     }
13343
13344     /* automatically move to the next field with double speed */
13345     player->programmed_action = move_direction;
13346
13347     if (player->move_delay_reset_counter == 0)
13348     {
13349       player->move_delay_reset_counter = 2;     /* two double speed steps */
13350
13351       DOUBLE_PLAYER_SPEED(player);
13352     }
13353
13354     PlayLevelSoundAction(x, y, ACTION_PASSING);
13355   }
13356   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13357   {
13358     RemoveField(x, y);
13359
13360     if (mode != DF_SNAP)
13361     {
13362       GfxElement[x][y] = GFX_ELEMENT(element);
13363       player->is_digging = TRUE;
13364     }
13365
13366     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13367
13368     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13369                                         player->index_bit, dig_side);
13370
13371     if (mode == DF_SNAP)
13372     {
13373       if (level.block_snap_field)
13374         setFieldForSnapping(x, y, element, move_direction);
13375       else
13376         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13377
13378       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13379                                           player->index_bit, dig_side);
13380     }
13381   }
13382   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13383   {
13384     RemoveField(x, y);
13385
13386     if (is_player && mode != DF_SNAP)
13387     {
13388       GfxElement[x][y] = element;
13389       player->is_collecting = TRUE;
13390     }
13391
13392     if (element == EL_SPEED_PILL)
13393     {
13394       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13395     }
13396     else if (element == EL_EXTRA_TIME && level.time > 0)
13397     {
13398       TimeLeft += level.extra_time;
13399
13400       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13401
13402       DisplayGameControlValues();
13403     }
13404     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13405     {
13406       player->shield_normal_time_left += level.shield_normal_time;
13407       if (element == EL_SHIELD_DEADLY)
13408         player->shield_deadly_time_left += level.shield_deadly_time;
13409     }
13410     else if (element == EL_DYNAMITE ||
13411              element == EL_EM_DYNAMITE ||
13412              element == EL_SP_DISK_RED)
13413     {
13414       if (player->inventory_size < MAX_INVENTORY_SIZE)
13415         player->inventory_element[player->inventory_size++] = element;
13416
13417       DrawGameDoorValues();
13418     }
13419     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13420     {
13421       player->dynabomb_count++;
13422       player->dynabombs_left++;
13423     }
13424     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13425     {
13426       player->dynabomb_size++;
13427     }
13428     else if (element == EL_DYNABOMB_INCREASE_POWER)
13429     {
13430       player->dynabomb_xl = TRUE;
13431     }
13432     else if (IS_KEY(element))
13433     {
13434       player->key[KEY_NR(element)] = TRUE;
13435
13436       DrawGameDoorValues();
13437     }
13438     else if (element == EL_DC_KEY_WHITE)
13439     {
13440       player->num_white_keys++;
13441
13442       /* display white keys? */
13443       /* DrawGameDoorValues(); */
13444     }
13445     else if (IS_ENVELOPE(element))
13446     {
13447       player->show_envelope = element;
13448     }
13449     else if (element == EL_EMC_LENSES)
13450     {
13451       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13452
13453       RedrawAllInvisibleElementsForLenses();
13454     }
13455     else if (element == EL_EMC_MAGNIFIER)
13456     {
13457       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13458
13459       RedrawAllInvisibleElementsForMagnifier();
13460     }
13461     else if (IS_DROPPABLE(element) ||
13462              IS_THROWABLE(element))     /* can be collected and dropped */
13463     {
13464       int i;
13465
13466       if (collect_count == 0)
13467         player->inventory_infinite_element = element;
13468       else
13469         for (i = 0; i < collect_count; i++)
13470           if (player->inventory_size < MAX_INVENTORY_SIZE)
13471             player->inventory_element[player->inventory_size++] = element;
13472
13473       DrawGameDoorValues();
13474     }
13475     else if (collect_count > 0)
13476     {
13477       local_player->gems_still_needed -= collect_count;
13478       if (local_player->gems_still_needed < 0)
13479         local_player->gems_still_needed = 0;
13480
13481       game.snapshot.collected_item = TRUE;
13482
13483       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13484
13485       DisplayGameControlValues();
13486     }
13487
13488     RaiseScoreElement(element);
13489     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13490
13491     if (is_player)
13492       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13493                                           player->index_bit, dig_side);
13494
13495     if (mode == DF_SNAP)
13496     {
13497       if (level.block_snap_field)
13498         setFieldForSnapping(x, y, element, move_direction);
13499       else
13500         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13501
13502       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13503                                           player->index_bit, dig_side);
13504     }
13505   }
13506   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13507   {
13508     if (mode == DF_SNAP && element != EL_BD_ROCK)
13509       return MP_NO_ACTION;
13510
13511     if (CAN_FALL(element) && dy)
13512       return MP_NO_ACTION;
13513
13514     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13515         !(element == EL_SPRING && level.use_spring_bug))
13516       return MP_NO_ACTION;
13517
13518     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13519         ((move_direction & MV_VERTICAL &&
13520           ((element_info[element].move_pattern & MV_LEFT &&
13521             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13522            (element_info[element].move_pattern & MV_RIGHT &&
13523             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13524          (move_direction & MV_HORIZONTAL &&
13525           ((element_info[element].move_pattern & MV_UP &&
13526             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13527            (element_info[element].move_pattern & MV_DOWN &&
13528             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13529       return MP_NO_ACTION;
13530
13531     /* do not push elements already moving away faster than player */
13532     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13533         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13534       return MP_NO_ACTION;
13535
13536     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13537     {
13538       if (player->push_delay_value == -1 || !player_was_pushing)
13539         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13540     }
13541     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13542     {
13543       if (player->push_delay_value == -1)
13544         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13545     }
13546     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13547     {
13548       if (!player->is_pushing)
13549         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13550     }
13551
13552     player->is_pushing = TRUE;
13553     player->is_active = TRUE;
13554
13555     if (!(IN_LEV_FIELD(nextx, nexty) &&
13556           (IS_FREE(nextx, nexty) ||
13557            (IS_SB_ELEMENT(element) &&
13558             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13559            (IS_CUSTOM_ELEMENT(element) &&
13560             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13561       return MP_NO_ACTION;
13562
13563     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13564       return MP_NO_ACTION;
13565
13566     if (player->push_delay == -1)       /* new pushing; restart delay */
13567       player->push_delay = 0;
13568
13569     if (player->push_delay < player->push_delay_value &&
13570         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13571         element != EL_SPRING && element != EL_BALLOON)
13572     {
13573       /* make sure that there is no move delay before next try to push */
13574       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13575         player->move_delay = 0;
13576
13577       return MP_NO_ACTION;
13578     }
13579
13580     if (IS_CUSTOM_ELEMENT(element) &&
13581         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13582     {
13583       if (!DigFieldByCE(nextx, nexty, element))
13584         return MP_NO_ACTION;
13585     }
13586
13587     if (IS_SB_ELEMENT(element))
13588     {
13589       if (element == EL_SOKOBAN_FIELD_FULL)
13590       {
13591         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13592         local_player->sokobanfields_still_needed++;
13593       }
13594
13595       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13596       {
13597         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13598         local_player->sokobanfields_still_needed--;
13599       }
13600
13601       Feld[x][y] = EL_SOKOBAN_OBJECT;
13602
13603       if (Back[x][y] == Back[nextx][nexty])
13604         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13605       else if (Back[x][y] != 0)
13606         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13607                                     ACTION_EMPTYING);
13608       else
13609         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13610                                     ACTION_FILLING);
13611
13612       if (local_player->sokobanfields_still_needed == 0 &&
13613           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13614       {
13615         PlayerWins(player);
13616
13617         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13618       }
13619     }
13620     else
13621       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13622
13623     InitMovingField(x, y, move_direction);
13624     GfxAction[x][y] = ACTION_PUSHING;
13625
13626     if (mode == DF_SNAP)
13627       ContinueMoving(x, y);
13628     else
13629       MovPos[x][y] = (dx != 0 ? dx : dy);
13630
13631     Pushed[x][y] = TRUE;
13632     Pushed[nextx][nexty] = TRUE;
13633
13634     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13635       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13636     else
13637       player->push_delay_value = -1;    /* get new value later */
13638
13639     /* check for element change _after_ element has been pushed */
13640     if (game.use_change_when_pushing_bug)
13641     {
13642       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13643                                  player->index_bit, dig_side);
13644       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13645                                           player->index_bit, dig_side);
13646     }
13647   }
13648   else if (IS_SWITCHABLE(element))
13649   {
13650     if (PLAYER_SWITCHING(player, x, y))
13651     {
13652       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13653                                           player->index_bit, dig_side);
13654
13655       return MP_ACTION;
13656     }
13657
13658     player->is_switching = TRUE;
13659     player->switch_x = x;
13660     player->switch_y = y;
13661
13662     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13663
13664     if (element == EL_ROBOT_WHEEL)
13665     {
13666       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13667       ZX = x;
13668       ZY = y;
13669
13670       game.robot_wheel_active = TRUE;
13671
13672       TEST_DrawLevelField(x, y);
13673     }
13674     else if (element == EL_SP_TERMINAL)
13675     {
13676       int xx, yy;
13677
13678       SCAN_PLAYFIELD(xx, yy)
13679       {
13680         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13681         {
13682           Bang(xx, yy);
13683         }
13684         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13685         {
13686           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13687
13688           ResetGfxAnimation(xx, yy);
13689           TEST_DrawLevelField(xx, yy);
13690         }
13691       }
13692     }
13693     else if (IS_BELT_SWITCH(element))
13694     {
13695       ToggleBeltSwitch(x, y);
13696     }
13697     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13698              element == EL_SWITCHGATE_SWITCH_DOWN ||
13699              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13700              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13701     {
13702       ToggleSwitchgateSwitch(x, y);
13703     }
13704     else if (element == EL_LIGHT_SWITCH ||
13705              element == EL_LIGHT_SWITCH_ACTIVE)
13706     {
13707       ToggleLightSwitch(x, y);
13708     }
13709     else if (element == EL_TIMEGATE_SWITCH ||
13710              element == EL_DC_TIMEGATE_SWITCH)
13711     {
13712       ActivateTimegateSwitch(x, y);
13713     }
13714     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13715              element == EL_BALLOON_SWITCH_RIGHT ||
13716              element == EL_BALLOON_SWITCH_UP    ||
13717              element == EL_BALLOON_SWITCH_DOWN  ||
13718              element == EL_BALLOON_SWITCH_NONE  ||
13719              element == EL_BALLOON_SWITCH_ANY)
13720     {
13721       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13722                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13723                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13724                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13725                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13726                              move_direction);
13727     }
13728     else if (element == EL_LAMP)
13729     {
13730       Feld[x][y] = EL_LAMP_ACTIVE;
13731       local_player->lights_still_needed--;
13732
13733       ResetGfxAnimation(x, y);
13734       TEST_DrawLevelField(x, y);
13735     }
13736     else if (element == EL_TIME_ORB_FULL)
13737     {
13738       Feld[x][y] = EL_TIME_ORB_EMPTY;
13739
13740       if (level.time > 0 || level.use_time_orb_bug)
13741       {
13742         TimeLeft += level.time_orb_time;
13743         game.no_time_limit = FALSE;
13744
13745         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13746
13747         DisplayGameControlValues();
13748       }
13749
13750       ResetGfxAnimation(x, y);
13751       TEST_DrawLevelField(x, y);
13752     }
13753     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13754              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13755     {
13756       int xx, yy;
13757
13758       game.ball_state = !game.ball_state;
13759
13760       SCAN_PLAYFIELD(xx, yy)
13761       {
13762         int e = Feld[xx][yy];
13763
13764         if (game.ball_state)
13765         {
13766           if (e == EL_EMC_MAGIC_BALL)
13767             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13768           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13769             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13770         }
13771         else
13772         {
13773           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13774             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13775           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13776             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13777         }
13778       }
13779     }
13780
13781     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13782                                         player->index_bit, dig_side);
13783
13784     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13785                                         player->index_bit, dig_side);
13786
13787     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13788                                         player->index_bit, dig_side);
13789
13790     return MP_ACTION;
13791   }
13792   else
13793   {
13794     if (!PLAYER_SWITCHING(player, x, y))
13795     {
13796       player->is_switching = TRUE;
13797       player->switch_x = x;
13798       player->switch_y = y;
13799
13800       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13801                                  player->index_bit, dig_side);
13802       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13803                                           player->index_bit, dig_side);
13804
13805       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13806                                  player->index_bit, dig_side);
13807       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13808                                           player->index_bit, dig_side);
13809     }
13810
13811     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13812                                player->index_bit, dig_side);
13813     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13814                                         player->index_bit, dig_side);
13815
13816     return MP_NO_ACTION;
13817   }
13818
13819   player->push_delay = -1;
13820
13821   if (is_player)                /* function can also be called by EL_PENGUIN */
13822   {
13823     if (Feld[x][y] != element)          /* really digged/collected something */
13824     {
13825       player->is_collecting = !player->is_digging;
13826       player->is_active = TRUE;
13827     }
13828   }
13829
13830   return MP_MOVING;
13831 }
13832
13833 static boolean DigFieldByCE(int x, int y, int digging_element)
13834 {
13835   int element = Feld[x][y];
13836
13837   if (!IS_FREE(x, y))
13838   {
13839     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13840                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13841                   ACTION_BREAKING);
13842
13843     /* no element can dig solid indestructible elements */
13844     if (IS_INDESTRUCTIBLE(element) &&
13845         !IS_DIGGABLE(element) &&
13846         !IS_COLLECTIBLE(element))
13847       return FALSE;
13848
13849     if (AmoebaNr[x][y] &&
13850         (element == EL_AMOEBA_FULL ||
13851          element == EL_BD_AMOEBA ||
13852          element == EL_AMOEBA_GROWING))
13853     {
13854       AmoebaCnt[AmoebaNr[x][y]]--;
13855       AmoebaCnt2[AmoebaNr[x][y]]--;
13856     }
13857
13858     if (IS_MOVING(x, y))
13859       RemoveMovingField(x, y);
13860     else
13861     {
13862       RemoveField(x, y);
13863       TEST_DrawLevelField(x, y);
13864     }
13865
13866     /* if digged element was about to explode, prevent the explosion */
13867     ExplodeField[x][y] = EX_TYPE_NONE;
13868
13869     PlayLevelSoundAction(x, y, action);
13870   }
13871
13872   Store[x][y] = EL_EMPTY;
13873
13874   /* this makes it possible to leave the removed element again */
13875   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13876     Store[x][y] = element;
13877
13878   return TRUE;
13879 }
13880
13881 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13882 {
13883   int jx = player->jx, jy = player->jy;
13884   int x = jx + dx, y = jy + dy;
13885   int snap_direction = (dx == -1 ? MV_LEFT  :
13886                         dx == +1 ? MV_RIGHT :
13887                         dy == -1 ? MV_UP    :
13888                         dy == +1 ? MV_DOWN  : MV_NONE);
13889   boolean can_continue_snapping = (level.continuous_snapping &&
13890                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13891
13892   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13893     return FALSE;
13894
13895   if (!player->active || !IN_LEV_FIELD(x, y))
13896     return FALSE;
13897
13898   if (dx && dy)
13899     return FALSE;
13900
13901   if (!dx && !dy)
13902   {
13903     if (player->MovPos == 0)
13904       player->is_pushing = FALSE;
13905
13906     player->is_snapping = FALSE;
13907
13908     if (player->MovPos == 0)
13909     {
13910       player->is_moving = FALSE;
13911       player->is_digging = FALSE;
13912       player->is_collecting = FALSE;
13913     }
13914
13915     return FALSE;
13916   }
13917
13918   /* prevent snapping with already pressed snap key when not allowed */
13919   if (player->is_snapping && !can_continue_snapping)
13920     return FALSE;
13921
13922   player->MovDir = snap_direction;
13923
13924   if (player->MovPos == 0)
13925   {
13926     player->is_moving = FALSE;
13927     player->is_digging = FALSE;
13928     player->is_collecting = FALSE;
13929   }
13930
13931   player->is_dropping = FALSE;
13932   player->is_dropping_pressed = FALSE;
13933   player->drop_pressed_delay = 0;
13934
13935   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13936     return FALSE;
13937
13938   player->is_snapping = TRUE;
13939   player->is_active = TRUE;
13940
13941   if (player->MovPos == 0)
13942   {
13943     player->is_moving = FALSE;
13944     player->is_digging = FALSE;
13945     player->is_collecting = FALSE;
13946   }
13947
13948   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13949     TEST_DrawLevelField(player->last_jx, player->last_jy);
13950
13951   TEST_DrawLevelField(x, y);
13952
13953   return TRUE;
13954 }
13955
13956 static boolean DropElement(struct PlayerInfo *player)
13957 {
13958   int old_element, new_element;
13959   int dropx = player->jx, dropy = player->jy;
13960   int drop_direction = player->MovDir;
13961   int drop_side = drop_direction;
13962   int drop_element = get_next_dropped_element(player);
13963
13964   player->is_dropping_pressed = TRUE;
13965
13966   /* do not drop an element on top of another element; when holding drop key
13967      pressed without moving, dropped element must move away before the next
13968      element can be dropped (this is especially important if the next element
13969      is dynamite, which can be placed on background for historical reasons) */
13970   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13971     return MP_ACTION;
13972
13973   if (IS_THROWABLE(drop_element))
13974   {
13975     dropx += GET_DX_FROM_DIR(drop_direction);
13976     dropy += GET_DY_FROM_DIR(drop_direction);
13977
13978     if (!IN_LEV_FIELD(dropx, dropy))
13979       return FALSE;
13980   }
13981
13982   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13983   new_element = drop_element;           /* default: no change when dropping */
13984
13985   /* check if player is active, not moving and ready to drop */
13986   if (!player->active || player->MovPos || player->drop_delay > 0)
13987     return FALSE;
13988
13989   /* check if player has anything that can be dropped */
13990   if (new_element == EL_UNDEFINED)
13991     return FALSE;
13992
13993   /* check if drop key was pressed long enough for EM style dynamite */
13994   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13995     return FALSE;
13996
13997   /* check if anything can be dropped at the current position */
13998   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13999     return FALSE;
14000
14001   /* collected custom elements can only be dropped on empty fields */
14002   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14003     return FALSE;
14004
14005   if (old_element != EL_EMPTY)
14006     Back[dropx][dropy] = old_element;   /* store old element on this field */
14007
14008   ResetGfxAnimation(dropx, dropy);
14009   ResetRandomAnimationValue(dropx, dropy);
14010
14011   if (player->inventory_size > 0 ||
14012       player->inventory_infinite_element != EL_UNDEFINED)
14013   {
14014     if (player->inventory_size > 0)
14015     {
14016       player->inventory_size--;
14017
14018       DrawGameDoorValues();
14019
14020       if (new_element == EL_DYNAMITE)
14021         new_element = EL_DYNAMITE_ACTIVE;
14022       else if (new_element == EL_EM_DYNAMITE)
14023         new_element = EL_EM_DYNAMITE_ACTIVE;
14024       else if (new_element == EL_SP_DISK_RED)
14025         new_element = EL_SP_DISK_RED_ACTIVE;
14026     }
14027
14028     Feld[dropx][dropy] = new_element;
14029
14030     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14031       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14032                           el2img(Feld[dropx][dropy]), 0);
14033
14034     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14035
14036     /* needed if previous element just changed to "empty" in the last frame */
14037     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14038
14039     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14040                                player->index_bit, drop_side);
14041     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14042                                         CE_PLAYER_DROPS_X,
14043                                         player->index_bit, drop_side);
14044
14045     TestIfElementTouchesCustomElement(dropx, dropy);
14046   }
14047   else          /* player is dropping a dyna bomb */
14048   {
14049     player->dynabombs_left--;
14050
14051     Feld[dropx][dropy] = new_element;
14052
14053     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14054       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14055                           el2img(Feld[dropx][dropy]), 0);
14056
14057     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14058   }
14059
14060   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14061     InitField_WithBug1(dropx, dropy, FALSE);
14062
14063   new_element = Feld[dropx][dropy];     /* element might have changed */
14064
14065   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14066       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14067   {
14068     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14069       MovDir[dropx][dropy] = drop_direction;
14070
14071     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14072
14073     /* do not cause impact style collision by dropping elements that can fall */
14074     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14075   }
14076
14077   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14078   player->is_dropping = TRUE;
14079
14080   player->drop_pressed_delay = 0;
14081   player->is_dropping_pressed = FALSE;
14082
14083   player->drop_x = dropx;
14084   player->drop_y = dropy;
14085
14086   return TRUE;
14087 }
14088
14089 /* ------------------------------------------------------------------------- */
14090 /* game sound playing functions                                              */
14091 /* ------------------------------------------------------------------------- */
14092
14093 static int *loop_sound_frame = NULL;
14094 static int *loop_sound_volume = NULL;
14095
14096 void InitPlayLevelSound()
14097 {
14098   int num_sounds = getSoundListSize();
14099
14100   checked_free(loop_sound_frame);
14101   checked_free(loop_sound_volume);
14102
14103   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14104   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14105 }
14106
14107 static void PlayLevelSound(int x, int y, int nr)
14108 {
14109   int sx = SCREENX(x), sy = SCREENY(y);
14110   int volume, stereo_position;
14111   int max_distance = 8;
14112   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14113
14114   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14115       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14116     return;
14117
14118   if (!IN_LEV_FIELD(x, y) ||
14119       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14120       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14121     return;
14122
14123   volume = SOUND_MAX_VOLUME;
14124
14125   if (!IN_SCR_FIELD(sx, sy))
14126   {
14127     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14128     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14129
14130     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14131   }
14132
14133   stereo_position = (SOUND_MAX_LEFT +
14134                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14135                      (SCR_FIELDX + 2 * max_distance));
14136
14137   if (IS_LOOP_SOUND(nr))
14138   {
14139     /* This assures that quieter loop sounds do not overwrite louder ones,
14140        while restarting sound volume comparison with each new game frame. */
14141
14142     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14143       return;
14144
14145     loop_sound_volume[nr] = volume;
14146     loop_sound_frame[nr] = FrameCounter;
14147   }
14148
14149   PlaySoundExt(nr, volume, stereo_position, type);
14150 }
14151
14152 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14153 {
14154   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14155                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14156                  y < LEVELY(BY1) ? LEVELY(BY1) :
14157                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14158                  sound_action);
14159 }
14160
14161 static void PlayLevelSoundAction(int x, int y, int action)
14162 {
14163   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14164 }
14165
14166 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14167 {
14168   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14169
14170   if (sound_effect != SND_UNDEFINED)
14171     PlayLevelSound(x, y, sound_effect);
14172 }
14173
14174 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14175                                               int action)
14176 {
14177   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14178
14179   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14180     PlayLevelSound(x, y, sound_effect);
14181 }
14182
14183 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14184 {
14185   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14186
14187   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14188     PlayLevelSound(x, y, sound_effect);
14189 }
14190
14191 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14192 {
14193   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14194
14195   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14196     StopSound(sound_effect);
14197 }
14198
14199 static void PlayLevelMusic()
14200 {
14201   if (levelset.music[level_nr] != MUS_UNDEFINED)
14202     PlayMusic(levelset.music[level_nr]);        /* from config file */
14203   else
14204     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14205 }
14206
14207 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14208 {
14209   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14210   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14211   int x = xx - 1 - offset;
14212   int y = yy - 1 - offset;
14213
14214   switch (sample)
14215   {
14216     case SAMPLE_blank:
14217       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14218       break;
14219
14220     case SAMPLE_roll:
14221       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14222       break;
14223
14224     case SAMPLE_stone:
14225       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14226       break;
14227
14228     case SAMPLE_nut:
14229       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14230       break;
14231
14232     case SAMPLE_crack:
14233       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14234       break;
14235
14236     case SAMPLE_bug:
14237       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14238       break;
14239
14240     case SAMPLE_tank:
14241       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14242       break;
14243
14244     case SAMPLE_android_clone:
14245       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14246       break;
14247
14248     case SAMPLE_android_move:
14249       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14250       break;
14251
14252     case SAMPLE_spring:
14253       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14254       break;
14255
14256     case SAMPLE_slurp:
14257       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14258       break;
14259
14260     case SAMPLE_eater:
14261       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14262       break;
14263
14264     case SAMPLE_eater_eat:
14265       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14266       break;
14267
14268     case SAMPLE_alien:
14269       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14270       break;
14271
14272     case SAMPLE_collect:
14273       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14274       break;
14275
14276     case SAMPLE_diamond:
14277       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14278       break;
14279
14280     case SAMPLE_squash:
14281       /* !!! CHECK THIS !!! */
14282 #if 1
14283       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14284 #else
14285       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14286 #endif
14287       break;
14288
14289     case SAMPLE_wonderfall:
14290       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14291       break;
14292
14293     case SAMPLE_drip:
14294       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14295       break;
14296
14297     case SAMPLE_push:
14298       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14299       break;
14300
14301     case SAMPLE_dirt:
14302       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14303       break;
14304
14305     case SAMPLE_acid:
14306       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14307       break;
14308
14309     case SAMPLE_ball:
14310       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14311       break;
14312
14313     case SAMPLE_grow:
14314       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14315       break;
14316
14317     case SAMPLE_wonder:
14318       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14319       break;
14320
14321     case SAMPLE_door:
14322       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14323       break;
14324
14325     case SAMPLE_exit_open:
14326       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14327       break;
14328
14329     case SAMPLE_exit_leave:
14330       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14331       break;
14332
14333     case SAMPLE_dynamite:
14334       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14335       break;
14336
14337     case SAMPLE_tick:
14338       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14339       break;
14340
14341     case SAMPLE_press:
14342       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14343       break;
14344
14345     case SAMPLE_wheel:
14346       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14347       break;
14348
14349     case SAMPLE_boom:
14350       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14351       break;
14352
14353     case SAMPLE_die:
14354       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14355       break;
14356
14357     case SAMPLE_time:
14358       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14359       break;
14360
14361     default:
14362       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14363       break;
14364   }
14365 }
14366
14367 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14368 {
14369   int element = map_element_SP_to_RND(element_sp);
14370   int action = map_action_SP_to_RND(action_sp);
14371   int offset = (setup.sp_show_border_elements ? 0 : 1);
14372   int x = xx - offset;
14373   int y = yy - offset;
14374
14375   PlayLevelSoundElementAction(x, y, element, action);
14376 }
14377
14378 void RaiseScore(int value)
14379 {
14380   local_player->score += value;
14381
14382   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14383
14384   DisplayGameControlValues();
14385 }
14386
14387 void RaiseScoreElement(int element)
14388 {
14389   switch (element)
14390   {
14391     case EL_EMERALD:
14392     case EL_BD_DIAMOND:
14393     case EL_EMERALD_YELLOW:
14394     case EL_EMERALD_RED:
14395     case EL_EMERALD_PURPLE:
14396     case EL_SP_INFOTRON:
14397       RaiseScore(level.score[SC_EMERALD]);
14398       break;
14399     case EL_DIAMOND:
14400       RaiseScore(level.score[SC_DIAMOND]);
14401       break;
14402     case EL_CRYSTAL:
14403       RaiseScore(level.score[SC_CRYSTAL]);
14404       break;
14405     case EL_PEARL:
14406       RaiseScore(level.score[SC_PEARL]);
14407       break;
14408     case EL_BUG:
14409     case EL_BD_BUTTERFLY:
14410     case EL_SP_ELECTRON:
14411       RaiseScore(level.score[SC_BUG]);
14412       break;
14413     case EL_SPACESHIP:
14414     case EL_BD_FIREFLY:
14415     case EL_SP_SNIKSNAK:
14416       RaiseScore(level.score[SC_SPACESHIP]);
14417       break;
14418     case EL_YAMYAM:
14419     case EL_DARK_YAMYAM:
14420       RaiseScore(level.score[SC_YAMYAM]);
14421       break;
14422     case EL_ROBOT:
14423       RaiseScore(level.score[SC_ROBOT]);
14424       break;
14425     case EL_PACMAN:
14426       RaiseScore(level.score[SC_PACMAN]);
14427       break;
14428     case EL_NUT:
14429       RaiseScore(level.score[SC_NUT]);
14430       break;
14431     case EL_DYNAMITE:
14432     case EL_EM_DYNAMITE:
14433     case EL_SP_DISK_RED:
14434     case EL_DYNABOMB_INCREASE_NUMBER:
14435     case EL_DYNABOMB_INCREASE_SIZE:
14436     case EL_DYNABOMB_INCREASE_POWER:
14437       RaiseScore(level.score[SC_DYNAMITE]);
14438       break;
14439     case EL_SHIELD_NORMAL:
14440     case EL_SHIELD_DEADLY:
14441       RaiseScore(level.score[SC_SHIELD]);
14442       break;
14443     case EL_EXTRA_TIME:
14444       RaiseScore(level.extra_time_score);
14445       break;
14446     case EL_KEY_1:
14447     case EL_KEY_2:
14448     case EL_KEY_3:
14449     case EL_KEY_4:
14450     case EL_EM_KEY_1:
14451     case EL_EM_KEY_2:
14452     case EL_EM_KEY_3:
14453     case EL_EM_KEY_4:
14454     case EL_EMC_KEY_5:
14455     case EL_EMC_KEY_6:
14456     case EL_EMC_KEY_7:
14457     case EL_EMC_KEY_8:
14458     case EL_DC_KEY_WHITE:
14459       RaiseScore(level.score[SC_KEY]);
14460       break;
14461     default:
14462       RaiseScore(element_info[element].collect_score);
14463       break;
14464   }
14465 }
14466
14467 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14468 {
14469   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14470   {
14471     /* closing door required in case of envelope style request dialogs */
14472     if (!skip_request)
14473       CloseDoor(DOOR_CLOSE_1);
14474
14475 #if defined(NETWORK_AVALIABLE)
14476     if (options.network)
14477       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14478     else
14479 #endif
14480     {
14481       if (quick_quit)
14482         FadeSkipNextFadeIn();
14483
14484       SetGameStatus(GAME_MODE_MAIN);
14485
14486       DrawMainMenu();
14487     }
14488   }
14489   else          /* continue playing the game */
14490   {
14491     if (tape.playing && tape.deactivate_display)
14492       TapeDeactivateDisplayOff(TRUE);
14493
14494     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14495
14496     if (tape.playing && tape.deactivate_display)
14497       TapeDeactivateDisplayOn();
14498   }
14499 }
14500
14501 void RequestQuitGame(boolean ask_if_really_quit)
14502 {
14503   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14504   boolean skip_request = AllPlayersGone || quick_quit;
14505
14506   RequestQuitGameExt(skip_request, quick_quit,
14507                      "Do you really want to quit the game?");
14508 }
14509
14510
14511 /* ------------------------------------------------------------------------- */
14512 /* random generator functions                                                */
14513 /* ------------------------------------------------------------------------- */
14514
14515 unsigned int InitEngineRandom_RND(int seed)
14516 {
14517   game.num_random_calls = 0;
14518
14519   return InitEngineRandom(seed);
14520 }
14521
14522 unsigned int RND(int max)
14523 {
14524   if (max > 0)
14525   {
14526     game.num_random_calls++;
14527
14528     return GetEngineRandom(max);
14529   }
14530
14531   return 0;
14532 }
14533
14534
14535 /* ------------------------------------------------------------------------- */
14536 /* game engine snapshot handling functions                                   */
14537 /* ------------------------------------------------------------------------- */
14538
14539 struct EngineSnapshotInfo
14540 {
14541   /* runtime values for custom element collect score */
14542   int collect_score[NUM_CUSTOM_ELEMENTS];
14543
14544   /* runtime values for group element choice position */
14545   int choice_pos[NUM_GROUP_ELEMENTS];
14546
14547   /* runtime values for belt position animations */
14548   int belt_graphic[4][NUM_BELT_PARTS];
14549   int belt_anim_mode[4][NUM_BELT_PARTS];
14550 };
14551
14552 static struct EngineSnapshotInfo engine_snapshot_rnd;
14553 static char *snapshot_level_identifier = NULL;
14554 static int snapshot_level_nr = -1;
14555
14556 static void SaveEngineSnapshotValues_RND()
14557 {
14558   static int belt_base_active_element[4] =
14559   {
14560     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14561     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14562     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14563     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14564   };
14565   int i, j;
14566
14567   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14568   {
14569     int element = EL_CUSTOM_START + i;
14570
14571     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14572   }
14573
14574   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14575   {
14576     int element = EL_GROUP_START + i;
14577
14578     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14579   }
14580
14581   for (i = 0; i < 4; i++)
14582   {
14583     for (j = 0; j < NUM_BELT_PARTS; j++)
14584     {
14585       int element = belt_base_active_element[i] + j;
14586       int graphic = el2img(element);
14587       int anim_mode = graphic_info[graphic].anim_mode;
14588
14589       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14590       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14591     }
14592   }
14593 }
14594
14595 static void LoadEngineSnapshotValues_RND()
14596 {
14597   unsigned int num_random_calls = game.num_random_calls;
14598   int i, j;
14599
14600   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14601   {
14602     int element = EL_CUSTOM_START + i;
14603
14604     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14605   }
14606
14607   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14608   {
14609     int element = EL_GROUP_START + i;
14610
14611     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14612   }
14613
14614   for (i = 0; i < 4; i++)
14615   {
14616     for (j = 0; j < NUM_BELT_PARTS; j++)
14617     {
14618       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14619       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14620
14621       graphic_info[graphic].anim_mode = anim_mode;
14622     }
14623   }
14624
14625   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14626   {
14627     InitRND(tape.random_seed);
14628     for (i = 0; i < num_random_calls; i++)
14629       RND(1);
14630   }
14631
14632   if (game.num_random_calls != num_random_calls)
14633   {
14634     Error(ERR_INFO, "number of random calls out of sync");
14635     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14636     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14637     Error(ERR_EXIT, "this should not happen -- please debug");
14638   }
14639 }
14640
14641 void FreeEngineSnapshotSingle()
14642 {
14643   FreeSnapshotSingle();
14644
14645   setString(&snapshot_level_identifier, NULL);
14646   snapshot_level_nr = -1;
14647 }
14648
14649 void FreeEngineSnapshotList()
14650 {
14651   FreeSnapshotList();
14652 }
14653
14654 ListNode *SaveEngineSnapshotBuffers()
14655 {
14656   ListNode *buffers = NULL;
14657
14658   /* copy some special values to a structure better suited for the snapshot */
14659
14660   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14661     SaveEngineSnapshotValues_RND();
14662   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14663     SaveEngineSnapshotValues_EM();
14664   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14665     SaveEngineSnapshotValues_SP(&buffers);
14666
14667   /* save values stored in special snapshot structure */
14668
14669   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14670     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14671   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14672     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14673   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14674     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14675
14676   /* save further RND engine values */
14677
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14681
14682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14686
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14692
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14696
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14698
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14700
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14703
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14722
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14725
14726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14729
14730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14732
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14738
14739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14741
14742 #if 0
14743   ListNode *node = engine_snapshot_list_rnd;
14744   int num_bytes = 0;
14745
14746   while (node != NULL)
14747   {
14748     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14749
14750     node = node->next;
14751   }
14752
14753   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14754 #endif
14755
14756   return buffers;
14757 }
14758
14759 void SaveEngineSnapshotSingle()
14760 {
14761   ListNode *buffers = SaveEngineSnapshotBuffers();
14762
14763   /* finally save all snapshot buffers to single snapshot */
14764   SaveSnapshotSingle(buffers);
14765
14766   /* save level identification information */
14767   setString(&snapshot_level_identifier, leveldir_current->identifier);
14768   snapshot_level_nr = level_nr;
14769 }
14770
14771 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14772 {
14773   boolean save_snapshot =
14774     (initial_snapshot ||
14775      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14776      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14777       game.snapshot.changed_action) ||
14778      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14779       game.snapshot.collected_item));
14780
14781   game.snapshot.changed_action = FALSE;
14782   game.snapshot.collected_item = FALSE;
14783
14784   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14785       tape.quick_resume ||
14786       !save_snapshot)
14787     return FALSE;
14788
14789   ListNode *buffers = SaveEngineSnapshotBuffers();
14790
14791   /* finally save all snapshot buffers to snapshot list */
14792   SaveSnapshotToList(buffers);
14793
14794   return TRUE;
14795 }
14796
14797 boolean SaveEngineSnapshotToList()
14798 {
14799   return SaveEngineSnapshotToListExt(FALSE);
14800 }
14801
14802 void SaveEngineSnapshotToListInitial()
14803 {
14804   FreeEngineSnapshotList();
14805
14806   SaveEngineSnapshotToListExt(TRUE);
14807 }
14808
14809 void LoadEngineSnapshotValues()
14810 {
14811   /* restore special values from snapshot structure */
14812
14813   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14814     LoadEngineSnapshotValues_RND();
14815   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14816     LoadEngineSnapshotValues_EM();
14817   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14818     LoadEngineSnapshotValues_SP();
14819 }
14820
14821 void LoadEngineSnapshotSingle()
14822 {
14823   LoadSnapshotSingle();
14824
14825   LoadEngineSnapshotValues();
14826 }
14827
14828 void LoadEngineSnapshot_Undo(int steps)
14829 {
14830   LoadSnapshotFromList_Older(steps);
14831
14832   LoadEngineSnapshotValues();
14833 }
14834
14835 void LoadEngineSnapshot_Redo(int steps)
14836 {
14837   LoadSnapshotFromList_Newer(steps);
14838
14839   LoadEngineSnapshotValues();
14840 }
14841
14842 boolean CheckEngineSnapshotSingle()
14843 {
14844   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14845           snapshot_level_nr == level_nr);
14846 }
14847
14848 boolean CheckEngineSnapshotList()
14849 {
14850   return CheckSnapshotList();
14851 }
14852
14853
14854 /* ---------- new game button stuff ---------------------------------------- */
14855
14856 static struct
14857 {
14858   int graphic;
14859   struct XY *pos;
14860   int gadget_id;
14861   char *infotext;
14862 } gamebutton_info[NUM_GAME_BUTTONS] =
14863 {
14864   {
14865     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14866     GAME_CTRL_ID_STOP,                  "stop game"
14867   },
14868   {
14869     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14870     GAME_CTRL_ID_PAUSE,                 "pause game"
14871   },
14872   {
14873     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14874     GAME_CTRL_ID_PLAY,                  "play game"
14875   },
14876   {
14877     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14878     GAME_CTRL_ID_UNDO,                  "undo step"
14879   },
14880   {
14881     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14882     GAME_CTRL_ID_REDO,                  "redo step"
14883   },
14884   {
14885     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14886     GAME_CTRL_ID_SAVE,                  "save game"
14887   },
14888   {
14889     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14890     GAME_CTRL_ID_PAUSE2,                "pause game"
14891   },
14892   {
14893     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14894     GAME_CTRL_ID_LOAD,                  "load game"
14895   },
14896   {
14897     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14898     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14899   },
14900   {
14901     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14902     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14903   },
14904   {
14905     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14906     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14907   }
14908 };
14909
14910 void CreateGameButtons()
14911 {
14912   int i;
14913
14914   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14915   {
14916     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14917     struct XY *pos = gamebutton_info[i].pos;
14918     struct GadgetInfo *gi;
14919     int button_type;
14920     boolean checked;
14921     unsigned int event_mask;
14922     int base_x = (tape.show_game_buttons ? VX : DX);
14923     int base_y = (tape.show_game_buttons ? VY : DY);
14924     int gd_x   = gfx->src_x;
14925     int gd_y   = gfx->src_y;
14926     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14927     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14928     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14929     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14930     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14931     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14932     int id = i;
14933
14934     if (gfx->bitmap == NULL)
14935     {
14936       game_gadget[id] = NULL;
14937
14938       continue;
14939     }
14940
14941     if (id == GAME_CTRL_ID_STOP ||
14942         id == GAME_CTRL_ID_PLAY ||
14943         id == GAME_CTRL_ID_SAVE ||
14944         id == GAME_CTRL_ID_LOAD)
14945     {
14946       button_type = GD_TYPE_NORMAL_BUTTON;
14947       checked = FALSE;
14948       event_mask = GD_EVENT_RELEASED;
14949     }
14950     else if (id == GAME_CTRL_ID_UNDO ||
14951              id == GAME_CTRL_ID_REDO)
14952     {
14953       button_type = GD_TYPE_NORMAL_BUTTON;
14954       checked = FALSE;
14955       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14956     }
14957     else
14958     {
14959       button_type = GD_TYPE_CHECK_BUTTON;
14960       checked =
14961         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14962          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14963          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14964       event_mask = GD_EVENT_PRESSED;
14965     }
14966
14967     gi = CreateGadget(GDI_CUSTOM_ID, id,
14968                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14969                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14970                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14971                       GDI_WIDTH, gfx->width,
14972                       GDI_HEIGHT, gfx->height,
14973                       GDI_TYPE, button_type,
14974                       GDI_STATE, GD_BUTTON_UNPRESSED,
14975                       GDI_CHECKED, checked,
14976                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14977                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14978                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14979                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14980                       GDI_DIRECT_DRAW, FALSE,
14981                       GDI_EVENT_MASK, event_mask,
14982                       GDI_CALLBACK_ACTION, HandleGameButtons,
14983                       GDI_END);
14984
14985     if (gi == NULL)
14986       Error(ERR_EXIT, "cannot create gadget");
14987
14988     game_gadget[id] = gi;
14989   }
14990 }
14991
14992 void FreeGameButtons()
14993 {
14994   int i;
14995
14996   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14997     FreeGadget(game_gadget[i]);
14998 }
14999
15000 static void MapGameButtonsAtSamePosition(int id)
15001 {
15002   int i;
15003
15004   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15005     if (i != id &&
15006         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15007         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15008       MapGadget(game_gadget[i]);
15009 }
15010
15011 static void UnmapGameButtonsAtSamePosition(int id)
15012 {
15013   int i;
15014
15015   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15016     if (i != id &&
15017         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15018         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15019       UnmapGadget(game_gadget[i]);
15020 }
15021
15022 void MapUndoRedoButtons()
15023 {
15024   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15025   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15026
15027   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15028   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15029 }
15030
15031 void UnmapUndoRedoButtons()
15032 {
15033   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15034   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15035
15036   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15037   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15038 }
15039
15040 void MapGameButtons()
15041 {
15042   int i;
15043
15044   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15045     if (i != GAME_CTRL_ID_UNDO &&
15046         i != GAME_CTRL_ID_REDO)
15047       MapGadget(game_gadget[i]);
15048
15049   if (setup.show_snapshot_buttons)
15050   {
15051     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15052     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15053     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15054   }
15055   else
15056   {
15057     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15058     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15059     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15060   }
15061
15062   RedrawGameButtons();
15063 }
15064
15065 void UnmapGameButtons()
15066 {
15067   int i;
15068
15069   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15070     UnmapGadget(game_gadget[i]);
15071 }
15072
15073 void RedrawGameButtons()
15074 {
15075   int i;
15076
15077   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15078     RedrawGadget(game_gadget[i]);
15079
15080   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15081   redraw_mask &= ~REDRAW_ALL;
15082 }
15083
15084 void GameUndoRedoExt()
15085 {
15086   ClearPlayerAction();
15087
15088   tape.pausing = TRUE;
15089
15090   RedrawPlayfield();
15091   UpdateAndDisplayGameControlValues();
15092
15093   DrawCompleteVideoDisplay();
15094   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15095   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15096   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15097                     VIDEO_STATE_1STEP_OFF), 0);
15098
15099   BackToFront();
15100 }
15101
15102 void GameUndo(int steps)
15103 {
15104   if (!CheckEngineSnapshotList())
15105     return;
15106
15107   LoadEngineSnapshot_Undo(steps);
15108
15109   GameUndoRedoExt();
15110 }
15111
15112 void GameRedo(int steps)
15113 {
15114   if (!CheckEngineSnapshotList())
15115     return;
15116
15117   LoadEngineSnapshot_Redo(steps);
15118
15119   GameUndoRedoExt();
15120 }
15121
15122 static void HandleGameButtonsExt(int id, int button)
15123 {
15124   static boolean game_undo_executed = FALSE;
15125   int steps = BUTTON_STEPSIZE(button);
15126   boolean handle_game_buttons =
15127     (game_status == GAME_MODE_PLAYING ||
15128      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15129
15130   if (!handle_game_buttons)
15131     return;
15132
15133   switch (id)
15134   {
15135     case GAME_CTRL_ID_STOP:
15136       if (game_status == GAME_MODE_MAIN)
15137         break;
15138
15139       if (tape.playing)
15140         TapeStop();
15141       else
15142         RequestQuitGame(TRUE);
15143
15144       break;
15145
15146     case GAME_CTRL_ID_PAUSE:
15147     case GAME_CTRL_ID_PAUSE2:
15148       if (options.network && game_status == GAME_MODE_PLAYING)
15149       {
15150 #if defined(NETWORK_AVALIABLE)
15151         if (tape.pausing)
15152           SendToServer_ContinuePlaying();
15153         else
15154           SendToServer_PausePlaying();
15155 #endif
15156       }
15157       else
15158         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15159
15160       game_undo_executed = FALSE;
15161
15162       break;
15163
15164     case GAME_CTRL_ID_PLAY:
15165       if (game_status == GAME_MODE_MAIN)
15166       {
15167         StartGameActions(options.network, setup.autorecord, level.random_seed);
15168       }
15169       else if (tape.pausing)
15170       {
15171 #if defined(NETWORK_AVALIABLE)
15172         if (options.network)
15173           SendToServer_ContinuePlaying();
15174         else
15175 #endif
15176           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15177       }
15178       break;
15179
15180     case GAME_CTRL_ID_UNDO:
15181       // Important: When using "save snapshot when collecting an item" mode,
15182       // load last (current) snapshot for first "undo" after pressing "pause"
15183       // (else the last-but-one snapshot would be loaded, because the snapshot
15184       // pointer already points to the last snapshot when pressing "pause",
15185       // which is fine for "every step/move" mode, but not for "every collect")
15186       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15187           !game_undo_executed)
15188         steps--;
15189
15190       game_undo_executed = TRUE;
15191
15192       GameUndo(steps);
15193       break;
15194
15195     case GAME_CTRL_ID_REDO:
15196       GameRedo(steps);
15197       break;
15198
15199     case GAME_CTRL_ID_SAVE:
15200       TapeQuickSave();
15201       break;
15202
15203     case GAME_CTRL_ID_LOAD:
15204       TapeQuickLoad();
15205       break;
15206
15207     case SOUND_CTRL_ID_MUSIC:
15208       if (setup.sound_music)
15209       { 
15210         setup.sound_music = FALSE;
15211
15212         FadeMusic();
15213       }
15214       else if (audio.music_available)
15215       { 
15216         setup.sound = setup.sound_music = TRUE;
15217
15218         SetAudioMode(setup.sound);
15219
15220         PlayLevelMusic();
15221       }
15222       break;
15223
15224     case SOUND_CTRL_ID_LOOPS:
15225       if (setup.sound_loops)
15226         setup.sound_loops = FALSE;
15227       else if (audio.loops_available)
15228       {
15229         setup.sound = setup.sound_loops = TRUE;
15230
15231         SetAudioMode(setup.sound);
15232       }
15233       break;
15234
15235     case SOUND_CTRL_ID_SIMPLE:
15236       if (setup.sound_simple)
15237         setup.sound_simple = FALSE;
15238       else if (audio.sound_available)
15239       {
15240         setup.sound = setup.sound_simple = TRUE;
15241
15242         SetAudioMode(setup.sound);
15243       }
15244       break;
15245
15246     default:
15247       break;
15248   }
15249 }
15250
15251 static void HandleGameButtons(struct GadgetInfo *gi)
15252 {
15253   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15254 }
15255
15256 void HandleSoundButtonKeys(Key key)
15257 {
15258
15259   if (key == setup.shortcut.sound_simple)
15260     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15261   else if (key == setup.shortcut.sound_loops)
15262     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15263   else if (key == setup.shortcut.sound_music)
15264     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15265 }