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