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