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