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