fixed order of some drawing operations to be related to correct screen
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_PAUSE2             6
978 #define GAME_CTRL_ID_LOAD               7
979 #define SOUND_CTRL_ID_MUSIC             8
980 #define SOUND_CTRL_ID_LOOPS             9
981 #define SOUND_CTRL_ID_SIMPLE            10
982
983 #define NUM_GAME_BUTTONS                11
984
985
986 /* forward declaration for internal use */
987
988 static void CreateField(int, int, int);
989
990 static void ResetGfxAnimation(int, int);
991
992 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
993 static void AdvanceFrameAndPlayerCounters(int);
994
995 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
996 static boolean MovePlayer(struct PlayerInfo *, int, int);
997 static void ScrollPlayer(struct PlayerInfo *, int);
998 static void ScrollScreen(struct PlayerInfo *, int);
999
1000 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1001 static boolean DigFieldByCE(int, int, int);
1002 static boolean SnapField(struct PlayerInfo *, int, int);
1003 static boolean DropElement(struct PlayerInfo *);
1004
1005 static void InitBeltMovement(void);
1006 static void CloseAllOpenTimegates(void);
1007 static void CheckGravityMovement(struct PlayerInfo *);
1008 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1009 static void KillPlayerUnlessEnemyProtected(int, int);
1010 static void KillPlayerUnlessExplosionProtected(int, int);
1011
1012 static void TestIfPlayerTouchesCustomElement(int, int);
1013 static void TestIfElementTouchesCustomElement(int, int);
1014 static void TestIfElementHitsCustomElement(int, int, int);
1015
1016 static void HandleElementChange(int, int, int);
1017 static void ExecuteCustomElementAction(int, int, int, int);
1018 static boolean ChangeElement(int, int, int, int);
1019
1020 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1021 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1022         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1023 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1024         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1025 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1026         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1027 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1028         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1029
1030 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1031 #define CheckElementChange(x, y, e, te, ev)                             \
1032         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1033 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1034         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1035 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1036         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1037
1038 static void PlayLevelSound(int, int, int);
1039 static void PlayLevelSoundNearest(int, int, int);
1040 static void PlayLevelSoundAction(int, int, int);
1041 static void PlayLevelSoundElementAction(int, int, int, int);
1042 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1043 static void PlayLevelSoundActionIfLoop(int, int, int);
1044 static void StopLevelSoundActionIfLoop(int, int, int);
1045 static void PlayLevelMusic();
1046
1047 static void HandleGameButtons(struct GadgetInfo *);
1048
1049 int AmoebeNachbarNr(int, int);
1050 void AmoebeUmwandeln(int, int);
1051 void ContinueMoving(int, int);
1052 void Bang(int, int);
1053 void InitMovDir(int, int);
1054 void InitAmoebaNr(int, int);
1055 int NewHiScore(void);
1056
1057 void TestIfGoodThingHitsBadThing(int, int, int);
1058 void TestIfBadThingHitsGoodThing(int, int, int);
1059 void TestIfPlayerTouchesBadThing(int, int);
1060 void TestIfPlayerRunsIntoBadThing(int, int, int);
1061 void TestIfBadThingTouchesPlayer(int, int);
1062 void TestIfBadThingRunsIntoPlayer(int, int, int);
1063 void TestIfFriendTouchesBadThing(int, int);
1064 void TestIfBadThingTouchesFriend(int, int);
1065 void TestIfBadThingTouchesOtherBadThing(int, int);
1066 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1067
1068 void KillPlayer(struct PlayerInfo *);
1069 void BuryPlayer(struct PlayerInfo *);
1070 void RemovePlayer(struct PlayerInfo *);
1071
1072 static int getInvisibleActiveFromInvisibleElement(int);
1073 static int getInvisibleFromInvisibleActiveElement(int);
1074
1075 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1076
1077 /* for detection of endless loops, caused by custom element programming */
1078 /* (using maximal playfield width x 10 is just a rough approximation) */
1079 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1080
1081 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1082 {                                                                       \
1083   if (recursion_loop_detected)                                          \
1084     return (rc);                                                        \
1085                                                                         \
1086   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1087   {                                                                     \
1088     recursion_loop_detected = TRUE;                                     \
1089     recursion_loop_element = (e);                                       \
1090   }                                                                     \
1091                                                                         \
1092   recursion_loop_depth++;                                               \
1093 }
1094
1095 #define RECURSION_LOOP_DETECTION_END()                                  \
1096 {                                                                       \
1097   recursion_loop_depth--;                                               \
1098 }
1099
1100 static int recursion_loop_depth;
1101 static boolean recursion_loop_detected;
1102 static boolean recursion_loop_element;
1103
1104 static int map_player_action[MAX_PLAYERS];
1105
1106
1107 /* ------------------------------------------------------------------------- */
1108 /* definition of elements that automatically change to other elements after  */
1109 /* a specified time, eventually calling a function when changing             */
1110 /* ------------------------------------------------------------------------- */
1111
1112 /* forward declaration for changer functions */
1113 static void InitBuggyBase(int, int);
1114 static void WarnBuggyBase(int, int);
1115
1116 static void InitTrap(int, int);
1117 static void ActivateTrap(int, int);
1118 static void ChangeActiveTrap(int, int);
1119
1120 static void InitRobotWheel(int, int);
1121 static void RunRobotWheel(int, int);
1122 static void StopRobotWheel(int, int);
1123
1124 static void InitTimegateWheel(int, int);
1125 static void RunTimegateWheel(int, int);
1126
1127 static void InitMagicBallDelay(int, int);
1128 static void ActivateMagicBall(int, int);
1129
1130 struct ChangingElementInfo
1131 {
1132   int element;
1133   int target_element;
1134   int change_delay;
1135   void (*pre_change_function)(int x, int y);
1136   void (*change_function)(int x, int y);
1137   void (*post_change_function)(int x, int y);
1138 };
1139
1140 static struct ChangingElementInfo change_delay_list[] =
1141 {
1142   {
1143     EL_NUT_BREAKING,
1144     EL_EMERALD,
1145     6,
1146     NULL,
1147     NULL,
1148     NULL
1149   },
1150   {
1151     EL_PEARL_BREAKING,
1152     EL_EMPTY,
1153     8,
1154     NULL,
1155     NULL,
1156     NULL
1157   },
1158   {
1159     EL_EXIT_OPENING,
1160     EL_EXIT_OPEN,
1161     29,
1162     NULL,
1163     NULL,
1164     NULL
1165   },
1166   {
1167     EL_EXIT_CLOSING,
1168     EL_EXIT_CLOSED,
1169     29,
1170     NULL,
1171     NULL,
1172     NULL
1173   },
1174   {
1175     EL_STEEL_EXIT_OPENING,
1176     EL_STEEL_EXIT_OPEN,
1177     29,
1178     NULL,
1179     NULL,
1180     NULL
1181   },
1182   {
1183     EL_STEEL_EXIT_CLOSING,
1184     EL_STEEL_EXIT_CLOSED,
1185     29,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_EM_EXIT_OPENING,
1192     EL_EM_EXIT_OPEN,
1193     29,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EM_EXIT_CLOSING,
1200     EL_EMPTY,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EM_STEEL_EXIT_OPENING,
1208     EL_EM_STEEL_EXIT_OPEN,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_EM_STEEL_EXIT_CLOSING,
1216     EL_STEELWALL,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_SP_EXIT_OPENING,
1224     EL_SP_EXIT_OPEN,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_SP_EXIT_CLOSING,
1232     EL_SP_EXIT_CLOSED,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_SWITCHGATE_OPENING,
1240     EL_SWITCHGATE_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_SWITCHGATE_CLOSING,
1248     EL_SWITCHGATE_CLOSED,
1249     29,
1250     NULL,
1251     NULL,
1252     NULL
1253   },
1254   {
1255     EL_TIMEGATE_OPENING,
1256     EL_TIMEGATE_OPEN,
1257     29,
1258     NULL,
1259     NULL,
1260     NULL
1261   },
1262   {
1263     EL_TIMEGATE_CLOSING,
1264     EL_TIMEGATE_CLOSED,
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270
1271   {
1272     EL_ACID_SPLASH_LEFT,
1273     EL_EMPTY,
1274     8,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_ACID_SPLASH_RIGHT,
1281     EL_EMPTY,
1282     8,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_BUGGY_BASE,
1289     EL_SP_BUGGY_BASE_ACTIVATING,
1290     0,
1291     InitBuggyBase,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SP_BUGGY_BASE_ACTIVATING,
1297     EL_SP_BUGGY_BASE_ACTIVE,
1298     0,
1299     InitBuggyBase,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_BUGGY_BASE_ACTIVE,
1305     EL_SP_BUGGY_BASE,
1306     0,
1307     InitBuggyBase,
1308     WarnBuggyBase,
1309     NULL
1310   },
1311   {
1312     EL_TRAP,
1313     EL_TRAP_ACTIVE,
1314     0,
1315     InitTrap,
1316     NULL,
1317     ActivateTrap
1318   },
1319   {
1320     EL_TRAP_ACTIVE,
1321     EL_TRAP,
1322     31,
1323     NULL,
1324     ChangeActiveTrap,
1325     NULL
1326   },
1327   {
1328     EL_ROBOT_WHEEL_ACTIVE,
1329     EL_ROBOT_WHEEL,
1330     0,
1331     InitRobotWheel,
1332     RunRobotWheel,
1333     StopRobotWheel
1334   },
1335   {
1336     EL_TIMEGATE_SWITCH_ACTIVE,
1337     EL_TIMEGATE_SWITCH,
1338     0,
1339     InitTimegateWheel,
1340     RunTimegateWheel,
1341     NULL
1342   },
1343   {
1344     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1345     EL_DC_TIMEGATE_SWITCH,
1346     0,
1347     InitTimegateWheel,
1348     RunTimegateWheel,
1349     NULL
1350   },
1351   {
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     0,
1355     InitMagicBallDelay,
1356     NULL,
1357     ActivateMagicBall
1358   },
1359   {
1360     EL_EMC_SPRING_BUMPER_ACTIVE,
1361     EL_EMC_SPRING_BUMPER,
1362     8,
1363     NULL,
1364     NULL,
1365     NULL
1366   },
1367   {
1368     EL_DIAGONAL_SHRINKING,
1369     EL_UNDEFINED,
1370     0,
1371     NULL,
1372     NULL,
1373     NULL
1374   },
1375   {
1376     EL_DIAGONAL_GROWING,
1377     EL_UNDEFINED,
1378     0,
1379     NULL,
1380     NULL,
1381     NULL,
1382   },
1383
1384   {
1385     EL_UNDEFINED,
1386     EL_UNDEFINED,
1387     -1,
1388     NULL,
1389     NULL,
1390     NULL
1391   }
1392 };
1393
1394 struct
1395 {
1396   int element;
1397   int push_delay_fixed, push_delay_random;
1398 }
1399 push_delay_list[] =
1400 {
1401   { EL_SPRING,                  0, 0 },
1402   { EL_BALLOON,                 0, 0 },
1403
1404   { EL_SOKOBAN_OBJECT,          2, 0 },
1405   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1406   { EL_SATELLITE,               2, 0 },
1407   { EL_SP_DISK_YELLOW,          2, 0 },
1408
1409   { EL_UNDEFINED,               0, 0 },
1410 };
1411
1412 struct
1413 {
1414   int element;
1415   int move_stepsize;
1416 }
1417 move_stepsize_list[] =
1418 {
1419   { EL_AMOEBA_DROP,             2 },
1420   { EL_AMOEBA_DROPPING,         2 },
1421   { EL_QUICKSAND_FILLING,       1 },
1422   { EL_QUICKSAND_EMPTYING,      1 },
1423   { EL_QUICKSAND_FAST_FILLING,  2 },
1424   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1425   { EL_MAGIC_WALL_FILLING,      2 },
1426   { EL_MAGIC_WALL_EMPTYING,     2 },
1427   { EL_BD_MAGIC_WALL_FILLING,   2 },
1428   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1429   { EL_DC_MAGIC_WALL_FILLING,   2 },
1430   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1431
1432   { EL_UNDEFINED,               0 },
1433 };
1434
1435 struct
1436 {
1437   int element;
1438   int count;
1439 }
1440 collect_count_list[] =
1441 {
1442   { EL_EMERALD,                 1 },
1443   { EL_BD_DIAMOND,              1 },
1444   { EL_EMERALD_YELLOW,          1 },
1445   { EL_EMERALD_RED,             1 },
1446   { EL_EMERALD_PURPLE,          1 },
1447   { EL_DIAMOND,                 3 },
1448   { EL_SP_INFOTRON,             1 },
1449   { EL_PEARL,                   5 },
1450   { EL_CRYSTAL,                 8 },
1451
1452   { EL_UNDEFINED,               0 },
1453 };
1454
1455 struct
1456 {
1457   int element;
1458   int direction;
1459 }
1460 access_direction_list[] =
1461 {
1462   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1463   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1464   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1465   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1466   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1467   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1468   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1469   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1470   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1471   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1472   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1473
1474   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1475   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1476   { EL_SP_PORT_UP,                                                   MV_DOWN },
1477   { EL_SP_PORT_DOWN,                                         MV_UP           },
1478   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1479   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1480   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1481   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1482   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1483   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1484   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1485   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1486   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1487   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1488   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1489   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1490   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1491   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1492   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1493
1494   { EL_UNDEFINED,                       MV_NONE                              }
1495 };
1496
1497 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1498
1499 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1500 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1501 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1502                                  IS_JUST_CHANGING(x, y))
1503
1504 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1505
1506 /* static variables for playfield scan mode (scanning forward or backward) */
1507 static int playfield_scan_start_x = 0;
1508 static int playfield_scan_start_y = 0;
1509 static int playfield_scan_delta_x = 1;
1510 static int playfield_scan_delta_y = 1;
1511
1512 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1513                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1514                                      (y) += playfield_scan_delta_y)     \
1515                                 for ((x) = playfield_scan_start_x;      \
1516                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1517                                      (x) += playfield_scan_delta_x)
1518
1519 #ifdef DEBUG
1520 void DEBUG_SetMaximumDynamite()
1521 {
1522   int i;
1523
1524   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1525     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1526       local_player->inventory_element[local_player->inventory_size++] =
1527         EL_DYNAMITE;
1528 }
1529 #endif
1530
1531 static void InitPlayfieldScanModeVars()
1532 {
1533   if (game.use_reverse_scan_direction)
1534   {
1535     playfield_scan_start_x = lev_fieldx - 1;
1536     playfield_scan_start_y = lev_fieldy - 1;
1537
1538     playfield_scan_delta_x = -1;
1539     playfield_scan_delta_y = -1;
1540   }
1541   else
1542   {
1543     playfield_scan_start_x = 0;
1544     playfield_scan_start_y = 0;
1545
1546     playfield_scan_delta_x = 1;
1547     playfield_scan_delta_y = 1;
1548   }
1549 }
1550
1551 static void InitPlayfieldScanMode(int mode)
1552 {
1553   game.use_reverse_scan_direction =
1554     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1555
1556   InitPlayfieldScanModeVars();
1557 }
1558
1559 static int get_move_delay_from_stepsize(int move_stepsize)
1560 {
1561   move_stepsize =
1562     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1563
1564   /* make sure that stepsize value is always a power of 2 */
1565   move_stepsize = (1 << log_2(move_stepsize));
1566
1567   return TILEX / move_stepsize;
1568 }
1569
1570 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1571                                boolean init_game)
1572 {
1573   int player_nr = player->index_nr;
1574   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1575   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1576
1577   /* do no immediately change move delay -- the player might just be moving */
1578   player->move_delay_value_next = move_delay;
1579
1580   /* information if player can move must be set separately */
1581   player->cannot_move = cannot_move;
1582
1583   if (init_game)
1584   {
1585     player->move_delay       = game.initial_move_delay[player_nr];
1586     player->move_delay_value = game.initial_move_delay_value[player_nr];
1587
1588     player->move_delay_value_next = -1;
1589
1590     player->move_delay_reset_counter = 0;
1591   }
1592 }
1593
1594 void GetPlayerConfig()
1595 {
1596   GameFrameDelay = setup.game_frame_delay;
1597
1598   if (!audio.sound_available)
1599     setup.sound_simple = FALSE;
1600
1601   if (!audio.loops_available)
1602     setup.sound_loops = FALSE;
1603
1604   if (!audio.music_available)
1605     setup.sound_music = FALSE;
1606
1607   if (!video.fullscreen_available)
1608     setup.fullscreen = FALSE;
1609
1610   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1611
1612   SetAudioMode(setup.sound);
1613   InitJoysticks();
1614 }
1615
1616 int GetElementFromGroupElement(int element)
1617 {
1618   if (IS_GROUP_ELEMENT(element))
1619   {
1620     struct ElementGroupInfo *group = element_info[element].group;
1621     int last_anim_random_frame = gfx.anim_random_frame;
1622     int element_pos;
1623
1624     if (group->choice_mode == ANIM_RANDOM)
1625       gfx.anim_random_frame = RND(group->num_elements_resolved);
1626
1627     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1628                                     group->choice_mode, 0,
1629                                     group->choice_pos);
1630
1631     if (group->choice_mode == ANIM_RANDOM)
1632       gfx.anim_random_frame = last_anim_random_frame;
1633
1634     group->choice_pos++;
1635
1636     element = group->element_resolved[element_pos];
1637   }
1638
1639   return element;
1640 }
1641
1642 static void InitPlayerField(int x, int y, int element, boolean init_game)
1643 {
1644   if (element == EL_SP_MURPHY)
1645   {
1646     if (init_game)
1647     {
1648       if (stored_player[0].present)
1649       {
1650         Feld[x][y] = EL_SP_MURPHY_CLONE;
1651
1652         return;
1653       }
1654       else
1655       {
1656         stored_player[0].initial_element = element;
1657         stored_player[0].use_murphy = TRUE;
1658
1659         if (!level.use_artwork_element[0])
1660           stored_player[0].artwork_element = EL_SP_MURPHY;
1661       }
1662
1663       Feld[x][y] = EL_PLAYER_1;
1664     }
1665   }
1666
1667   if (init_game)
1668   {
1669     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1670     int jx = player->jx, jy = player->jy;
1671
1672     player->present = TRUE;
1673
1674     player->block_last_field = (element == EL_SP_MURPHY ?
1675                                 level.sp_block_last_field :
1676                                 level.block_last_field);
1677
1678     /* ---------- initialize player's last field block delay --------------- */
1679
1680     /* always start with reliable default value (no adjustment needed) */
1681     player->block_delay_adjustment = 0;
1682
1683     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1684     if (player->block_last_field && element == EL_SP_MURPHY)
1685       player->block_delay_adjustment = 1;
1686
1687     /* special case 2: in game engines before 3.1.1, blocking was different */
1688     if (game.use_block_last_field_bug)
1689       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1690
1691     if (!options.network || player->connected)
1692     {
1693       player->active = TRUE;
1694
1695       /* remove potentially duplicate players */
1696       if (StorePlayer[jx][jy] == Feld[x][y])
1697         StorePlayer[jx][jy] = 0;
1698
1699       StorePlayer[x][y] = Feld[x][y];
1700
1701 #if DEBUG_INIT_PLAYER
1702       if (options.debug)
1703       {
1704         printf("- player element %d activated", player->element_nr);
1705         printf(" (local player is %d and currently %s)\n",
1706                local_player->element_nr,
1707                local_player->active ? "active" : "not active");
1708       }
1709     }
1710 #endif
1711
1712     Feld[x][y] = EL_EMPTY;
1713
1714     player->jx = player->last_jx = x;
1715     player->jy = player->last_jy = y;
1716   }
1717
1718   if (!init_game)
1719   {
1720     int player_nr = GET_PLAYER_NR(element);
1721     struct PlayerInfo *player = &stored_player[player_nr];
1722
1723     if (player->active && player->killed)
1724       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1725   }
1726 }
1727
1728 static void InitField(int x, int y, boolean init_game)
1729 {
1730   int element = Feld[x][y];
1731
1732   switch (element)
1733   {
1734     case EL_SP_MURPHY:
1735     case EL_PLAYER_1:
1736     case EL_PLAYER_2:
1737     case EL_PLAYER_3:
1738     case EL_PLAYER_4:
1739       InitPlayerField(x, y, element, init_game);
1740       break;
1741
1742     case EL_SOKOBAN_FIELD_PLAYER:
1743       element = Feld[x][y] = EL_PLAYER_1;
1744       InitField(x, y, init_game);
1745
1746       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1747       InitField(x, y, init_game);
1748       break;
1749
1750     case EL_SOKOBAN_FIELD_EMPTY:
1751       local_player->sokobanfields_still_needed++;
1752       break;
1753
1754     case EL_STONEBLOCK:
1755       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1756         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1757       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1758         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1759       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1760         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1761       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1762         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1763       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1764         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1765       break;
1766
1767     case EL_BUG:
1768     case EL_BUG_RIGHT:
1769     case EL_BUG_UP:
1770     case EL_BUG_LEFT:
1771     case EL_BUG_DOWN:
1772     case EL_SPACESHIP:
1773     case EL_SPACESHIP_RIGHT:
1774     case EL_SPACESHIP_UP:
1775     case EL_SPACESHIP_LEFT:
1776     case EL_SPACESHIP_DOWN:
1777     case EL_BD_BUTTERFLY:
1778     case EL_BD_BUTTERFLY_RIGHT:
1779     case EL_BD_BUTTERFLY_UP:
1780     case EL_BD_BUTTERFLY_LEFT:
1781     case EL_BD_BUTTERFLY_DOWN:
1782     case EL_BD_FIREFLY:
1783     case EL_BD_FIREFLY_RIGHT:
1784     case EL_BD_FIREFLY_UP:
1785     case EL_BD_FIREFLY_LEFT:
1786     case EL_BD_FIREFLY_DOWN:
1787     case EL_PACMAN_RIGHT:
1788     case EL_PACMAN_UP:
1789     case EL_PACMAN_LEFT:
1790     case EL_PACMAN_DOWN:
1791     case EL_YAMYAM:
1792     case EL_YAMYAM_LEFT:
1793     case EL_YAMYAM_RIGHT:
1794     case EL_YAMYAM_UP:
1795     case EL_YAMYAM_DOWN:
1796     case EL_DARK_YAMYAM:
1797     case EL_ROBOT:
1798     case EL_PACMAN:
1799     case EL_SP_SNIKSNAK:
1800     case EL_SP_ELECTRON:
1801     case EL_MOLE:
1802     case EL_MOLE_LEFT:
1803     case EL_MOLE_RIGHT:
1804     case EL_MOLE_UP:
1805     case EL_MOLE_DOWN:
1806       InitMovDir(x, y);
1807       break;
1808
1809     case EL_AMOEBA_FULL:
1810     case EL_BD_AMOEBA:
1811       InitAmoebaNr(x, y);
1812       break;
1813
1814     case EL_AMOEBA_DROP:
1815       if (y == lev_fieldy - 1)
1816       {
1817         Feld[x][y] = EL_AMOEBA_GROWING;
1818         Store[x][y] = EL_AMOEBA_WET;
1819       }
1820       break;
1821
1822     case EL_DYNAMITE_ACTIVE:
1823     case EL_SP_DISK_RED_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1828       MovDelay[x][y] = 96;
1829       break;
1830
1831     case EL_EM_DYNAMITE_ACTIVE:
1832       MovDelay[x][y] = 32;
1833       break;
1834
1835     case EL_LAMP:
1836       local_player->lights_still_needed++;
1837       break;
1838
1839     case EL_PENGUIN:
1840       local_player->friends_still_needed++;
1841       break;
1842
1843     case EL_PIG:
1844     case EL_DRAGON:
1845       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1846       break;
1847
1848     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1849     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1850     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1853     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1856     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1860       if (init_game)
1861       {
1862         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1865
1866         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1867         {
1868           game.belt_dir[belt_nr] = belt_dir;
1869           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1870         }
1871         else    /* more than one switch -- set it like the first switch */
1872         {
1873           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1874         }
1875       }
1876       break;
1877
1878     case EL_LIGHT_SWITCH_ACTIVE:
1879       if (init_game)
1880         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1881       break;
1882
1883     case EL_INVISIBLE_STEELWALL:
1884     case EL_INVISIBLE_WALL:
1885     case EL_INVISIBLE_SAND:
1886       if (game.light_time_left > 0 ||
1887           game.lenses_time_left > 0)
1888         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1889       break;
1890
1891     case EL_EMC_MAGIC_BALL:
1892       if (game.ball_state)
1893         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1894       break;
1895
1896     case EL_EMC_MAGIC_BALL_SWITCH:
1897       if (game.ball_state)
1898         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1899       break;
1900
1901     case EL_TRIGGER_PLAYER:
1902     case EL_TRIGGER_ELEMENT:
1903     case EL_TRIGGER_CE_VALUE:
1904     case EL_TRIGGER_CE_SCORE:
1905     case EL_SELF:
1906     case EL_ANY_ELEMENT:
1907     case EL_CURRENT_CE_VALUE:
1908     case EL_CURRENT_CE_SCORE:
1909     case EL_PREV_CE_1:
1910     case EL_PREV_CE_2:
1911     case EL_PREV_CE_3:
1912     case EL_PREV_CE_4:
1913     case EL_PREV_CE_5:
1914     case EL_PREV_CE_6:
1915     case EL_PREV_CE_7:
1916     case EL_PREV_CE_8:
1917     case EL_NEXT_CE_1:
1918     case EL_NEXT_CE_2:
1919     case EL_NEXT_CE_3:
1920     case EL_NEXT_CE_4:
1921     case EL_NEXT_CE_5:
1922     case EL_NEXT_CE_6:
1923     case EL_NEXT_CE_7:
1924     case EL_NEXT_CE_8:
1925       /* reference elements should not be used on the playfield */
1926       Feld[x][y] = EL_EMPTY;
1927       break;
1928
1929     default:
1930       if (IS_CUSTOM_ELEMENT(element))
1931       {
1932         if (CAN_MOVE(element))
1933           InitMovDir(x, y);
1934
1935         if (!element_info[element].use_last_ce_value || init_game)
1936           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1937       }
1938       else if (IS_GROUP_ELEMENT(element))
1939       {
1940         Feld[x][y] = GetElementFromGroupElement(element);
1941
1942         InitField(x, y, init_game);
1943       }
1944
1945       break;
1946   }
1947
1948   if (!init_game)
1949     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1950 }
1951
1952 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1953 {
1954   InitField(x, y, init_game);
1955
1956   /* not needed to call InitMovDir() -- already done by InitField()! */
1957   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1958       CAN_MOVE(Feld[x][y]))
1959     InitMovDir(x, y);
1960 }
1961
1962 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1963 {
1964   int old_element = Feld[x][y];
1965
1966   InitField(x, y, init_game);
1967
1968   /* not needed to call InitMovDir() -- already done by InitField()! */
1969   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1970       CAN_MOVE(old_element) &&
1971       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1972     InitMovDir(x, y);
1973
1974   /* this case is in fact a combination of not less than three bugs:
1975      first, it calls InitMovDir() for elements that can move, although this is
1976      already done by InitField(); then, it checks the element that was at this
1977      field _before_ the call to InitField() (which can change it); lastly, it
1978      was not called for "mole with direction" elements, which were treated as
1979      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1980   */
1981 }
1982
1983 static int get_key_element_from_nr(int key_nr)
1984 {
1985   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1986                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1987                           EL_EM_KEY_1 : EL_KEY_1);
1988
1989   return key_base_element + key_nr;
1990 }
1991
1992 static int get_next_dropped_element(struct PlayerInfo *player)
1993 {
1994   return (player->inventory_size > 0 ?
1995           player->inventory_element[player->inventory_size - 1] :
1996           player->inventory_infinite_element != EL_UNDEFINED ?
1997           player->inventory_infinite_element :
1998           player->dynabombs_left > 0 ?
1999           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2000           EL_UNDEFINED);
2001 }
2002
2003 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2004 {
2005   /* pos >= 0: get element from bottom of the stack;
2006      pos <  0: get element from top of the stack */
2007
2008   if (pos < 0)
2009   {
2010     int min_inventory_size = -pos;
2011     int inventory_pos = player->inventory_size - min_inventory_size;
2012     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2013
2014     return (player->inventory_size >= min_inventory_size ?
2015             player->inventory_element[inventory_pos] :
2016             player->inventory_infinite_element != EL_UNDEFINED ?
2017             player->inventory_infinite_element :
2018             player->dynabombs_left >= min_dynabombs_left ?
2019             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2020             EL_UNDEFINED);
2021   }
2022   else
2023   {
2024     int min_dynabombs_left = pos + 1;
2025     int min_inventory_size = pos + 1 - player->dynabombs_left;
2026     int inventory_pos = pos - player->dynabombs_left;
2027
2028     return (player->inventory_infinite_element != EL_UNDEFINED ?
2029             player->inventory_infinite_element :
2030             player->dynabombs_left >= min_dynabombs_left ?
2031             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032             player->inventory_size >= min_inventory_size ?
2033             player->inventory_element[inventory_pos] :
2034             EL_UNDEFINED);
2035   }
2036 }
2037
2038 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2039 {
2040   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2041   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2042   int compare_result;
2043
2044   if (gpo1->sort_priority != gpo2->sort_priority)
2045     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2046   else
2047     compare_result = gpo1->nr - gpo2->nr;
2048
2049   return compare_result;
2050 }
2051
2052 void InitGameControlValues()
2053 {
2054   int i;
2055
2056   for (i = 0; game_panel_controls[i].nr != -1; i++)
2057   {
2058     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2059     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2060     struct TextPosInfo *pos = gpc->pos;
2061     int nr = gpc->nr;
2062     int type = gpc->type;
2063
2064     if (nr != i)
2065     {
2066       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2067       Error(ERR_EXIT, "this should not happen -- please debug");
2068     }
2069
2070     /* force update of game controls after initialization */
2071     gpc->value = gpc->last_value = -1;
2072     gpc->frame = gpc->last_frame = -1;
2073     gpc->gfx_frame = -1;
2074
2075     /* determine panel value width for later calculation of alignment */
2076     if (type == TYPE_INTEGER || type == TYPE_STRING)
2077     {
2078       pos->width = pos->size * getFontWidth(pos->font);
2079       pos->height = getFontHeight(pos->font);
2080     }
2081     else if (type == TYPE_ELEMENT)
2082     {
2083       pos->width = pos->size;
2084       pos->height = pos->size;
2085     }
2086
2087     /* fill structure for game panel draw order */
2088     gpo->nr = gpc->nr;
2089     gpo->sort_priority = pos->sort_priority;
2090   }
2091
2092   /* sort game panel controls according to sort_priority and control number */
2093   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2094         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2095 }
2096
2097 void UpdatePlayfieldElementCount()
2098 {
2099   boolean use_element_count = FALSE;
2100   int i, j, x, y;
2101
2102   /* first check if it is needed at all to calculate playfield element count */
2103   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2104     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2105       use_element_count = TRUE;
2106
2107   if (!use_element_count)
2108     return;
2109
2110   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2111     element_info[i].element_count = 0;
2112
2113   SCAN_PLAYFIELD(x, y)
2114   {
2115     element_info[Feld[x][y]].element_count++;
2116   }
2117
2118   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2119     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2120       if (IS_IN_GROUP(j, i))
2121         element_info[EL_GROUP_START + i].element_count +=
2122           element_info[j].element_count;
2123 }
2124
2125 void UpdateGameControlValues()
2126 {
2127   int i, k;
2128   int time = (local_player->LevelSolved ?
2129               local_player->LevelSolved_CountingTime :
2130               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2131               level.native_em_level->lev->time :
2132               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2133               level.native_sp_level->game_sp->time_played :
2134               game.no_time_limit ? TimePlayed : TimeLeft);
2135   int score = (local_player->LevelSolved ?
2136                local_player->LevelSolved_CountingScore :
2137                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2138                level.native_em_level->lev->score :
2139                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2140                level.native_sp_level->game_sp->score :
2141                local_player->score);
2142   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2143               level.native_em_level->lev->required :
2144               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2145               level.native_sp_level->game_sp->infotrons_still_needed :
2146               local_player->gems_still_needed);
2147   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148                      level.native_em_level->lev->required > 0 :
2149                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2150                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2151                      local_player->gems_still_needed > 0 ||
2152                      local_player->sokobanfields_still_needed > 0 ||
2153                      local_player->lights_still_needed > 0);
2154
2155   UpdatePlayfieldElementCount();
2156
2157   /* update game panel control values */
2158
2159   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2160   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2161
2162   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2163   for (i = 0; i < MAX_NUM_KEYS; i++)
2164     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2167
2168   if (game.centered_player_nr == -1)
2169   {
2170     for (i = 0; i < MAX_PLAYERS; i++)
2171     {
2172       /* only one player in Supaplex game engine */
2173       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2174         break;
2175
2176       for (k = 0; k < MAX_NUM_KEYS; k++)
2177       {
2178         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179         {
2180           if (level.native_em_level->ply[i]->keys & (1 << k))
2181             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182               get_key_element_from_nr(k);
2183         }
2184         else if (stored_player[i].key[k])
2185           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2186             get_key_element_from_nr(k);
2187       }
2188
2189       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2190         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2191           level.native_em_level->ply[i]->dynamite;
2192       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2193         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194           level.native_sp_level->game_sp->red_disk_count;
2195       else
2196         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197           stored_player[i].inventory_size;
2198
2199       if (stored_player[i].num_white_keys > 0)
2200         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2201           EL_DC_KEY_WHITE;
2202
2203       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204         stored_player[i].num_white_keys;
2205     }
2206   }
2207   else
2208   {
2209     int player_nr = game.centered_player_nr;
2210
2211     for (k = 0; k < MAX_NUM_KEYS; k++)
2212     {
2213       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214       {
2215         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217             get_key_element_from_nr(k);
2218       }
2219       else if (stored_player[player_nr].key[k])
2220         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221           get_key_element_from_nr(k);
2222     }
2223
2224     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226         level.native_em_level->ply[player_nr]->dynamite;
2227     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2228       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229         level.native_sp_level->game_sp->red_disk_count;
2230     else
2231       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232         stored_player[player_nr].inventory_size;
2233
2234     if (stored_player[player_nr].num_white_keys > 0)
2235       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2236
2237     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2238       stored_player[player_nr].num_white_keys;
2239   }
2240
2241   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2242   {
2243     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2244       get_inventory_element_from_pos(local_player, i);
2245     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2246       get_inventory_element_from_pos(local_player, -i - 1);
2247   }
2248
2249   game_panel_controls[GAME_PANEL_SCORE].value = score;
2250   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2251
2252   game_panel_controls[GAME_PANEL_TIME].value = time;
2253
2254   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2255   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2256   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2257
2258   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2259
2260   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2261     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2262      EL_EMPTY);
2263   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2264     local_player->shield_normal_time_left;
2265   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2266     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2269     local_player->shield_deadly_time_left;
2270
2271   game_panel_controls[GAME_PANEL_EXIT].value =
2272     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2273
2274   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2275     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2276   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2277     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2278      EL_EMC_MAGIC_BALL_SWITCH);
2279
2280   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2281     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2282   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2283     game.light_time_left;
2284
2285   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2286     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2287   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2288     game.timegate_time_left;
2289
2290   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2291     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2292
2293   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2294     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2295   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2296     game.lenses_time_left;
2297
2298   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2299     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2301     game.magnify_time_left;
2302
2303   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2304     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2305      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2306      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2307      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2308      EL_BALLOON_SWITCH_NONE);
2309
2310   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2311     local_player->dynabomb_count;
2312   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2313     local_player->dynabomb_size;
2314   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2315     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2316
2317   game_panel_controls[GAME_PANEL_PENGUINS].value =
2318     local_player->friends_still_needed;
2319
2320   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2321     local_player->sokobanfields_still_needed;
2322   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2323     local_player->sokobanfields_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2326     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2327
2328   for (i = 0; i < NUM_BELTS; i++)
2329   {
2330     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2331       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2332        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2333     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2334       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2338     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2339   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2340     game.magic_wall_time_left;
2341
2342   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2343     local_player->gravity;
2344
2345   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2346     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2347
2348   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2349     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2350       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2351        game.panel.element[i].id : EL_UNDEFINED);
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2355       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2356        element_info[game.panel.element_count[i].id].element_count : 0);
2357
2358   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2359     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2360       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2361        element_info[game.panel.ce_score[i].id].collect_score : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2366        element_info[game.panel.ce_score_element[i].id].collect_score :
2367        EL_UNDEFINED);
2368
2369   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2372
2373   /* update game panel control frames */
2374
2375   for (i = 0; game_panel_controls[i].nr != -1; i++)
2376   {
2377     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2378
2379     if (gpc->type == TYPE_ELEMENT)
2380     {
2381       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2382       {
2383         int last_anim_random_frame = gfx.anim_random_frame;
2384         int element = gpc->value;
2385         int graphic = el2panelimg(element);
2386
2387         if (gpc->value != gpc->last_value)
2388         {
2389           gpc->gfx_frame = 0;
2390           gpc->gfx_random = INIT_GFX_RANDOM();
2391         }
2392         else
2393         {
2394           gpc->gfx_frame++;
2395
2396           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2397               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2398             gpc->gfx_random = INIT_GFX_RANDOM();
2399         }
2400
2401         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2402           gfx.anim_random_frame = gpc->gfx_random;
2403
2404         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2405           gpc->gfx_frame = element_info[element].collect_score;
2406
2407         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2408                                               gpc->gfx_frame);
2409
2410         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2411           gfx.anim_random_frame = last_anim_random_frame;
2412       }
2413     }
2414   }
2415 }
2416
2417 void DisplayGameControlValues()
2418 {
2419   boolean redraw_panel = FALSE;
2420   int i;
2421
2422   for (i = 0; game_panel_controls[i].nr != -1; i++)
2423   {
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2425
2426     if (PANEL_DEACTIVATED(gpc->pos))
2427       continue;
2428
2429     if (gpc->value == gpc->last_value &&
2430         gpc->frame == gpc->last_frame)
2431       continue;
2432
2433     redraw_panel = TRUE;
2434   }
2435
2436   if (!redraw_panel)
2437     return;
2438
2439   /* copy default game door content to main double buffer */
2440
2441   /* !!! CHECK AGAIN !!! */
2442   SetPanelBackground();
2443   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2444   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2445
2446   /* redraw game control buttons */
2447   RedrawGameButtons();
2448
2449   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2450
2451   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2452   {
2453     int nr = game_panel_order[i].nr;
2454     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2455     struct TextPosInfo *pos = gpc->pos;
2456     int type = gpc->type;
2457     int value = gpc->value;
2458     int frame = gpc->frame;
2459     int size = pos->size;
2460     int font = pos->font;
2461     boolean draw_masked = pos->draw_masked;
2462     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2463
2464     if (PANEL_DEACTIVATED(pos))
2465       continue;
2466
2467     gpc->last_value = value;
2468     gpc->last_frame = frame;
2469
2470     if (type == TYPE_INTEGER)
2471     {
2472       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2473           nr == GAME_PANEL_TIME)
2474       {
2475         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2476
2477         if (use_dynamic_size)           /* use dynamic number of digits */
2478         {
2479           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2480           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2481           int size2 = size1 + 1;
2482           int font1 = pos->font;
2483           int font2 = pos->font_alt;
2484
2485           size = (value < value_change ? size1 : size2);
2486           font = (value < value_change ? font1 : font2);
2487         }
2488       }
2489
2490       /* correct text size if "digits" is zero or less */
2491       if (size <= 0)
2492         size = strlen(int2str(value, size));
2493
2494       /* dynamically correct text alignment */
2495       pos->width = size * getFontWidth(font);
2496
2497       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2498                   int2str(value, size), font, mask_mode);
2499     }
2500     else if (type == TYPE_ELEMENT)
2501     {
2502       int element, graphic;
2503       Bitmap *src_bitmap;
2504       int src_x, src_y;
2505       int width, height;
2506       int dst_x = PANEL_XPOS(pos);
2507       int dst_y = PANEL_YPOS(pos);
2508
2509       if (value != EL_UNDEFINED && value != EL_EMPTY)
2510       {
2511         element = value;
2512         graphic = el2panelimg(value);
2513
2514         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2515
2516         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2517           size = TILESIZE;
2518
2519         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2520                               &src_x, &src_y);
2521
2522         width  = graphic_info[graphic].width  * size / TILESIZE;
2523         height = graphic_info[graphic].height * size / TILESIZE;
2524
2525         if (draw_masked)
2526           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2527                            dst_x, dst_y);
2528         else
2529           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2530                      dst_x, dst_y);
2531       }
2532     }
2533     else if (type == TYPE_STRING)
2534     {
2535       boolean active = (value != 0);
2536       char *state_normal = "off";
2537       char *state_active = "on";
2538       char *state = (active ? state_active : state_normal);
2539       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2540                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2541                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2542                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2543
2544       if (nr == GAME_PANEL_GRAVITY_STATE)
2545       {
2546         int font1 = pos->font;          /* (used for normal state) */
2547         int font2 = pos->font_alt;      /* (used for active state) */
2548
2549         font = (active ? font2 : font1);
2550       }
2551
2552       if (s != NULL)
2553       {
2554         char *s_cut;
2555
2556         if (size <= 0)
2557         {
2558           /* don't truncate output if "chars" is zero or less */
2559           size = strlen(s);
2560
2561           /* dynamically correct text alignment */
2562           pos->width = size * getFontWidth(font);
2563         }
2564
2565         s_cut = getStringCopyN(s, size);
2566
2567         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2568                     s_cut, font, mask_mode);
2569
2570         free(s_cut);
2571       }
2572     }
2573
2574     redraw_mask |= REDRAW_DOOR_1;
2575   }
2576
2577   SetGameStatus(GAME_MODE_PLAYING);
2578 }
2579
2580 void UpdateAndDisplayGameControlValues()
2581 {
2582   if (tape.deactivate_display)
2583     return;
2584
2585   UpdateGameControlValues();
2586   DisplayGameControlValues();
2587 }
2588
2589 void UpdateGameDoorValues()
2590 {
2591   UpdateGameControlValues();
2592 }
2593
2594 void DrawGameDoorValues()
2595 {
2596   DisplayGameControlValues();
2597 }
2598
2599
2600 /*
2601   =============================================================================
2602   InitGameEngine()
2603   -----------------------------------------------------------------------------
2604   initialize game engine due to level / tape version number
2605   =============================================================================
2606 */
2607
2608 static void InitGameEngine()
2609 {
2610   int i, j, k, l, x, y;
2611
2612   /* set game engine from tape file when re-playing, else from level file */
2613   game.engine_version = (tape.playing ? tape.engine_version :
2614                          level.game_version);
2615
2616   /* set single or multi-player game mode (needed for re-playing tapes) */
2617   game.team_mode = setup.team_mode;
2618
2619   if (tape.playing)
2620   {
2621     int num_players = 0;
2622
2623     for (i = 0; i < MAX_PLAYERS; i++)
2624       if (tape.player_participates[i])
2625         num_players++;
2626
2627     /* multi-player tapes contain input data for more than one player */
2628     game.team_mode = (num_players > 1);
2629   }
2630
2631   /* ---------------------------------------------------------------------- */
2632   /* set flags for bugs and changes according to active game engine version */
2633   /* ---------------------------------------------------------------------- */
2634
2635   /*
2636     Summary of bugfix/change:
2637     Fixed handling for custom elements that change when pushed by the player.
2638
2639     Fixed/changed in version:
2640     3.1.0
2641
2642     Description:
2643     Before 3.1.0, custom elements that "change when pushing" changed directly
2644     after the player started pushing them (until then handled in "DigField()").
2645     Since 3.1.0, these custom elements are not changed until the "pushing"
2646     move of the element is finished (now handled in "ContinueMoving()").
2647
2648     Affected levels/tapes:
2649     The first condition is generally needed for all levels/tapes before version
2650     3.1.0, which might use the old behaviour before it was changed; known tapes
2651     that are affected are some tapes from the level set "Walpurgis Gardens" by
2652     Jamie Cullen.
2653     The second condition is an exception from the above case and is needed for
2654     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2655     above (including some development versions of 3.1.0), but before it was
2656     known that this change would break tapes like the above and was fixed in
2657     3.1.1, so that the changed behaviour was active although the engine version
2658     while recording maybe was before 3.1.0. There is at least one tape that is
2659     affected by this exception, which is the tape for the one-level set "Bug
2660     Machine" by Juergen Bonhagen.
2661   */
2662
2663   game.use_change_when_pushing_bug =
2664     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2665      !(tape.playing &&
2666        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2667        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2668
2669   /*
2670     Summary of bugfix/change:
2671     Fixed handling for blocking the field the player leaves when moving.
2672
2673     Fixed/changed in version:
2674     3.1.1
2675
2676     Description:
2677     Before 3.1.1, when "block last field when moving" was enabled, the field
2678     the player is leaving when moving was blocked for the time of the move,
2679     and was directly unblocked afterwards. This resulted in the last field
2680     being blocked for exactly one less than the number of frames of one player
2681     move. Additionally, even when blocking was disabled, the last field was
2682     blocked for exactly one frame.
2683     Since 3.1.1, due to changes in player movement handling, the last field
2684     is not blocked at all when blocking is disabled. When blocking is enabled,
2685     the last field is blocked for exactly the number of frames of one player
2686     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2687     last field is blocked for exactly one more than the number of frames of
2688     one player move.
2689
2690     Affected levels/tapes:
2691     (!!! yet to be determined -- probably many !!!)
2692   */
2693
2694   game.use_block_last_field_bug =
2695     (game.engine_version < VERSION_IDENT(3,1,1,0));
2696
2697   /* ---------------------------------------------------------------------- */
2698
2699   /* set maximal allowed number of custom element changes per game frame */
2700   game.max_num_changes_per_frame = 1;
2701
2702   /* default scan direction: scan playfield from top/left to bottom/right */
2703   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2704
2705   /* dynamically adjust element properties according to game engine version */
2706   InitElementPropertiesEngine(game.engine_version);
2707
2708 #if 0
2709   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2710   printf("          tape version == %06d [%s] [file: %06d]\n",
2711          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2712          tape.file_version);
2713   printf("       => game.engine_version == %06d\n", game.engine_version);
2714 #endif
2715
2716   /* ---------- initialize player's initial move delay --------------------- */
2717
2718   /* dynamically adjust player properties according to level information */
2719   for (i = 0; i < MAX_PLAYERS; i++)
2720     game.initial_move_delay_value[i] =
2721       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2722
2723   /* dynamically adjust player properties according to game engine version */
2724   for (i = 0; i < MAX_PLAYERS; i++)
2725     game.initial_move_delay[i] =
2726       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2727        game.initial_move_delay_value[i] : 0);
2728
2729   /* ---------- initialize player's initial push delay --------------------- */
2730
2731   /* dynamically adjust player properties according to game engine version */
2732   game.initial_push_delay_value =
2733     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2734
2735   /* ---------- initialize changing elements ------------------------------- */
2736
2737   /* initialize changing elements information */
2738   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2739   {
2740     struct ElementInfo *ei = &element_info[i];
2741
2742     /* this pointer might have been changed in the level editor */
2743     ei->change = &ei->change_page[0];
2744
2745     if (!IS_CUSTOM_ELEMENT(i))
2746     {
2747       ei->change->target_element = EL_EMPTY_SPACE;
2748       ei->change->delay_fixed = 0;
2749       ei->change->delay_random = 0;
2750       ei->change->delay_frames = 1;
2751     }
2752
2753     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2754     {
2755       ei->has_change_event[j] = FALSE;
2756
2757       ei->event_page_nr[j] = 0;
2758       ei->event_page[j] = &ei->change_page[0];
2759     }
2760   }
2761
2762   /* add changing elements from pre-defined list */
2763   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2764   {
2765     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2766     struct ElementInfo *ei = &element_info[ch_delay->element];
2767
2768     ei->change->target_element       = ch_delay->target_element;
2769     ei->change->delay_fixed          = ch_delay->change_delay;
2770
2771     ei->change->pre_change_function  = ch_delay->pre_change_function;
2772     ei->change->change_function      = ch_delay->change_function;
2773     ei->change->post_change_function = ch_delay->post_change_function;
2774
2775     ei->change->can_change = TRUE;
2776     ei->change->can_change_or_has_action = TRUE;
2777
2778     ei->has_change_event[CE_DELAY] = TRUE;
2779
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2782   }
2783
2784   /* ---------- initialize internal run-time variables --------------------- */
2785
2786   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2787   {
2788     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2789
2790     for (j = 0; j < ei->num_change_pages; j++)
2791     {
2792       ei->change_page[j].can_change_or_has_action =
2793         (ei->change_page[j].can_change |
2794          ei->change_page[j].has_action);
2795     }
2796   }
2797
2798   /* add change events from custom element configuration */
2799   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2800   {
2801     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2802
2803     for (j = 0; j < ei->num_change_pages; j++)
2804     {
2805       if (!ei->change_page[j].can_change_or_has_action)
2806         continue;
2807
2808       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2809       {
2810         /* only add event page for the first page found with this event */
2811         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2812         {
2813           ei->has_change_event[k] = TRUE;
2814
2815           ei->event_page_nr[k] = j;
2816           ei->event_page[k] = &ei->change_page[j];
2817         }
2818       }
2819     }
2820   }
2821
2822   /* ---------- initialize reference elements in change conditions --------- */
2823
2824   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2825   {
2826     int element = EL_CUSTOM_START + i;
2827     struct ElementInfo *ei = &element_info[element];
2828
2829     for (j = 0; j < ei->num_change_pages; j++)
2830     {
2831       int trigger_element = ei->change_page[j].initial_trigger_element;
2832
2833       if (trigger_element >= EL_PREV_CE_8 &&
2834           trigger_element <= EL_NEXT_CE_8)
2835         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2836
2837       ei->change_page[j].trigger_element = trigger_element;
2838     }
2839   }
2840
2841   /* ---------- initialize run-time trigger player and element ------------- */
2842
2843   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2844   {
2845     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2846
2847     for (j = 0; j < ei->num_change_pages; j++)
2848     {
2849       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2852       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2853       ei->change_page[j].actual_trigger_ce_value = 0;
2854       ei->change_page[j].actual_trigger_ce_score = 0;
2855     }
2856   }
2857
2858   /* ---------- initialize trigger events ---------------------------------- */
2859
2860   /* initialize trigger events information */
2861   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2862     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2863       trigger_events[i][j] = FALSE;
2864
2865   /* add trigger events from element change event properties */
2866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867   {
2868     struct ElementInfo *ei = &element_info[i];
2869
2870     for (j = 0; j < ei->num_change_pages; j++)
2871     {
2872       if (!ei->change_page[j].can_change_or_has_action)
2873         continue;
2874
2875       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2876       {
2877         int trigger_element = ei->change_page[j].trigger_element;
2878
2879         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2880         {
2881           if (ei->change_page[j].has_event[k])
2882           {
2883             if (IS_GROUP_ELEMENT(trigger_element))
2884             {
2885               struct ElementGroupInfo *group =
2886                 element_info[trigger_element].group;
2887
2888               for (l = 0; l < group->num_elements_resolved; l++)
2889                 trigger_events[group->element_resolved[l]][k] = TRUE;
2890             }
2891             else if (trigger_element == EL_ANY_ELEMENT)
2892               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2893                 trigger_events[l][k] = TRUE;
2894             else
2895               trigger_events[trigger_element][k] = TRUE;
2896           }
2897         }
2898       }
2899     }
2900   }
2901
2902   /* ---------- initialize push delay -------------------------------------- */
2903
2904   /* initialize push delay values to default */
2905   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2906   {
2907     if (!IS_CUSTOM_ELEMENT(i))
2908     {
2909       /* set default push delay values (corrected since version 3.0.7-1) */
2910       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2911       {
2912         element_info[i].push_delay_fixed = 2;
2913         element_info[i].push_delay_random = 8;
2914       }
2915       else
2916       {
2917         element_info[i].push_delay_fixed = 8;
2918         element_info[i].push_delay_random = 8;
2919       }
2920     }
2921   }
2922
2923   /* set push delay value for certain elements from pre-defined list */
2924   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2925   {
2926     int e = push_delay_list[i].element;
2927
2928     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2929     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2930   }
2931
2932   /* set push delay value for Supaplex elements for newer engine versions */
2933   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2934   {
2935     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2936     {
2937       if (IS_SP_ELEMENT(i))
2938       {
2939         /* set SP push delay to just enough to push under a falling zonk */
2940         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2941
2942         element_info[i].push_delay_fixed  = delay;
2943         element_info[i].push_delay_random = 0;
2944       }
2945     }
2946   }
2947
2948   /* ---------- initialize move stepsize ----------------------------------- */
2949
2950   /* initialize move stepsize values to default */
2951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2952     if (!IS_CUSTOM_ELEMENT(i))
2953       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2954
2955   /* set move stepsize value for certain elements from pre-defined list */
2956   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2957   {
2958     int e = move_stepsize_list[i].element;
2959
2960     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2961   }
2962
2963   /* ---------- initialize collect score ----------------------------------- */
2964
2965   /* initialize collect score values for custom elements from initial value */
2966   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2967     if (IS_CUSTOM_ELEMENT(i))
2968       element_info[i].collect_score = element_info[i].collect_score_initial;
2969
2970   /* ---------- initialize collect count ----------------------------------- */
2971
2972   /* initialize collect count values for non-custom elements */
2973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974     if (!IS_CUSTOM_ELEMENT(i))
2975       element_info[i].collect_count_initial = 0;
2976
2977   /* add collect count values for all elements from pre-defined list */
2978   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2979     element_info[collect_count_list[i].element].collect_count_initial =
2980       collect_count_list[i].count;
2981
2982   /* ---------- initialize access direction -------------------------------- */
2983
2984   /* initialize access direction values to default (access from every side) */
2985   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2986     if (!IS_CUSTOM_ELEMENT(i))
2987       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2988
2989   /* set access direction value for certain elements from pre-defined list */
2990   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2991     element_info[access_direction_list[i].element].access_direction =
2992       access_direction_list[i].direction;
2993
2994   /* ---------- initialize explosion content ------------------------------- */
2995   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2996   {
2997     if (IS_CUSTOM_ELEMENT(i))
2998       continue;
2999
3000     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3001     {
3002       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3003
3004       element_info[i].content.e[x][y] =
3005         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3006          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3007          i == EL_PLAYER_3 ? EL_EMERALD :
3008          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3009          i == EL_MOLE ? EL_EMERALD_RED :
3010          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3011          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3012          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3013          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3014          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3015          i == EL_WALL_EMERALD ? EL_EMERALD :
3016          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3017          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3018          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3019          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3020          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3021          i == EL_WALL_PEARL ? EL_PEARL :
3022          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3023          EL_EMPTY);
3024     }
3025   }
3026
3027   /* ---------- initialize recursion detection ------------------------------ */
3028   recursion_loop_depth = 0;
3029   recursion_loop_detected = FALSE;
3030   recursion_loop_element = EL_UNDEFINED;
3031
3032   /* ---------- initialize graphics engine ---------------------------------- */
3033   game.scroll_delay_value =
3034     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3035      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3036   game.scroll_delay_value =
3037     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3038
3039   /* ---------- initialize game engine snapshots ---------------------------- */
3040   for (i = 0; i < MAX_PLAYERS; i++)
3041     game.snapshot.last_action[i] = 0;
3042   game.snapshot.changed_action = FALSE;
3043   game.snapshot.collected_item = FALSE;
3044   game.snapshot.mode =
3045     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3046      SNAPSHOT_MODE_EVERY_STEP :
3047      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3048      SNAPSHOT_MODE_EVERY_MOVE :
3049      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3050      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3051 }
3052
3053 int get_num_special_action(int element, int action_first, int action_last)
3054 {
3055   int num_special_action = 0;
3056   int i, j;
3057
3058   for (i = action_first; i <= action_last; i++)
3059   {
3060     boolean found = FALSE;
3061
3062     for (j = 0; j < NUM_DIRECTIONS; j++)
3063       if (el_act_dir2img(element, i, j) !=
3064           el_act_dir2img(element, ACTION_DEFAULT, j))
3065         found = TRUE;
3066
3067     if (found)
3068       num_special_action++;
3069     else
3070       break;
3071   }
3072
3073   return num_special_action;
3074 }
3075
3076
3077 /*
3078   =============================================================================
3079   InitGame()
3080   -----------------------------------------------------------------------------
3081   initialize and start new game
3082   =============================================================================
3083 */
3084
3085 void InitGame()
3086 {
3087   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3088   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3089   int fade_mask = REDRAW_FIELD;
3090
3091   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3092   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3093   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3094   int initial_move_dir = MV_DOWN;
3095   int i, j, x, y;
3096
3097   // required here to update video display before fading (FIX THIS)
3098   DrawMaskedBorder(REDRAW_DOOR_2);
3099
3100   if (!game.restart_level)
3101     CloseDoor(DOOR_CLOSE_1);
3102
3103   SetGameStatus(GAME_MODE_PLAYING);
3104
3105   /* needed if different viewport properties defined for playing */
3106   ChangeViewportPropertiesIfNeeded();
3107
3108   if (level_editor_test_game)
3109     FadeSkipNextFadeIn();
3110   else
3111     FadeSetEnterScreen();
3112
3113   if (CheckIfGlobalBorderHasChanged())
3114     fade_mask = REDRAW_ALL;
3115
3116   FadeOut(fade_mask);
3117
3118   ClearField();
3119
3120   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3121
3122   DrawCompleteVideoDisplay();
3123
3124   InitGameEngine();
3125   InitGameControlValues();
3126
3127   /* don't play tapes over network */
3128   network_playing = (options.network && !tape.playing);
3129
3130   for (i = 0; i < MAX_PLAYERS; i++)
3131   {
3132     struct PlayerInfo *player = &stored_player[i];
3133
3134     player->index_nr = i;
3135     player->index_bit = (1 << i);
3136     player->element_nr = EL_PLAYER_1 + i;
3137
3138     player->present = FALSE;
3139     player->active = FALSE;
3140     player->mapped = FALSE;
3141
3142     player->killed = FALSE;
3143     player->reanimated = FALSE;
3144
3145     player->action = 0;
3146     player->effective_action = 0;
3147     player->programmed_action = 0;
3148
3149     player->score = 0;
3150     player->score_final = 0;
3151
3152     player->gems_still_needed = level.gems_needed;
3153     player->sokobanfields_still_needed = 0;
3154     player->lights_still_needed = 0;
3155     player->friends_still_needed = 0;
3156
3157     for (j = 0; j < MAX_NUM_KEYS; j++)
3158       player->key[j] = FALSE;
3159
3160     player->num_white_keys = 0;
3161
3162     player->dynabomb_count = 0;
3163     player->dynabomb_size = 1;
3164     player->dynabombs_left = 0;
3165     player->dynabomb_xl = FALSE;
3166
3167     player->MovDir = initial_move_dir;
3168     player->MovPos = 0;
3169     player->GfxPos = 0;
3170     player->GfxDir = initial_move_dir;
3171     player->GfxAction = ACTION_DEFAULT;
3172     player->Frame = 0;
3173     player->StepFrame = 0;
3174
3175     player->initial_element = player->element_nr;
3176     player->artwork_element =
3177       (level.use_artwork_element[i] ? level.artwork_element[i] :
3178        player->element_nr);
3179     player->use_murphy = FALSE;
3180
3181     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3182     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3183
3184     player->gravity = level.initial_player_gravity[i];
3185
3186     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3187
3188     player->actual_frame_counter = 0;
3189
3190     player->step_counter = 0;
3191
3192     player->last_move_dir = initial_move_dir;
3193
3194     player->is_active = FALSE;
3195
3196     player->is_waiting = FALSE;
3197     player->is_moving = FALSE;
3198     player->is_auto_moving = FALSE;
3199     player->is_digging = FALSE;
3200     player->is_snapping = FALSE;
3201     player->is_collecting = FALSE;
3202     player->is_pushing = FALSE;
3203     player->is_switching = FALSE;
3204     player->is_dropping = FALSE;
3205     player->is_dropping_pressed = FALSE;
3206
3207     player->is_bored = FALSE;
3208     player->is_sleeping = FALSE;
3209
3210     player->frame_counter_bored = -1;
3211     player->frame_counter_sleeping = -1;
3212
3213     player->anim_delay_counter = 0;
3214     player->post_delay_counter = 0;
3215
3216     player->dir_waiting = initial_move_dir;
3217     player->action_waiting = ACTION_DEFAULT;
3218     player->last_action_waiting = ACTION_DEFAULT;
3219     player->special_action_bored = ACTION_DEFAULT;
3220     player->special_action_sleeping = ACTION_DEFAULT;
3221
3222     player->switch_x = -1;
3223     player->switch_y = -1;
3224
3225     player->drop_x = -1;
3226     player->drop_y = -1;
3227
3228     player->show_envelope = 0;
3229
3230     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3231
3232     player->push_delay       = -1;      /* initialized when pushing starts */
3233     player->push_delay_value = game.initial_push_delay_value;
3234
3235     player->drop_delay = 0;
3236     player->drop_pressed_delay = 0;
3237
3238     player->last_jx = -1;
3239     player->last_jy = -1;
3240     player->jx = -1;
3241     player->jy = -1;
3242
3243     player->shield_normal_time_left = 0;
3244     player->shield_deadly_time_left = 0;
3245
3246     player->inventory_infinite_element = EL_UNDEFINED;
3247     player->inventory_size = 0;
3248
3249     if (level.use_initial_inventory[i])
3250     {
3251       for (j = 0; j < level.initial_inventory_size[i]; j++)
3252       {
3253         int element = level.initial_inventory_content[i][j];
3254         int collect_count = element_info[element].collect_count_initial;
3255         int k;
3256
3257         if (!IS_CUSTOM_ELEMENT(element))
3258           collect_count = 1;
3259
3260         if (collect_count == 0)
3261           player->inventory_infinite_element = element;
3262         else
3263           for (k = 0; k < collect_count; k++)
3264             if (player->inventory_size < MAX_INVENTORY_SIZE)
3265               player->inventory_element[player->inventory_size++] = element;
3266       }
3267     }
3268
3269     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3270     SnapField(player, 0, 0);
3271
3272     player->LevelSolved = FALSE;
3273     player->GameOver = FALSE;
3274
3275     player->LevelSolved_GameWon = FALSE;
3276     player->LevelSolved_GameEnd = FALSE;
3277     player->LevelSolved_PanelOff = FALSE;
3278     player->LevelSolved_SaveTape = FALSE;
3279     player->LevelSolved_SaveScore = FALSE;
3280     player->LevelSolved_CountingTime = 0;
3281     player->LevelSolved_CountingScore = 0;
3282
3283     map_player_action[i] = i;
3284   }
3285
3286   network_player_action_received = FALSE;
3287
3288 #if defined(NETWORK_AVALIABLE)
3289   /* initial null action */
3290   if (network_playing)
3291     SendToServer_MovePlayer(MV_NONE);
3292 #endif
3293
3294   ZX = ZY = -1;
3295   ExitX = ExitY = -1;
3296
3297   FrameCounter = 0;
3298   TimeFrames = 0;
3299   TimePlayed = 0;
3300   TimeLeft = level.time;
3301   TapeTime = 0;
3302
3303   ScreenMovDir = MV_NONE;
3304   ScreenMovPos = 0;
3305   ScreenGfxPos = 0;
3306
3307   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3308
3309   AllPlayersGone = FALSE;
3310
3311   game.no_time_limit = (level.time == 0);
3312
3313   game.yamyam_content_nr = 0;
3314   game.robot_wheel_active = FALSE;
3315   game.magic_wall_active = FALSE;
3316   game.magic_wall_time_left = 0;
3317   game.light_time_left = 0;
3318   game.timegate_time_left = 0;
3319   game.switchgate_pos = 0;
3320   game.wind_direction = level.wind_direction_initial;
3321
3322   game.lenses_time_left = 0;
3323   game.magnify_time_left = 0;
3324
3325   game.ball_state = level.ball_state_initial;
3326   game.ball_content_nr = 0;
3327
3328   game.envelope_active = FALSE;
3329
3330   /* set focus to local player for network games, else to all players */
3331   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3332   game.centered_player_nr_next = game.centered_player_nr;
3333   game.set_centered_player = FALSE;
3334
3335   if (network_playing && tape.recording)
3336   {
3337     /* store client dependent player focus when recording network games */
3338     tape.centered_player_nr_next = game.centered_player_nr_next;
3339     tape.set_centered_player = TRUE;
3340   }
3341
3342   for (i = 0; i < NUM_BELTS; i++)
3343   {
3344     game.belt_dir[i] = MV_NONE;
3345     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3346   }
3347
3348   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3349     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3350
3351 #if DEBUG_INIT_PLAYER
3352   if (options.debug)
3353   {
3354     printf("Player status at level initialization:\n");
3355   }
3356 #endif
3357
3358   SCAN_PLAYFIELD(x, y)
3359   {
3360     Feld[x][y] = level.field[x][y];
3361     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3362     ChangeDelay[x][y] = 0;
3363     ChangePage[x][y] = -1;
3364     CustomValue[x][y] = 0;              /* initialized in InitField() */
3365     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3366     AmoebaNr[x][y] = 0;
3367     WasJustMoving[x][y] = 0;
3368     WasJustFalling[x][y] = 0;
3369     CheckCollision[x][y] = 0;
3370     CheckImpact[x][y] = 0;
3371     Stop[x][y] = FALSE;
3372     Pushed[x][y] = FALSE;
3373
3374     ChangeCount[x][y] = 0;
3375     ChangeEvent[x][y] = -1;
3376
3377     ExplodePhase[x][y] = 0;
3378     ExplodeDelay[x][y] = 0;
3379     ExplodeField[x][y] = EX_TYPE_NONE;
3380
3381     RunnerVisit[x][y] = 0;
3382     PlayerVisit[x][y] = 0;
3383
3384     GfxFrame[x][y] = 0;
3385     GfxRandom[x][y] = INIT_GFX_RANDOM();
3386     GfxElement[x][y] = EL_UNDEFINED;
3387     GfxAction[x][y] = ACTION_DEFAULT;
3388     GfxDir[x][y] = MV_NONE;
3389     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3390   }
3391
3392   SCAN_PLAYFIELD(x, y)
3393   {
3394     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3395       emulate_bd = FALSE;
3396     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3397       emulate_sb = FALSE;
3398     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3399       emulate_sp = FALSE;
3400
3401     InitField(x, y, TRUE);
3402
3403     ResetGfxAnimation(x, y);
3404   }
3405
3406   InitBeltMovement();
3407
3408   for (i = 0; i < MAX_PLAYERS; i++)
3409   {
3410     struct PlayerInfo *player = &stored_player[i];
3411
3412     /* set number of special actions for bored and sleeping animation */
3413     player->num_special_action_bored =
3414       get_num_special_action(player->artwork_element,
3415                              ACTION_BORING_1, ACTION_BORING_LAST);
3416     player->num_special_action_sleeping =
3417       get_num_special_action(player->artwork_element,
3418                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3419   }
3420
3421   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3422                     emulate_sb ? EMU_SOKOBAN :
3423                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3424
3425   /* initialize type of slippery elements */
3426   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3427   {
3428     if (!IS_CUSTOM_ELEMENT(i))
3429     {
3430       /* default: elements slip down either to the left or right randomly */
3431       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3432
3433       /* SP style elements prefer to slip down on the left side */
3434       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3435         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3436
3437       /* BD style elements prefer to slip down on the left side */
3438       if (game.emulation == EMU_BOULDERDASH)
3439         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3440     }
3441   }
3442
3443   /* initialize explosion and ignition delay */
3444   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3445   {
3446     if (!IS_CUSTOM_ELEMENT(i))
3447     {
3448       int num_phase = 8;
3449       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3450                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3451                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3452       int last_phase = (num_phase + 1) * delay;
3453       int half_phase = (num_phase / 2) * delay;
3454
3455       element_info[i].explosion_delay = last_phase - 1;
3456       element_info[i].ignition_delay = half_phase;
3457
3458       if (i == EL_BLACK_ORB)
3459         element_info[i].ignition_delay = 1;
3460     }
3461   }
3462
3463   /* correct non-moving belts to start moving left */
3464   for (i = 0; i < NUM_BELTS; i++)
3465     if (game.belt_dir[i] == MV_NONE)
3466       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3467
3468 #if USE_NEW_PLAYER_ASSIGNMENTS
3469   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3470   /* choose default local player */
3471   local_player = &stored_player[0];
3472
3473   for (i = 0; i < MAX_PLAYERS; i++)
3474     stored_player[i].connected = FALSE;
3475
3476   local_player->connected = TRUE;
3477   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3478
3479   if (tape.playing)
3480   {
3481     for (i = 0; i < MAX_PLAYERS; i++)
3482       stored_player[i].connected = tape.player_participates[i];
3483   }
3484   else if (game.team_mode && !options.network)
3485   {
3486     /* try to guess locally connected team mode players (needed for correct
3487        assignment of player figures from level to locally playing players) */
3488
3489     for (i = 0; i < MAX_PLAYERS; i++)
3490       if (setup.input[i].use_joystick ||
3491           setup.input[i].key.left != KSYM_UNDEFINED)
3492         stored_player[i].connected = TRUE;
3493   }
3494
3495 #if DEBUG_INIT_PLAYER
3496   if (options.debug)
3497   {
3498     printf("Player status after level initialization:\n");
3499
3500     for (i = 0; i < MAX_PLAYERS; i++)
3501     {
3502       struct PlayerInfo *player = &stored_player[i];
3503
3504       printf("- player %d: present == %d, connected == %d, active == %d",
3505              i + 1,
3506              player->present,
3507              player->connected,
3508              player->active);
3509
3510       if (local_player == player)
3511         printf(" (local player)");
3512
3513       printf("\n");
3514     }
3515   }
3516 #endif
3517
3518 #if DEBUG_INIT_PLAYER
3519   if (options.debug)
3520     printf("Reassigning players ...\n");
3521 #endif
3522
3523   /* check if any connected player was not found in playfield */
3524   for (i = 0; i < MAX_PLAYERS; i++)
3525   {
3526     struct PlayerInfo *player = &stored_player[i];
3527
3528     if (player->connected && !player->present)
3529     {
3530       struct PlayerInfo *field_player = NULL;
3531
3532 #if DEBUG_INIT_PLAYER
3533       if (options.debug)
3534         printf("- looking for field player for player %d ...\n", i + 1);
3535 #endif
3536
3537       /* assign first free player found that is present in the playfield */
3538
3539       /* first try: look for unmapped playfield player that is not connected */
3540       for (j = 0; j < MAX_PLAYERS; j++)
3541         if (field_player == NULL &&
3542             stored_player[j].present &&
3543             !stored_player[j].mapped &&
3544             !stored_player[j].connected)
3545           field_player = &stored_player[j];
3546
3547       /* second try: look for *any* unmapped playfield player */
3548       for (j = 0; j < MAX_PLAYERS; j++)
3549         if (field_player == NULL &&
3550             stored_player[j].present &&
3551             !stored_player[j].mapped)
3552           field_player = &stored_player[j];
3553
3554       if (field_player != NULL)
3555       {
3556         int jx = field_player->jx, jy = field_player->jy;
3557
3558 #if DEBUG_INIT_PLAYER
3559         if (options.debug)
3560           printf("- found player %d\n", field_player->index_nr + 1);
3561 #endif
3562
3563         player->present = FALSE;
3564         player->active = FALSE;
3565
3566         field_player->present = TRUE;
3567         field_player->active = TRUE;
3568
3569         /*
3570         player->initial_element = field_player->initial_element;
3571         player->artwork_element = field_player->artwork_element;
3572
3573         player->block_last_field       = field_player->block_last_field;
3574         player->block_delay_adjustment = field_player->block_delay_adjustment;
3575         */
3576
3577         StorePlayer[jx][jy] = field_player->element_nr;
3578
3579         field_player->jx = field_player->last_jx = jx;
3580         field_player->jy = field_player->last_jy = jy;
3581
3582         if (local_player == player)
3583           local_player = field_player;
3584
3585         map_player_action[field_player->index_nr] = i;
3586
3587         field_player->mapped = TRUE;
3588
3589 #if DEBUG_INIT_PLAYER
3590         if (options.debug)
3591           printf("- map_player_action[%d] == %d\n",
3592                  field_player->index_nr + 1, i + 1);
3593 #endif
3594       }
3595     }
3596
3597     if (player->connected && player->present)
3598       player->mapped = TRUE;
3599   }
3600
3601 #if DEBUG_INIT_PLAYER
3602   if (options.debug)
3603   {
3604     printf("Player status after player assignment (first stage):\n");
3605
3606     for (i = 0; i < MAX_PLAYERS; i++)
3607     {
3608       struct PlayerInfo *player = &stored_player[i];
3609
3610       printf("- player %d: present == %d, connected == %d, active == %d",
3611              i + 1,
3612              player->present,
3613              player->connected,
3614              player->active);
3615
3616       if (local_player == player)
3617         printf(" (local player)");
3618
3619       printf("\n");
3620     }
3621   }
3622 #endif
3623
3624 #else
3625
3626   /* check if any connected player was not found in playfield */
3627   for (i = 0; i < MAX_PLAYERS; i++)
3628   {
3629     struct PlayerInfo *player = &stored_player[i];
3630
3631     if (player->connected && !player->present)
3632     {
3633       for (j = 0; j < MAX_PLAYERS; j++)
3634       {
3635         struct PlayerInfo *field_player = &stored_player[j];
3636         int jx = field_player->jx, jy = field_player->jy;
3637
3638         /* assign first free player found that is present in the playfield */
3639         if (field_player->present && !field_player->connected)
3640         {
3641           player->present = TRUE;
3642           player->active = TRUE;
3643
3644           field_player->present = FALSE;
3645           field_player->active = FALSE;
3646
3647           player->initial_element = field_player->initial_element;
3648           player->artwork_element = field_player->artwork_element;
3649
3650           player->block_last_field       = field_player->block_last_field;
3651           player->block_delay_adjustment = field_player->block_delay_adjustment;
3652
3653           StorePlayer[jx][jy] = player->element_nr;
3654
3655           player->jx = player->last_jx = jx;
3656           player->jy = player->last_jy = jy;
3657
3658           break;
3659         }
3660       }
3661     }
3662   }
3663 #endif
3664
3665 #if 0
3666   printf("::: local_player->present == %d\n", local_player->present);
3667 #endif
3668
3669   if (tape.playing)
3670   {
3671     /* when playing a tape, eliminate all players who do not participate */
3672
3673 #if USE_NEW_PLAYER_ASSIGNMENTS
3674
3675     if (!game.team_mode)
3676     {
3677       for (i = 0; i < MAX_PLAYERS; i++)
3678       {
3679         if (stored_player[i].active &&
3680             !tape.player_participates[map_player_action[i]])
3681         {
3682           struct PlayerInfo *player = &stored_player[i];
3683           int jx = player->jx, jy = player->jy;
3684
3685 #if DEBUG_INIT_PLAYER
3686           if (options.debug)
3687             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3688 #endif
3689
3690           player->active = FALSE;
3691           StorePlayer[jx][jy] = 0;
3692           Feld[jx][jy] = EL_EMPTY;
3693         }
3694       }
3695     }
3696
3697 #else
3698
3699     for (i = 0; i < MAX_PLAYERS; i++)
3700     {
3701       if (stored_player[i].active &&
3702           !tape.player_participates[i])
3703       {
3704         struct PlayerInfo *player = &stored_player[i];
3705         int jx = player->jx, jy = player->jy;
3706
3707         player->active = FALSE;
3708         StorePlayer[jx][jy] = 0;
3709         Feld[jx][jy] = EL_EMPTY;
3710       }
3711     }
3712 #endif
3713   }
3714   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3715   {
3716     /* when in single player mode, eliminate all but the first active player */
3717
3718     for (i = 0; i < MAX_PLAYERS; i++)
3719     {
3720       if (stored_player[i].active)
3721       {
3722         for (j = i + 1; j < MAX_PLAYERS; j++)
3723         {
3724           if (stored_player[j].active)
3725           {
3726             struct PlayerInfo *player = &stored_player[j];
3727             int jx = player->jx, jy = player->jy;
3728
3729             player->active = FALSE;
3730             player->present = FALSE;
3731
3732             StorePlayer[jx][jy] = 0;
3733             Feld[jx][jy] = EL_EMPTY;
3734           }
3735         }
3736       }
3737     }
3738   }
3739
3740   /* when recording the game, store which players take part in the game */
3741   if (tape.recording)
3742   {
3743 #if USE_NEW_PLAYER_ASSIGNMENTS
3744     for (i = 0; i < MAX_PLAYERS; i++)
3745       if (stored_player[i].connected)
3746         tape.player_participates[i] = TRUE;
3747 #else
3748     for (i = 0; i < MAX_PLAYERS; i++)
3749       if (stored_player[i].active)
3750         tape.player_participates[i] = TRUE;
3751 #endif
3752   }
3753
3754 #if DEBUG_INIT_PLAYER
3755   if (options.debug)
3756   {
3757     printf("Player status after player assignment (final stage):\n");
3758
3759     for (i = 0; i < MAX_PLAYERS; i++)
3760     {
3761       struct PlayerInfo *player = &stored_player[i];
3762
3763       printf("- player %d: present == %d, connected == %d, active == %d",
3764              i + 1,
3765              player->present,
3766              player->connected,
3767              player->active);
3768
3769       if (local_player == player)
3770         printf(" (local player)");
3771
3772       printf("\n");
3773     }
3774   }
3775 #endif
3776
3777   if (BorderElement == EL_EMPTY)
3778   {
3779     SBX_Left = 0;
3780     SBX_Right = lev_fieldx - SCR_FIELDX;
3781     SBY_Upper = 0;
3782     SBY_Lower = lev_fieldy - SCR_FIELDY;
3783   }
3784   else
3785   {
3786     SBX_Left = -1;
3787     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3788     SBY_Upper = -1;
3789     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3790   }
3791
3792   if (full_lev_fieldx <= SCR_FIELDX)
3793     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3794   if (full_lev_fieldy <= SCR_FIELDY)
3795     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3796
3797   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3798     SBX_Left--;
3799   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3800     SBY_Upper--;
3801
3802   /* if local player not found, look for custom element that might create
3803      the player (make some assumptions about the right custom element) */
3804   if (!local_player->present)
3805   {
3806     int start_x = 0, start_y = 0;
3807     int found_rating = 0;
3808     int found_element = EL_UNDEFINED;
3809     int player_nr = local_player->index_nr;
3810
3811     SCAN_PLAYFIELD(x, y)
3812     {
3813       int element = Feld[x][y];
3814       int content;
3815       int xx, yy;
3816       boolean is_player;
3817
3818       if (level.use_start_element[player_nr] &&
3819           level.start_element[player_nr] == element &&
3820           found_rating < 4)
3821       {
3822         start_x = x;
3823         start_y = y;
3824
3825         found_rating = 4;
3826         found_element = element;
3827       }
3828
3829       if (!IS_CUSTOM_ELEMENT(element))
3830         continue;
3831
3832       if (CAN_CHANGE(element))
3833       {
3834         for (i = 0; i < element_info[element].num_change_pages; i++)
3835         {
3836           /* check for player created from custom element as single target */
3837           content = element_info[element].change_page[i].target_element;
3838           is_player = ELEM_IS_PLAYER(content);
3839
3840           if (is_player && (found_rating < 3 ||
3841                             (found_rating == 3 && element < found_element)))
3842           {
3843             start_x = x;
3844             start_y = y;
3845
3846             found_rating = 3;
3847             found_element = element;
3848           }
3849         }
3850       }
3851
3852       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3853       {
3854         /* check for player created from custom element as explosion content */
3855         content = element_info[element].content.e[xx][yy];
3856         is_player = ELEM_IS_PLAYER(content);
3857
3858         if (is_player && (found_rating < 2 ||
3859                           (found_rating == 2 && element < found_element)))
3860         {
3861           start_x = x + xx - 1;
3862           start_y = y + yy - 1;
3863
3864           found_rating = 2;
3865           found_element = element;
3866         }
3867
3868         if (!CAN_CHANGE(element))
3869           continue;
3870
3871         for (i = 0; i < element_info[element].num_change_pages; i++)
3872         {
3873           /* check for player created from custom element as extended target */
3874           content =
3875             element_info[element].change_page[i].target_content.e[xx][yy];
3876
3877           is_player = ELEM_IS_PLAYER(content);
3878
3879           if (is_player && (found_rating < 1 ||
3880                             (found_rating == 1 && element < found_element)))
3881           {
3882             start_x = x + xx - 1;
3883             start_y = y + yy - 1;
3884
3885             found_rating = 1;
3886             found_element = element;
3887           }
3888         }
3889       }
3890     }
3891
3892     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3893                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3894                 start_x - MIDPOSX);
3895
3896     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3897                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3898                 start_y - MIDPOSY);
3899   }
3900   else
3901   {
3902     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3903                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3904                 local_player->jx - MIDPOSX);
3905
3906     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3907                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3908                 local_player->jy - MIDPOSY);
3909   }
3910
3911   /* !!! FIX THIS (START) !!! */
3912   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3913   {
3914     InitGameEngine_EM();
3915   }
3916   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3917   {
3918     InitGameEngine_SP();
3919   }
3920   else
3921   {
3922     DrawLevel(REDRAW_FIELD);
3923     DrawAllPlayers();
3924
3925     /* after drawing the level, correct some elements */
3926     if (game.timegate_time_left == 0)
3927       CloseAllOpenTimegates();
3928   }
3929
3930   /* blit playfield from scroll buffer to normal back buffer for fading in */
3931   BlitScreenToBitmap(backbuffer);
3932   /* !!! FIX THIS (END) !!! */
3933
3934   DrawMaskedBorder(fade_mask);
3935
3936   FadeIn(fade_mask);
3937
3938 #if 1
3939   // full screen redraw is required at this point in the following cases:
3940   // - special editor door undrawn when game was started from level editor
3941   // - drawing area (playfield) was changed and has to be removed completely
3942   redraw_mask = REDRAW_ALL;
3943   BackToFront();
3944 #endif
3945
3946   if (!game.restart_level)
3947   {
3948     /* copy default game door content to main double buffer */
3949
3950     /* !!! CHECK AGAIN !!! */
3951     SetPanelBackground();
3952     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3953     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3954   }
3955
3956   SetPanelBackground();
3957   SetDrawBackgroundMask(REDRAW_DOOR_1);
3958
3959   UpdateAndDisplayGameControlValues();
3960
3961   if (!game.restart_level)
3962   {
3963     UnmapGameButtons();
3964     UnmapTapeButtons();
3965
3966     FreeGameButtons();
3967     CreateGameButtons();
3968
3969     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3970     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3971     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3972
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     SetGameStatus(GAME_MODE_MAIN);
4426
4427     DrawMainMenu();
4428
4429     return;
4430   }
4431
4432   if (!local_player->LevelSolved_SaveScore)
4433   {
4434     SetGameStatus(GAME_MODE_MAIN);
4435
4436     DrawMainMenu();
4437
4438     return;
4439   }
4440
4441   if (level_nr == leveldir_current->handicap_level)
4442   {
4443     leveldir_current->handicap_level++;
4444
4445     SaveLevelSetup_SeriesInfo();
4446   }
4447
4448   if (level_nr < leveldir_current->last_level)
4449     raise_level = TRUE;                 /* advance to next level */
4450
4451   if ((hi_pos = NewHiScore()) >= 0) 
4452   {
4453     SetGameStatus(GAME_MODE_SCORES);
4454
4455     DrawHallOfFame(hi_pos);
4456
4457     if (raise_level)
4458     {
4459       level_nr++;
4460       TapeErase();
4461     }
4462   }
4463   else
4464   {
4465     SetGameStatus(GAME_MODE_MAIN);
4466
4467     if (raise_level)
4468     {
4469       level_nr++;
4470       TapeErase();
4471     }
4472
4473     DrawMainMenu();
4474   }
4475 }
4476
4477 int NewHiScore()
4478 {
4479   int k, l;
4480   int position = -1;
4481
4482   LoadScore(level_nr);
4483
4484   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4485       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4486     return -1;
4487
4488   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4489   {
4490     if (local_player->score_final > highscore[k].Score)
4491     {
4492       /* player has made it to the hall of fame */
4493
4494       if (k < MAX_SCORE_ENTRIES - 1)
4495       {
4496         int m = MAX_SCORE_ENTRIES - 1;
4497
4498 #ifdef ONE_PER_NAME
4499         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4500           if (strEqual(setup.player_name, highscore[l].Name))
4501             m = l;
4502         if (m == k)     /* player's new highscore overwrites his old one */
4503           goto put_into_list;
4504 #endif
4505
4506         for (l = m; l > k; l--)
4507         {
4508           strcpy(highscore[l].Name, highscore[l - 1].Name);
4509           highscore[l].Score = highscore[l - 1].Score;
4510         }
4511       }
4512
4513 #ifdef ONE_PER_NAME
4514       put_into_list:
4515 #endif
4516       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4517       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4518       highscore[k].Score = local_player->score_final; 
4519       position = k;
4520       break;
4521     }
4522
4523 #ifdef ONE_PER_NAME
4524     else if (!strncmp(setup.player_name, highscore[k].Name,
4525                       MAX_PLAYER_NAME_LEN))
4526       break;    /* player already there with a higher score */
4527 #endif
4528
4529   }
4530
4531   if (position >= 0) 
4532     SaveScore(level_nr);
4533
4534   return position;
4535 }
4536
4537 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4538 {
4539   int element = Feld[x][y];
4540   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4541   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4542   int horiz_move = (dx != 0);
4543   int sign = (horiz_move ? dx : dy);
4544   int step = sign * element_info[element].move_stepsize;
4545
4546   /* special values for move stepsize for spring and things on conveyor belt */
4547   if (horiz_move)
4548   {
4549     if (CAN_FALL(element) &&
4550         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4551       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4552     else if (element == EL_SPRING)
4553       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4554   }
4555
4556   return step;
4557 }
4558
4559 inline static int getElementMoveStepsize(int x, int y)
4560 {
4561   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4562 }
4563
4564 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4565 {
4566   if (player->GfxAction != action || player->GfxDir != dir)
4567   {
4568     player->GfxAction = action;
4569     player->GfxDir = dir;
4570     player->Frame = 0;
4571     player->StepFrame = 0;
4572   }
4573 }
4574
4575 static void ResetGfxFrame(int x, int y, boolean redraw)
4576 {
4577   int element = Feld[x][y];
4578   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4579   int last_gfx_frame = GfxFrame[x][y];
4580
4581   if (graphic_info[graphic].anim_global_sync)
4582     GfxFrame[x][y] = FrameCounter;
4583   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4584     GfxFrame[x][y] = CustomValue[x][y];
4585   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4586     GfxFrame[x][y] = element_info[element].collect_score;
4587   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4588     GfxFrame[x][y] = ChangeDelay[x][y];
4589
4590   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4591     DrawLevelGraphicAnimation(x, y, graphic);
4592 }
4593
4594 static void ResetGfxAnimation(int x, int y)
4595 {
4596   GfxAction[x][y] = ACTION_DEFAULT;
4597   GfxDir[x][y] = MovDir[x][y];
4598   GfxFrame[x][y] = 0;
4599
4600   ResetGfxFrame(x, y, FALSE);
4601 }
4602
4603 static void ResetRandomAnimationValue(int x, int y)
4604 {
4605   GfxRandom[x][y] = INIT_GFX_RANDOM();
4606 }
4607
4608 void InitMovingField(int x, int y, int direction)
4609 {
4610   int element = Feld[x][y];
4611   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4612   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4613   int newx = x + dx;
4614   int newy = y + dy;
4615   boolean is_moving_before, is_moving_after;
4616
4617   /* check if element was/is moving or being moved before/after mode change */
4618   is_moving_before = (WasJustMoving[x][y] != 0);
4619   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4620
4621   /* reset animation only for moving elements which change direction of moving
4622      or which just started or stopped moving
4623      (else CEs with property "can move" / "not moving" are reset each frame) */
4624   if (is_moving_before != is_moving_after ||
4625       direction != MovDir[x][y])
4626     ResetGfxAnimation(x, y);
4627
4628   MovDir[x][y] = direction;
4629   GfxDir[x][y] = direction;
4630
4631   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4632                      direction == MV_DOWN && CAN_FALL(element) ?
4633                      ACTION_FALLING : ACTION_MOVING);
4634
4635   /* this is needed for CEs with property "can move" / "not moving" */
4636
4637   if (is_moving_after)
4638   {
4639     if (Feld[newx][newy] == EL_EMPTY)
4640       Feld[newx][newy] = EL_BLOCKED;
4641
4642     MovDir[newx][newy] = MovDir[x][y];
4643
4644     CustomValue[newx][newy] = CustomValue[x][y];
4645
4646     GfxFrame[newx][newy] = GfxFrame[x][y];
4647     GfxRandom[newx][newy] = GfxRandom[x][y];
4648     GfxAction[newx][newy] = GfxAction[x][y];
4649     GfxDir[newx][newy] = GfxDir[x][y];
4650   }
4651 }
4652
4653 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4654 {
4655   int direction = MovDir[x][y];
4656   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4657   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4658
4659   *goes_to_x = newx;
4660   *goes_to_y = newy;
4661 }
4662
4663 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4664 {
4665   int oldx = x, oldy = y;
4666   int direction = MovDir[x][y];
4667
4668   if (direction == MV_LEFT)
4669     oldx++;
4670   else if (direction == MV_RIGHT)
4671     oldx--;
4672   else if (direction == MV_UP)
4673     oldy++;
4674   else if (direction == MV_DOWN)
4675     oldy--;
4676
4677   *comes_from_x = oldx;
4678   *comes_from_y = oldy;
4679 }
4680
4681 int MovingOrBlocked2Element(int x, int y)
4682 {
4683   int element = Feld[x][y];
4684
4685   if (element == EL_BLOCKED)
4686   {
4687     int oldx, oldy;
4688
4689     Blocked2Moving(x, y, &oldx, &oldy);
4690     return Feld[oldx][oldy];
4691   }
4692   else
4693     return element;
4694 }
4695
4696 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4697 {
4698   /* like MovingOrBlocked2Element(), but if element is moving
4699      and (x,y) is the field the moving element is just leaving,
4700      return EL_BLOCKED instead of the element value */
4701   int element = Feld[x][y];
4702
4703   if (IS_MOVING(x, y))
4704   {
4705     if (element == EL_BLOCKED)
4706     {
4707       int oldx, oldy;
4708
4709       Blocked2Moving(x, y, &oldx, &oldy);
4710       return Feld[oldx][oldy];
4711     }
4712     else
4713       return EL_BLOCKED;
4714   }
4715   else
4716     return element;
4717 }
4718
4719 static void RemoveField(int x, int y)
4720 {
4721   Feld[x][y] = EL_EMPTY;
4722
4723   MovPos[x][y] = 0;
4724   MovDir[x][y] = 0;
4725   MovDelay[x][y] = 0;
4726
4727   CustomValue[x][y] = 0;
4728
4729   AmoebaNr[x][y] = 0;
4730   ChangeDelay[x][y] = 0;
4731   ChangePage[x][y] = -1;
4732   Pushed[x][y] = FALSE;
4733
4734   GfxElement[x][y] = EL_UNDEFINED;
4735   GfxAction[x][y] = ACTION_DEFAULT;
4736   GfxDir[x][y] = MV_NONE;
4737 }
4738
4739 void RemoveMovingField(int x, int y)
4740 {
4741   int oldx = x, oldy = y, newx = x, newy = y;
4742   int element = Feld[x][y];
4743   int next_element = EL_UNDEFINED;
4744
4745   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4746     return;
4747
4748   if (IS_MOVING(x, y))
4749   {
4750     Moving2Blocked(x, y, &newx, &newy);
4751
4752     if (Feld[newx][newy] != EL_BLOCKED)
4753     {
4754       /* element is moving, but target field is not free (blocked), but
4755          already occupied by something different (example: acid pool);
4756          in this case, only remove the moving field, but not the target */
4757
4758       RemoveField(oldx, oldy);
4759
4760       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4761
4762       TEST_DrawLevelField(oldx, oldy);
4763
4764       return;
4765     }
4766   }
4767   else if (element == EL_BLOCKED)
4768   {
4769     Blocked2Moving(x, y, &oldx, &oldy);
4770     if (!IS_MOVING(oldx, oldy))
4771       return;
4772   }
4773
4774   if (element == EL_BLOCKED &&
4775       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4776        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4777        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4778        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4779        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4780        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4781     next_element = get_next_element(Feld[oldx][oldy]);
4782
4783   RemoveField(oldx, oldy);
4784   RemoveField(newx, newy);
4785
4786   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4787
4788   if (next_element != EL_UNDEFINED)
4789     Feld[oldx][oldy] = next_element;
4790
4791   TEST_DrawLevelField(oldx, oldy);
4792   TEST_DrawLevelField(newx, newy);
4793 }
4794
4795 void DrawDynamite(int x, int y)
4796 {
4797   int sx = SCREENX(x), sy = SCREENY(y);
4798   int graphic = el2img(Feld[x][y]);
4799   int frame;
4800
4801   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4802     return;
4803
4804   if (IS_WALKABLE_INSIDE(Back[x][y]))
4805     return;
4806
4807   if (Back[x][y])
4808     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4809   else if (Store[x][y])
4810     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4811
4812   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4813
4814   if (Back[x][y] || Store[x][y])
4815     DrawGraphicThruMask(sx, sy, graphic, frame);
4816   else
4817     DrawGraphic(sx, sy, graphic, frame);
4818 }
4819
4820 void CheckDynamite(int x, int y)
4821 {
4822   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4823   {
4824     MovDelay[x][y]--;
4825
4826     if (MovDelay[x][y] != 0)
4827     {
4828       DrawDynamite(x, y);
4829       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4830
4831       return;
4832     }
4833   }
4834
4835   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4836
4837   Bang(x, y);
4838 }
4839
4840 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4841 {
4842   boolean num_checked_players = 0;
4843   int i;
4844
4845   for (i = 0; i < MAX_PLAYERS; i++)
4846   {
4847     if (stored_player[i].active)
4848     {
4849       int sx = stored_player[i].jx;
4850       int sy = stored_player[i].jy;
4851
4852       if (num_checked_players == 0)
4853       {
4854         *sx1 = *sx2 = sx;
4855         *sy1 = *sy2 = sy;
4856       }
4857       else
4858       {
4859         *sx1 = MIN(*sx1, sx);
4860         *sy1 = MIN(*sy1, sy);
4861         *sx2 = MAX(*sx2, sx);
4862         *sy2 = MAX(*sy2, sy);
4863       }
4864
4865       num_checked_players++;
4866     }
4867   }
4868 }
4869
4870 static boolean checkIfAllPlayersFitToScreen_RND()
4871 {
4872   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4873
4874   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4875
4876   return (sx2 - sx1 < SCR_FIELDX &&
4877           sy2 - sy1 < SCR_FIELDY);
4878 }
4879
4880 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4881 {
4882   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4883
4884   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4885
4886   *sx = (sx1 + sx2) / 2;
4887   *sy = (sy1 + sy2) / 2;
4888 }
4889
4890 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4891                         boolean center_screen, boolean quick_relocation)
4892 {
4893   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4894   boolean no_delay = (tape.warp_forward);
4895   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4896   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4897   int new_scroll_x, new_scroll_y;
4898
4899   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4900   {
4901     /* case 1: quick relocation inside visible screen (without scrolling) */
4902
4903     RedrawPlayfield();
4904
4905     return;
4906   }
4907
4908   if (!level.shifted_relocation || center_screen)
4909   {
4910     /* relocation _with_ centering of screen */
4911
4912     new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4913                     x > SBX_Right + MIDPOSX ? SBX_Right :
4914                     x - MIDPOSX);
4915
4916     new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4917                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4918                     y - MIDPOSY);
4919   }
4920   else
4921   {
4922     /* relocation _without_ centering of screen */
4923
4924     int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4925                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
4926                            old_x - MIDPOSX);
4927
4928     int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4929                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4930                            old_y - MIDPOSY);
4931
4932     int offset_x = x + (scroll_x - center_scroll_x);
4933     int offset_y = y + (scroll_y - center_scroll_y);
4934
4935     new_scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4936                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4937                     offset_x - MIDPOSX);
4938
4939     new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4940                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4941                     offset_y - MIDPOSY);
4942   }
4943
4944   if (quick_relocation)
4945   {
4946     /* case 2: quick relocation (redraw without visible scrolling) */
4947
4948     scroll_x = new_scroll_x;
4949     scroll_y = new_scroll_y;
4950
4951     RedrawPlayfield();
4952
4953     return;
4954   }
4955
4956   /* case 3: visible relocation (with scrolling to new position) */
4957
4958   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4959
4960   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4961   {
4962     int dx = 0, dy = 0;
4963     int fx = FX, fy = FY;
4964
4965     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4966     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4967
4968     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4969       break;
4970
4971     scroll_x -= dx;
4972     scroll_y -= dy;
4973
4974     fx += dx * TILEX / 2;
4975     fy += dy * TILEY / 2;
4976
4977     ScrollLevel(dx, dy);
4978     DrawAllPlayers();
4979
4980     /* scroll in two steps of half tile size to make things smoother */
4981     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4982     Delay(wait_delay_value);
4983
4984     /* scroll second step to align at full tile size */
4985     BackToFront();
4986     Delay(wait_delay_value);
4987   }
4988
4989   DrawAllPlayers();
4990   BackToFront();
4991   Delay(wait_delay_value);
4992 }
4993
4994 void RelocatePlayer(int jx, int jy, int el_player_raw)
4995 {
4996   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4997   int player_nr = GET_PLAYER_NR(el_player);
4998   struct PlayerInfo *player = &stored_player[player_nr];
4999   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5000   boolean no_delay = (tape.warp_forward);
5001   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5002   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5003   int old_jx = player->jx;
5004   int old_jy = player->jy;
5005   int old_element = Feld[old_jx][old_jy];
5006   int element = Feld[jx][jy];
5007   boolean player_relocated = (old_jx != jx || old_jy != jy);
5008
5009   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5010   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5011   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5012   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5013   int leave_side_horiz = move_dir_horiz;
5014   int leave_side_vert  = move_dir_vert;
5015   int enter_side = enter_side_horiz | enter_side_vert;
5016   int leave_side = leave_side_horiz | leave_side_vert;
5017
5018   if (player->GameOver)         /* do not reanimate dead player */
5019     return;
5020
5021   if (!player_relocated)        /* no need to relocate the player */
5022     return;
5023
5024   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5025   {
5026     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5027     DrawLevelField(jx, jy);
5028   }
5029
5030   if (player->present)
5031   {
5032     while (player->MovPos)
5033     {
5034       ScrollPlayer(player, SCROLL_GO_ON);
5035       ScrollScreen(NULL, SCROLL_GO_ON);
5036
5037       AdvanceFrameAndPlayerCounters(player->index_nr);
5038
5039       DrawPlayer(player);
5040
5041       BackToFront();
5042       Delay(wait_delay_value);
5043     }
5044
5045     DrawPlayer(player);         /* needed here only to cleanup last field */
5046     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5047
5048     player->is_moving = FALSE;
5049   }
5050
5051   if (IS_CUSTOM_ELEMENT(old_element))
5052     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5053                                CE_LEFT_BY_PLAYER,
5054                                player->index_bit, leave_side);
5055
5056   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5057                                       CE_PLAYER_LEAVES_X,
5058                                       player->index_bit, leave_side);
5059
5060   Feld[jx][jy] = el_player;
5061   InitPlayerField(jx, jy, el_player, TRUE);
5062
5063   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5064      possible that the relocation target field did not contain a player element,
5065      but a walkable element, to which the new player was relocated -- in this
5066      case, restore that (already initialized!) element on the player field */
5067   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5068   {
5069     Feld[jx][jy] = element;     /* restore previously existing element */
5070   }
5071
5072   /* only visually relocate centered player */
5073   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5074                      FALSE, level.instant_relocation);
5075
5076   TestIfPlayerTouchesBadThing(jx, jy);
5077   TestIfPlayerTouchesCustomElement(jx, jy);
5078
5079   if (IS_CUSTOM_ELEMENT(element))
5080     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5081                                player->index_bit, enter_side);
5082
5083   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5084                                       player->index_bit, enter_side);
5085
5086   if (player->is_switching)
5087   {
5088     /* ensure that relocation while still switching an element does not cause
5089        a new element to be treated as also switched directly after relocation
5090        (this is important for teleporter switches that teleport the player to
5091        a place where another teleporter switch is in the same direction, which
5092        would then incorrectly be treated as immediately switched before the
5093        direction key that caused the switch was released) */
5094
5095     player->switch_x += jx - old_jx;
5096     player->switch_y += jy - old_jy;
5097   }
5098 }
5099
5100 void Explode(int ex, int ey, int phase, int mode)
5101 {
5102   int x, y;
5103   int last_phase;
5104   int border_element;
5105
5106   /* !!! eliminate this variable !!! */
5107   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5108
5109   if (game.explosions_delayed)
5110   {
5111     ExplodeField[ex][ey] = mode;
5112     return;
5113   }
5114
5115   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5116   {
5117     int center_element = Feld[ex][ey];
5118     int artwork_element, explosion_element;     /* set these values later */
5119
5120     /* remove things displayed in background while burning dynamite */
5121     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5122       Back[ex][ey] = 0;
5123
5124     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5125     {
5126       /* put moving element to center field (and let it explode there) */
5127       center_element = MovingOrBlocked2Element(ex, ey);
5128       RemoveMovingField(ex, ey);
5129       Feld[ex][ey] = center_element;
5130     }
5131
5132     /* now "center_element" is finally determined -- set related values now */
5133     artwork_element = center_element;           /* for custom player artwork */
5134     explosion_element = center_element;         /* for custom player artwork */
5135
5136     if (IS_PLAYER(ex, ey))
5137     {
5138       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5139
5140       artwork_element = stored_player[player_nr].artwork_element;
5141
5142       if (level.use_explosion_element[player_nr])
5143       {
5144         explosion_element = level.explosion_element[player_nr];
5145         artwork_element = explosion_element;
5146       }
5147     }
5148
5149     if (mode == EX_TYPE_NORMAL ||
5150         mode == EX_TYPE_CENTER ||
5151         mode == EX_TYPE_CROSS)
5152       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5153
5154     last_phase = element_info[explosion_element].explosion_delay + 1;
5155
5156     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5157     {
5158       int xx = x - ex + 1;
5159       int yy = y - ey + 1;
5160       int element;
5161
5162       if (!IN_LEV_FIELD(x, y) ||
5163           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5164           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5165         continue;
5166
5167       element = Feld[x][y];
5168
5169       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5170       {
5171         element = MovingOrBlocked2Element(x, y);
5172
5173         if (!IS_EXPLOSION_PROOF(element))
5174           RemoveMovingField(x, y);
5175       }
5176
5177       /* indestructible elements can only explode in center (but not flames) */
5178       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5179                                            mode == EX_TYPE_BORDER)) ||
5180           element == EL_FLAMES)
5181         continue;
5182
5183       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5184          behaviour, for example when touching a yamyam that explodes to rocks
5185          with active deadly shield, a rock is created under the player !!! */
5186       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5187 #if 0
5188       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5189           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5190            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5191 #else
5192       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5193 #endif
5194       {
5195         if (IS_ACTIVE_BOMB(element))
5196         {
5197           /* re-activate things under the bomb like gate or penguin */
5198           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5199           Back[x][y] = 0;
5200         }
5201
5202         continue;
5203       }
5204
5205       /* save walkable background elements while explosion on same tile */
5206       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5207           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5208         Back[x][y] = element;
5209
5210       /* ignite explodable elements reached by other explosion */
5211       if (element == EL_EXPLOSION)
5212         element = Store2[x][y];
5213
5214       if (AmoebaNr[x][y] &&
5215           (element == EL_AMOEBA_FULL ||
5216            element == EL_BD_AMOEBA ||
5217            element == EL_AMOEBA_GROWING))
5218       {
5219         AmoebaCnt[AmoebaNr[x][y]]--;
5220         AmoebaCnt2[AmoebaNr[x][y]]--;
5221       }
5222
5223       RemoveField(x, y);
5224
5225       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5226       {
5227         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5228
5229         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5230
5231         if (PLAYERINFO(ex, ey)->use_murphy)
5232           Store[x][y] = EL_EMPTY;
5233       }
5234
5235       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5236          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5237       else if (ELEM_IS_PLAYER(center_element))
5238         Store[x][y] = EL_EMPTY;
5239       else if (center_element == EL_YAMYAM)
5240         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5241       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5242         Store[x][y] = element_info[center_element].content.e[xx][yy];
5243 #if 1
5244       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5245          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5246          otherwise) -- FIX THIS !!! */
5247       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5248         Store[x][y] = element_info[element].content.e[1][1];
5249 #else
5250       else if (!CAN_EXPLODE(element))
5251         Store[x][y] = element_info[element].content.e[1][1];
5252 #endif
5253       else
5254         Store[x][y] = EL_EMPTY;
5255
5256       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5257           center_element == EL_AMOEBA_TO_DIAMOND)
5258         Store2[x][y] = element;
5259
5260       Feld[x][y] = EL_EXPLOSION;
5261       GfxElement[x][y] = artwork_element;
5262
5263       ExplodePhase[x][y] = 1;
5264       ExplodeDelay[x][y] = last_phase;
5265
5266       Stop[x][y] = TRUE;
5267     }
5268
5269     if (center_element == EL_YAMYAM)
5270       game.yamyam_content_nr =
5271         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5272
5273     return;
5274   }
5275
5276   if (Stop[ex][ey])
5277     return;
5278
5279   x = ex;
5280   y = ey;
5281
5282   if (phase == 1)
5283     GfxFrame[x][y] = 0;         /* restart explosion animation */
5284
5285   last_phase = ExplodeDelay[x][y];
5286
5287   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5288
5289   /* this can happen if the player leaves an explosion just in time */
5290   if (GfxElement[x][y] == EL_UNDEFINED)
5291     GfxElement[x][y] = EL_EMPTY;
5292
5293   border_element = Store2[x][y];
5294   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5295     border_element = StorePlayer[x][y];
5296
5297   if (phase == element_info[border_element].ignition_delay ||
5298       phase == last_phase)
5299   {
5300     boolean border_explosion = FALSE;
5301
5302     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5303         !PLAYER_EXPLOSION_PROTECTED(x, y))
5304     {
5305       KillPlayerUnlessExplosionProtected(x, y);
5306       border_explosion = TRUE;
5307     }
5308     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5309     {
5310       Feld[x][y] = Store2[x][y];
5311       Store2[x][y] = 0;
5312       Bang(x, y);
5313       border_explosion = TRUE;
5314     }
5315     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5316     {
5317       AmoebeUmwandeln(x, y);
5318       Store2[x][y] = 0;
5319       border_explosion = TRUE;
5320     }
5321
5322     /* if an element just explodes due to another explosion (chain-reaction),
5323        do not immediately end the new explosion when it was the last frame of
5324        the explosion (as it would be done in the following "if"-statement!) */
5325     if (border_explosion && phase == last_phase)
5326       return;
5327   }
5328
5329   if (phase == last_phase)
5330   {
5331     int element;
5332
5333     element = Feld[x][y] = Store[x][y];
5334     Store[x][y] = Store2[x][y] = 0;
5335     GfxElement[x][y] = EL_UNDEFINED;
5336
5337     /* player can escape from explosions and might therefore be still alive */
5338     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5339         element <= EL_PLAYER_IS_EXPLODING_4)
5340     {
5341       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5342       int explosion_element = EL_PLAYER_1 + player_nr;
5343       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5344       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5345
5346       if (level.use_explosion_element[player_nr])
5347         explosion_element = level.explosion_element[player_nr];
5348
5349       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5350                     element_info[explosion_element].content.e[xx][yy]);
5351     }
5352
5353     /* restore probably existing indestructible background element */
5354     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5355       element = Feld[x][y] = Back[x][y];
5356     Back[x][y] = 0;
5357
5358     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5359     GfxDir[x][y] = MV_NONE;
5360     ChangeDelay[x][y] = 0;
5361     ChangePage[x][y] = -1;
5362
5363     CustomValue[x][y] = 0;
5364
5365     InitField_WithBug2(x, y, FALSE);
5366
5367     TEST_DrawLevelField(x, y);
5368
5369     TestIfElementTouchesCustomElement(x, y);
5370
5371     if (GFX_CRUMBLED(element))
5372       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5373
5374     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5375       StorePlayer[x][y] = 0;
5376
5377     if (ELEM_IS_PLAYER(element))
5378       RelocatePlayer(x, y, element);
5379   }
5380   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5381   {
5382     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5383     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5384
5385     if (phase == delay)
5386       TEST_DrawLevelFieldCrumbled(x, y);
5387
5388     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5389     {
5390       DrawLevelElement(x, y, Back[x][y]);
5391       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5392     }
5393     else if (IS_WALKABLE_UNDER(Back[x][y]))
5394     {
5395       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5396       DrawLevelElementThruMask(x, y, Back[x][y]);
5397     }
5398     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5399       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5400   }
5401 }
5402
5403 void DynaExplode(int ex, int ey)
5404 {
5405   int i, j;
5406   int dynabomb_element = Feld[ex][ey];
5407   int dynabomb_size = 1;
5408   boolean dynabomb_xl = FALSE;
5409   struct PlayerInfo *player;
5410   static int xy[4][2] =
5411   {
5412     { 0, -1 },
5413     { -1, 0 },
5414     { +1, 0 },
5415     { 0, +1 }
5416   };
5417
5418   if (IS_ACTIVE_BOMB(dynabomb_element))
5419   {
5420     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5421     dynabomb_size = player->dynabomb_size;
5422     dynabomb_xl = player->dynabomb_xl;
5423     player->dynabombs_left++;
5424   }
5425
5426   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5427
5428   for (i = 0; i < NUM_DIRECTIONS; i++)
5429   {
5430     for (j = 1; j <= dynabomb_size; j++)
5431     {
5432       int x = ex + j * xy[i][0];
5433       int y = ey + j * xy[i][1];
5434       int element;
5435
5436       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5437         break;
5438
5439       element = Feld[x][y];
5440
5441       /* do not restart explosions of fields with active bombs */
5442       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5443         continue;
5444
5445       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5446
5447       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5448           !IS_DIGGABLE(element) && !dynabomb_xl)
5449         break;
5450     }
5451   }
5452 }
5453
5454 void Bang(int x, int y)
5455 {
5456   int element = MovingOrBlocked2Element(x, y);
5457   int explosion_type = EX_TYPE_NORMAL;
5458
5459   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5460   {
5461     struct PlayerInfo *player = PLAYERINFO(x, y);
5462
5463     element = Feld[x][y] = player->initial_element;
5464
5465     if (level.use_explosion_element[player->index_nr])
5466     {
5467       int explosion_element = level.explosion_element[player->index_nr];
5468
5469       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5470         explosion_type = EX_TYPE_CROSS;
5471       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5472         explosion_type = EX_TYPE_CENTER;
5473     }
5474   }
5475
5476   switch (element)
5477   {
5478     case EL_BUG:
5479     case EL_SPACESHIP:
5480     case EL_BD_BUTTERFLY:
5481     case EL_BD_FIREFLY:
5482     case EL_YAMYAM:
5483     case EL_DARK_YAMYAM:
5484     case EL_ROBOT:
5485     case EL_PACMAN:
5486     case EL_MOLE:
5487       RaiseScoreElement(element);
5488       break;
5489
5490     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5491     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5492     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5493     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5494     case EL_DYNABOMB_INCREASE_NUMBER:
5495     case EL_DYNABOMB_INCREASE_SIZE:
5496     case EL_DYNABOMB_INCREASE_POWER:
5497       explosion_type = EX_TYPE_DYNA;
5498       break;
5499
5500     case EL_DC_LANDMINE:
5501       explosion_type = EX_TYPE_CENTER;
5502       break;
5503
5504     case EL_PENGUIN:
5505     case EL_LAMP:
5506     case EL_LAMP_ACTIVE:
5507     case EL_AMOEBA_TO_DIAMOND:
5508       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5509         explosion_type = EX_TYPE_CENTER;
5510       break;
5511
5512     default:
5513       if (element_info[element].explosion_type == EXPLODES_CROSS)
5514         explosion_type = EX_TYPE_CROSS;
5515       else if (element_info[element].explosion_type == EXPLODES_1X1)
5516         explosion_type = EX_TYPE_CENTER;
5517       break;
5518   }
5519
5520   if (explosion_type == EX_TYPE_DYNA)
5521     DynaExplode(x, y);
5522   else
5523     Explode(x, y, EX_PHASE_START, explosion_type);
5524
5525   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5526 }
5527
5528 void SplashAcid(int x, int y)
5529 {
5530   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5531       (!IN_LEV_FIELD(x - 1, y - 2) ||
5532        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5533     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5534
5535   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5536       (!IN_LEV_FIELD(x + 1, y - 2) ||
5537        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5538     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5539
5540   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5541 }
5542
5543 static void InitBeltMovement()
5544 {
5545   static int belt_base_element[4] =
5546   {
5547     EL_CONVEYOR_BELT_1_LEFT,
5548     EL_CONVEYOR_BELT_2_LEFT,
5549     EL_CONVEYOR_BELT_3_LEFT,
5550     EL_CONVEYOR_BELT_4_LEFT
5551   };
5552   static int belt_base_active_element[4] =
5553   {
5554     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5555     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5556     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5557     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5558   };
5559
5560   int x, y, i, j;
5561
5562   /* set frame order for belt animation graphic according to belt direction */
5563   for (i = 0; i < NUM_BELTS; i++)
5564   {
5565     int belt_nr = i;
5566
5567     for (j = 0; j < NUM_BELT_PARTS; j++)
5568     {
5569       int element = belt_base_active_element[belt_nr] + j;
5570       int graphic_1 = el2img(element);
5571       int graphic_2 = el2panelimg(element);
5572
5573       if (game.belt_dir[i] == MV_LEFT)
5574       {
5575         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5576         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5577       }
5578       else
5579       {
5580         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5581         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5582       }
5583     }
5584   }
5585
5586   SCAN_PLAYFIELD(x, y)
5587   {
5588     int element = Feld[x][y];
5589
5590     for (i = 0; i < NUM_BELTS; i++)
5591     {
5592       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5593       {
5594         int e_belt_nr = getBeltNrFromBeltElement(element);
5595         int belt_nr = i;
5596
5597         if (e_belt_nr == belt_nr)
5598         {
5599           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5600
5601           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5602         }
5603       }
5604     }
5605   }
5606 }
5607
5608 static void ToggleBeltSwitch(int x, int y)
5609 {
5610   static int belt_base_element[4] =
5611   {
5612     EL_CONVEYOR_BELT_1_LEFT,
5613     EL_CONVEYOR_BELT_2_LEFT,
5614     EL_CONVEYOR_BELT_3_LEFT,
5615     EL_CONVEYOR_BELT_4_LEFT
5616   };
5617   static int belt_base_active_element[4] =
5618   {
5619     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5620     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5621     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5622     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5623   };
5624   static int belt_base_switch_element[4] =
5625   {
5626     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5627     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5628     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5629     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5630   };
5631   static int belt_move_dir[4] =
5632   {
5633     MV_LEFT,
5634     MV_NONE,
5635     MV_RIGHT,
5636     MV_NONE,
5637   };
5638
5639   int element = Feld[x][y];
5640   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5641   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5642   int belt_dir = belt_move_dir[belt_dir_nr];
5643   int xx, yy, i;
5644
5645   if (!IS_BELT_SWITCH(element))
5646     return;
5647
5648   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5649   game.belt_dir[belt_nr] = belt_dir;
5650
5651   if (belt_dir_nr == 3)
5652     belt_dir_nr = 1;
5653
5654   /* set frame order for belt animation graphic according to belt direction */
5655   for (i = 0; i < NUM_BELT_PARTS; i++)
5656   {
5657     int element = belt_base_active_element[belt_nr] + i;
5658     int graphic_1 = el2img(element);
5659     int graphic_2 = el2panelimg(element);
5660
5661     if (belt_dir == MV_LEFT)
5662     {
5663       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5664       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5665     }
5666     else
5667     {
5668       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5669       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5670     }
5671   }
5672
5673   SCAN_PLAYFIELD(xx, yy)
5674   {
5675     int element = Feld[xx][yy];
5676
5677     if (IS_BELT_SWITCH(element))
5678     {
5679       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5680
5681       if (e_belt_nr == belt_nr)
5682       {
5683         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5684         TEST_DrawLevelField(xx, yy);
5685       }
5686     }
5687     else if (IS_BELT(element) && belt_dir != MV_NONE)
5688     {
5689       int e_belt_nr = getBeltNrFromBeltElement(element);
5690
5691       if (e_belt_nr == belt_nr)
5692       {
5693         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5694
5695         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5696         TEST_DrawLevelField(xx, yy);
5697       }
5698     }
5699     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5700     {
5701       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5702
5703       if (e_belt_nr == belt_nr)
5704       {
5705         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5706
5707         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5708         TEST_DrawLevelField(xx, yy);
5709       }
5710     }
5711   }
5712 }
5713
5714 static void ToggleSwitchgateSwitch(int x, int y)
5715 {
5716   int xx, yy;
5717
5718   game.switchgate_pos = !game.switchgate_pos;
5719
5720   SCAN_PLAYFIELD(xx, yy)
5721   {
5722     int element = Feld[xx][yy];
5723
5724     if (element == EL_SWITCHGATE_SWITCH_UP)
5725     {
5726       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5727       TEST_DrawLevelField(xx, yy);
5728     }
5729     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5730     {
5731       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5732       TEST_DrawLevelField(xx, yy);
5733     }
5734     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5735     {
5736       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5737       TEST_DrawLevelField(xx, yy);
5738     }
5739     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5740     {
5741       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5742       TEST_DrawLevelField(xx, yy);
5743     }
5744     else if (element == EL_SWITCHGATE_OPEN ||
5745              element == EL_SWITCHGATE_OPENING)
5746     {
5747       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5748
5749       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5750     }
5751     else if (element == EL_SWITCHGATE_CLOSED ||
5752              element == EL_SWITCHGATE_CLOSING)
5753     {
5754       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5755
5756       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5757     }
5758   }
5759 }
5760
5761 static int getInvisibleActiveFromInvisibleElement(int element)
5762 {
5763   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5764           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5765           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5766           element);
5767 }
5768
5769 static int getInvisibleFromInvisibleActiveElement(int element)
5770 {
5771   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5772           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5773           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5774           element);
5775 }
5776
5777 static void RedrawAllLightSwitchesAndInvisibleElements()
5778 {
5779   int x, y;
5780
5781   SCAN_PLAYFIELD(x, y)
5782   {
5783     int element = Feld[x][y];
5784
5785     if (element == EL_LIGHT_SWITCH &&
5786         game.light_time_left > 0)
5787     {
5788       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5789       TEST_DrawLevelField(x, y);
5790     }
5791     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5792              game.light_time_left == 0)
5793     {
5794       Feld[x][y] = EL_LIGHT_SWITCH;
5795       TEST_DrawLevelField(x, y);
5796     }
5797     else if (element == EL_EMC_DRIPPER &&
5798              game.light_time_left > 0)
5799     {
5800       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5801       TEST_DrawLevelField(x, y);
5802     }
5803     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5804              game.light_time_left == 0)
5805     {
5806       Feld[x][y] = EL_EMC_DRIPPER;
5807       TEST_DrawLevelField(x, y);
5808     }
5809     else if (element == EL_INVISIBLE_STEELWALL ||
5810              element == EL_INVISIBLE_WALL ||
5811              element == EL_INVISIBLE_SAND)
5812     {
5813       if (game.light_time_left > 0)
5814         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5815
5816       TEST_DrawLevelField(x, y);
5817
5818       /* uncrumble neighbour fields, if needed */
5819       if (element == EL_INVISIBLE_SAND)
5820         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5821     }
5822     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5823              element == EL_INVISIBLE_WALL_ACTIVE ||
5824              element == EL_INVISIBLE_SAND_ACTIVE)
5825     {
5826       if (game.light_time_left == 0)
5827         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5828
5829       TEST_DrawLevelField(x, y);
5830
5831       /* re-crumble neighbour fields, if needed */
5832       if (element == EL_INVISIBLE_SAND)
5833         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5834     }
5835   }
5836 }
5837
5838 static void RedrawAllInvisibleElementsForLenses()
5839 {
5840   int x, y;
5841
5842   SCAN_PLAYFIELD(x, y)
5843   {
5844     int element = Feld[x][y];
5845
5846     if (element == EL_EMC_DRIPPER &&
5847         game.lenses_time_left > 0)
5848     {
5849       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5850       TEST_DrawLevelField(x, y);
5851     }
5852     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5853              game.lenses_time_left == 0)
5854     {
5855       Feld[x][y] = EL_EMC_DRIPPER;
5856       TEST_DrawLevelField(x, y);
5857     }
5858     else if (element == EL_INVISIBLE_STEELWALL ||
5859              element == EL_INVISIBLE_WALL ||
5860              element == EL_INVISIBLE_SAND)
5861     {
5862       if (game.lenses_time_left > 0)
5863         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5864
5865       TEST_DrawLevelField(x, y);
5866
5867       /* uncrumble neighbour fields, if needed */
5868       if (element == EL_INVISIBLE_SAND)
5869         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5870     }
5871     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5872              element == EL_INVISIBLE_WALL_ACTIVE ||
5873              element == EL_INVISIBLE_SAND_ACTIVE)
5874     {
5875       if (game.lenses_time_left == 0)
5876         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5877
5878       TEST_DrawLevelField(x, y);
5879
5880       /* re-crumble neighbour fields, if needed */
5881       if (element == EL_INVISIBLE_SAND)
5882         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5883     }
5884   }
5885 }
5886
5887 static void RedrawAllInvisibleElementsForMagnifier()
5888 {
5889   int x, y;
5890
5891   SCAN_PLAYFIELD(x, y)
5892   {
5893     int element = Feld[x][y];
5894
5895     if (element == EL_EMC_FAKE_GRASS &&
5896         game.magnify_time_left > 0)
5897     {
5898       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5899       TEST_DrawLevelField(x, y);
5900     }
5901     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5902              game.magnify_time_left == 0)
5903     {
5904       Feld[x][y] = EL_EMC_FAKE_GRASS;
5905       TEST_DrawLevelField(x, y);
5906     }
5907     else if (IS_GATE_GRAY(element) &&
5908              game.magnify_time_left > 0)
5909     {
5910       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5911                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5912                     IS_EM_GATE_GRAY(element) ?
5913                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5914                     IS_EMC_GATE_GRAY(element) ?
5915                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5916                     IS_DC_GATE_GRAY(element) ?
5917                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5918                     element);
5919       TEST_DrawLevelField(x, y);
5920     }
5921     else if (IS_GATE_GRAY_ACTIVE(element) &&
5922              game.magnify_time_left == 0)
5923     {
5924       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5925                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5926                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5927                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5928                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5929                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5930                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5931                     EL_DC_GATE_WHITE_GRAY :
5932                     element);
5933       TEST_DrawLevelField(x, y);
5934     }
5935   }
5936 }
5937
5938 static void ToggleLightSwitch(int x, int y)
5939 {
5940   int element = Feld[x][y];
5941
5942   game.light_time_left =
5943     (element == EL_LIGHT_SWITCH ?
5944      level.time_light * FRAMES_PER_SECOND : 0);
5945
5946   RedrawAllLightSwitchesAndInvisibleElements();
5947 }
5948
5949 static void ActivateTimegateSwitch(int x, int y)
5950 {
5951   int xx, yy;
5952
5953   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5954
5955   SCAN_PLAYFIELD(xx, yy)
5956   {
5957     int element = Feld[xx][yy];
5958
5959     if (element == EL_TIMEGATE_CLOSED ||
5960         element == EL_TIMEGATE_CLOSING)
5961     {
5962       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5963       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5964     }
5965
5966     /*
5967     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5968     {
5969       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5970       TEST_DrawLevelField(xx, yy);
5971     }
5972     */
5973
5974   }
5975
5976   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5977                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5978 }
5979
5980 void Impact(int x, int y)
5981 {
5982   boolean last_line = (y == lev_fieldy - 1);
5983   boolean object_hit = FALSE;
5984   boolean impact = (last_line || object_hit);
5985   int element = Feld[x][y];
5986   int smashed = EL_STEELWALL;
5987
5988   if (!last_line)       /* check if element below was hit */
5989   {
5990     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5991       return;
5992
5993     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5994                                          MovDir[x][y + 1] != MV_DOWN ||
5995                                          MovPos[x][y + 1] <= TILEY / 2));
5996
5997     /* do not smash moving elements that left the smashed field in time */
5998     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5999         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6000       object_hit = FALSE;
6001
6002 #if USE_QUICKSAND_IMPACT_BUGFIX
6003     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6004     {
6005       RemoveMovingField(x, y + 1);
6006       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6007       Feld[x][y + 2] = EL_ROCK;
6008       TEST_DrawLevelField(x, y + 2);
6009
6010       object_hit = TRUE;
6011     }
6012
6013     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6014     {
6015       RemoveMovingField(x, y + 1);
6016       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6017       Feld[x][y + 2] = EL_ROCK;
6018       TEST_DrawLevelField(x, y + 2);
6019
6020       object_hit = TRUE;
6021     }
6022 #endif
6023
6024     if (object_hit)
6025       smashed = MovingOrBlocked2Element(x, y + 1);
6026
6027     impact = (last_line || object_hit);
6028   }
6029
6030   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6031   {
6032     SplashAcid(x, y + 1);
6033     return;
6034   }
6035
6036   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6037   /* only reset graphic animation if graphic really changes after impact */
6038   if (impact &&
6039       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6040   {
6041     ResetGfxAnimation(x, y);
6042     TEST_DrawLevelField(x, y);
6043   }
6044
6045   if (impact && CAN_EXPLODE_IMPACT(element))
6046   {
6047     Bang(x, y);
6048     return;
6049   }
6050   else if (impact && element == EL_PEARL &&
6051            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6052   {
6053     ResetGfxAnimation(x, y);
6054
6055     Feld[x][y] = EL_PEARL_BREAKING;
6056     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6057     return;
6058   }
6059   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6060   {
6061     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6062
6063     return;
6064   }
6065
6066   if (impact && element == EL_AMOEBA_DROP)
6067   {
6068     if (object_hit && IS_PLAYER(x, y + 1))
6069       KillPlayerUnlessEnemyProtected(x, y + 1);
6070     else if (object_hit && smashed == EL_PENGUIN)
6071       Bang(x, y + 1);
6072     else
6073     {
6074       Feld[x][y] = EL_AMOEBA_GROWING;
6075       Store[x][y] = EL_AMOEBA_WET;
6076
6077       ResetRandomAnimationValue(x, y);
6078     }
6079     return;
6080   }
6081
6082   if (object_hit)               /* check which object was hit */
6083   {
6084     if ((CAN_PASS_MAGIC_WALL(element) && 
6085          (smashed == EL_MAGIC_WALL ||
6086           smashed == EL_BD_MAGIC_WALL)) ||
6087         (CAN_PASS_DC_MAGIC_WALL(element) &&
6088          smashed == EL_DC_MAGIC_WALL))
6089     {
6090       int xx, yy;
6091       int activated_magic_wall =
6092         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6093          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6094          EL_DC_MAGIC_WALL_ACTIVE);
6095
6096       /* activate magic wall / mill */
6097       SCAN_PLAYFIELD(xx, yy)
6098       {
6099         if (Feld[xx][yy] == smashed)
6100           Feld[xx][yy] = activated_magic_wall;
6101       }
6102
6103       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6104       game.magic_wall_active = TRUE;
6105
6106       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6107                             SND_MAGIC_WALL_ACTIVATING :
6108                             smashed == EL_BD_MAGIC_WALL ?
6109                             SND_BD_MAGIC_WALL_ACTIVATING :
6110                             SND_DC_MAGIC_WALL_ACTIVATING));
6111     }
6112
6113     if (IS_PLAYER(x, y + 1))
6114     {
6115       if (CAN_SMASH_PLAYER(element))
6116       {
6117         KillPlayerUnlessEnemyProtected(x, y + 1);
6118         return;
6119       }
6120     }
6121     else if (smashed == EL_PENGUIN)
6122     {
6123       if (CAN_SMASH_PLAYER(element))
6124       {
6125         Bang(x, y + 1);
6126         return;
6127       }
6128     }
6129     else if (element == EL_BD_DIAMOND)
6130     {
6131       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6132       {
6133         Bang(x, y + 1);
6134         return;
6135       }
6136     }
6137     else if (((element == EL_SP_INFOTRON ||
6138                element == EL_SP_ZONK) &&
6139               (smashed == EL_SP_SNIKSNAK ||
6140                smashed == EL_SP_ELECTRON ||
6141                smashed == EL_SP_DISK_ORANGE)) ||
6142              (element == EL_SP_INFOTRON &&
6143               smashed == EL_SP_DISK_YELLOW))
6144     {
6145       Bang(x, y + 1);
6146       return;
6147     }
6148     else if (CAN_SMASH_EVERYTHING(element))
6149     {
6150       if (IS_CLASSIC_ENEMY(smashed) ||
6151           CAN_EXPLODE_SMASHED(smashed))
6152       {
6153         Bang(x, y + 1);
6154         return;
6155       }
6156       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6157       {
6158         if (smashed == EL_LAMP ||
6159             smashed == EL_LAMP_ACTIVE)
6160         {
6161           Bang(x, y + 1);
6162           return;
6163         }
6164         else if (smashed == EL_NUT)
6165         {
6166           Feld[x][y + 1] = EL_NUT_BREAKING;
6167           PlayLevelSound(x, y, SND_NUT_BREAKING);
6168           RaiseScoreElement(EL_NUT);
6169           return;
6170         }
6171         else if (smashed == EL_PEARL)
6172         {
6173           ResetGfxAnimation(x, y);
6174
6175           Feld[x][y + 1] = EL_PEARL_BREAKING;
6176           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6177           return;
6178         }
6179         else if (smashed == EL_DIAMOND)
6180         {
6181           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6182           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6183           return;
6184         }
6185         else if (IS_BELT_SWITCH(smashed))
6186         {
6187           ToggleBeltSwitch(x, y + 1);
6188         }
6189         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6190                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6191                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6192                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6193         {
6194           ToggleSwitchgateSwitch(x, y + 1);
6195         }
6196         else if (smashed == EL_LIGHT_SWITCH ||
6197                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6198         {
6199           ToggleLightSwitch(x, y + 1);
6200         }
6201         else
6202         {
6203           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6204
6205           CheckElementChangeBySide(x, y + 1, smashed, element,
6206                                    CE_SWITCHED, CH_SIDE_TOP);
6207           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6208                                             CH_SIDE_TOP);
6209         }
6210       }
6211       else
6212       {
6213         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6214       }
6215     }
6216   }
6217
6218   /* play sound of magic wall / mill */
6219   if (!last_line &&
6220       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6221        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6222        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6223   {
6224     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6225       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6226     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6227       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6228     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6229       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6230
6231     return;
6232   }
6233
6234   /* play sound of object that hits the ground */
6235   if (last_line || object_hit)
6236     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6237 }
6238
6239 inline static void TurnRoundExt(int x, int y)
6240 {
6241   static struct
6242   {
6243     int dx, dy;
6244   } move_xy[] =
6245   {
6246     {  0,  0 },
6247     { -1,  0 },
6248     { +1,  0 },
6249     {  0,  0 },
6250     {  0, -1 },
6251     {  0,  0 }, { 0, 0 }, { 0, 0 },
6252     {  0, +1 }
6253   };
6254   static struct
6255   {
6256     int left, right, back;
6257   } turn[] =
6258   {
6259     { 0,        0,              0        },
6260     { MV_DOWN,  MV_UP,          MV_RIGHT },
6261     { MV_UP,    MV_DOWN,        MV_LEFT  },
6262     { 0,        0,              0        },
6263     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6264     { 0,        0,              0        },
6265     { 0,        0,              0        },
6266     { 0,        0,              0        },
6267     { MV_RIGHT, MV_LEFT,        MV_UP    }
6268   };
6269
6270   int element = Feld[x][y];
6271   int move_pattern = element_info[element].move_pattern;
6272
6273   int old_move_dir = MovDir[x][y];
6274   int left_dir  = turn[old_move_dir].left;
6275   int right_dir = turn[old_move_dir].right;
6276   int back_dir  = turn[old_move_dir].back;
6277
6278   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6279   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6280   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6281   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6282
6283   int left_x  = x + left_dx,  left_y  = y + left_dy;
6284   int right_x = x + right_dx, right_y = y + right_dy;
6285   int move_x  = x + move_dx,  move_y  = y + move_dy;
6286
6287   int xx, yy;
6288
6289   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6290   {
6291     TestIfBadThingTouchesOtherBadThing(x, y);
6292
6293     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6294       MovDir[x][y] = right_dir;
6295     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6296       MovDir[x][y] = left_dir;
6297
6298     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6299       MovDelay[x][y] = 9;
6300     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6301       MovDelay[x][y] = 1;
6302   }
6303   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6304   {
6305     TestIfBadThingTouchesOtherBadThing(x, y);
6306
6307     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6308       MovDir[x][y] = left_dir;
6309     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6310       MovDir[x][y] = right_dir;
6311
6312     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6313       MovDelay[x][y] = 9;
6314     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6315       MovDelay[x][y] = 1;
6316   }
6317   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6318   {
6319     TestIfBadThingTouchesOtherBadThing(x, y);
6320
6321     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6322       MovDir[x][y] = left_dir;
6323     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6324       MovDir[x][y] = right_dir;
6325
6326     if (MovDir[x][y] != old_move_dir)
6327       MovDelay[x][y] = 9;
6328   }
6329   else if (element == EL_YAMYAM)
6330   {
6331     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6332     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6333
6334     if (can_turn_left && can_turn_right)
6335       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6336     else if (can_turn_left)
6337       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6338     else if (can_turn_right)
6339       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6340     else
6341       MovDir[x][y] = back_dir;
6342
6343     MovDelay[x][y] = 16 + 16 * RND(3);
6344   }
6345   else if (element == EL_DARK_YAMYAM)
6346   {
6347     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6348                                                          left_x, left_y);
6349     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6350                                                          right_x, right_y);
6351
6352     if (can_turn_left && can_turn_right)
6353       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6354     else if (can_turn_left)
6355       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6356     else if (can_turn_right)
6357       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6358     else
6359       MovDir[x][y] = back_dir;
6360
6361     MovDelay[x][y] = 16 + 16 * RND(3);
6362   }
6363   else if (element == EL_PACMAN)
6364   {
6365     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6366     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6367
6368     if (can_turn_left && can_turn_right)
6369       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6370     else if (can_turn_left)
6371       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6372     else if (can_turn_right)
6373       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6374     else
6375       MovDir[x][y] = back_dir;
6376
6377     MovDelay[x][y] = 6 + RND(40);
6378   }
6379   else if (element == EL_PIG)
6380   {
6381     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6382     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6383     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6384     boolean should_turn_left, should_turn_right, should_move_on;
6385     int rnd_value = 24;
6386     int rnd = RND(rnd_value);
6387
6388     should_turn_left = (can_turn_left &&
6389                         (!can_move_on ||
6390                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6391                                                    y + back_dy + left_dy)));
6392     should_turn_right = (can_turn_right &&
6393                          (!can_move_on ||
6394                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6395                                                     y + back_dy + right_dy)));
6396     should_move_on = (can_move_on &&
6397                       (!can_turn_left ||
6398                        !can_turn_right ||
6399                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6400                                                  y + move_dy + left_dy) ||
6401                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6402                                                  y + move_dy + right_dy)));
6403
6404     if (should_turn_left || should_turn_right || should_move_on)
6405     {
6406       if (should_turn_left && should_turn_right && should_move_on)
6407         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6408                         rnd < 2 * rnd_value / 3 ? right_dir :
6409                         old_move_dir);
6410       else if (should_turn_left && should_turn_right)
6411         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6412       else if (should_turn_left && should_move_on)
6413         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6414       else if (should_turn_right && should_move_on)
6415         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6416       else if (should_turn_left)
6417         MovDir[x][y] = left_dir;
6418       else if (should_turn_right)
6419         MovDir[x][y] = right_dir;
6420       else if (should_move_on)
6421         MovDir[x][y] = old_move_dir;
6422     }
6423     else if (can_move_on && rnd > rnd_value / 8)
6424       MovDir[x][y] = old_move_dir;
6425     else if (can_turn_left && can_turn_right)
6426       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6427     else if (can_turn_left && rnd > rnd_value / 8)
6428       MovDir[x][y] = left_dir;
6429     else if (can_turn_right && rnd > rnd_value/8)
6430       MovDir[x][y] = right_dir;
6431     else
6432       MovDir[x][y] = back_dir;
6433
6434     xx = x + move_xy[MovDir[x][y]].dx;
6435     yy = y + move_xy[MovDir[x][y]].dy;
6436
6437     if (!IN_LEV_FIELD(xx, yy) ||
6438         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6439       MovDir[x][y] = old_move_dir;
6440
6441     MovDelay[x][y] = 0;
6442   }
6443   else if (element == EL_DRAGON)
6444   {
6445     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6446     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6447     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6448     int rnd_value = 24;
6449     int rnd = RND(rnd_value);
6450
6451     if (can_move_on && rnd > rnd_value / 8)
6452       MovDir[x][y] = old_move_dir;
6453     else if (can_turn_left && can_turn_right)
6454       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6455     else if (can_turn_left && rnd > rnd_value / 8)
6456       MovDir[x][y] = left_dir;
6457     else if (can_turn_right && rnd > rnd_value / 8)
6458       MovDir[x][y] = right_dir;
6459     else
6460       MovDir[x][y] = back_dir;
6461
6462     xx = x + move_xy[MovDir[x][y]].dx;
6463     yy = y + move_xy[MovDir[x][y]].dy;
6464
6465     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6466       MovDir[x][y] = old_move_dir;
6467
6468     MovDelay[x][y] = 0;
6469   }
6470   else if (element == EL_MOLE)
6471   {
6472     boolean can_move_on =
6473       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6474                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6475                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6476     if (!can_move_on)
6477     {
6478       boolean can_turn_left =
6479         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6480                               IS_AMOEBOID(Feld[left_x][left_y])));
6481
6482       boolean can_turn_right =
6483         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6484                               IS_AMOEBOID(Feld[right_x][right_y])));
6485
6486       if (can_turn_left && can_turn_right)
6487         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6488       else if (can_turn_left)
6489         MovDir[x][y] = left_dir;
6490       else
6491         MovDir[x][y] = right_dir;
6492     }
6493
6494     if (MovDir[x][y] != old_move_dir)
6495       MovDelay[x][y] = 9;
6496   }
6497   else if (element == EL_BALLOON)
6498   {
6499     MovDir[x][y] = game.wind_direction;
6500     MovDelay[x][y] = 0;
6501   }
6502   else if (element == EL_SPRING)
6503   {
6504     if (MovDir[x][y] & MV_HORIZONTAL)
6505     {
6506       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6507           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6508       {
6509         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6510         ResetGfxAnimation(move_x, move_y);
6511         TEST_DrawLevelField(move_x, move_y);
6512
6513         MovDir[x][y] = back_dir;
6514       }
6515       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6516                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6517         MovDir[x][y] = MV_NONE;
6518     }
6519
6520     MovDelay[x][y] = 0;
6521   }
6522   else if (element == EL_ROBOT ||
6523            element == EL_SATELLITE ||
6524            element == EL_PENGUIN ||
6525            element == EL_EMC_ANDROID)
6526   {
6527     int attr_x = -1, attr_y = -1;
6528
6529     if (AllPlayersGone)
6530     {
6531       attr_x = ExitX;
6532       attr_y = ExitY;
6533     }
6534     else
6535     {
6536       int i;
6537
6538       for (i = 0; i < MAX_PLAYERS; i++)
6539       {
6540         struct PlayerInfo *player = &stored_player[i];
6541         int jx = player->jx, jy = player->jy;
6542
6543         if (!player->active)
6544           continue;
6545
6546         if (attr_x == -1 ||
6547             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6548         {
6549           attr_x = jx;
6550           attr_y = jy;
6551         }
6552       }
6553     }
6554
6555     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6556         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6557          game.engine_version < VERSION_IDENT(3,1,0,0)))
6558     {
6559       attr_x = ZX;
6560       attr_y = ZY;
6561     }
6562
6563     if (element == EL_PENGUIN)
6564     {
6565       int i;
6566       static int xy[4][2] =
6567       {
6568         { 0, -1 },
6569         { -1, 0 },
6570         { +1, 0 },
6571         { 0, +1 }
6572       };
6573
6574       for (i = 0; i < NUM_DIRECTIONS; i++)
6575       {
6576         int ex = x + xy[i][0];
6577         int ey = y + xy[i][1];
6578
6579         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6580                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6581                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6582                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6583         {
6584           attr_x = ex;
6585           attr_y = ey;
6586           break;
6587         }
6588       }
6589     }
6590
6591     MovDir[x][y] = MV_NONE;
6592     if (attr_x < x)
6593       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6594     else if (attr_x > x)
6595       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6596     if (attr_y < y)
6597       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6598     else if (attr_y > y)
6599       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6600
6601     if (element == EL_ROBOT)
6602     {
6603       int newx, newy;
6604
6605       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6606         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6607       Moving2Blocked(x, y, &newx, &newy);
6608
6609       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6610         MovDelay[x][y] = 8 + 8 * !RND(3);
6611       else
6612         MovDelay[x][y] = 16;
6613     }
6614     else if (element == EL_PENGUIN)
6615     {
6616       int newx, newy;
6617
6618       MovDelay[x][y] = 1;
6619
6620       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6621       {
6622         boolean first_horiz = RND(2);
6623         int new_move_dir = MovDir[x][y];
6624
6625         MovDir[x][y] =
6626           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6627         Moving2Blocked(x, y, &newx, &newy);
6628
6629         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6630           return;
6631
6632         MovDir[x][y] =
6633           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6634         Moving2Blocked(x, y, &newx, &newy);
6635
6636         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6637           return;
6638
6639         MovDir[x][y] = old_move_dir;
6640         return;
6641       }
6642     }
6643     else if (element == EL_SATELLITE)
6644     {
6645       int newx, newy;
6646
6647       MovDelay[x][y] = 1;
6648
6649       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6650       {
6651         boolean first_horiz = RND(2);
6652         int new_move_dir = MovDir[x][y];
6653
6654         MovDir[x][y] =
6655           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6656         Moving2Blocked(x, y, &newx, &newy);
6657
6658         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6659           return;
6660
6661         MovDir[x][y] =
6662           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6663         Moving2Blocked(x, y, &newx, &newy);
6664
6665         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6666           return;
6667
6668         MovDir[x][y] = old_move_dir;
6669         return;
6670       }
6671     }
6672     else if (element == EL_EMC_ANDROID)
6673     {
6674       static int check_pos[16] =
6675       {
6676         -1,             /*  0 => (invalid)          */
6677         7,              /*  1 => MV_LEFT            */
6678         3,              /*  2 => MV_RIGHT           */
6679         -1,             /*  3 => (invalid)          */
6680         1,              /*  4 =>            MV_UP   */
6681         0,              /*  5 => MV_LEFT  | MV_UP   */
6682         2,              /*  6 => MV_RIGHT | MV_UP   */
6683         -1,             /*  7 => (invalid)          */
6684         5,              /*  8 =>            MV_DOWN */
6685         6,              /*  9 => MV_LEFT  | MV_DOWN */
6686         4,              /* 10 => MV_RIGHT | MV_DOWN */
6687         -1,             /* 11 => (invalid)          */
6688         -1,             /* 12 => (invalid)          */
6689         -1,             /* 13 => (invalid)          */
6690         -1,             /* 14 => (invalid)          */
6691         -1,             /* 15 => (invalid)          */
6692       };
6693       static struct
6694       {
6695         int dx, dy;
6696         int dir;
6697       } check_xy[8] =
6698       {
6699         { -1, -1,       MV_LEFT  | MV_UP   },
6700         {  0, -1,                  MV_UP   },
6701         { +1, -1,       MV_RIGHT | MV_UP   },
6702         { +1,  0,       MV_RIGHT           },
6703         { +1, +1,       MV_RIGHT | MV_DOWN },
6704         {  0, +1,                  MV_DOWN },
6705         { -1, +1,       MV_LEFT  | MV_DOWN },
6706         { -1,  0,       MV_LEFT            },
6707       };
6708       int start_pos, check_order;
6709       boolean can_clone = FALSE;
6710       int i;
6711
6712       /* check if there is any free field around current position */
6713       for (i = 0; i < 8; i++)
6714       {
6715         int newx = x + check_xy[i].dx;
6716         int newy = y + check_xy[i].dy;
6717
6718         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6719         {
6720           can_clone = TRUE;
6721
6722           break;
6723         }
6724       }
6725
6726       if (can_clone)            /* randomly find an element to clone */
6727       {
6728         can_clone = FALSE;
6729
6730         start_pos = check_pos[RND(8)];
6731         check_order = (RND(2) ? -1 : +1);
6732
6733         for (i = 0; i < 8; i++)
6734         {
6735           int pos_raw = start_pos + i * check_order;
6736           int pos = (pos_raw + 8) % 8;
6737           int newx = x + check_xy[pos].dx;
6738           int newy = y + check_xy[pos].dy;
6739
6740           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6741           {
6742             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6743             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6744
6745             Store[x][y] = Feld[newx][newy];
6746
6747             can_clone = TRUE;
6748
6749             break;
6750           }
6751         }
6752       }
6753
6754       if (can_clone)            /* randomly find a direction to move */
6755       {
6756         can_clone = FALSE;
6757
6758         start_pos = check_pos[RND(8)];
6759         check_order = (RND(2) ? -1 : +1);
6760
6761         for (i = 0; i < 8; i++)
6762         {
6763           int pos_raw = start_pos + i * check_order;
6764           int pos = (pos_raw + 8) % 8;
6765           int newx = x + check_xy[pos].dx;
6766           int newy = y + check_xy[pos].dy;
6767           int new_move_dir = check_xy[pos].dir;
6768
6769           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6770           {
6771             MovDir[x][y] = new_move_dir;
6772             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6773
6774             can_clone = TRUE;
6775
6776             break;
6777           }
6778         }
6779       }
6780
6781       if (can_clone)            /* cloning and moving successful */
6782         return;
6783
6784       /* cannot clone -- try to move towards player */
6785
6786       start_pos = check_pos[MovDir[x][y] & 0x0f];
6787       check_order = (RND(2) ? -1 : +1);
6788
6789       for (i = 0; i < 3; i++)
6790       {
6791         /* first check start_pos, then previous/next or (next/previous) pos */
6792         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6793         int pos = (pos_raw + 8) % 8;
6794         int newx = x + check_xy[pos].dx;
6795         int newy = y + check_xy[pos].dy;
6796         int new_move_dir = check_xy[pos].dir;
6797
6798         if (IS_PLAYER(newx, newy))
6799           break;
6800
6801         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6802         {
6803           MovDir[x][y] = new_move_dir;
6804           MovDelay[x][y] = level.android_move_time * 8 + 1;
6805
6806           break;
6807         }
6808       }
6809     }
6810   }
6811   else if (move_pattern == MV_TURNING_LEFT ||
6812            move_pattern == MV_TURNING_RIGHT ||
6813            move_pattern == MV_TURNING_LEFT_RIGHT ||
6814            move_pattern == MV_TURNING_RIGHT_LEFT ||
6815            move_pattern == MV_TURNING_RANDOM ||
6816            move_pattern == MV_ALL_DIRECTIONS)
6817   {
6818     boolean can_turn_left =
6819       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6820     boolean can_turn_right =
6821       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6822
6823     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6824       return;
6825
6826     if (move_pattern == MV_TURNING_LEFT)
6827       MovDir[x][y] = left_dir;
6828     else if (move_pattern == MV_TURNING_RIGHT)
6829       MovDir[x][y] = right_dir;
6830     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6831       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6832     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6833       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6834     else if (move_pattern == MV_TURNING_RANDOM)
6835       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6836                       can_turn_right && !can_turn_left ? right_dir :
6837                       RND(2) ? left_dir : right_dir);
6838     else if (can_turn_left && can_turn_right)
6839       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6840     else if (can_turn_left)
6841       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6842     else if (can_turn_right)
6843       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6844     else
6845       MovDir[x][y] = back_dir;
6846
6847     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6848   }
6849   else if (move_pattern == MV_HORIZONTAL ||
6850            move_pattern == MV_VERTICAL)
6851   {
6852     if (move_pattern & old_move_dir)
6853       MovDir[x][y] = back_dir;
6854     else if (move_pattern == MV_HORIZONTAL)
6855       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6856     else if (move_pattern == MV_VERTICAL)
6857       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6858
6859     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6860   }
6861   else if (move_pattern & MV_ANY_DIRECTION)
6862   {
6863     MovDir[x][y] = move_pattern;
6864     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6865   }
6866   else if (move_pattern & MV_WIND_DIRECTION)
6867   {
6868     MovDir[x][y] = game.wind_direction;
6869     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6870   }
6871   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6872   {
6873     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6874       MovDir[x][y] = left_dir;
6875     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6876       MovDir[x][y] = right_dir;
6877
6878     if (MovDir[x][y] != old_move_dir)
6879       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6880   }
6881   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6882   {
6883     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6884       MovDir[x][y] = right_dir;
6885     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6886       MovDir[x][y] = left_dir;
6887
6888     if (MovDir[x][y] != old_move_dir)
6889       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6890   }
6891   else if (move_pattern == MV_TOWARDS_PLAYER ||
6892            move_pattern == MV_AWAY_FROM_PLAYER)
6893   {
6894     int attr_x = -1, attr_y = -1;
6895     int newx, newy;
6896     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6897
6898     if (AllPlayersGone)
6899     {
6900       attr_x = ExitX;
6901       attr_y = ExitY;
6902     }
6903     else
6904     {
6905       int i;
6906
6907       for (i = 0; i < MAX_PLAYERS; i++)
6908       {
6909         struct PlayerInfo *player = &stored_player[i];
6910         int jx = player->jx, jy = player->jy;
6911
6912         if (!player->active)
6913           continue;
6914
6915         if (attr_x == -1 ||
6916             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6917         {
6918           attr_x = jx;
6919           attr_y = jy;
6920         }
6921       }
6922     }
6923
6924     MovDir[x][y] = MV_NONE;
6925     if (attr_x < x)
6926       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6927     else if (attr_x > x)
6928       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6929     if (attr_y < y)
6930       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6931     else if (attr_y > y)
6932       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6933
6934     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6935
6936     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6937     {
6938       boolean first_horiz = RND(2);
6939       int new_move_dir = MovDir[x][y];
6940
6941       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6942       {
6943         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6944         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6945
6946         return;
6947       }
6948
6949       MovDir[x][y] =
6950         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6951       Moving2Blocked(x, y, &newx, &newy);
6952
6953       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6954         return;
6955
6956       MovDir[x][y] =
6957         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6958       Moving2Blocked(x, y, &newx, &newy);
6959
6960       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6961         return;
6962
6963       MovDir[x][y] = old_move_dir;
6964     }
6965   }
6966   else if (move_pattern == MV_WHEN_PUSHED ||
6967            move_pattern == MV_WHEN_DROPPED)
6968   {
6969     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6970       MovDir[x][y] = MV_NONE;
6971
6972     MovDelay[x][y] = 0;
6973   }
6974   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6975   {
6976     static int test_xy[7][2] =
6977     {
6978       { 0, -1 },
6979       { -1, 0 },
6980       { +1, 0 },
6981       { 0, +1 },
6982       { 0, -1 },
6983       { -1, 0 },
6984       { +1, 0 },
6985     };
6986     static int test_dir[7] =
6987     {
6988       MV_UP,
6989       MV_LEFT,
6990       MV_RIGHT,
6991       MV_DOWN,
6992       MV_UP,
6993       MV_LEFT,
6994       MV_RIGHT,
6995     };
6996     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6997     int move_preference = -1000000;     /* start with very low preference */
6998     int new_move_dir = MV_NONE;
6999     int start_test = RND(4);
7000     int i;
7001
7002     for (i = 0; i < NUM_DIRECTIONS; i++)
7003     {
7004       int move_dir = test_dir[start_test + i];
7005       int move_dir_preference;
7006
7007       xx = x + test_xy[start_test + i][0];
7008       yy = y + test_xy[start_test + i][1];
7009
7010       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7011           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7012       {
7013         new_move_dir = move_dir;
7014
7015         break;
7016       }
7017
7018       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7019         continue;
7020
7021       move_dir_preference = -1 * RunnerVisit[xx][yy];
7022       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7023         move_dir_preference = PlayerVisit[xx][yy];
7024
7025       if (move_dir_preference > move_preference)
7026       {
7027         /* prefer field that has not been visited for the longest time */
7028         move_preference = move_dir_preference;
7029         new_move_dir = move_dir;
7030       }
7031       else if (move_dir_preference == move_preference &&
7032                move_dir == old_move_dir)
7033       {
7034         /* prefer last direction when all directions are preferred equally */
7035         move_preference = move_dir_preference;
7036         new_move_dir = move_dir;
7037       }
7038     }
7039
7040     MovDir[x][y] = new_move_dir;
7041     if (old_move_dir != new_move_dir)
7042       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7043   }
7044 }
7045
7046 static void TurnRound(int x, int y)
7047 {
7048   int direction = MovDir[x][y];
7049
7050   TurnRoundExt(x, y);
7051
7052   GfxDir[x][y] = MovDir[x][y];
7053
7054   if (direction != MovDir[x][y])
7055     GfxFrame[x][y] = 0;
7056
7057   if (MovDelay[x][y])
7058     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7059
7060   ResetGfxFrame(x, y, FALSE);
7061 }
7062
7063 static boolean JustBeingPushed(int x, int y)
7064 {
7065   int i;
7066
7067   for (i = 0; i < MAX_PLAYERS; i++)
7068   {
7069     struct PlayerInfo *player = &stored_player[i];
7070
7071     if (player->active && player->is_pushing && player->MovPos)
7072     {
7073       int next_jx = player->jx + (player->jx - player->last_jx);
7074       int next_jy = player->jy + (player->jy - player->last_jy);
7075
7076       if (x == next_jx && y == next_jy)
7077         return TRUE;
7078     }
7079   }
7080
7081   return FALSE;
7082 }
7083
7084 void StartMoving(int x, int y)
7085 {
7086   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7087   int element = Feld[x][y];
7088
7089   if (Stop[x][y])
7090     return;
7091
7092   if (MovDelay[x][y] == 0)
7093     GfxAction[x][y] = ACTION_DEFAULT;
7094
7095   if (CAN_FALL(element) && y < lev_fieldy - 1)
7096   {
7097     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7098         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7099       if (JustBeingPushed(x, y))
7100         return;
7101
7102     if (element == EL_QUICKSAND_FULL)
7103     {
7104       if (IS_FREE(x, y + 1))
7105       {
7106         InitMovingField(x, y, MV_DOWN);
7107         started_moving = TRUE;
7108
7109         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7110 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7111         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7112           Store[x][y] = EL_ROCK;
7113 #else
7114         Store[x][y] = EL_ROCK;
7115 #endif
7116
7117         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7118       }
7119       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7120       {
7121         if (!MovDelay[x][y])
7122         {
7123           MovDelay[x][y] = TILEY + 1;
7124
7125           ResetGfxAnimation(x, y);
7126           ResetGfxAnimation(x, y + 1);
7127         }
7128
7129         if (MovDelay[x][y])
7130         {
7131           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7132           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7133
7134           MovDelay[x][y]--;
7135           if (MovDelay[x][y])
7136             return;
7137         }
7138
7139         Feld[x][y] = EL_QUICKSAND_EMPTY;
7140         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7141         Store[x][y + 1] = Store[x][y];
7142         Store[x][y] = 0;
7143
7144         PlayLevelSoundAction(x, y, ACTION_FILLING);
7145       }
7146       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7147       {
7148         if (!MovDelay[x][y])
7149         {
7150           MovDelay[x][y] = TILEY + 1;
7151
7152           ResetGfxAnimation(x, y);
7153           ResetGfxAnimation(x, y + 1);
7154         }
7155
7156         if (MovDelay[x][y])
7157         {
7158           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7159           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7160
7161           MovDelay[x][y]--;
7162           if (MovDelay[x][y])
7163             return;
7164         }
7165
7166         Feld[x][y] = EL_QUICKSAND_EMPTY;
7167         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7168         Store[x][y + 1] = Store[x][y];
7169         Store[x][y] = 0;
7170
7171         PlayLevelSoundAction(x, y, ACTION_FILLING);
7172       }
7173     }
7174     else if (element == EL_QUICKSAND_FAST_FULL)
7175     {
7176       if (IS_FREE(x, y + 1))
7177       {
7178         InitMovingField(x, y, MV_DOWN);
7179         started_moving = TRUE;
7180
7181         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7182 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7183         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7184           Store[x][y] = EL_ROCK;
7185 #else
7186         Store[x][y] = EL_ROCK;
7187 #endif
7188
7189         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7190       }
7191       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7192       {
7193         if (!MovDelay[x][y])
7194         {
7195           MovDelay[x][y] = TILEY + 1;
7196
7197           ResetGfxAnimation(x, y);
7198           ResetGfxAnimation(x, y + 1);
7199         }
7200
7201         if (MovDelay[x][y])
7202         {
7203           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7204           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7205
7206           MovDelay[x][y]--;
7207           if (MovDelay[x][y])
7208             return;
7209         }
7210
7211         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7212         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7213         Store[x][y + 1] = Store[x][y];
7214         Store[x][y] = 0;
7215
7216         PlayLevelSoundAction(x, y, ACTION_FILLING);
7217       }
7218       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7219       {
7220         if (!MovDelay[x][y])
7221         {
7222           MovDelay[x][y] = TILEY + 1;
7223
7224           ResetGfxAnimation(x, y);
7225           ResetGfxAnimation(x, y + 1);
7226         }
7227
7228         if (MovDelay[x][y])
7229         {
7230           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7231           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7232
7233           MovDelay[x][y]--;
7234           if (MovDelay[x][y])
7235             return;
7236         }
7237
7238         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7239         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7240         Store[x][y + 1] = Store[x][y];
7241         Store[x][y] = 0;
7242
7243         PlayLevelSoundAction(x, y, ACTION_FILLING);
7244       }
7245     }
7246     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7247              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7248     {
7249       InitMovingField(x, y, MV_DOWN);
7250       started_moving = TRUE;
7251
7252       Feld[x][y] = EL_QUICKSAND_FILLING;
7253       Store[x][y] = element;
7254
7255       PlayLevelSoundAction(x, y, ACTION_FILLING);
7256     }
7257     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7258              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7259     {
7260       InitMovingField(x, y, MV_DOWN);
7261       started_moving = TRUE;
7262
7263       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7264       Store[x][y] = element;
7265
7266       PlayLevelSoundAction(x, y, ACTION_FILLING);
7267     }
7268     else if (element == EL_MAGIC_WALL_FULL)
7269     {
7270       if (IS_FREE(x, y + 1))
7271       {
7272         InitMovingField(x, y, MV_DOWN);
7273         started_moving = TRUE;
7274
7275         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7276         Store[x][y] = EL_CHANGED(Store[x][y]);
7277       }
7278       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7279       {
7280         if (!MovDelay[x][y])
7281           MovDelay[x][y] = TILEY / 4 + 1;
7282
7283         if (MovDelay[x][y])
7284         {
7285           MovDelay[x][y]--;
7286           if (MovDelay[x][y])
7287             return;
7288         }
7289
7290         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7291         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7292         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7293         Store[x][y] = 0;
7294       }
7295     }
7296     else if (element == EL_BD_MAGIC_WALL_FULL)
7297     {
7298       if (IS_FREE(x, y + 1))
7299       {
7300         InitMovingField(x, y, MV_DOWN);
7301         started_moving = TRUE;
7302
7303         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7304         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7305       }
7306       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7307       {
7308         if (!MovDelay[x][y])
7309           MovDelay[x][y] = TILEY / 4 + 1;
7310
7311         if (MovDelay[x][y])
7312         {
7313           MovDelay[x][y]--;
7314           if (MovDelay[x][y])
7315             return;
7316         }
7317
7318         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7319         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7320         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7321         Store[x][y] = 0;
7322       }
7323     }
7324     else if (element == EL_DC_MAGIC_WALL_FULL)
7325     {
7326       if (IS_FREE(x, y + 1))
7327       {
7328         InitMovingField(x, y, MV_DOWN);
7329         started_moving = TRUE;
7330
7331         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7332         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7333       }
7334       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7335       {
7336         if (!MovDelay[x][y])
7337           MovDelay[x][y] = TILEY / 4 + 1;
7338
7339         if (MovDelay[x][y])
7340         {
7341           MovDelay[x][y]--;
7342           if (MovDelay[x][y])
7343             return;
7344         }
7345
7346         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7347         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7348         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7349         Store[x][y] = 0;
7350       }
7351     }
7352     else if ((CAN_PASS_MAGIC_WALL(element) &&
7353               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7354                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7355              (CAN_PASS_DC_MAGIC_WALL(element) &&
7356               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7357
7358     {
7359       InitMovingField(x, y, MV_DOWN);
7360       started_moving = TRUE;
7361
7362       Feld[x][y] =
7363         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7364          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7365          EL_DC_MAGIC_WALL_FILLING);
7366       Store[x][y] = element;
7367     }
7368     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7369     {
7370       SplashAcid(x, y + 1);
7371
7372       InitMovingField(x, y, MV_DOWN);
7373       started_moving = TRUE;
7374
7375       Store[x][y] = EL_ACID;
7376     }
7377     else if (
7378              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7379               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7380              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7381               CAN_FALL(element) && WasJustFalling[x][y] &&
7382               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7383
7384              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7385               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7386               (Feld[x][y + 1] == EL_BLOCKED)))
7387     {
7388       /* this is needed for a special case not covered by calling "Impact()"
7389          from "ContinueMoving()": if an element moves to a tile directly below
7390          another element which was just falling on that tile (which was empty
7391          in the previous frame), the falling element above would just stop
7392          instead of smashing the element below (in previous version, the above
7393          element was just checked for "moving" instead of "falling", resulting
7394          in incorrect smashes caused by horizontal movement of the above
7395          element; also, the case of the player being the element to smash was
7396          simply not covered here... :-/ ) */
7397
7398       CheckCollision[x][y] = 0;
7399       CheckImpact[x][y] = 0;
7400
7401       Impact(x, y);
7402     }
7403     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7404     {
7405       if (MovDir[x][y] == MV_NONE)
7406       {
7407         InitMovingField(x, y, MV_DOWN);
7408         started_moving = TRUE;
7409       }
7410     }
7411     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7412     {
7413       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7414         MovDir[x][y] = MV_DOWN;
7415
7416       InitMovingField(x, y, MV_DOWN);
7417       started_moving = TRUE;
7418     }
7419     else if (element == EL_AMOEBA_DROP)
7420     {
7421       Feld[x][y] = EL_AMOEBA_GROWING;
7422       Store[x][y] = EL_AMOEBA_WET;
7423     }
7424     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7425               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7426              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7427              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7428     {
7429       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7430                                 (IS_FREE(x - 1, y + 1) ||
7431                                  Feld[x - 1][y + 1] == EL_ACID));
7432       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7433                                 (IS_FREE(x + 1, y + 1) ||
7434                                  Feld[x + 1][y + 1] == EL_ACID));
7435       boolean can_fall_any  = (can_fall_left || can_fall_right);
7436       boolean can_fall_both = (can_fall_left && can_fall_right);
7437       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7438
7439       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7440       {
7441         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7442           can_fall_right = FALSE;
7443         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7444           can_fall_left = FALSE;
7445         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7446           can_fall_right = FALSE;
7447         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7448           can_fall_left = FALSE;
7449
7450         can_fall_any  = (can_fall_left || can_fall_right);
7451         can_fall_both = FALSE;
7452       }
7453
7454       if (can_fall_both)
7455       {
7456         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7457           can_fall_right = FALSE;       /* slip down on left side */
7458         else
7459           can_fall_left = !(can_fall_right = RND(2));
7460
7461         can_fall_both = FALSE;
7462       }
7463
7464       if (can_fall_any)
7465       {
7466         /* if not determined otherwise, prefer left side for slipping down */
7467         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7468         started_moving = TRUE;
7469       }
7470     }
7471     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7472     {
7473       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7474       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7475       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7476       int belt_dir = game.belt_dir[belt_nr];
7477
7478       if ((belt_dir == MV_LEFT  && left_is_free) ||
7479           (belt_dir == MV_RIGHT && right_is_free))
7480       {
7481         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7482
7483         InitMovingField(x, y, belt_dir);
7484         started_moving = TRUE;
7485
7486         Pushed[x][y] = TRUE;
7487         Pushed[nextx][y] = TRUE;
7488
7489         GfxAction[x][y] = ACTION_DEFAULT;
7490       }
7491       else
7492       {
7493         MovDir[x][y] = 0;       /* if element was moving, stop it */
7494       }
7495     }
7496   }
7497
7498   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7499   if (CAN_MOVE(element) && !started_moving)
7500   {
7501     int move_pattern = element_info[element].move_pattern;
7502     int newx, newy;
7503
7504     Moving2Blocked(x, y, &newx, &newy);
7505
7506     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7507       return;
7508
7509     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7510         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7511     {
7512       WasJustMoving[x][y] = 0;
7513       CheckCollision[x][y] = 0;
7514
7515       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7516
7517       if (Feld[x][y] != element)        /* element has changed */
7518         return;
7519     }
7520
7521     if (!MovDelay[x][y])        /* start new movement phase */
7522     {
7523       /* all objects that can change their move direction after each step
7524          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7525
7526       if (element != EL_YAMYAM &&
7527           element != EL_DARK_YAMYAM &&
7528           element != EL_PACMAN &&
7529           !(move_pattern & MV_ANY_DIRECTION) &&
7530           move_pattern != MV_TURNING_LEFT &&
7531           move_pattern != MV_TURNING_RIGHT &&
7532           move_pattern != MV_TURNING_LEFT_RIGHT &&
7533           move_pattern != MV_TURNING_RIGHT_LEFT &&
7534           move_pattern != MV_TURNING_RANDOM)
7535       {
7536         TurnRound(x, y);
7537
7538         if (MovDelay[x][y] && (element == EL_BUG ||
7539                                element == EL_SPACESHIP ||
7540                                element == EL_SP_SNIKSNAK ||
7541                                element == EL_SP_ELECTRON ||
7542                                element == EL_MOLE))
7543           TEST_DrawLevelField(x, y);
7544       }
7545     }
7546
7547     if (MovDelay[x][y])         /* wait some time before next movement */
7548     {
7549       MovDelay[x][y]--;
7550
7551       if (element == EL_ROBOT ||
7552           element == EL_YAMYAM ||
7553           element == EL_DARK_YAMYAM)
7554       {
7555         DrawLevelElementAnimationIfNeeded(x, y, element);
7556         PlayLevelSoundAction(x, y, ACTION_WAITING);
7557       }
7558       else if (element == EL_SP_ELECTRON)
7559         DrawLevelElementAnimationIfNeeded(x, y, element);
7560       else if (element == EL_DRAGON)
7561       {
7562         int i;
7563         int dir = MovDir[x][y];
7564         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7565         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7566         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7567                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7568                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7569                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7570         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7571
7572         GfxAction[x][y] = ACTION_ATTACKING;
7573
7574         if (IS_PLAYER(x, y))
7575           DrawPlayerField(x, y);
7576         else
7577           TEST_DrawLevelField(x, y);
7578
7579         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7580
7581         for (i = 1; i <= 3; i++)
7582         {
7583           int xx = x + i * dx;
7584           int yy = y + i * dy;
7585           int sx = SCREENX(xx);
7586           int sy = SCREENY(yy);
7587           int flame_graphic = graphic + (i - 1);
7588
7589           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7590             break;
7591
7592           if (MovDelay[x][y])
7593           {
7594             int flamed = MovingOrBlocked2Element(xx, yy);
7595
7596             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7597               Bang(xx, yy);
7598             else
7599               RemoveMovingField(xx, yy);
7600
7601             ChangeDelay[xx][yy] = 0;
7602
7603             Feld[xx][yy] = EL_FLAMES;
7604
7605             if (IN_SCR_FIELD(sx, sy))
7606             {
7607               TEST_DrawLevelFieldCrumbled(xx, yy);
7608               DrawGraphic(sx, sy, flame_graphic, frame);
7609             }
7610           }
7611           else
7612           {
7613             if (Feld[xx][yy] == EL_FLAMES)
7614               Feld[xx][yy] = EL_EMPTY;
7615             TEST_DrawLevelField(xx, yy);
7616           }
7617         }
7618       }
7619
7620       if (MovDelay[x][y])       /* element still has to wait some time */
7621       {
7622         PlayLevelSoundAction(x, y, ACTION_WAITING);
7623
7624         return;
7625       }
7626     }
7627
7628     /* now make next step */
7629
7630     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7631
7632     if (DONT_COLLIDE_WITH(element) &&
7633         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7634         !PLAYER_ENEMY_PROTECTED(newx, newy))
7635     {
7636       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7637
7638       return;
7639     }
7640
7641     else if (CAN_MOVE_INTO_ACID(element) &&
7642              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7643              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7644              (MovDir[x][y] == MV_DOWN ||
7645               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7646     {
7647       SplashAcid(newx, newy);
7648       Store[x][y] = EL_ACID;
7649     }
7650     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7651     {
7652       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7653           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7654           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7655           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7656       {
7657         RemoveField(x, y);
7658         TEST_DrawLevelField(x, y);
7659
7660         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7661         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7662           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7663
7664         local_player->friends_still_needed--;
7665         if (!local_player->friends_still_needed &&
7666             !local_player->GameOver && AllPlayersGone)
7667           PlayerWins(local_player);
7668
7669         return;
7670       }
7671       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7672       {
7673         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7674           TEST_DrawLevelField(newx, newy);
7675         else
7676           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7677       }
7678       else if (!IS_FREE(newx, newy))
7679       {
7680         GfxAction[x][y] = ACTION_WAITING;
7681
7682         if (IS_PLAYER(x, y))
7683           DrawPlayerField(x, y);
7684         else
7685           TEST_DrawLevelField(x, y);
7686
7687         return;
7688       }
7689     }
7690     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7691     {
7692       if (IS_FOOD_PIG(Feld[newx][newy]))
7693       {
7694         if (IS_MOVING(newx, newy))
7695           RemoveMovingField(newx, newy);
7696         else
7697         {
7698           Feld[newx][newy] = EL_EMPTY;
7699           TEST_DrawLevelField(newx, newy);
7700         }
7701
7702         PlayLevelSound(x, y, SND_PIG_DIGGING);
7703       }
7704       else if (!IS_FREE(newx, newy))
7705       {
7706         if (IS_PLAYER(x, y))
7707           DrawPlayerField(x, y);
7708         else
7709           TEST_DrawLevelField(x, y);
7710
7711         return;
7712       }
7713     }
7714     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7715     {
7716       if (Store[x][y] != EL_EMPTY)
7717       {
7718         boolean can_clone = FALSE;
7719         int xx, yy;
7720
7721         /* check if element to clone is still there */
7722         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7723         {
7724           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7725           {
7726             can_clone = TRUE;
7727
7728             break;
7729           }
7730         }
7731
7732         /* cannot clone or target field not free anymore -- do not clone */
7733         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7734           Store[x][y] = EL_EMPTY;
7735       }
7736
7737       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7738       {
7739         if (IS_MV_DIAGONAL(MovDir[x][y]))
7740         {
7741           int diagonal_move_dir = MovDir[x][y];
7742           int stored = Store[x][y];
7743           int change_delay = 8;
7744           int graphic;
7745
7746           /* android is moving diagonally */
7747
7748           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7749
7750           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7751           GfxElement[x][y] = EL_EMC_ANDROID;
7752           GfxAction[x][y] = ACTION_SHRINKING;
7753           GfxDir[x][y] = diagonal_move_dir;
7754           ChangeDelay[x][y] = change_delay;
7755
7756           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7757                                    GfxDir[x][y]);
7758
7759           DrawLevelGraphicAnimation(x, y, graphic);
7760           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7761
7762           if (Feld[newx][newy] == EL_ACID)
7763           {
7764             SplashAcid(newx, newy);
7765
7766             return;
7767           }
7768
7769           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7770
7771           Store[newx][newy] = EL_EMC_ANDROID;
7772           GfxElement[newx][newy] = EL_EMC_ANDROID;
7773           GfxAction[newx][newy] = ACTION_GROWING;
7774           GfxDir[newx][newy] = diagonal_move_dir;
7775           ChangeDelay[newx][newy] = change_delay;
7776
7777           graphic = el_act_dir2img(GfxElement[newx][newy],
7778                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7779
7780           DrawLevelGraphicAnimation(newx, newy, graphic);
7781           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7782
7783           return;
7784         }
7785         else
7786         {
7787           Feld[newx][newy] = EL_EMPTY;
7788           TEST_DrawLevelField(newx, newy);
7789
7790           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7791         }
7792       }
7793       else if (!IS_FREE(newx, newy))
7794       {
7795         return;
7796       }
7797     }
7798     else if (IS_CUSTOM_ELEMENT(element) &&
7799              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7800     {
7801       if (!DigFieldByCE(newx, newy, element))
7802         return;
7803
7804       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7805       {
7806         RunnerVisit[x][y] = FrameCounter;
7807         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7808       }
7809     }
7810     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7811     {
7812       if (!IS_FREE(newx, newy))
7813       {
7814         if (IS_PLAYER(x, y))
7815           DrawPlayerField(x, y);
7816         else
7817           TEST_DrawLevelField(x, y);
7818
7819         return;
7820       }
7821       else
7822       {
7823         boolean wanna_flame = !RND(10);
7824         int dx = newx - x, dy = newy - y;
7825         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7826         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7827         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7828                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7829         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7830                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7831
7832         if ((wanna_flame ||
7833              IS_CLASSIC_ENEMY(element1) ||
7834              IS_CLASSIC_ENEMY(element2)) &&
7835             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7836             element1 != EL_FLAMES && element2 != EL_FLAMES)
7837         {
7838           ResetGfxAnimation(x, y);
7839           GfxAction[x][y] = ACTION_ATTACKING;
7840
7841           if (IS_PLAYER(x, y))
7842             DrawPlayerField(x, y);
7843           else
7844             TEST_DrawLevelField(x, y);
7845
7846           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7847
7848           MovDelay[x][y] = 50;
7849
7850           Feld[newx][newy] = EL_FLAMES;
7851           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7852             Feld[newx1][newy1] = EL_FLAMES;
7853           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7854             Feld[newx2][newy2] = EL_FLAMES;
7855
7856           return;
7857         }
7858       }
7859     }
7860     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7861              Feld[newx][newy] == EL_DIAMOND)
7862     {
7863       if (IS_MOVING(newx, newy))
7864         RemoveMovingField(newx, newy);
7865       else
7866       {
7867         Feld[newx][newy] = EL_EMPTY;
7868         TEST_DrawLevelField(newx, newy);
7869       }
7870
7871       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7872     }
7873     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7874              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7875     {
7876       if (AmoebaNr[newx][newy])
7877       {
7878         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7879         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7880             Feld[newx][newy] == EL_BD_AMOEBA)
7881           AmoebaCnt[AmoebaNr[newx][newy]]--;
7882       }
7883
7884       if (IS_MOVING(newx, newy))
7885       {
7886         RemoveMovingField(newx, newy);
7887       }
7888       else
7889       {
7890         Feld[newx][newy] = EL_EMPTY;
7891         TEST_DrawLevelField(newx, newy);
7892       }
7893
7894       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7895     }
7896     else if ((element == EL_PACMAN || element == EL_MOLE)
7897              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7898     {
7899       if (AmoebaNr[newx][newy])
7900       {
7901         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7902         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7903             Feld[newx][newy] == EL_BD_AMOEBA)
7904           AmoebaCnt[AmoebaNr[newx][newy]]--;
7905       }
7906
7907       if (element == EL_MOLE)
7908       {
7909         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7910         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7911
7912         ResetGfxAnimation(x, y);
7913         GfxAction[x][y] = ACTION_DIGGING;
7914         TEST_DrawLevelField(x, y);
7915
7916         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7917
7918         return;                         /* wait for shrinking amoeba */
7919       }
7920       else      /* element == EL_PACMAN */
7921       {
7922         Feld[newx][newy] = EL_EMPTY;
7923         TEST_DrawLevelField(newx, newy);
7924         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7925       }
7926     }
7927     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7928              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7929               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7930     {
7931       /* wait for shrinking amoeba to completely disappear */
7932       return;
7933     }
7934     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7935     {
7936       /* object was running against a wall */
7937
7938       TurnRound(x, y);
7939
7940       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7941         DrawLevelElementAnimation(x, y, element);
7942
7943       if (DONT_TOUCH(element))
7944         TestIfBadThingTouchesPlayer(x, y);
7945
7946       return;
7947     }
7948
7949     InitMovingField(x, y, MovDir[x][y]);
7950
7951     PlayLevelSoundAction(x, y, ACTION_MOVING);
7952   }
7953
7954   if (MovDir[x][y])
7955     ContinueMoving(x, y);
7956 }
7957
7958 void ContinueMoving(int x, int y)
7959 {
7960   int element = Feld[x][y];
7961   struct ElementInfo *ei = &element_info[element];
7962   int direction = MovDir[x][y];
7963   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7964   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7965   int newx = x + dx, newy = y + dy;
7966   int stored = Store[x][y];
7967   int stored_new = Store[newx][newy];
7968   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7969   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7970   boolean last_line = (newy == lev_fieldy - 1);
7971
7972   MovPos[x][y] += getElementMoveStepsize(x, y);
7973
7974   if (pushed_by_player) /* special case: moving object pushed by player */
7975     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7976
7977   if (ABS(MovPos[x][y]) < TILEX)
7978   {
7979     TEST_DrawLevelField(x, y);
7980
7981     return;     /* element is still moving */
7982   }
7983
7984   /* element reached destination field */
7985
7986   Feld[x][y] = EL_EMPTY;
7987   Feld[newx][newy] = element;
7988   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7989
7990   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7991   {
7992     element = Feld[newx][newy] = EL_ACID;
7993   }
7994   else if (element == EL_MOLE)
7995   {
7996     Feld[x][y] = EL_SAND;
7997
7998     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
7999   }
8000   else if (element == EL_QUICKSAND_FILLING)
8001   {
8002     element = Feld[newx][newy] = get_next_element(element);
8003     Store[newx][newy] = Store[x][y];
8004   }
8005   else if (element == EL_QUICKSAND_EMPTYING)
8006   {
8007     Feld[x][y] = get_next_element(element);
8008     element = Feld[newx][newy] = Store[x][y];
8009   }
8010   else if (element == EL_QUICKSAND_FAST_FILLING)
8011   {
8012     element = Feld[newx][newy] = get_next_element(element);
8013     Store[newx][newy] = Store[x][y];
8014   }
8015   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8016   {
8017     Feld[x][y] = get_next_element(element);
8018     element = Feld[newx][newy] = Store[x][y];
8019   }
8020   else if (element == EL_MAGIC_WALL_FILLING)
8021   {
8022     element = Feld[newx][newy] = get_next_element(element);
8023     if (!game.magic_wall_active)
8024       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8025     Store[newx][newy] = Store[x][y];
8026   }
8027   else if (element == EL_MAGIC_WALL_EMPTYING)
8028   {
8029     Feld[x][y] = get_next_element(element);
8030     if (!game.magic_wall_active)
8031       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8032     element = Feld[newx][newy] = Store[x][y];
8033
8034     InitField(newx, newy, FALSE);
8035   }
8036   else if (element == EL_BD_MAGIC_WALL_FILLING)
8037   {
8038     element = Feld[newx][newy] = get_next_element(element);
8039     if (!game.magic_wall_active)
8040       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8041     Store[newx][newy] = Store[x][y];
8042   }
8043   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8044   {
8045     Feld[x][y] = get_next_element(element);
8046     if (!game.magic_wall_active)
8047       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8048     element = Feld[newx][newy] = Store[x][y];
8049
8050     InitField(newx, newy, FALSE);
8051   }
8052   else if (element == EL_DC_MAGIC_WALL_FILLING)
8053   {
8054     element = Feld[newx][newy] = get_next_element(element);
8055     if (!game.magic_wall_active)
8056       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8057     Store[newx][newy] = Store[x][y];
8058   }
8059   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8060   {
8061     Feld[x][y] = get_next_element(element);
8062     if (!game.magic_wall_active)
8063       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8064     element = Feld[newx][newy] = Store[x][y];
8065
8066     InitField(newx, newy, FALSE);
8067   }
8068   else if (element == EL_AMOEBA_DROPPING)
8069   {
8070     Feld[x][y] = get_next_element(element);
8071     element = Feld[newx][newy] = Store[x][y];
8072   }
8073   else if (element == EL_SOKOBAN_OBJECT)
8074   {
8075     if (Back[x][y])
8076       Feld[x][y] = Back[x][y];
8077
8078     if (Back[newx][newy])
8079       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8080
8081     Back[x][y] = Back[newx][newy] = 0;
8082   }
8083
8084   Store[x][y] = EL_EMPTY;
8085   MovPos[x][y] = 0;
8086   MovDir[x][y] = 0;
8087   MovDelay[x][y] = 0;
8088
8089   MovDelay[newx][newy] = 0;
8090
8091   if (CAN_CHANGE_OR_HAS_ACTION(element))
8092   {
8093     /* copy element change control values to new field */
8094     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8095     ChangePage[newx][newy]  = ChangePage[x][y];
8096     ChangeCount[newx][newy] = ChangeCount[x][y];
8097     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8098   }
8099
8100   CustomValue[newx][newy] = CustomValue[x][y];
8101
8102   ChangeDelay[x][y] = 0;
8103   ChangePage[x][y] = -1;
8104   ChangeCount[x][y] = 0;
8105   ChangeEvent[x][y] = -1;
8106
8107   CustomValue[x][y] = 0;
8108
8109   /* copy animation control values to new field */
8110   GfxFrame[newx][newy]  = GfxFrame[x][y];
8111   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8112   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8113   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8114
8115   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8116
8117   /* some elements can leave other elements behind after moving */
8118   if (ei->move_leave_element != EL_EMPTY &&
8119       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8120       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8121   {
8122     int move_leave_element = ei->move_leave_element;
8123
8124     /* this makes it possible to leave the removed element again */
8125     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8126       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8127
8128     Feld[x][y] = move_leave_element;
8129
8130     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8131       MovDir[x][y] = direction;
8132
8133     InitField(x, y, FALSE);
8134
8135     if (GFX_CRUMBLED(Feld[x][y]))
8136       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8137
8138     if (ELEM_IS_PLAYER(move_leave_element))
8139       RelocatePlayer(x, y, move_leave_element);
8140   }
8141
8142   /* do this after checking for left-behind element */
8143   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8144
8145   if (!CAN_MOVE(element) ||
8146       (CAN_FALL(element) && direction == MV_DOWN &&
8147        (element == EL_SPRING ||
8148         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8149         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8150     GfxDir[x][y] = MovDir[newx][newy] = 0;
8151
8152   TEST_DrawLevelField(x, y);
8153   TEST_DrawLevelField(newx, newy);
8154
8155   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8156
8157   /* prevent pushed element from moving on in pushed direction */
8158   if (pushed_by_player && CAN_MOVE(element) &&
8159       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8160       !(element_info[element].move_pattern & direction))
8161     TurnRound(newx, newy);
8162
8163   /* prevent elements on conveyor belt from moving on in last direction */
8164   if (pushed_by_conveyor && CAN_FALL(element) &&
8165       direction & MV_HORIZONTAL)
8166     MovDir[newx][newy] = 0;
8167
8168   if (!pushed_by_player)
8169   {
8170     int nextx = newx + dx, nexty = newy + dy;
8171     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8172
8173     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8174
8175     if (CAN_FALL(element) && direction == MV_DOWN)
8176       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8177
8178     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8179       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8180
8181     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8182       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8183   }
8184
8185   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8186   {
8187     TestIfBadThingTouchesPlayer(newx, newy);
8188     TestIfBadThingTouchesFriend(newx, newy);
8189
8190     if (!IS_CUSTOM_ELEMENT(element))
8191       TestIfBadThingTouchesOtherBadThing(newx, newy);
8192   }
8193   else if (element == EL_PENGUIN)
8194     TestIfFriendTouchesBadThing(newx, newy);
8195
8196   if (DONT_GET_HIT_BY(element))
8197   {
8198     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8199   }
8200
8201   /* give the player one last chance (one more frame) to move away */
8202   if (CAN_FALL(element) && direction == MV_DOWN &&
8203       (last_line || (!IS_FREE(x, newy + 1) &&
8204                      (!IS_PLAYER(x, newy + 1) ||
8205                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8206     Impact(x, newy);
8207
8208   if (pushed_by_player && !game.use_change_when_pushing_bug)
8209   {
8210     int push_side = MV_DIR_OPPOSITE(direction);
8211     struct PlayerInfo *player = PLAYERINFO(x, y);
8212
8213     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8214                                player->index_bit, push_side);
8215     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8216                                         player->index_bit, push_side);
8217   }
8218
8219   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8220     MovDelay[newx][newy] = 1;
8221
8222   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8223
8224   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8225   TestIfElementHitsCustomElement(newx, newy, direction);
8226   TestIfPlayerTouchesCustomElement(newx, newy);
8227   TestIfElementTouchesCustomElement(newx, newy);
8228
8229   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8230       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8231     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8232                              MV_DIR_OPPOSITE(direction));
8233 }
8234
8235 int AmoebeNachbarNr(int ax, int ay)
8236 {
8237   int i;
8238   int element = Feld[ax][ay];
8239   int group_nr = 0;
8240   static int xy[4][2] =
8241   {
8242     { 0, -1 },
8243     { -1, 0 },
8244     { +1, 0 },
8245     { 0, +1 }
8246   };
8247
8248   for (i = 0; i < NUM_DIRECTIONS; i++)
8249   {
8250     int x = ax + xy[i][0];
8251     int y = ay + xy[i][1];
8252
8253     if (!IN_LEV_FIELD(x, y))
8254       continue;
8255
8256     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8257       group_nr = AmoebaNr[x][y];
8258   }
8259
8260   return group_nr;
8261 }
8262
8263 void AmoebenVereinigen(int ax, int ay)
8264 {
8265   int i, x, y, xx, yy;
8266   int new_group_nr = AmoebaNr[ax][ay];
8267   static int xy[4][2] =
8268   {
8269     { 0, -1 },
8270     { -1, 0 },
8271     { +1, 0 },
8272     { 0, +1 }
8273   };
8274
8275   if (new_group_nr == 0)
8276     return;
8277
8278   for (i = 0; i < NUM_DIRECTIONS; i++)
8279   {
8280     x = ax + xy[i][0];
8281     y = ay + xy[i][1];
8282
8283     if (!IN_LEV_FIELD(x, y))
8284       continue;
8285
8286     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8287          Feld[x][y] == EL_BD_AMOEBA ||
8288          Feld[x][y] == EL_AMOEBA_DEAD) &&
8289         AmoebaNr[x][y] != new_group_nr)
8290     {
8291       int old_group_nr = AmoebaNr[x][y];
8292
8293       if (old_group_nr == 0)
8294         return;
8295
8296       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8297       AmoebaCnt[old_group_nr] = 0;
8298       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8299       AmoebaCnt2[old_group_nr] = 0;
8300
8301       SCAN_PLAYFIELD(xx, yy)
8302       {
8303         if (AmoebaNr[xx][yy] == old_group_nr)
8304           AmoebaNr[xx][yy] = new_group_nr;
8305       }
8306     }
8307   }
8308 }
8309
8310 void AmoebeUmwandeln(int ax, int ay)
8311 {
8312   int i, x, y;
8313
8314   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8315   {
8316     int group_nr = AmoebaNr[ax][ay];
8317
8318 #ifdef DEBUG
8319     if (group_nr == 0)
8320     {
8321       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8322       printf("AmoebeUmwandeln(): This should never happen!\n");
8323       return;
8324     }
8325 #endif
8326
8327     SCAN_PLAYFIELD(x, y)
8328     {
8329       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8330       {
8331         AmoebaNr[x][y] = 0;
8332         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8333       }
8334     }
8335
8336     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8337                             SND_AMOEBA_TURNING_TO_GEM :
8338                             SND_AMOEBA_TURNING_TO_ROCK));
8339     Bang(ax, ay);
8340   }
8341   else
8342   {
8343     static int xy[4][2] =
8344     {
8345       { 0, -1 },
8346       { -1, 0 },
8347       { +1, 0 },
8348       { 0, +1 }
8349     };
8350
8351     for (i = 0; i < NUM_DIRECTIONS; i++)
8352     {
8353       x = ax + xy[i][0];
8354       y = ay + xy[i][1];
8355
8356       if (!IN_LEV_FIELD(x, y))
8357         continue;
8358
8359       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8360       {
8361         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8362                               SND_AMOEBA_TURNING_TO_GEM :
8363                               SND_AMOEBA_TURNING_TO_ROCK));
8364         Bang(x, y);
8365       }
8366     }
8367   }
8368 }
8369
8370 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8371 {
8372   int x, y;
8373   int group_nr = AmoebaNr[ax][ay];
8374   boolean done = FALSE;
8375
8376 #ifdef DEBUG
8377   if (group_nr == 0)
8378   {
8379     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8380     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8381     return;
8382   }
8383 #endif
8384
8385   SCAN_PLAYFIELD(x, y)
8386   {
8387     if (AmoebaNr[x][y] == group_nr &&
8388         (Feld[x][y] == EL_AMOEBA_DEAD ||
8389          Feld[x][y] == EL_BD_AMOEBA ||
8390          Feld[x][y] == EL_AMOEBA_GROWING))
8391     {
8392       AmoebaNr[x][y] = 0;
8393       Feld[x][y] = new_element;
8394       InitField(x, y, FALSE);
8395       TEST_DrawLevelField(x, y);
8396       done = TRUE;
8397     }
8398   }
8399
8400   if (done)
8401     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8402                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8403                             SND_BD_AMOEBA_TURNING_TO_GEM));
8404 }
8405
8406 void AmoebeWaechst(int x, int y)
8407 {
8408   static unsigned int sound_delay = 0;
8409   static unsigned int sound_delay_value = 0;
8410
8411   if (!MovDelay[x][y])          /* start new growing cycle */
8412   {
8413     MovDelay[x][y] = 7;
8414
8415     if (DelayReached(&sound_delay, sound_delay_value))
8416     {
8417       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8418       sound_delay_value = 30;
8419     }
8420   }
8421
8422   if (MovDelay[x][y])           /* wait some time before growing bigger */
8423   {
8424     MovDelay[x][y]--;
8425     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8426     {
8427       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8428                                            6 - MovDelay[x][y]);
8429
8430       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8431     }
8432
8433     if (!MovDelay[x][y])
8434     {
8435       Feld[x][y] = Store[x][y];
8436       Store[x][y] = 0;
8437       TEST_DrawLevelField(x, y);
8438     }
8439   }
8440 }
8441
8442 void AmoebaDisappearing(int x, int y)
8443 {
8444   static unsigned int sound_delay = 0;
8445   static unsigned int sound_delay_value = 0;
8446
8447   if (!MovDelay[x][y])          /* start new shrinking cycle */
8448   {
8449     MovDelay[x][y] = 7;
8450
8451     if (DelayReached(&sound_delay, sound_delay_value))
8452       sound_delay_value = 30;
8453   }
8454
8455   if (MovDelay[x][y])           /* wait some time before shrinking */
8456   {
8457     MovDelay[x][y]--;
8458     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8459     {
8460       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8461                                            6 - MovDelay[x][y]);
8462
8463       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8464     }
8465
8466     if (!MovDelay[x][y])
8467     {
8468       Feld[x][y] = EL_EMPTY;
8469       TEST_DrawLevelField(x, y);
8470
8471       /* don't let mole enter this field in this cycle;
8472          (give priority to objects falling to this field from above) */
8473       Stop[x][y] = TRUE;
8474     }
8475   }
8476 }
8477
8478 void AmoebeAbleger(int ax, int ay)
8479 {
8480   int i;
8481   int element = Feld[ax][ay];
8482   int graphic = el2img(element);
8483   int newax = ax, neway = ay;
8484   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8485   static int xy[4][2] =
8486   {
8487     { 0, -1 },
8488     { -1, 0 },
8489     { +1, 0 },
8490     { 0, +1 }
8491   };
8492
8493   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8494   {
8495     Feld[ax][ay] = EL_AMOEBA_DEAD;
8496     TEST_DrawLevelField(ax, ay);
8497     return;
8498   }
8499
8500   if (IS_ANIMATED(graphic))
8501     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8502
8503   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8504     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8505
8506   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8507   {
8508     MovDelay[ax][ay]--;
8509     if (MovDelay[ax][ay])
8510       return;
8511   }
8512
8513   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8514   {
8515     int start = RND(4);
8516     int x = ax + xy[start][0];
8517     int y = ay + xy[start][1];
8518
8519     if (!IN_LEV_FIELD(x, y))
8520       return;
8521
8522     if (IS_FREE(x, y) ||
8523         CAN_GROW_INTO(Feld[x][y]) ||
8524         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8525         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8526     {
8527       newax = x;
8528       neway = y;
8529     }
8530
8531     if (newax == ax && neway == ay)
8532       return;
8533   }
8534   else                          /* normal or "filled" (BD style) amoeba */
8535   {
8536     int start = RND(4);
8537     boolean waiting_for_player = FALSE;
8538
8539     for (i = 0; i < NUM_DIRECTIONS; i++)
8540     {
8541       int j = (start + i) % 4;
8542       int x = ax + xy[j][0];
8543       int y = ay + xy[j][1];
8544
8545       if (!IN_LEV_FIELD(x, y))
8546         continue;
8547
8548       if (IS_FREE(x, y) ||
8549           CAN_GROW_INTO(Feld[x][y]) ||
8550           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8551           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8552       {
8553         newax = x;
8554         neway = y;
8555         break;
8556       }
8557       else if (IS_PLAYER(x, y))
8558         waiting_for_player = TRUE;
8559     }
8560
8561     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8562     {
8563       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8564       {
8565         Feld[ax][ay] = EL_AMOEBA_DEAD;
8566         TEST_DrawLevelField(ax, ay);
8567         AmoebaCnt[AmoebaNr[ax][ay]]--;
8568
8569         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8570         {
8571           if (element == EL_AMOEBA_FULL)
8572             AmoebeUmwandeln(ax, ay);
8573           else if (element == EL_BD_AMOEBA)
8574             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8575         }
8576       }
8577       return;
8578     }
8579     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8580     {
8581       /* amoeba gets larger by growing in some direction */
8582
8583       int new_group_nr = AmoebaNr[ax][ay];
8584
8585 #ifdef DEBUG
8586   if (new_group_nr == 0)
8587   {
8588     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8589     printf("AmoebeAbleger(): This should never happen!\n");
8590     return;
8591   }
8592 #endif
8593
8594       AmoebaNr[newax][neway] = new_group_nr;
8595       AmoebaCnt[new_group_nr]++;
8596       AmoebaCnt2[new_group_nr]++;
8597
8598       /* if amoeba touches other amoeba(s) after growing, unify them */
8599       AmoebenVereinigen(newax, neway);
8600
8601       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8602       {
8603         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8604         return;
8605       }
8606     }
8607   }
8608
8609   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8610       (neway == lev_fieldy - 1 && newax != ax))
8611   {
8612     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8613     Store[newax][neway] = element;
8614   }
8615   else if (neway == ay || element == EL_EMC_DRIPPER)
8616   {
8617     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8618
8619     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8620   }
8621   else
8622   {
8623     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8624     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8625     Store[ax][ay] = EL_AMOEBA_DROP;
8626     ContinueMoving(ax, ay);
8627     return;
8628   }
8629
8630   TEST_DrawLevelField(newax, neway);
8631 }
8632
8633 void Life(int ax, int ay)
8634 {
8635   int x1, y1, x2, y2;
8636   int life_time = 40;
8637   int element = Feld[ax][ay];
8638   int graphic = el2img(element);
8639   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8640                          level.biomaze);
8641   boolean changed = FALSE;
8642
8643   if (IS_ANIMATED(graphic))
8644     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8645
8646   if (Stop[ax][ay])
8647     return;
8648
8649   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8650     MovDelay[ax][ay] = life_time;
8651
8652   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8653   {
8654     MovDelay[ax][ay]--;
8655     if (MovDelay[ax][ay])
8656       return;
8657   }
8658
8659   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8660   {
8661     int xx = ax+x1, yy = ay+y1;
8662     int nachbarn = 0;
8663
8664     if (!IN_LEV_FIELD(xx, yy))
8665       continue;
8666
8667     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8668     {
8669       int x = xx+x2, y = yy+y2;
8670
8671       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8672         continue;
8673
8674       if (((Feld[x][y] == element ||
8675             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8676            !Stop[x][y]) ||
8677           (IS_FREE(x, y) && Stop[x][y]))
8678         nachbarn++;
8679     }
8680
8681     if (xx == ax && yy == ay)           /* field in the middle */
8682     {
8683       if (nachbarn < life_parameter[0] ||
8684           nachbarn > life_parameter[1])
8685       {
8686         Feld[xx][yy] = EL_EMPTY;
8687         if (!Stop[xx][yy])
8688           TEST_DrawLevelField(xx, yy);
8689         Stop[xx][yy] = TRUE;
8690         changed = TRUE;
8691       }
8692     }
8693     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8694     {                                   /* free border field */
8695       if (nachbarn >= life_parameter[2] &&
8696           nachbarn <= life_parameter[3])
8697       {
8698         Feld[xx][yy] = element;
8699         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8700         if (!Stop[xx][yy])
8701           TEST_DrawLevelField(xx, yy);
8702         Stop[xx][yy] = TRUE;
8703         changed = TRUE;
8704       }
8705     }
8706   }
8707
8708   if (changed)
8709     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8710                    SND_GAME_OF_LIFE_GROWING);
8711 }
8712
8713 static void InitRobotWheel(int x, int y)
8714 {
8715   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8716 }
8717
8718 static void RunRobotWheel(int x, int y)
8719 {
8720   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8721 }
8722
8723 static void StopRobotWheel(int x, int y)
8724 {
8725   if (ZX == x && ZY == y)
8726   {
8727     ZX = ZY = -1;
8728
8729     game.robot_wheel_active = FALSE;
8730   }
8731 }
8732
8733 static void InitTimegateWheel(int x, int y)
8734 {
8735   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8736 }
8737
8738 static void RunTimegateWheel(int x, int y)
8739 {
8740   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8741 }
8742
8743 static void InitMagicBallDelay(int x, int y)
8744 {
8745   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8746 }
8747
8748 static void ActivateMagicBall(int bx, int by)
8749 {
8750   int x, y;
8751
8752   if (level.ball_random)
8753   {
8754     int pos_border = RND(8);    /* select one of the eight border elements */
8755     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8756     int xx = pos_content % 3;
8757     int yy = pos_content / 3;
8758
8759     x = bx - 1 + xx;
8760     y = by - 1 + yy;
8761
8762     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8763       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8764   }
8765   else
8766   {
8767     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8768     {
8769       int xx = x - bx + 1;
8770       int yy = y - by + 1;
8771
8772       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8773         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8774     }
8775   }
8776
8777   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8778 }
8779
8780 void CheckExit(int x, int y)
8781 {
8782   if (local_player->gems_still_needed > 0 ||
8783       local_player->sokobanfields_still_needed > 0 ||
8784       local_player->lights_still_needed > 0)
8785   {
8786     int element = Feld[x][y];
8787     int graphic = el2img(element);
8788
8789     if (IS_ANIMATED(graphic))
8790       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8791
8792     return;
8793   }
8794
8795   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8796     return;
8797
8798   Feld[x][y] = EL_EXIT_OPENING;
8799
8800   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8801 }
8802
8803 void CheckExitEM(int x, int y)
8804 {
8805   if (local_player->gems_still_needed > 0 ||
8806       local_player->sokobanfields_still_needed > 0 ||
8807       local_player->lights_still_needed > 0)
8808   {
8809     int element = Feld[x][y];
8810     int graphic = el2img(element);
8811
8812     if (IS_ANIMATED(graphic))
8813       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8814
8815     return;
8816   }
8817
8818   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8819     return;
8820
8821   Feld[x][y] = EL_EM_EXIT_OPENING;
8822
8823   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8824 }
8825
8826 void CheckExitSteel(int x, int y)
8827 {
8828   if (local_player->gems_still_needed > 0 ||
8829       local_player->sokobanfields_still_needed > 0 ||
8830       local_player->lights_still_needed > 0)
8831   {
8832     int element = Feld[x][y];
8833     int graphic = el2img(element);
8834
8835     if (IS_ANIMATED(graphic))
8836       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8837
8838     return;
8839   }
8840
8841   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8842     return;
8843
8844   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8845
8846   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8847 }
8848
8849 void CheckExitSteelEM(int x, int y)
8850 {
8851   if (local_player->gems_still_needed > 0 ||
8852       local_player->sokobanfields_still_needed > 0 ||
8853       local_player->lights_still_needed > 0)
8854   {
8855     int element = Feld[x][y];
8856     int graphic = el2img(element);
8857
8858     if (IS_ANIMATED(graphic))
8859       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8860
8861     return;
8862   }
8863
8864   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8865     return;
8866
8867   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8868
8869   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8870 }
8871
8872 void CheckExitSP(int x, int y)
8873 {
8874   if (local_player->gems_still_needed > 0)
8875   {
8876     int element = Feld[x][y];
8877     int graphic = el2img(element);
8878
8879     if (IS_ANIMATED(graphic))
8880       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8881
8882     return;
8883   }
8884
8885   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8886     return;
8887
8888   Feld[x][y] = EL_SP_EXIT_OPENING;
8889
8890   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8891 }
8892
8893 static void CloseAllOpenTimegates()
8894 {
8895   int x, y;
8896
8897   SCAN_PLAYFIELD(x, y)
8898   {
8899     int element = Feld[x][y];
8900
8901     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8902     {
8903       Feld[x][y] = EL_TIMEGATE_CLOSING;
8904
8905       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8906     }
8907   }
8908 }
8909
8910 void DrawTwinkleOnField(int x, int y)
8911 {
8912   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8913     return;
8914
8915   if (Feld[x][y] == EL_BD_DIAMOND)
8916     return;
8917
8918   if (MovDelay[x][y] == 0)      /* next animation frame */
8919     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8920
8921   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8922   {
8923     MovDelay[x][y]--;
8924
8925     DrawLevelElementAnimation(x, y, Feld[x][y]);
8926
8927     if (MovDelay[x][y] != 0)
8928     {
8929       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8930                                            10 - MovDelay[x][y]);
8931
8932       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8933     }
8934   }
8935 }
8936
8937 void MauerWaechst(int x, int y)
8938 {
8939   int delay = 6;
8940
8941   if (!MovDelay[x][y])          /* next animation frame */
8942     MovDelay[x][y] = 3 * delay;
8943
8944   if (MovDelay[x][y])           /* wait some time before next frame */
8945   {
8946     MovDelay[x][y]--;
8947
8948     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8949     {
8950       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8951       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8952
8953       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8954     }
8955
8956     if (!MovDelay[x][y])
8957     {
8958       if (MovDir[x][y] == MV_LEFT)
8959       {
8960         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8961           TEST_DrawLevelField(x - 1, y);
8962       }
8963       else if (MovDir[x][y] == MV_RIGHT)
8964       {
8965         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8966           TEST_DrawLevelField(x + 1, y);
8967       }
8968       else if (MovDir[x][y] == MV_UP)
8969       {
8970         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8971           TEST_DrawLevelField(x, y - 1);
8972       }
8973       else
8974       {
8975         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8976           TEST_DrawLevelField(x, y + 1);
8977       }
8978
8979       Feld[x][y] = Store[x][y];
8980       Store[x][y] = 0;
8981       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8982       TEST_DrawLevelField(x, y);
8983     }
8984   }
8985 }
8986
8987 void MauerAbleger(int ax, int ay)
8988 {
8989   int element = Feld[ax][ay];
8990   int graphic = el2img(element);
8991   boolean oben_frei = FALSE, unten_frei = FALSE;
8992   boolean links_frei = FALSE, rechts_frei = FALSE;
8993   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8994   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8995   boolean new_wall = FALSE;
8996
8997   if (IS_ANIMATED(graphic))
8998     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8999
9000   if (!MovDelay[ax][ay])        /* start building new wall */
9001     MovDelay[ax][ay] = 6;
9002
9003   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9004   {
9005     MovDelay[ax][ay]--;
9006     if (MovDelay[ax][ay])
9007       return;
9008   }
9009
9010   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9011     oben_frei = TRUE;
9012   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9013     unten_frei = TRUE;
9014   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9015     links_frei = TRUE;
9016   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9017     rechts_frei = TRUE;
9018
9019   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9020       element == EL_EXPANDABLE_WALL_ANY)
9021   {
9022     if (oben_frei)
9023     {
9024       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9025       Store[ax][ay-1] = element;
9026       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9027       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9028         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9029                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9030       new_wall = TRUE;
9031     }
9032     if (unten_frei)
9033     {
9034       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9035       Store[ax][ay+1] = element;
9036       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9037       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9038         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9039                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9040       new_wall = TRUE;
9041     }
9042   }
9043
9044   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9045       element == EL_EXPANDABLE_WALL_ANY ||
9046       element == EL_EXPANDABLE_WALL ||
9047       element == EL_BD_EXPANDABLE_WALL)
9048   {
9049     if (links_frei)
9050     {
9051       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9052       Store[ax-1][ay] = element;
9053       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9054       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9055         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9056                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9057       new_wall = TRUE;
9058     }
9059
9060     if (rechts_frei)
9061     {
9062       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9063       Store[ax+1][ay] = element;
9064       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9065       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9066         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9067                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9068       new_wall = TRUE;
9069     }
9070   }
9071
9072   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9073     TEST_DrawLevelField(ax, ay);
9074
9075   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9076     oben_massiv = TRUE;
9077   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9078     unten_massiv = TRUE;
9079   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9080     links_massiv = TRUE;
9081   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9082     rechts_massiv = TRUE;
9083
9084   if (((oben_massiv && unten_massiv) ||
9085        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9086        element == EL_EXPANDABLE_WALL) &&
9087       ((links_massiv && rechts_massiv) ||
9088        element == EL_EXPANDABLE_WALL_VERTICAL))
9089     Feld[ax][ay] = EL_WALL;
9090
9091   if (new_wall)
9092     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9093 }
9094
9095 void MauerAblegerStahl(int ax, int ay)
9096 {
9097   int element = Feld[ax][ay];
9098   int graphic = el2img(element);
9099   boolean oben_frei = FALSE, unten_frei = FALSE;
9100   boolean links_frei = FALSE, rechts_frei = FALSE;
9101   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9102   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9103   boolean new_wall = FALSE;
9104
9105   if (IS_ANIMATED(graphic))
9106     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9107
9108   if (!MovDelay[ax][ay])        /* start building new wall */
9109     MovDelay[ax][ay] = 6;
9110
9111   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9112   {
9113     MovDelay[ax][ay]--;
9114     if (MovDelay[ax][ay])
9115       return;
9116   }
9117
9118   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9119     oben_frei = TRUE;
9120   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9121     unten_frei = TRUE;
9122   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9123     links_frei = TRUE;
9124   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9125     rechts_frei = TRUE;
9126
9127   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9128       element == EL_EXPANDABLE_STEELWALL_ANY)
9129   {
9130     if (oben_frei)
9131     {
9132       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9133       Store[ax][ay-1] = element;
9134       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9135       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9136         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9137                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9138       new_wall = TRUE;
9139     }
9140     if (unten_frei)
9141     {
9142       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9143       Store[ax][ay+1] = element;
9144       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9145       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9146         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9147                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9148       new_wall = TRUE;
9149     }
9150   }
9151
9152   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9153       element == EL_EXPANDABLE_STEELWALL_ANY)
9154   {
9155     if (links_frei)
9156     {
9157       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9158       Store[ax-1][ay] = element;
9159       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9160       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9161         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9162                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9163       new_wall = TRUE;
9164     }
9165
9166     if (rechts_frei)
9167     {
9168       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9169       Store[ax+1][ay] = element;
9170       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9171       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9172         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9173                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9174       new_wall = TRUE;
9175     }
9176   }
9177
9178   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9179     oben_massiv = TRUE;
9180   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9181     unten_massiv = TRUE;
9182   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9183     links_massiv = TRUE;
9184   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9185     rechts_massiv = TRUE;
9186
9187   if (((oben_massiv && unten_massiv) ||
9188        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9189       ((links_massiv && rechts_massiv) ||
9190        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9191     Feld[ax][ay] = EL_STEELWALL;
9192
9193   if (new_wall)
9194     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9195 }
9196
9197 void CheckForDragon(int x, int y)
9198 {
9199   int i, j;
9200   boolean dragon_found = FALSE;
9201   static int xy[4][2] =
9202   {
9203     { 0, -1 },
9204     { -1, 0 },
9205     { +1, 0 },
9206     { 0, +1 }
9207   };
9208
9209   for (i = 0; i < NUM_DIRECTIONS; i++)
9210   {
9211     for (j = 0; j < 4; j++)
9212     {
9213       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9214
9215       if (IN_LEV_FIELD(xx, yy) &&
9216           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9217       {
9218         if (Feld[xx][yy] == EL_DRAGON)
9219           dragon_found = TRUE;
9220       }
9221       else
9222         break;
9223     }
9224   }
9225
9226   if (!dragon_found)
9227   {
9228     for (i = 0; i < NUM_DIRECTIONS; i++)
9229     {
9230       for (j = 0; j < 3; j++)
9231       {
9232         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9233   
9234         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9235         {
9236           Feld[xx][yy] = EL_EMPTY;
9237           TEST_DrawLevelField(xx, yy);
9238         }
9239         else
9240           break;
9241       }
9242     }
9243   }
9244 }
9245
9246 static void InitBuggyBase(int x, int y)
9247 {
9248   int element = Feld[x][y];
9249   int activating_delay = FRAMES_PER_SECOND / 4;
9250
9251   ChangeDelay[x][y] =
9252     (element == EL_SP_BUGGY_BASE ?
9253      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9254      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9255      activating_delay :
9256      element == EL_SP_BUGGY_BASE_ACTIVE ?
9257      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9258 }
9259
9260 static void WarnBuggyBase(int x, int y)
9261 {
9262   int i;
9263   static int xy[4][2] =
9264   {
9265     { 0, -1 },
9266     { -1, 0 },
9267     { +1, 0 },
9268     { 0, +1 }
9269   };
9270
9271   for (i = 0; i < NUM_DIRECTIONS; i++)
9272   {
9273     int xx = x + xy[i][0];
9274     int yy = y + xy[i][1];
9275
9276     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9277     {
9278       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9279
9280       break;
9281     }
9282   }
9283 }
9284
9285 static void InitTrap(int x, int y)
9286 {
9287   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9288 }
9289
9290 static void ActivateTrap(int x, int y)
9291 {
9292   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9293 }
9294
9295 static void ChangeActiveTrap(int x, int y)
9296 {
9297   int graphic = IMG_TRAP_ACTIVE;
9298
9299   /* if new animation frame was drawn, correct crumbled sand border */
9300   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9301     TEST_DrawLevelFieldCrumbled(x, y);
9302 }
9303
9304 static int getSpecialActionElement(int element, int number, int base_element)
9305 {
9306   return (element != EL_EMPTY ? element :
9307           number != -1 ? base_element + number - 1 :
9308           EL_EMPTY);
9309 }
9310
9311 static int getModifiedActionNumber(int value_old, int operator, int operand,
9312                                    int value_min, int value_max)
9313 {
9314   int value_new = (operator == CA_MODE_SET      ? operand :
9315                    operator == CA_MODE_ADD      ? value_old + operand :
9316                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9317                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9318                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9319                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9320                    value_old);
9321
9322   return (value_new < value_min ? value_min :
9323           value_new > value_max ? value_max :
9324           value_new);
9325 }
9326
9327 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9328 {
9329   struct ElementInfo *ei = &element_info[element];
9330   struct ElementChangeInfo *change = &ei->change_page[page];
9331   int target_element = change->target_element;
9332   int action_type = change->action_type;
9333   int action_mode = change->action_mode;
9334   int action_arg = change->action_arg;
9335   int action_element = change->action_element;
9336   int i;
9337
9338   if (!change->has_action)
9339     return;
9340
9341   /* ---------- determine action paramater values -------------------------- */
9342
9343   int level_time_value =
9344     (level.time > 0 ? TimeLeft :
9345      TimePlayed);
9346
9347   int action_arg_element_raw =
9348     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9349      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9350      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9351      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9352      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9353      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9354      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9355      EL_EMPTY);
9356   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9357
9358   int action_arg_direction =
9359     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9360      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9361      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9362      change->actual_trigger_side :
9363      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9364      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9365      MV_NONE);
9366
9367   int action_arg_number_min =
9368     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9369      CA_ARG_MIN);
9370
9371   int action_arg_number_max =
9372     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9373      action_type == CA_SET_LEVEL_GEMS ? 999 :
9374      action_type == CA_SET_LEVEL_TIME ? 9999 :
9375      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9376      action_type == CA_SET_CE_VALUE ? 9999 :
9377      action_type == CA_SET_CE_SCORE ? 9999 :
9378      CA_ARG_MAX);
9379
9380   int action_arg_number_reset =
9381     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9382      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9383      action_type == CA_SET_LEVEL_TIME ? level.time :
9384      action_type == CA_SET_LEVEL_SCORE ? 0 :
9385      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9386      action_type == CA_SET_CE_SCORE ? 0 :
9387      0);
9388
9389   int action_arg_number =
9390     (action_arg <= CA_ARG_MAX ? action_arg :
9391      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9392      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9393      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9394      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9395      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9396      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9397      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9398      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9399      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9400      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9401      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9402      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9403      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9404      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9405      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9406      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9407      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9408      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9409      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9410      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9411      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9412      -1);
9413
9414   int action_arg_number_old =
9415     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9416      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9417      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9418      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9419      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9420      0);
9421
9422   int action_arg_number_new =
9423     getModifiedActionNumber(action_arg_number_old,
9424                             action_mode, action_arg_number,
9425                             action_arg_number_min, action_arg_number_max);
9426
9427   int trigger_player_bits =
9428     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9429      change->actual_trigger_player_bits : change->trigger_player);
9430
9431   int action_arg_player_bits =
9432     (action_arg >= CA_ARG_PLAYER_1 &&
9433      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9434      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9435      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9436      PLAYER_BITS_ANY);
9437
9438   /* ---------- execute action  -------------------------------------------- */
9439
9440   switch (action_type)
9441   {
9442     case CA_NO_ACTION:
9443     {
9444       return;
9445     }
9446
9447     /* ---------- level actions  ------------------------------------------- */
9448
9449     case CA_RESTART_LEVEL:
9450     {
9451       game.restart_level = TRUE;
9452
9453       break;
9454     }
9455
9456     case CA_SHOW_ENVELOPE:
9457     {
9458       int element = getSpecialActionElement(action_arg_element,
9459                                             action_arg_number, EL_ENVELOPE_1);
9460
9461       if (IS_ENVELOPE(element))
9462         local_player->show_envelope = element;
9463
9464       break;
9465     }
9466
9467     case CA_SET_LEVEL_TIME:
9468     {
9469       if (level.time > 0)       /* only modify limited time value */
9470       {
9471         TimeLeft = action_arg_number_new;
9472
9473         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9474
9475         DisplayGameControlValues();
9476
9477         if (!TimeLeft && setup.time_limit)
9478           for (i = 0; i < MAX_PLAYERS; i++)
9479             KillPlayer(&stored_player[i]);
9480       }
9481
9482       break;
9483     }
9484
9485     case CA_SET_LEVEL_SCORE:
9486     {
9487       local_player->score = action_arg_number_new;
9488
9489       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9490
9491       DisplayGameControlValues();
9492
9493       break;
9494     }
9495
9496     case CA_SET_LEVEL_GEMS:
9497     {
9498       local_player->gems_still_needed = action_arg_number_new;
9499
9500       game.snapshot.collected_item = TRUE;
9501
9502       game_panel_controls[GAME_PANEL_GEMS].value =
9503         local_player->gems_still_needed;
9504
9505       DisplayGameControlValues();
9506
9507       break;
9508     }
9509
9510     case CA_SET_LEVEL_WIND:
9511     {
9512       game.wind_direction = action_arg_direction;
9513
9514       break;
9515     }
9516
9517     case CA_SET_LEVEL_RANDOM_SEED:
9518     {
9519       /* ensure that setting a new random seed while playing is predictable */
9520       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9521
9522       break;
9523     }
9524
9525     /* ---------- player actions  ------------------------------------------ */
9526
9527     case CA_MOVE_PLAYER:
9528     {
9529       /* automatically move to the next field in specified direction */
9530       for (i = 0; i < MAX_PLAYERS; i++)
9531         if (trigger_player_bits & (1 << i))
9532           stored_player[i].programmed_action = action_arg_direction;
9533
9534       break;
9535     }
9536
9537     case CA_EXIT_PLAYER:
9538     {
9539       for (i = 0; i < MAX_PLAYERS; i++)
9540         if (action_arg_player_bits & (1 << i))
9541           PlayerWins(&stored_player[i]);
9542
9543       break;
9544     }
9545
9546     case CA_KILL_PLAYER:
9547     {
9548       for (i = 0; i < MAX_PLAYERS; i++)
9549         if (action_arg_player_bits & (1 << i))
9550           KillPlayer(&stored_player[i]);
9551
9552       break;
9553     }
9554
9555     case CA_SET_PLAYER_KEYS:
9556     {
9557       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9558       int element = getSpecialActionElement(action_arg_element,
9559                                             action_arg_number, EL_KEY_1);
9560
9561       if (IS_KEY(element))
9562       {
9563         for (i = 0; i < MAX_PLAYERS; i++)
9564         {
9565           if (trigger_player_bits & (1 << i))
9566           {
9567             stored_player[i].key[KEY_NR(element)] = key_state;
9568
9569             DrawGameDoorValues();
9570           }
9571         }
9572       }
9573
9574       break;
9575     }
9576
9577     case CA_SET_PLAYER_SPEED:
9578     {
9579       for (i = 0; i < MAX_PLAYERS; i++)
9580       {
9581         if (trigger_player_bits & (1 << i))
9582         {
9583           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9584
9585           if (action_arg == CA_ARG_SPEED_FASTER &&
9586               stored_player[i].cannot_move)
9587           {
9588             action_arg_number = STEPSIZE_VERY_SLOW;
9589           }
9590           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9591                    action_arg == CA_ARG_SPEED_FASTER)
9592           {
9593             action_arg_number = 2;
9594             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9595                            CA_MODE_MULTIPLY);
9596           }
9597           else if (action_arg == CA_ARG_NUMBER_RESET)
9598           {
9599             action_arg_number = level.initial_player_stepsize[i];
9600           }
9601
9602           move_stepsize =
9603             getModifiedActionNumber(move_stepsize,
9604                                     action_mode,
9605                                     action_arg_number,
9606                                     action_arg_number_min,
9607                                     action_arg_number_max);
9608
9609           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9610         }
9611       }
9612
9613       break;
9614     }
9615
9616     case CA_SET_PLAYER_SHIELD:
9617     {
9618       for (i = 0; i < MAX_PLAYERS; i++)
9619       {
9620         if (trigger_player_bits & (1 << i))
9621         {
9622           if (action_arg == CA_ARG_SHIELD_OFF)
9623           {
9624             stored_player[i].shield_normal_time_left = 0;
9625             stored_player[i].shield_deadly_time_left = 0;
9626           }
9627           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9628           {
9629             stored_player[i].shield_normal_time_left = 999999;
9630           }
9631           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9632           {
9633             stored_player[i].shield_normal_time_left = 999999;
9634             stored_player[i].shield_deadly_time_left = 999999;
9635           }
9636         }
9637       }
9638
9639       break;
9640     }
9641
9642     case CA_SET_PLAYER_GRAVITY:
9643     {
9644       for (i = 0; i < MAX_PLAYERS; i++)
9645       {
9646         if (trigger_player_bits & (1 << i))
9647         {
9648           stored_player[i].gravity =
9649             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9650              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9651              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9652              stored_player[i].gravity);
9653         }
9654       }
9655
9656       break;
9657     }
9658
9659     case CA_SET_PLAYER_ARTWORK:
9660     {
9661       for (i = 0; i < MAX_PLAYERS; i++)
9662       {
9663         if (trigger_player_bits & (1 << i))
9664         {
9665           int artwork_element = action_arg_element;
9666
9667           if (action_arg == CA_ARG_ELEMENT_RESET)
9668             artwork_element =
9669               (level.use_artwork_element[i] ? level.artwork_element[i] :
9670                stored_player[i].element_nr);
9671
9672           if (stored_player[i].artwork_element != artwork_element)
9673             stored_player[i].Frame = 0;
9674
9675           stored_player[i].artwork_element = artwork_element;
9676
9677           SetPlayerWaiting(&stored_player[i], FALSE);
9678
9679           /* set number of special actions for bored and sleeping animation */
9680           stored_player[i].num_special_action_bored =
9681             get_num_special_action(artwork_element,
9682                                    ACTION_BORING_1, ACTION_BORING_LAST);
9683           stored_player[i].num_special_action_sleeping =
9684             get_num_special_action(artwork_element,
9685                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9686         }
9687       }
9688
9689       break;
9690     }
9691
9692     case CA_SET_PLAYER_INVENTORY:
9693     {
9694       for (i = 0; i < MAX_PLAYERS; i++)
9695       {
9696         struct PlayerInfo *player = &stored_player[i];
9697         int j, k;
9698
9699         if (trigger_player_bits & (1 << i))
9700         {
9701           int inventory_element = action_arg_element;
9702
9703           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9704               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9705               action_arg == CA_ARG_ELEMENT_ACTION)
9706           {
9707             int element = inventory_element;
9708             int collect_count = element_info[element].collect_count_initial;
9709
9710             if (!IS_CUSTOM_ELEMENT(element))
9711               collect_count = 1;
9712
9713             if (collect_count == 0)
9714               player->inventory_infinite_element = element;
9715             else
9716               for (k = 0; k < collect_count; k++)
9717                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9718                   player->inventory_element[player->inventory_size++] =
9719                     element;
9720           }
9721           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9722                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9723                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9724           {
9725             if (player->inventory_infinite_element != EL_UNDEFINED &&
9726                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9727                                      action_arg_element_raw))
9728               player->inventory_infinite_element = EL_UNDEFINED;
9729
9730             for (k = 0, j = 0; j < player->inventory_size; j++)
9731             {
9732               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9733                                         action_arg_element_raw))
9734                 player->inventory_element[k++] = player->inventory_element[j];
9735             }
9736
9737             player->inventory_size = k;
9738           }
9739           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9740           {
9741             if (player->inventory_size > 0)
9742             {
9743               for (j = 0; j < player->inventory_size - 1; j++)
9744                 player->inventory_element[j] = player->inventory_element[j + 1];
9745
9746               player->inventory_size--;
9747             }
9748           }
9749           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9750           {
9751             if (player->inventory_size > 0)
9752               player->inventory_size--;
9753           }
9754           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9755           {
9756             player->inventory_infinite_element = EL_UNDEFINED;
9757             player->inventory_size = 0;
9758           }
9759           else if (action_arg == CA_ARG_INVENTORY_RESET)
9760           {
9761             player->inventory_infinite_element = EL_UNDEFINED;
9762             player->inventory_size = 0;
9763
9764             if (level.use_initial_inventory[i])
9765             {
9766               for (j = 0; j < level.initial_inventory_size[i]; j++)
9767               {
9768                 int element = level.initial_inventory_content[i][j];
9769                 int collect_count = element_info[element].collect_count_initial;
9770
9771                 if (!IS_CUSTOM_ELEMENT(element))
9772                   collect_count = 1;
9773
9774                 if (collect_count == 0)
9775                   player->inventory_infinite_element = element;
9776                 else
9777                   for (k = 0; k < collect_count; k++)
9778                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9779                       player->inventory_element[player->inventory_size++] =
9780                         element;
9781               }
9782             }
9783           }
9784         }
9785       }
9786
9787       break;
9788     }
9789
9790     /* ---------- CE actions  ---------------------------------------------- */
9791
9792     case CA_SET_CE_VALUE:
9793     {
9794       int last_ce_value = CustomValue[x][y];
9795
9796       CustomValue[x][y] = action_arg_number_new;
9797
9798       if (CustomValue[x][y] != last_ce_value)
9799       {
9800         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9801         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9802
9803         if (CustomValue[x][y] == 0)
9804         {
9805           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9806           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9807         }
9808       }
9809
9810       break;
9811     }
9812
9813     case CA_SET_CE_SCORE:
9814     {
9815       int last_ce_score = ei->collect_score;
9816
9817       ei->collect_score = action_arg_number_new;
9818
9819       if (ei->collect_score != last_ce_score)
9820       {
9821         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9822         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9823
9824         if (ei->collect_score == 0)
9825         {
9826           int xx, yy;
9827
9828           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9829           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9830
9831           /*
9832             This is a very special case that seems to be a mixture between
9833             CheckElementChange() and CheckTriggeredElementChange(): while
9834             the first one only affects single elements that are triggered
9835             directly, the second one affects multiple elements in the playfield
9836             that are triggered indirectly by another element. This is a third
9837             case: Changing the CE score always affects multiple identical CEs,
9838             so every affected CE must be checked, not only the single CE for
9839             which the CE score was changed in the first place (as every instance
9840             of that CE shares the same CE score, and therefore also can change)!
9841           */
9842           SCAN_PLAYFIELD(xx, yy)
9843           {
9844             if (Feld[xx][yy] == element)
9845               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9846                                  CE_SCORE_GETS_ZERO);
9847           }
9848         }
9849       }
9850
9851       break;
9852     }
9853
9854     case CA_SET_CE_ARTWORK:
9855     {
9856       int artwork_element = action_arg_element;
9857       boolean reset_frame = FALSE;
9858       int xx, yy;
9859
9860       if (action_arg == CA_ARG_ELEMENT_RESET)
9861         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9862                            element);
9863
9864       if (ei->gfx_element != artwork_element)
9865         reset_frame = TRUE;
9866
9867       ei->gfx_element = artwork_element;
9868
9869       SCAN_PLAYFIELD(xx, yy)
9870       {
9871         if (Feld[xx][yy] == element)
9872         {
9873           if (reset_frame)
9874           {
9875             ResetGfxAnimation(xx, yy);
9876             ResetRandomAnimationValue(xx, yy);
9877           }
9878
9879           TEST_DrawLevelField(xx, yy);
9880         }
9881       }
9882
9883       break;
9884     }
9885
9886     /* ---------- engine actions  ------------------------------------------ */
9887
9888     case CA_SET_ENGINE_SCAN_MODE:
9889     {
9890       InitPlayfieldScanMode(action_arg);
9891
9892       break;
9893     }
9894
9895     default:
9896       break;
9897   }
9898 }
9899
9900 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9901 {
9902   int old_element = Feld[x][y];
9903   int new_element = GetElementFromGroupElement(element);
9904   int previous_move_direction = MovDir[x][y];
9905   int last_ce_value = CustomValue[x][y];
9906   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9907   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9908   boolean add_player_onto_element = (new_element_is_player &&
9909                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9910                                      IS_WALKABLE(old_element));
9911
9912   if (!add_player_onto_element)
9913   {
9914     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9915       RemoveMovingField(x, y);
9916     else
9917       RemoveField(x, y);
9918
9919     Feld[x][y] = new_element;
9920
9921     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9922       MovDir[x][y] = previous_move_direction;
9923
9924     if (element_info[new_element].use_last_ce_value)
9925       CustomValue[x][y] = last_ce_value;
9926
9927     InitField_WithBug1(x, y, FALSE);
9928
9929     new_element = Feld[x][y];   /* element may have changed */
9930
9931     ResetGfxAnimation(x, y);
9932     ResetRandomAnimationValue(x, y);
9933
9934     TEST_DrawLevelField(x, y);
9935
9936     if (GFX_CRUMBLED(new_element))
9937       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9938   }
9939
9940   /* check if element under the player changes from accessible to unaccessible
9941      (needed for special case of dropping element which then changes) */
9942   /* (must be checked after creating new element for walkable group elements) */
9943   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9944       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9945   {
9946     Bang(x, y);
9947
9948     return;
9949   }
9950
9951   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9952   if (new_element_is_player)
9953     RelocatePlayer(x, y, new_element);
9954
9955   if (is_change)
9956     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9957
9958   TestIfBadThingTouchesPlayer(x, y);
9959   TestIfPlayerTouchesCustomElement(x, y);
9960   TestIfElementTouchesCustomElement(x, y);
9961 }
9962
9963 static void CreateField(int x, int y, int element)
9964 {
9965   CreateFieldExt(x, y, element, FALSE);
9966 }
9967
9968 static void CreateElementFromChange(int x, int y, int element)
9969 {
9970   element = GET_VALID_RUNTIME_ELEMENT(element);
9971
9972   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9973   {
9974     int old_element = Feld[x][y];
9975
9976     /* prevent changed element from moving in same engine frame
9977        unless both old and new element can either fall or move */
9978     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9979         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9980       Stop[x][y] = TRUE;
9981   }
9982
9983   CreateFieldExt(x, y, element, TRUE);
9984 }
9985
9986 static boolean ChangeElement(int x, int y, int element, int page)
9987 {
9988   struct ElementInfo *ei = &element_info[element];
9989   struct ElementChangeInfo *change = &ei->change_page[page];
9990   int ce_value = CustomValue[x][y];
9991   int ce_score = ei->collect_score;
9992   int target_element;
9993   int old_element = Feld[x][y];
9994
9995   /* always use default change event to prevent running into a loop */
9996   if (ChangeEvent[x][y] == -1)
9997     ChangeEvent[x][y] = CE_DELAY;
9998
9999   if (ChangeEvent[x][y] == CE_DELAY)
10000   {
10001     /* reset actual trigger element, trigger player and action element */
10002     change->actual_trigger_element = EL_EMPTY;
10003     change->actual_trigger_player = EL_EMPTY;
10004     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10005     change->actual_trigger_side = CH_SIDE_NONE;
10006     change->actual_trigger_ce_value = 0;
10007     change->actual_trigger_ce_score = 0;
10008   }
10009
10010   /* do not change elements more than a specified maximum number of changes */
10011   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10012     return FALSE;
10013
10014   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10015
10016   if (change->explode)
10017   {
10018     Bang(x, y);
10019
10020     return TRUE;
10021   }
10022
10023   if (change->use_target_content)
10024   {
10025     boolean complete_replace = TRUE;
10026     boolean can_replace[3][3];
10027     int xx, yy;
10028
10029     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10030     {
10031       boolean is_empty;
10032       boolean is_walkable;
10033       boolean is_diggable;
10034       boolean is_collectible;
10035       boolean is_removable;
10036       boolean is_destructible;
10037       int ex = x + xx - 1;
10038       int ey = y + yy - 1;
10039       int content_element = change->target_content.e[xx][yy];
10040       int e;
10041
10042       can_replace[xx][yy] = TRUE;
10043
10044       if (ex == x && ey == y)   /* do not check changing element itself */
10045         continue;
10046
10047       if (content_element == EL_EMPTY_SPACE)
10048       {
10049         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10050
10051         continue;
10052       }
10053
10054       if (!IN_LEV_FIELD(ex, ey))
10055       {
10056         can_replace[xx][yy] = FALSE;
10057         complete_replace = FALSE;
10058
10059         continue;
10060       }
10061
10062       e = Feld[ex][ey];
10063
10064       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10065         e = MovingOrBlocked2Element(ex, ey);
10066
10067       is_empty = (IS_FREE(ex, ey) ||
10068                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10069
10070       is_walkable     = (is_empty || IS_WALKABLE(e));
10071       is_diggable     = (is_empty || IS_DIGGABLE(e));
10072       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10073       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10074       is_removable    = (is_diggable || is_collectible);
10075
10076       can_replace[xx][yy] =
10077         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10078           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10079           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10080           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10081           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10082           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10083          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10084
10085       if (!can_replace[xx][yy])
10086         complete_replace = FALSE;
10087     }
10088
10089     if (!change->only_if_complete || complete_replace)
10090     {
10091       boolean something_has_changed = FALSE;
10092
10093       if (change->only_if_complete && change->use_random_replace &&
10094           RND(100) < change->random_percentage)
10095         return FALSE;
10096
10097       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10098       {
10099         int ex = x + xx - 1;
10100         int ey = y + yy - 1;
10101         int content_element;
10102
10103         if (can_replace[xx][yy] && (!change->use_random_replace ||
10104                                     RND(100) < change->random_percentage))
10105         {
10106           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10107             RemoveMovingField(ex, ey);
10108
10109           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10110
10111           content_element = change->target_content.e[xx][yy];
10112           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10113                                               ce_value, ce_score);
10114
10115           CreateElementFromChange(ex, ey, target_element);
10116
10117           something_has_changed = TRUE;
10118
10119           /* for symmetry reasons, freeze newly created border elements */
10120           if (ex != x || ey != y)
10121             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10122         }
10123       }
10124
10125       if (something_has_changed)
10126       {
10127         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10128         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10129       }
10130     }
10131   }
10132   else
10133   {
10134     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10135                                         ce_value, ce_score);
10136
10137     if (element == EL_DIAGONAL_GROWING ||
10138         element == EL_DIAGONAL_SHRINKING)
10139     {
10140       target_element = Store[x][y];
10141
10142       Store[x][y] = EL_EMPTY;
10143     }
10144
10145     CreateElementFromChange(x, y, target_element);
10146
10147     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10148     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10149   }
10150
10151   /* this uses direct change before indirect change */
10152   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10153
10154   return TRUE;
10155 }
10156
10157 static void HandleElementChange(int x, int y, int page)
10158 {
10159   int element = MovingOrBlocked2Element(x, y);
10160   struct ElementInfo *ei = &element_info[element];
10161   struct ElementChangeInfo *change = &ei->change_page[page];
10162   boolean handle_action_before_change = FALSE;
10163
10164 #ifdef DEBUG
10165   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10166       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10167   {
10168     printf("\n\n");
10169     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10170            x, y, element, element_info[element].token_name);
10171     printf("HandleElementChange(): This should never happen!\n");
10172     printf("\n\n");
10173   }
10174 #endif
10175
10176   /* this can happen with classic bombs on walkable, changing elements */
10177   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10178   {
10179     return;
10180   }
10181
10182   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10183   {
10184     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10185
10186     if (change->can_change)
10187     {
10188       /* !!! not clear why graphic animation should be reset at all here !!! */
10189       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10190       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10191
10192       /*
10193         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10194
10195         When using an animation frame delay of 1 (this only happens with
10196         "sp_zonk.moving.left/right" in the classic graphics), the default
10197         (non-moving) animation shows wrong animation frames (while the
10198         moving animation, like "sp_zonk.moving.left/right", is correct,
10199         so this graphical bug never shows up with the classic graphics).
10200         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10201         be drawn instead of the correct frames 0,1,2,3. This is caused by
10202         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10203         an element change: First when the change delay ("ChangeDelay[][]")
10204         counter has reached zero after decrementing, then a second time in
10205         the next frame (after "GfxFrame[][]" was already incremented) when
10206         "ChangeDelay[][]" is reset to the initial delay value again.
10207
10208         This causes frame 0 to be drawn twice, while the last frame won't
10209         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10210
10211         As some animations may already be cleverly designed around this bug
10212         (at least the "Snake Bite" snake tail animation does this), it cannot
10213         simply be fixed here without breaking such existing animations.
10214         Unfortunately, it cannot easily be detected if a graphics set was
10215         designed "before" or "after" the bug was fixed. As a workaround,
10216         a new graphics set option "game.graphics_engine_version" was added
10217         to be able to specify the game's major release version for which the
10218         graphics set was designed, which can then be used to decide if the
10219         bugfix should be used (version 4 and above) or not (version 3 or
10220         below, or if no version was specified at all, as with old sets).
10221
10222         (The wrong/fixed animation frames can be tested with the test level set
10223         "test_gfxframe" and level "000", which contains a specially prepared
10224         custom element at level position (x/y) == (11/9) which uses the zonk
10225         animation mentioned above. Using "game.graphics_engine_version: 4"
10226         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10227         This can also be seen from the debug output for this test element.)
10228       */
10229
10230       /* when a custom element is about to change (for example by change delay),
10231          do not reset graphic animation when the custom element is moving */
10232       if (game.graphics_engine_version < 4 &&
10233           !IS_MOVING(x, y))
10234       {
10235         ResetGfxAnimation(x, y);
10236         ResetRandomAnimationValue(x, y);
10237       }
10238
10239       if (change->pre_change_function)
10240         change->pre_change_function(x, y);
10241     }
10242   }
10243
10244   ChangeDelay[x][y]--;
10245
10246   if (ChangeDelay[x][y] != 0)           /* continue element change */
10247   {
10248     if (change->can_change)
10249     {
10250       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10251
10252       if (IS_ANIMATED(graphic))
10253         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10254
10255       if (change->change_function)
10256         change->change_function(x, y);
10257     }
10258   }
10259   else                                  /* finish element change */
10260   {
10261     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10262     {
10263       page = ChangePage[x][y];
10264       ChangePage[x][y] = -1;
10265
10266       change = &ei->change_page[page];
10267     }
10268
10269     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10270     {
10271       ChangeDelay[x][y] = 1;            /* try change after next move step */
10272       ChangePage[x][y] = page;          /* remember page to use for change */
10273
10274       return;
10275     }
10276
10277     /* special case: set new level random seed before changing element */
10278     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10279       handle_action_before_change = TRUE;
10280
10281     if (change->has_action && handle_action_before_change)
10282       ExecuteCustomElementAction(x, y, element, page);
10283
10284     if (change->can_change)
10285     {
10286       if (ChangeElement(x, y, element, page))
10287       {
10288         if (change->post_change_function)
10289           change->post_change_function(x, y);
10290       }
10291     }
10292
10293     if (change->has_action && !handle_action_before_change)
10294       ExecuteCustomElementAction(x, y, element, page);
10295   }
10296 }
10297
10298 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10299                                               int trigger_element,
10300                                               int trigger_event,
10301                                               int trigger_player,
10302                                               int trigger_side,
10303                                               int trigger_page)
10304 {
10305   boolean change_done_any = FALSE;
10306   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10307   int i;
10308
10309   if (!(trigger_events[trigger_element][trigger_event]))
10310     return FALSE;
10311
10312   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10313
10314   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10315   {
10316     int element = EL_CUSTOM_START + i;
10317     boolean change_done = FALSE;
10318     int p;
10319
10320     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10321         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10322       continue;
10323
10324     for (p = 0; p < element_info[element].num_change_pages; p++)
10325     {
10326       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10327
10328       if (change->can_change_or_has_action &&
10329           change->has_event[trigger_event] &&
10330           change->trigger_side & trigger_side &&
10331           change->trigger_player & trigger_player &&
10332           change->trigger_page & trigger_page_bits &&
10333           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10334       {
10335         change->actual_trigger_element = trigger_element;
10336         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10337         change->actual_trigger_player_bits = trigger_player;
10338         change->actual_trigger_side = trigger_side;
10339         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10340         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10341
10342         if ((change->can_change && !change_done) || change->has_action)
10343         {
10344           int x, y;
10345
10346           SCAN_PLAYFIELD(x, y)
10347           {
10348             if (Feld[x][y] == element)
10349             {
10350               if (change->can_change && !change_done)
10351               {
10352                 /* if element already changed in this frame, not only prevent
10353                    another element change (checked in ChangeElement()), but
10354                    also prevent additional element actions for this element */
10355
10356                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10357                     !level.use_action_after_change_bug)
10358                   continue;
10359
10360                 ChangeDelay[x][y] = 1;
10361                 ChangeEvent[x][y] = trigger_event;
10362
10363                 HandleElementChange(x, y, p);
10364               }
10365               else if (change->has_action)
10366               {
10367                 /* if element already changed in this frame, not only prevent
10368                    another element change (checked in ChangeElement()), but
10369                    also prevent additional element actions for this element */
10370
10371                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10372                     !level.use_action_after_change_bug)
10373                   continue;
10374
10375                 ExecuteCustomElementAction(x, y, element, p);
10376                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10377               }
10378             }
10379           }
10380
10381           if (change->can_change)
10382           {
10383             change_done = TRUE;
10384             change_done_any = TRUE;
10385           }
10386         }
10387       }
10388     }
10389   }
10390
10391   RECURSION_LOOP_DETECTION_END();
10392
10393   return change_done_any;
10394 }
10395
10396 static boolean CheckElementChangeExt(int x, int y,
10397                                      int element,
10398                                      int trigger_element,
10399                                      int trigger_event,
10400                                      int trigger_player,
10401                                      int trigger_side)
10402 {
10403   boolean change_done = FALSE;
10404   int p;
10405
10406   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10407       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10408     return FALSE;
10409
10410   if (Feld[x][y] == EL_BLOCKED)
10411   {
10412     Blocked2Moving(x, y, &x, &y);
10413     element = Feld[x][y];
10414   }
10415
10416   /* check if element has already changed or is about to change after moving */
10417   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10418        Feld[x][y] != element) ||
10419
10420       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10421        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10422         ChangePage[x][y] != -1)))
10423     return FALSE;
10424
10425   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10426
10427   for (p = 0; p < element_info[element].num_change_pages; p++)
10428   {
10429     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10430
10431     /* check trigger element for all events where the element that is checked
10432        for changing interacts with a directly adjacent element -- this is
10433        different to element changes that affect other elements to change on the
10434        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10435     boolean check_trigger_element =
10436       (trigger_event == CE_TOUCHING_X ||
10437        trigger_event == CE_HITTING_X ||
10438        trigger_event == CE_HIT_BY_X ||
10439        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10440
10441     if (change->can_change_or_has_action &&
10442         change->has_event[trigger_event] &&
10443         change->trigger_side & trigger_side &&
10444         change->trigger_player & trigger_player &&
10445         (!check_trigger_element ||
10446          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10447     {
10448       change->actual_trigger_element = trigger_element;
10449       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10450       change->actual_trigger_player_bits = trigger_player;
10451       change->actual_trigger_side = trigger_side;
10452       change->actual_trigger_ce_value = CustomValue[x][y];
10453       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10454
10455       /* special case: trigger element not at (x,y) position for some events */
10456       if (check_trigger_element)
10457       {
10458         static struct
10459         {
10460           int dx, dy;
10461         } move_xy[] =
10462           {
10463             {  0,  0 },
10464             { -1,  0 },
10465             { +1,  0 },
10466             {  0,  0 },
10467             {  0, -1 },
10468             {  0,  0 }, { 0, 0 }, { 0, 0 },
10469             {  0, +1 }
10470           };
10471
10472         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10473         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10474
10475         change->actual_trigger_ce_value = CustomValue[xx][yy];
10476         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10477       }
10478
10479       if (change->can_change && !change_done)
10480       {
10481         ChangeDelay[x][y] = 1;
10482         ChangeEvent[x][y] = trigger_event;
10483
10484         HandleElementChange(x, y, p);
10485
10486         change_done = TRUE;
10487       }
10488       else if (change->has_action)
10489       {
10490         ExecuteCustomElementAction(x, y, element, p);
10491         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10492       }
10493     }
10494   }
10495
10496   RECURSION_LOOP_DETECTION_END();
10497
10498   return change_done;
10499 }
10500
10501 static void PlayPlayerSound(struct PlayerInfo *player)
10502 {
10503   int jx = player->jx, jy = player->jy;
10504   int sound_element = player->artwork_element;
10505   int last_action = player->last_action_waiting;
10506   int action = player->action_waiting;
10507
10508   if (player->is_waiting)
10509   {
10510     if (action != last_action)
10511       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10512     else
10513       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10514   }
10515   else
10516   {
10517     if (action != last_action)
10518       StopSound(element_info[sound_element].sound[last_action]);
10519
10520     if (last_action == ACTION_SLEEPING)
10521       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10522   }
10523 }
10524
10525 static void PlayAllPlayersSound()
10526 {
10527   int i;
10528
10529   for (i = 0; i < MAX_PLAYERS; i++)
10530     if (stored_player[i].active)
10531       PlayPlayerSound(&stored_player[i]);
10532 }
10533
10534 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10535 {
10536   boolean last_waiting = player->is_waiting;
10537   int move_dir = player->MovDir;
10538
10539   player->dir_waiting = move_dir;
10540   player->last_action_waiting = player->action_waiting;
10541
10542   if (is_waiting)
10543   {
10544     if (!last_waiting)          /* not waiting -> waiting */
10545     {
10546       player->is_waiting = TRUE;
10547
10548       player->frame_counter_bored =
10549         FrameCounter +
10550         game.player_boring_delay_fixed +
10551         GetSimpleRandom(game.player_boring_delay_random);
10552       player->frame_counter_sleeping =
10553         FrameCounter +
10554         game.player_sleeping_delay_fixed +
10555         GetSimpleRandom(game.player_sleeping_delay_random);
10556
10557       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10558     }
10559
10560     if (game.player_sleeping_delay_fixed +
10561         game.player_sleeping_delay_random > 0 &&
10562         player->anim_delay_counter == 0 &&
10563         player->post_delay_counter == 0 &&
10564         FrameCounter >= player->frame_counter_sleeping)
10565       player->is_sleeping = TRUE;
10566     else if (game.player_boring_delay_fixed +
10567              game.player_boring_delay_random > 0 &&
10568              FrameCounter >= player->frame_counter_bored)
10569       player->is_bored = TRUE;
10570
10571     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10572                               player->is_bored ? ACTION_BORING :
10573                               ACTION_WAITING);
10574
10575     if (player->is_sleeping && player->use_murphy)
10576     {
10577       /* special case for sleeping Murphy when leaning against non-free tile */
10578
10579       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10580           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10581            !IS_MOVING(player->jx - 1, player->jy)))
10582         move_dir = MV_LEFT;
10583       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10584                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10585                 !IS_MOVING(player->jx + 1, player->jy)))
10586         move_dir = MV_RIGHT;
10587       else
10588         player->is_sleeping = FALSE;
10589
10590       player->dir_waiting = move_dir;
10591     }
10592
10593     if (player->is_sleeping)
10594     {
10595       if (player->num_special_action_sleeping > 0)
10596       {
10597         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10598         {
10599           int last_special_action = player->special_action_sleeping;
10600           int num_special_action = player->num_special_action_sleeping;
10601           int special_action =
10602             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10603              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10604              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10605              last_special_action + 1 : ACTION_SLEEPING);
10606           int special_graphic =
10607             el_act_dir2img(player->artwork_element, special_action, move_dir);
10608
10609           player->anim_delay_counter =
10610             graphic_info[special_graphic].anim_delay_fixed +
10611             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10612           player->post_delay_counter =
10613             graphic_info[special_graphic].post_delay_fixed +
10614             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10615
10616           player->special_action_sleeping = special_action;
10617         }
10618
10619         if (player->anim_delay_counter > 0)
10620         {
10621           player->action_waiting = player->special_action_sleeping;
10622           player->anim_delay_counter--;
10623         }
10624         else if (player->post_delay_counter > 0)
10625         {
10626           player->post_delay_counter--;
10627         }
10628       }
10629     }
10630     else if (player->is_bored)
10631     {
10632       if (player->num_special_action_bored > 0)
10633       {
10634         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10635         {
10636           int special_action =
10637             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10638           int special_graphic =
10639             el_act_dir2img(player->artwork_element, special_action, move_dir);
10640
10641           player->anim_delay_counter =
10642             graphic_info[special_graphic].anim_delay_fixed +
10643             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10644           player->post_delay_counter =
10645             graphic_info[special_graphic].post_delay_fixed +
10646             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10647
10648           player->special_action_bored = special_action;
10649         }
10650
10651         if (player->anim_delay_counter > 0)
10652         {
10653           player->action_waiting = player->special_action_bored;
10654           player->anim_delay_counter--;
10655         }
10656         else if (player->post_delay_counter > 0)
10657         {
10658           player->post_delay_counter--;
10659         }
10660       }
10661     }
10662   }
10663   else if (last_waiting)        /* waiting -> not waiting */
10664   {
10665     player->is_waiting = FALSE;
10666     player->is_bored = FALSE;
10667     player->is_sleeping = FALSE;
10668
10669     player->frame_counter_bored = -1;
10670     player->frame_counter_sleeping = -1;
10671
10672     player->anim_delay_counter = 0;
10673     player->post_delay_counter = 0;
10674
10675     player->dir_waiting = player->MovDir;
10676     player->action_waiting = ACTION_DEFAULT;
10677
10678     player->special_action_bored = ACTION_DEFAULT;
10679     player->special_action_sleeping = ACTION_DEFAULT;
10680   }
10681 }
10682
10683 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10684 {
10685   static boolean player_was_moving = FALSE;
10686   static boolean player_was_snapping = FALSE;
10687   static boolean player_was_dropping = FALSE;
10688
10689   if ((!player->is_moving  && player_was_moving) ||
10690       (player->MovPos == 0 && player_was_moving) ||
10691       (player->is_snapping && !player_was_snapping) ||
10692       (player->is_dropping && !player_was_dropping))
10693   {
10694     if (!SaveEngineSnapshotToList())
10695       return;
10696
10697     player_was_moving = FALSE;
10698     player_was_snapping = TRUE;
10699     player_was_dropping = TRUE;
10700   }
10701   else
10702   {
10703     if (player->is_moving)
10704       player_was_moving = TRUE;
10705
10706     if (!player->is_snapping)
10707       player_was_snapping = FALSE;
10708
10709     if (!player->is_dropping)
10710       player_was_dropping = FALSE;
10711   }
10712 }
10713
10714 static void CheckSingleStepMode(struct PlayerInfo *player)
10715 {
10716   if (tape.single_step && tape.recording && !tape.pausing)
10717   {
10718     /* as it is called "single step mode", just return to pause mode when the
10719        player stopped moving after one tile (or never starts moving at all) */
10720     if (!player->is_moving && !player->is_pushing)
10721     {
10722       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10723       SnapField(player, 0, 0);                  /* stop snapping */
10724     }
10725   }
10726
10727   CheckSaveEngineSnapshot(player);
10728 }
10729
10730 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10731 {
10732   int left      = player_action & JOY_LEFT;
10733   int right     = player_action & JOY_RIGHT;
10734   int up        = player_action & JOY_UP;
10735   int down      = player_action & JOY_DOWN;
10736   int button1   = player_action & JOY_BUTTON_1;
10737   int button2   = player_action & JOY_BUTTON_2;
10738   int dx        = (left ? -1 : right ? 1 : 0);
10739   int dy        = (up   ? -1 : down  ? 1 : 0);
10740
10741   if (!player->active || tape.pausing)
10742     return 0;
10743
10744   if (player_action)
10745   {
10746     if (button1)
10747       SnapField(player, dx, dy);
10748     else
10749     {
10750       if (button2)
10751         DropElement(player);
10752
10753       MovePlayer(player, dx, dy);
10754     }
10755
10756     CheckSingleStepMode(player);
10757
10758     SetPlayerWaiting(player, FALSE);
10759
10760     return player_action;
10761   }
10762   else
10763   {
10764     /* no actions for this player (no input at player's configured device) */
10765
10766     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10767     SnapField(player, 0, 0);
10768     CheckGravityMovementWhenNotMoving(player);
10769
10770     if (player->MovPos == 0)
10771       SetPlayerWaiting(player, TRUE);
10772
10773     if (player->MovPos == 0)    /* needed for tape.playing */
10774       player->is_moving = FALSE;
10775
10776     player->is_dropping = FALSE;
10777     player->is_dropping_pressed = FALSE;
10778     player->drop_pressed_delay = 0;
10779
10780     CheckSingleStepMode(player);
10781
10782     return 0;
10783   }
10784 }
10785
10786 static void CheckLevelTime()
10787 {
10788   int i;
10789
10790   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10791   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10792   {
10793     if (level.native_em_level->lev->home == 0)  /* all players at home */
10794     {
10795       PlayerWins(local_player);
10796
10797       AllPlayersGone = TRUE;
10798
10799       level.native_em_level->lev->home = -1;
10800     }
10801
10802     if (level.native_em_level->ply[0]->alive == 0 &&
10803         level.native_em_level->ply[1]->alive == 0 &&
10804         level.native_em_level->ply[2]->alive == 0 &&
10805         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10806       AllPlayersGone = TRUE;
10807   }
10808   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10809   {
10810     if (game_sp.LevelSolved &&
10811         !game_sp.GameOver)                              /* game won */
10812     {
10813       PlayerWins(local_player);
10814
10815       game_sp.GameOver = TRUE;
10816
10817       AllPlayersGone = TRUE;
10818     }
10819
10820     if (game_sp.GameOver)                               /* game lost */
10821       AllPlayersGone = TRUE;
10822   }
10823
10824   if (TimeFrames >= FRAMES_PER_SECOND)
10825   {
10826     TimeFrames = 0;
10827     TapeTime++;
10828
10829     for (i = 0; i < MAX_PLAYERS; i++)
10830     {
10831       struct PlayerInfo *player = &stored_player[i];
10832
10833       if (SHIELD_ON(player))
10834       {
10835         player->shield_normal_time_left--;
10836
10837         if (player->shield_deadly_time_left > 0)
10838           player->shield_deadly_time_left--;
10839       }
10840     }
10841
10842     if (!local_player->LevelSolved && !level.use_step_counter)
10843     {
10844       TimePlayed++;
10845
10846       if (TimeLeft > 0)
10847       {
10848         TimeLeft--;
10849
10850         if (TimeLeft <= 10 && setup.time_limit)
10851           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10852
10853         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10854            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10855
10856         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10857
10858         if (!TimeLeft && setup.time_limit)
10859         {
10860           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10861             level.native_em_level->lev->killed_out_of_time = TRUE;
10862           else
10863             for (i = 0; i < MAX_PLAYERS; i++)
10864               KillPlayer(&stored_player[i]);
10865         }
10866       }
10867       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10868       {
10869         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10870       }
10871
10872       level.native_em_level->lev->time =
10873         (game.no_time_limit ? TimePlayed : TimeLeft);
10874     }
10875
10876     if (tape.recording || tape.playing)
10877       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10878   }
10879
10880   if (tape.recording || tape.playing)
10881     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10882
10883   UpdateAndDisplayGameControlValues();
10884 }
10885
10886 void AdvanceFrameAndPlayerCounters(int player_nr)
10887 {
10888   int i;
10889
10890   /* advance frame counters (global frame counter and time frame counter) */
10891   FrameCounter++;
10892   TimeFrames++;
10893
10894   /* advance player counters (counters for move delay, move animation etc.) */
10895   for (i = 0; i < MAX_PLAYERS; i++)
10896   {
10897     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10898     int move_delay_value = stored_player[i].move_delay_value;
10899     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10900
10901     if (!advance_player_counters)       /* not all players may be affected */
10902       continue;
10903
10904     if (move_frames == 0)       /* less than one move per game frame */
10905     {
10906       int stepsize = TILEX / move_delay_value;
10907       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10908       int count = (stored_player[i].is_moving ?
10909                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10910
10911       if (count % delay == 0)
10912         move_frames = 1;
10913     }
10914
10915     stored_player[i].Frame += move_frames;
10916
10917     if (stored_player[i].MovPos != 0)
10918       stored_player[i].StepFrame += move_frames;
10919
10920     if (stored_player[i].move_delay > 0)
10921       stored_player[i].move_delay--;
10922
10923     /* due to bugs in previous versions, counter must count up, not down */
10924     if (stored_player[i].push_delay != -1)
10925       stored_player[i].push_delay++;
10926
10927     if (stored_player[i].drop_delay > 0)
10928       stored_player[i].drop_delay--;
10929
10930     if (stored_player[i].is_dropping_pressed)
10931       stored_player[i].drop_pressed_delay++;
10932   }
10933 }
10934
10935 void StartGameActions(boolean init_network_game, boolean record_tape,
10936                       int random_seed)
10937 {
10938   unsigned int new_random_seed = InitRND(random_seed);
10939
10940   if (record_tape)
10941     TapeStartRecording(new_random_seed);
10942
10943 #if defined(NETWORK_AVALIABLE)
10944   if (init_network_game)
10945   {
10946     SendToServer_StartPlaying();
10947
10948     return;
10949   }
10950 #endif
10951
10952   InitGame();
10953 }
10954
10955 void GameActions()
10956 {
10957   static unsigned int game_frame_delay = 0;
10958   unsigned int game_frame_delay_value;
10959   byte *recorded_player_action;
10960   byte summarized_player_action = 0;
10961   byte tape_action[MAX_PLAYERS];
10962   int i;
10963
10964   /* detect endless loops, caused by custom element programming */
10965   if (recursion_loop_detected && recursion_loop_depth == 0)
10966   {
10967     char *message = getStringCat3("Internal Error! Element ",
10968                                   EL_NAME(recursion_loop_element),
10969                                   " caused endless loop! Quit the game?");
10970
10971     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10972           EL_NAME(recursion_loop_element));
10973
10974     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10975
10976     recursion_loop_detected = FALSE;    /* if game should be continued */
10977
10978     free(message);
10979
10980     return;
10981   }
10982
10983   if (game.restart_level)
10984     StartGameActions(options.network, setup.autorecord, level.random_seed);
10985
10986   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10987   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10988   {
10989     if (level.native_em_level->lev->home == 0)  /* all players at home */
10990     {
10991       PlayerWins(local_player);
10992
10993       AllPlayersGone = TRUE;
10994
10995       level.native_em_level->lev->home = -1;
10996     }
10997
10998     if (level.native_em_level->ply[0]->alive == 0 &&
10999         level.native_em_level->ply[1]->alive == 0 &&
11000         level.native_em_level->ply[2]->alive == 0 &&
11001         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11002       AllPlayersGone = TRUE;
11003   }
11004   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11005   {
11006     if (game_sp.LevelSolved &&
11007         !game_sp.GameOver)                              /* game won */
11008     {
11009       PlayerWins(local_player);
11010
11011       game_sp.GameOver = TRUE;
11012
11013       AllPlayersGone = TRUE;
11014     }
11015
11016     if (game_sp.GameOver)                               /* game lost */
11017       AllPlayersGone = TRUE;
11018   }
11019
11020   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11021     GameWon();
11022
11023   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11024     TapeStop();
11025
11026   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11027     return;
11028
11029   game_frame_delay_value =
11030     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11031
11032   if (tape.playing && tape.warp_forward && !tape.pausing)
11033     game_frame_delay_value = 0;
11034
11035 #if 0
11036   /* ---------- main game synchronization point ---------- */
11037
11038   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11039
11040   printf("::: skip == %d\n", skip);
11041
11042 #else
11043   /* ---------- main game synchronization point ---------- */
11044
11045   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11046 #endif
11047
11048   if (network_playing && !network_player_action_received)
11049   {
11050     /* try to get network player actions in time */
11051
11052 #if defined(NETWORK_AVALIABLE)
11053     /* last chance to get network player actions without main loop delay */
11054     HandleNetworking();
11055 #endif
11056
11057     /* game was quit by network peer */
11058     if (game_status != GAME_MODE_PLAYING)
11059       return;
11060
11061     if (!network_player_action_received)
11062       return;           /* failed to get network player actions in time */
11063
11064     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11065   }
11066
11067   if (tape.pausing)
11068     return;
11069
11070   /* at this point we know that we really continue executing the game */
11071
11072   network_player_action_received = FALSE;
11073
11074   /* when playing tape, read previously recorded player input from tape data */
11075   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11076
11077   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11078   if (tape.pausing)
11079     return;
11080
11081   if (tape.set_centered_player)
11082   {
11083     game.centered_player_nr_next = tape.centered_player_nr_next;
11084     game.set_centered_player = TRUE;
11085   }
11086
11087   for (i = 0; i < MAX_PLAYERS; i++)
11088   {
11089     summarized_player_action |= stored_player[i].action;
11090
11091     if (!network_playing && (game.team_mode || tape.playing))
11092       stored_player[i].effective_action = stored_player[i].action;
11093   }
11094
11095 #if defined(NETWORK_AVALIABLE)
11096   if (network_playing)
11097     SendToServer_MovePlayer(summarized_player_action);
11098 #endif
11099
11100   // summarize all actions at local players mapped input device position
11101   // (this allows using different input devices in single player mode)
11102   if (!options.network && !game.team_mode)
11103     stored_player[map_player_action[local_player->index_nr]].effective_action =
11104       summarized_player_action;
11105
11106   if (tape.recording &&
11107       setup.team_mode &&
11108       setup.input_on_focus &&
11109       game.centered_player_nr != -1)
11110   {
11111     for (i = 0; i < MAX_PLAYERS; i++)
11112       stored_player[i].effective_action =
11113         (i == game.centered_player_nr ? summarized_player_action : 0);
11114   }
11115
11116   if (recorded_player_action != NULL)
11117     for (i = 0; i < MAX_PLAYERS; i++)
11118       stored_player[i].effective_action = recorded_player_action[i];
11119
11120   for (i = 0; i < MAX_PLAYERS; i++)
11121   {
11122     tape_action[i] = stored_player[i].effective_action;
11123
11124     /* (this may happen in the RND game engine if a player was not present on
11125        the playfield on level start, but appeared later from a custom element */
11126     if (setup.team_mode &&
11127         tape.recording &&
11128         tape_action[i] &&
11129         !tape.player_participates[i])
11130       tape.player_participates[i] = TRUE;
11131   }
11132
11133   /* only record actions from input devices, but not programmed actions */
11134   if (tape.recording)
11135     TapeRecordAction(tape_action);
11136
11137 #if USE_NEW_PLAYER_ASSIGNMENTS
11138   // !!! also map player actions in single player mode !!!
11139   // if (game.team_mode)
11140   if (1)
11141   {
11142     byte mapped_action[MAX_PLAYERS];
11143
11144 #if DEBUG_PLAYER_ACTIONS
11145     printf(":::");
11146     for (i = 0; i < MAX_PLAYERS; i++)
11147       printf(" %d, ", stored_player[i].effective_action);
11148 #endif
11149
11150     for (i = 0; i < MAX_PLAYERS; i++)
11151       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11152
11153     for (i = 0; i < MAX_PLAYERS; i++)
11154       stored_player[i].effective_action = mapped_action[i];
11155
11156 #if DEBUG_PLAYER_ACTIONS
11157     printf(" =>");
11158     for (i = 0; i < MAX_PLAYERS; i++)
11159       printf(" %d, ", stored_player[i].effective_action);
11160     printf("\n");
11161 #endif
11162   }
11163 #if DEBUG_PLAYER_ACTIONS
11164   else
11165   {
11166     printf(":::");
11167     for (i = 0; i < MAX_PLAYERS; i++)
11168       printf(" %d, ", stored_player[i].effective_action);
11169     printf("\n");
11170   }
11171 #endif
11172 #endif
11173
11174   for (i = 0; i < MAX_PLAYERS; i++)
11175   {
11176     // allow engine snapshot in case of changed movement attempt
11177     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11178         (stored_player[i].effective_action & KEY_MOTION))
11179       game.snapshot.changed_action = TRUE;
11180
11181     // allow engine snapshot in case of snapping/dropping attempt
11182     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11183         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11184       game.snapshot.changed_action = TRUE;
11185
11186     game.snapshot.last_action[i] = stored_player[i].effective_action;
11187   }
11188
11189   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11190   {
11191     GameActions_EM_Main();
11192   }
11193   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11194   {
11195     GameActions_SP_Main();
11196   }
11197   else
11198   {
11199     GameActions_RND_Main();
11200   }
11201
11202   BlitScreenToBitmap(backbuffer);
11203
11204   CheckLevelTime();
11205
11206   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11207
11208   if (options.debug)                    /* calculate frames per second */
11209   {
11210     static unsigned int fps_counter = 0;
11211     static int fps_frames = 0;
11212     unsigned int fps_delay_ms = Counter() - fps_counter;
11213
11214     fps_frames++;
11215
11216     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11217     {
11218       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11219
11220       fps_frames = 0;
11221       fps_counter = Counter();
11222     }
11223
11224     redraw_mask |= REDRAW_FPS;
11225   }
11226 }
11227
11228 void GameActions_EM_Main()
11229 {
11230   byte effective_action[MAX_PLAYERS];
11231   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11232   int i;
11233
11234   for (i = 0; i < MAX_PLAYERS; i++)
11235     effective_action[i] = stored_player[i].effective_action;
11236
11237   GameActions_EM(effective_action, warp_mode);
11238 }
11239
11240 void GameActions_SP_Main()
11241 {
11242   byte effective_action[MAX_PLAYERS];
11243   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11244   int i;
11245
11246   for (i = 0; i < MAX_PLAYERS; i++)
11247     effective_action[i] = stored_player[i].effective_action;
11248
11249   GameActions_SP(effective_action, warp_mode);
11250 }
11251
11252 void GameActions_RND_Main()
11253 {
11254   GameActions_RND();
11255 }
11256
11257 void GameActions_RND()
11258 {
11259   int magic_wall_x = 0, magic_wall_y = 0;
11260   int i, x, y, element, graphic;
11261
11262   InitPlayfieldScanModeVars();
11263
11264   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11265   {
11266     SCAN_PLAYFIELD(x, y)
11267     {
11268       ChangeCount[x][y] = 0;
11269       ChangeEvent[x][y] = -1;
11270     }
11271   }
11272
11273   if (game.set_centered_player)
11274   {
11275     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11276
11277     /* switching to "all players" only possible if all players fit to screen */
11278     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11279     {
11280       game.centered_player_nr_next = game.centered_player_nr;
11281       game.set_centered_player = FALSE;
11282     }
11283
11284     /* do not switch focus to non-existing (or non-active) player */
11285     if (game.centered_player_nr_next >= 0 &&
11286         !stored_player[game.centered_player_nr_next].active)
11287     {
11288       game.centered_player_nr_next = game.centered_player_nr;
11289       game.set_centered_player = FALSE;
11290     }
11291   }
11292
11293   if (game.set_centered_player &&
11294       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11295   {
11296     int sx, sy;
11297
11298     if (game.centered_player_nr_next == -1)
11299     {
11300       setScreenCenteredToAllPlayers(&sx, &sy);
11301     }
11302     else
11303     {
11304       sx = stored_player[game.centered_player_nr_next].jx;
11305       sy = stored_player[game.centered_player_nr_next].jy;
11306     }
11307
11308     game.centered_player_nr = game.centered_player_nr_next;
11309     game.set_centered_player = FALSE;
11310
11311     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11312     DrawGameDoorValues();
11313   }
11314
11315   for (i = 0; i < MAX_PLAYERS; i++)
11316   {
11317     int actual_player_action = stored_player[i].effective_action;
11318
11319 #if 1
11320     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11321        - rnd_equinox_tetrachloride 048
11322        - rnd_equinox_tetrachloride_ii 096
11323        - rnd_emanuel_schmieg 002
11324        - doctor_sloan_ww 001, 020
11325     */
11326     if (stored_player[i].MovPos == 0)
11327       CheckGravityMovement(&stored_player[i]);
11328 #endif
11329
11330     /* overwrite programmed action with tape action */
11331     if (stored_player[i].programmed_action)
11332       actual_player_action = stored_player[i].programmed_action;
11333
11334     PlayerActions(&stored_player[i], actual_player_action);
11335
11336     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11337   }
11338
11339   ScrollScreen(NULL, SCROLL_GO_ON);
11340
11341   /* for backwards compatibility, the following code emulates a fixed bug that
11342      occured when pushing elements (causing elements that just made their last
11343      pushing step to already (if possible) make their first falling step in the
11344      same game frame, which is bad); this code is also needed to use the famous
11345      "spring push bug" which is used in older levels and might be wanted to be
11346      used also in newer levels, but in this case the buggy pushing code is only
11347      affecting the "spring" element and no other elements */
11348
11349   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11350   {
11351     for (i = 0; i < MAX_PLAYERS; i++)
11352     {
11353       struct PlayerInfo *player = &stored_player[i];
11354       int x = player->jx;
11355       int y = player->jy;
11356
11357       if (player->active && player->is_pushing && player->is_moving &&
11358           IS_MOVING(x, y) &&
11359           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11360            Feld[x][y] == EL_SPRING))
11361       {
11362         ContinueMoving(x, y);
11363
11364         /* continue moving after pushing (this is actually a bug) */
11365         if (!IS_MOVING(x, y))
11366           Stop[x][y] = FALSE;
11367       }
11368     }
11369   }
11370
11371   SCAN_PLAYFIELD(x, y)
11372   {
11373     ChangeCount[x][y] = 0;
11374     ChangeEvent[x][y] = -1;
11375
11376     /* this must be handled before main playfield loop */
11377     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11378     {
11379       MovDelay[x][y]--;
11380       if (MovDelay[x][y] <= 0)
11381         RemoveField(x, y);
11382     }
11383
11384     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11385     {
11386       MovDelay[x][y]--;
11387       if (MovDelay[x][y] <= 0)
11388       {
11389         RemoveField(x, y);
11390         TEST_DrawLevelField(x, y);
11391
11392         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11393       }
11394     }
11395
11396 #if DEBUG
11397     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11398     {
11399       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11400       printf("GameActions(): This should never happen!\n");
11401
11402       ChangePage[x][y] = -1;
11403     }
11404 #endif
11405
11406     Stop[x][y] = FALSE;
11407     if (WasJustMoving[x][y] > 0)
11408       WasJustMoving[x][y]--;
11409     if (WasJustFalling[x][y] > 0)
11410       WasJustFalling[x][y]--;
11411     if (CheckCollision[x][y] > 0)
11412       CheckCollision[x][y]--;
11413     if (CheckImpact[x][y] > 0)
11414       CheckImpact[x][y]--;
11415
11416     GfxFrame[x][y]++;
11417
11418     /* reset finished pushing action (not done in ContinueMoving() to allow
11419        continuous pushing animation for elements with zero push delay) */
11420     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11421     {
11422       ResetGfxAnimation(x, y);
11423       TEST_DrawLevelField(x, y);
11424     }
11425
11426 #if DEBUG
11427     if (IS_BLOCKED(x, y))
11428     {
11429       int oldx, oldy;
11430
11431       Blocked2Moving(x, y, &oldx, &oldy);
11432       if (!IS_MOVING(oldx, oldy))
11433       {
11434         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11435         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11436         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11437         printf("GameActions(): This should never happen!\n");
11438       }
11439     }
11440 #endif
11441   }
11442
11443   SCAN_PLAYFIELD(x, y)
11444   {
11445     element = Feld[x][y];
11446     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11447
11448     ResetGfxFrame(x, y, TRUE);
11449
11450     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11451         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11452       ResetRandomAnimationValue(x, y);
11453
11454     SetRandomAnimationValue(x, y);
11455
11456     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11457
11458     if (IS_INACTIVE(element))
11459     {
11460       if (IS_ANIMATED(graphic))
11461         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11462
11463       continue;
11464     }
11465
11466     /* this may take place after moving, so 'element' may have changed */
11467     if (IS_CHANGING(x, y) &&
11468         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11469     {
11470       int page = element_info[element].event_page_nr[CE_DELAY];
11471
11472       HandleElementChange(x, y, page);
11473
11474       element = Feld[x][y];
11475       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11476     }
11477
11478     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11479     {
11480       StartMoving(x, y);
11481
11482       element = Feld[x][y];
11483       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11484
11485       if (IS_ANIMATED(graphic) &&
11486           !IS_MOVING(x, y) &&
11487           !Stop[x][y])
11488         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11489
11490       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11491         TEST_DrawTwinkleOnField(x, y);
11492     }
11493     else if ((element == EL_ACID ||
11494               element == EL_EXIT_OPEN ||
11495               element == EL_EM_EXIT_OPEN ||
11496               element == EL_SP_EXIT_OPEN ||
11497               element == EL_STEEL_EXIT_OPEN ||
11498               element == EL_EM_STEEL_EXIT_OPEN ||
11499               element == EL_SP_TERMINAL ||
11500               element == EL_SP_TERMINAL_ACTIVE ||
11501               element == EL_EXTRA_TIME ||
11502               element == EL_SHIELD_NORMAL ||
11503               element == EL_SHIELD_DEADLY) &&
11504              IS_ANIMATED(graphic))
11505       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11506     else if (IS_MOVING(x, y))
11507       ContinueMoving(x, y);
11508     else if (IS_ACTIVE_BOMB(element))
11509       CheckDynamite(x, y);
11510     else if (element == EL_AMOEBA_GROWING)
11511       AmoebeWaechst(x, y);
11512     else if (element == EL_AMOEBA_SHRINKING)
11513       AmoebaDisappearing(x, y);
11514
11515 #if !USE_NEW_AMOEBA_CODE
11516     else if (IS_AMOEBALIVE(element))
11517       AmoebeAbleger(x, y);
11518 #endif
11519
11520     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11521       Life(x, y);
11522     else if (element == EL_EXIT_CLOSED)
11523       CheckExit(x, y);
11524     else if (element == EL_EM_EXIT_CLOSED)
11525       CheckExitEM(x, y);
11526     else if (element == EL_STEEL_EXIT_CLOSED)
11527       CheckExitSteel(x, y);
11528     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11529       CheckExitSteelEM(x, y);
11530     else if (element == EL_SP_EXIT_CLOSED)
11531       CheckExitSP(x, y);
11532     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11533              element == EL_EXPANDABLE_STEELWALL_GROWING)
11534       MauerWaechst(x, y);
11535     else if (element == EL_EXPANDABLE_WALL ||
11536              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11537              element == EL_EXPANDABLE_WALL_VERTICAL ||
11538              element == EL_EXPANDABLE_WALL_ANY ||
11539              element == EL_BD_EXPANDABLE_WALL)
11540       MauerAbleger(x, y);
11541     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11542              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11543              element == EL_EXPANDABLE_STEELWALL_ANY)
11544       MauerAblegerStahl(x, y);
11545     else if (element == EL_FLAMES)
11546       CheckForDragon(x, y);
11547     else if (element == EL_EXPLOSION)
11548       ; /* drawing of correct explosion animation is handled separately */
11549     else if (element == EL_ELEMENT_SNAPPING ||
11550              element == EL_DIAGONAL_SHRINKING ||
11551              element == EL_DIAGONAL_GROWING)
11552     {
11553       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11554
11555       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11556     }
11557     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11558       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11559
11560     if (IS_BELT_ACTIVE(element))
11561       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11562
11563     if (game.magic_wall_active)
11564     {
11565       int jx = local_player->jx, jy = local_player->jy;
11566
11567       /* play the element sound at the position nearest to the player */
11568       if ((element == EL_MAGIC_WALL_FULL ||
11569            element == EL_MAGIC_WALL_ACTIVE ||
11570            element == EL_MAGIC_WALL_EMPTYING ||
11571            element == EL_BD_MAGIC_WALL_FULL ||
11572            element == EL_BD_MAGIC_WALL_ACTIVE ||
11573            element == EL_BD_MAGIC_WALL_EMPTYING ||
11574            element == EL_DC_MAGIC_WALL_FULL ||
11575            element == EL_DC_MAGIC_WALL_ACTIVE ||
11576            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11577           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11578       {
11579         magic_wall_x = x;
11580         magic_wall_y = y;
11581       }
11582     }
11583   }
11584
11585 #if USE_NEW_AMOEBA_CODE
11586   /* new experimental amoeba growth stuff */
11587   if (!(FrameCounter % 8))
11588   {
11589     static unsigned int random = 1684108901;
11590
11591     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11592     {
11593       x = RND(lev_fieldx);
11594       y = RND(lev_fieldy);
11595       element = Feld[x][y];
11596
11597       if (!IS_PLAYER(x,y) &&
11598           (element == EL_EMPTY ||
11599            CAN_GROW_INTO(element) ||
11600            element == EL_QUICKSAND_EMPTY ||
11601            element == EL_QUICKSAND_FAST_EMPTY ||
11602            element == EL_ACID_SPLASH_LEFT ||
11603            element == EL_ACID_SPLASH_RIGHT))
11604       {
11605         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11606             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11607             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11608             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11609           Feld[x][y] = EL_AMOEBA_DROP;
11610       }
11611
11612       random = random * 129 + 1;
11613     }
11614   }
11615 #endif
11616
11617   game.explosions_delayed = FALSE;
11618
11619   SCAN_PLAYFIELD(x, y)
11620   {
11621     element = Feld[x][y];
11622
11623     if (ExplodeField[x][y])
11624       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11625     else if (element == EL_EXPLOSION)
11626       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11627
11628     ExplodeField[x][y] = EX_TYPE_NONE;
11629   }
11630
11631   game.explosions_delayed = TRUE;
11632
11633   if (game.magic_wall_active)
11634   {
11635     if (!(game.magic_wall_time_left % 4))
11636     {
11637       int element = Feld[magic_wall_x][magic_wall_y];
11638
11639       if (element == EL_BD_MAGIC_WALL_FULL ||
11640           element == EL_BD_MAGIC_WALL_ACTIVE ||
11641           element == EL_BD_MAGIC_WALL_EMPTYING)
11642         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11643       else if (element == EL_DC_MAGIC_WALL_FULL ||
11644                element == EL_DC_MAGIC_WALL_ACTIVE ||
11645                element == EL_DC_MAGIC_WALL_EMPTYING)
11646         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11647       else
11648         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11649     }
11650
11651     if (game.magic_wall_time_left > 0)
11652     {
11653       game.magic_wall_time_left--;
11654
11655       if (!game.magic_wall_time_left)
11656       {
11657         SCAN_PLAYFIELD(x, y)
11658         {
11659           element = Feld[x][y];
11660
11661           if (element == EL_MAGIC_WALL_ACTIVE ||
11662               element == EL_MAGIC_WALL_FULL)
11663           {
11664             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11665             TEST_DrawLevelField(x, y);
11666           }
11667           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11668                    element == EL_BD_MAGIC_WALL_FULL)
11669           {
11670             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11671             TEST_DrawLevelField(x, y);
11672           }
11673           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11674                    element == EL_DC_MAGIC_WALL_FULL)
11675           {
11676             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11677             TEST_DrawLevelField(x, y);
11678           }
11679         }
11680
11681         game.magic_wall_active = FALSE;
11682       }
11683     }
11684   }
11685
11686   if (game.light_time_left > 0)
11687   {
11688     game.light_time_left--;
11689
11690     if (game.light_time_left == 0)
11691       RedrawAllLightSwitchesAndInvisibleElements();
11692   }
11693
11694   if (game.timegate_time_left > 0)
11695   {
11696     game.timegate_time_left--;
11697
11698     if (game.timegate_time_left == 0)
11699       CloseAllOpenTimegates();
11700   }
11701
11702   if (game.lenses_time_left > 0)
11703   {
11704     game.lenses_time_left--;
11705
11706     if (game.lenses_time_left == 0)
11707       RedrawAllInvisibleElementsForLenses();
11708   }
11709
11710   if (game.magnify_time_left > 0)
11711   {
11712     game.magnify_time_left--;
11713
11714     if (game.magnify_time_left == 0)
11715       RedrawAllInvisibleElementsForMagnifier();
11716   }
11717
11718   for (i = 0; i < MAX_PLAYERS; i++)
11719   {
11720     struct PlayerInfo *player = &stored_player[i];
11721
11722     if (SHIELD_ON(player))
11723     {
11724       if (player->shield_deadly_time_left)
11725         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11726       else if (player->shield_normal_time_left)
11727         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11728     }
11729   }
11730
11731 #if USE_DELAYED_GFX_REDRAW
11732   SCAN_PLAYFIELD(x, y)
11733   {
11734     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11735     {
11736       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11737          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11738
11739       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11740         DrawLevelField(x, y);
11741
11742       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11743         DrawLevelFieldCrumbled(x, y);
11744
11745       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11746         DrawLevelFieldCrumbledNeighbours(x, y);
11747
11748       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11749         DrawTwinkleOnField(x, y);
11750     }
11751
11752     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11753   }
11754 #endif
11755
11756   DrawAllPlayers();
11757   PlayAllPlayersSound();
11758
11759   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11760   {
11761     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11762
11763     local_player->show_envelope = 0;
11764   }
11765
11766   /* use random number generator in every frame to make it less predictable */
11767   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11768     RND(1);
11769 }
11770
11771 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11772 {
11773   int min_x = x, min_y = y, max_x = x, max_y = y;
11774   int i;
11775
11776   for (i = 0; i < MAX_PLAYERS; i++)
11777   {
11778     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11779
11780     if (!stored_player[i].active || &stored_player[i] == player)
11781       continue;
11782
11783     min_x = MIN(min_x, jx);
11784     min_y = MIN(min_y, jy);
11785     max_x = MAX(max_x, jx);
11786     max_y = MAX(max_y, jy);
11787   }
11788
11789   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11790 }
11791
11792 static boolean AllPlayersInVisibleScreen()
11793 {
11794   int i;
11795
11796   for (i = 0; i < MAX_PLAYERS; i++)
11797   {
11798     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11799
11800     if (!stored_player[i].active)
11801       continue;
11802
11803     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11804       return FALSE;
11805   }
11806
11807   return TRUE;
11808 }
11809
11810 void ScrollLevel(int dx, int dy)
11811 {
11812   int scroll_offset = 2 * TILEX_VAR;
11813   int x, y;
11814
11815   BlitBitmap(drawto_field, drawto_field,
11816              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11817              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11818              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11819              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11820              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11821              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11822
11823   if (dx != 0)
11824   {
11825     x = (dx == 1 ? BX1 : BX2);
11826     for (y = BY1; y <= BY2; y++)
11827       DrawScreenField(x, y);
11828   }
11829
11830   if (dy != 0)
11831   {
11832     y = (dy == 1 ? BY1 : BY2);
11833     for (x = BX1; x <= BX2; x++)
11834       DrawScreenField(x, y);
11835   }
11836
11837   redraw_mask |= REDRAW_FIELD;
11838 }
11839
11840 static boolean canFallDown(struct PlayerInfo *player)
11841 {
11842   int jx = player->jx, jy = player->jy;
11843
11844   return (IN_LEV_FIELD(jx, jy + 1) &&
11845           (IS_FREE(jx, jy + 1) ||
11846            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11847           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11848           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11849 }
11850
11851 static boolean canPassField(int x, int y, int move_dir)
11852 {
11853   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11854   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11855   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11856   int nextx = x + dx;
11857   int nexty = y + dy;
11858   int element = Feld[x][y];
11859
11860   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11861           !CAN_MOVE(element) &&
11862           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11863           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11864           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11865 }
11866
11867 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11868 {
11869   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11870   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11871   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11872   int newx = x + dx;
11873   int newy = y + dy;
11874
11875   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11876           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11877           (IS_DIGGABLE(Feld[newx][newy]) ||
11878            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11879            canPassField(newx, newy, move_dir)));
11880 }
11881
11882 static void CheckGravityMovement(struct PlayerInfo *player)
11883 {
11884   if (player->gravity && !player->programmed_action)
11885   {
11886     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11887     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11888     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11889     int jx = player->jx, jy = player->jy;
11890     boolean player_is_moving_to_valid_field =
11891       (!player_is_snapping &&
11892        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11893         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11894     boolean player_can_fall_down = canFallDown(player);
11895
11896     if (player_can_fall_down &&
11897         !player_is_moving_to_valid_field)
11898       player->programmed_action = MV_DOWN;
11899   }
11900 }
11901
11902 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11903 {
11904   return CheckGravityMovement(player);
11905
11906   if (player->gravity && !player->programmed_action)
11907   {
11908     int jx = player->jx, jy = player->jy;
11909     boolean field_under_player_is_free =
11910       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11911     boolean player_is_standing_on_valid_field =
11912       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11913        (IS_WALKABLE(Feld[jx][jy]) &&
11914         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11915
11916     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11917       player->programmed_action = MV_DOWN;
11918   }
11919 }
11920
11921 /*
11922   MovePlayerOneStep()
11923   -----------------------------------------------------------------------------
11924   dx, dy:               direction (non-diagonal) to try to move the player to
11925   real_dx, real_dy:     direction as read from input device (can be diagonal)
11926 */
11927
11928 boolean MovePlayerOneStep(struct PlayerInfo *player,
11929                           int dx, int dy, int real_dx, int real_dy)
11930 {
11931   int jx = player->jx, jy = player->jy;
11932   int new_jx = jx + dx, new_jy = jy + dy;
11933   int can_move;
11934   boolean player_can_move = !player->cannot_move;
11935
11936   if (!player->active || (!dx && !dy))
11937     return MP_NO_ACTION;
11938
11939   player->MovDir = (dx < 0 ? MV_LEFT :
11940                     dx > 0 ? MV_RIGHT :
11941                     dy < 0 ? MV_UP :
11942                     dy > 0 ? MV_DOWN :  MV_NONE);
11943
11944   if (!IN_LEV_FIELD(new_jx, new_jy))
11945     return MP_NO_ACTION;
11946
11947   if (!player_can_move)
11948   {
11949     if (player->MovPos == 0)
11950     {
11951       player->is_moving = FALSE;
11952       player->is_digging = FALSE;
11953       player->is_collecting = FALSE;
11954       player->is_snapping = FALSE;
11955       player->is_pushing = FALSE;
11956     }
11957   }
11958
11959   if (!options.network && game.centered_player_nr == -1 &&
11960       !AllPlayersInSight(player, new_jx, new_jy))
11961     return MP_NO_ACTION;
11962
11963   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11964   if (can_move != MP_MOVING)
11965     return can_move;
11966
11967   /* check if DigField() has caused relocation of the player */
11968   if (player->jx != jx || player->jy != jy)
11969     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11970
11971   StorePlayer[jx][jy] = 0;
11972   player->last_jx = jx;
11973   player->last_jy = jy;
11974   player->jx = new_jx;
11975   player->jy = new_jy;
11976   StorePlayer[new_jx][new_jy] = player->element_nr;
11977
11978   if (player->move_delay_value_next != -1)
11979   {
11980     player->move_delay_value = player->move_delay_value_next;
11981     player->move_delay_value_next = -1;
11982   }
11983
11984   player->MovPos =
11985     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11986
11987   player->step_counter++;
11988
11989   PlayerVisit[jx][jy] = FrameCounter;
11990
11991   player->is_moving = TRUE;
11992
11993 #if 1
11994   /* should better be called in MovePlayer(), but this breaks some tapes */
11995   ScrollPlayer(player, SCROLL_INIT);
11996 #endif
11997
11998   return MP_MOVING;
11999 }
12000
12001 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12002 {
12003   int jx = player->jx, jy = player->jy;
12004   int old_jx = jx, old_jy = jy;
12005   int moved = MP_NO_ACTION;
12006
12007   if (!player->active)
12008     return FALSE;
12009
12010   if (!dx && !dy)
12011   {
12012     if (player->MovPos == 0)
12013     {
12014       player->is_moving = FALSE;
12015       player->is_digging = FALSE;
12016       player->is_collecting = FALSE;
12017       player->is_snapping = FALSE;
12018       player->is_pushing = FALSE;
12019     }
12020
12021     return FALSE;
12022   }
12023
12024   if (player->move_delay > 0)
12025     return FALSE;
12026
12027   player->move_delay = -1;              /* set to "uninitialized" value */
12028
12029   /* store if player is automatically moved to next field */
12030   player->is_auto_moving = (player->programmed_action != MV_NONE);
12031
12032   /* remove the last programmed player action */
12033   player->programmed_action = 0;
12034
12035   if (player->MovPos)
12036   {
12037     /* should only happen if pre-1.2 tape recordings are played */
12038     /* this is only for backward compatibility */
12039
12040     int original_move_delay_value = player->move_delay_value;
12041
12042 #if DEBUG
12043     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12044            tape.counter);
12045 #endif
12046
12047     /* scroll remaining steps with finest movement resolution */
12048     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12049
12050     while (player->MovPos)
12051     {
12052       ScrollPlayer(player, SCROLL_GO_ON);
12053       ScrollScreen(NULL, SCROLL_GO_ON);
12054
12055       AdvanceFrameAndPlayerCounters(player->index_nr);
12056
12057       DrawAllPlayers();
12058       BackToFront();
12059     }
12060
12061     player->move_delay_value = original_move_delay_value;
12062   }
12063
12064   player->is_active = FALSE;
12065
12066   if (player->last_move_dir & MV_HORIZONTAL)
12067   {
12068     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12069       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12070   }
12071   else
12072   {
12073     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12074       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12075   }
12076
12077   if (!moved && !player->is_active)
12078   {
12079     player->is_moving = FALSE;
12080     player->is_digging = FALSE;
12081     player->is_collecting = FALSE;
12082     player->is_snapping = FALSE;
12083     player->is_pushing = FALSE;
12084   }
12085
12086   jx = player->jx;
12087   jy = player->jy;
12088
12089   if (moved & MP_MOVING && !ScreenMovPos &&
12090       (player->index_nr == game.centered_player_nr ||
12091        game.centered_player_nr == -1))
12092   {
12093     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12094     int offset = game.scroll_delay_value;
12095
12096     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12097     {
12098       /* actual player has left the screen -- scroll in that direction */
12099       if (jx != old_jx)         /* player has moved horizontally */
12100         scroll_x += (jx - old_jx);
12101       else                      /* player has moved vertically */
12102         scroll_y += (jy - old_jy);
12103     }
12104     else
12105     {
12106       if (jx != old_jx)         /* player has moved horizontally */
12107       {
12108         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12109             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12110           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12111
12112         /* don't scroll over playfield boundaries */
12113         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12114           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12115
12116         /* don't scroll more than one field at a time */
12117         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12118
12119         /* don't scroll against the player's moving direction */
12120         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12121             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12122           scroll_x = old_scroll_x;
12123       }
12124       else                      /* player has moved vertically */
12125       {
12126         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12127             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12128           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12129
12130         /* don't scroll over playfield boundaries */
12131         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12132           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12133
12134         /* don't scroll more than one field at a time */
12135         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12136
12137         /* don't scroll against the player's moving direction */
12138         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12139             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12140           scroll_y = old_scroll_y;
12141       }
12142     }
12143
12144     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12145     {
12146       if (!options.network && game.centered_player_nr == -1 &&
12147           !AllPlayersInVisibleScreen())
12148       {
12149         scroll_x = old_scroll_x;
12150         scroll_y = old_scroll_y;
12151       }
12152       else
12153       {
12154         ScrollScreen(player, SCROLL_INIT);
12155         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12156       }
12157     }
12158   }
12159
12160   player->StepFrame = 0;
12161
12162   if (moved & MP_MOVING)
12163   {
12164     if (old_jx != jx && old_jy == jy)
12165       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12166     else if (old_jx == jx && old_jy != jy)
12167       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12168
12169     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12170
12171     player->last_move_dir = player->MovDir;
12172     player->is_moving = TRUE;
12173     player->is_snapping = FALSE;
12174     player->is_switching = FALSE;
12175     player->is_dropping = FALSE;
12176     player->is_dropping_pressed = FALSE;
12177     player->drop_pressed_delay = 0;
12178
12179 #if 0
12180     /* should better be called here than above, but this breaks some tapes */
12181     ScrollPlayer(player, SCROLL_INIT);
12182 #endif
12183   }
12184   else
12185   {
12186     CheckGravityMovementWhenNotMoving(player);
12187
12188     player->is_moving = FALSE;
12189
12190     /* at this point, the player is allowed to move, but cannot move right now
12191        (e.g. because of something blocking the way) -- ensure that the player
12192        is also allowed to move in the next frame (in old versions before 3.1.1,
12193        the player was forced to wait again for eight frames before next try) */
12194
12195     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12196       player->move_delay = 0;   /* allow direct movement in the next frame */
12197   }
12198
12199   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12200     player->move_delay = player->move_delay_value;
12201
12202   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12203   {
12204     TestIfPlayerTouchesBadThing(jx, jy);
12205     TestIfPlayerTouchesCustomElement(jx, jy);
12206   }
12207
12208   if (!player->active)
12209     RemovePlayer(player);
12210
12211   return moved;
12212 }
12213
12214 void ScrollPlayer(struct PlayerInfo *player, int mode)
12215 {
12216   int jx = player->jx, jy = player->jy;
12217   int last_jx = player->last_jx, last_jy = player->last_jy;
12218   int move_stepsize = TILEX / player->move_delay_value;
12219
12220   if (!player->active)
12221     return;
12222
12223   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12224     return;
12225
12226   if (mode == SCROLL_INIT)
12227   {
12228     player->actual_frame_counter = FrameCounter;
12229     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12230
12231     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12232         Feld[last_jx][last_jy] == EL_EMPTY)
12233     {
12234       int last_field_block_delay = 0;   /* start with no blocking at all */
12235       int block_delay_adjustment = player->block_delay_adjustment;
12236
12237       /* if player blocks last field, add delay for exactly one move */
12238       if (player->block_last_field)
12239       {
12240         last_field_block_delay += player->move_delay_value;
12241
12242         /* when blocking enabled, prevent moving up despite gravity */
12243         if (player->gravity && player->MovDir == MV_UP)
12244           block_delay_adjustment = -1;
12245       }
12246
12247       /* add block delay adjustment (also possible when not blocking) */
12248       last_field_block_delay += block_delay_adjustment;
12249
12250       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12251       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12252     }
12253
12254     if (player->MovPos != 0)    /* player has not yet reached destination */
12255       return;
12256   }
12257   else if (!FrameReached(&player->actual_frame_counter, 1))
12258     return;
12259
12260   if (player->MovPos != 0)
12261   {
12262     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12263     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12264
12265     /* before DrawPlayer() to draw correct player graphic for this case */
12266     if (player->MovPos == 0)
12267       CheckGravityMovement(player);
12268   }
12269
12270   if (player->MovPos == 0)      /* player reached destination field */
12271   {
12272     if (player->move_delay_reset_counter > 0)
12273     {
12274       player->move_delay_reset_counter--;
12275
12276       if (player->move_delay_reset_counter == 0)
12277       {
12278         /* continue with normal speed after quickly moving through gate */
12279         HALVE_PLAYER_SPEED(player);
12280
12281         /* be able to make the next move without delay */
12282         player->move_delay = 0;
12283       }
12284     }
12285
12286     player->last_jx = jx;
12287     player->last_jy = jy;
12288
12289     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12290         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12291         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12292         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12293         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12294         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12295         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12296         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12297     {
12298       DrawPlayer(player);       /* needed here only to cleanup last field */
12299       RemovePlayer(player);
12300
12301       if (local_player->friends_still_needed == 0 ||
12302           IS_SP_ELEMENT(Feld[jx][jy]))
12303         PlayerWins(player);
12304     }
12305
12306     /* this breaks one level: "machine", level 000 */
12307     {
12308       int move_direction = player->MovDir;
12309       int enter_side = MV_DIR_OPPOSITE(move_direction);
12310       int leave_side = move_direction;
12311       int old_jx = last_jx;
12312       int old_jy = last_jy;
12313       int old_element = Feld[old_jx][old_jy];
12314       int new_element = Feld[jx][jy];
12315
12316       if (IS_CUSTOM_ELEMENT(old_element))
12317         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12318                                    CE_LEFT_BY_PLAYER,
12319                                    player->index_bit, leave_side);
12320
12321       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12322                                           CE_PLAYER_LEAVES_X,
12323                                           player->index_bit, leave_side);
12324
12325       if (IS_CUSTOM_ELEMENT(new_element))
12326         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12327                                    player->index_bit, enter_side);
12328
12329       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12330                                           CE_PLAYER_ENTERS_X,
12331                                           player->index_bit, enter_side);
12332
12333       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12334                                         CE_MOVE_OF_X, move_direction);
12335     }
12336
12337     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12338     {
12339       TestIfPlayerTouchesBadThing(jx, jy);
12340       TestIfPlayerTouchesCustomElement(jx, jy);
12341
12342       /* needed because pushed element has not yet reached its destination,
12343          so it would trigger a change event at its previous field location */
12344       if (!player->is_pushing)
12345         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12346
12347       if (!player->active)
12348         RemovePlayer(player);
12349     }
12350
12351     if (!local_player->LevelSolved && level.use_step_counter)
12352     {
12353       int i;
12354
12355       TimePlayed++;
12356
12357       if (TimeLeft > 0)
12358       {
12359         TimeLeft--;
12360
12361         if (TimeLeft <= 10 && setup.time_limit)
12362           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12363
12364         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12365
12366         DisplayGameControlValues();
12367
12368         if (!TimeLeft && setup.time_limit)
12369           for (i = 0; i < MAX_PLAYERS; i++)
12370             KillPlayer(&stored_player[i]);
12371       }
12372       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12373       {
12374         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12375
12376         DisplayGameControlValues();
12377       }
12378     }
12379
12380     if (tape.single_step && tape.recording && !tape.pausing &&
12381         !player->programmed_action)
12382       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12383
12384     if (!player->programmed_action)
12385       CheckSaveEngineSnapshot(player);
12386   }
12387 }
12388
12389 void ScrollScreen(struct PlayerInfo *player, int mode)
12390 {
12391   static unsigned int screen_frame_counter = 0;
12392
12393   if (mode == SCROLL_INIT)
12394   {
12395     /* set scrolling step size according to actual player's moving speed */
12396     ScrollStepSize = TILEX / player->move_delay_value;
12397
12398     screen_frame_counter = FrameCounter;
12399     ScreenMovDir = player->MovDir;
12400     ScreenMovPos = player->MovPos;
12401     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12402     return;
12403   }
12404   else if (!FrameReached(&screen_frame_counter, 1))
12405     return;
12406
12407   if (ScreenMovPos)
12408   {
12409     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12410     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12411     redraw_mask |= REDRAW_FIELD;
12412   }
12413   else
12414     ScreenMovDir = MV_NONE;
12415 }
12416
12417 void TestIfPlayerTouchesCustomElement(int x, int y)
12418 {
12419   static int xy[4][2] =
12420   {
12421     { 0, -1 },
12422     { -1, 0 },
12423     { +1, 0 },
12424     { 0, +1 }
12425   };
12426   static int trigger_sides[4][2] =
12427   {
12428     /* center side       border side */
12429     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12430     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12431     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12432     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12433   };
12434   static int touch_dir[4] =
12435   {
12436     MV_LEFT | MV_RIGHT,
12437     MV_UP   | MV_DOWN,
12438     MV_UP   | MV_DOWN,
12439     MV_LEFT | MV_RIGHT
12440   };
12441   int center_element = Feld[x][y];      /* should always be non-moving! */
12442   int i;
12443
12444   for (i = 0; i < NUM_DIRECTIONS; i++)
12445   {
12446     int xx = x + xy[i][0];
12447     int yy = y + xy[i][1];
12448     int center_side = trigger_sides[i][0];
12449     int border_side = trigger_sides[i][1];
12450     int border_element;
12451
12452     if (!IN_LEV_FIELD(xx, yy))
12453       continue;
12454
12455     if (IS_PLAYER(x, y))                /* player found at center element */
12456     {
12457       struct PlayerInfo *player = PLAYERINFO(x, y);
12458
12459       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12460         border_element = Feld[xx][yy];          /* may be moving! */
12461       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12462         border_element = Feld[xx][yy];
12463       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12464         border_element = MovingOrBlocked2Element(xx, yy);
12465       else
12466         continue;               /* center and border element do not touch */
12467
12468       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12469                                  player->index_bit, border_side);
12470       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12471                                           CE_PLAYER_TOUCHES_X,
12472                                           player->index_bit, border_side);
12473
12474       {
12475         /* use player element that is initially defined in the level playfield,
12476            not the player element that corresponds to the runtime player number
12477            (example: a level that contains EL_PLAYER_3 as the only player would
12478            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12479         int player_element = PLAYERINFO(x, y)->initial_element;
12480
12481         CheckElementChangeBySide(xx, yy, border_element, player_element,
12482                                  CE_TOUCHING_X, border_side);
12483       }
12484     }
12485     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12486     {
12487       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12488
12489       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12490       {
12491         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12492           continue;             /* center and border element do not touch */
12493       }
12494
12495       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12496                                  player->index_bit, center_side);
12497       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12498                                           CE_PLAYER_TOUCHES_X,
12499                                           player->index_bit, center_side);
12500
12501       {
12502         /* use player element that is initially defined in the level playfield,
12503            not the player element that corresponds to the runtime player number
12504            (example: a level that contains EL_PLAYER_3 as the only player would
12505            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12506         int player_element = PLAYERINFO(xx, yy)->initial_element;
12507
12508         CheckElementChangeBySide(x, y, center_element, player_element,
12509                                  CE_TOUCHING_X, center_side);
12510       }
12511
12512       break;
12513     }
12514   }
12515 }
12516
12517 void TestIfElementTouchesCustomElement(int x, int y)
12518 {
12519   static int xy[4][2] =
12520   {
12521     { 0, -1 },
12522     { -1, 0 },
12523     { +1, 0 },
12524     { 0, +1 }
12525   };
12526   static int trigger_sides[4][2] =
12527   {
12528     /* center side      border side */
12529     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12530     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12531     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12532     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12533   };
12534   static int touch_dir[4] =
12535   {
12536     MV_LEFT | MV_RIGHT,
12537     MV_UP   | MV_DOWN,
12538     MV_UP   | MV_DOWN,
12539     MV_LEFT | MV_RIGHT
12540   };
12541   boolean change_center_element = FALSE;
12542   int center_element = Feld[x][y];      /* should always be non-moving! */
12543   int border_element_old[NUM_DIRECTIONS];
12544   int i;
12545
12546   for (i = 0; i < NUM_DIRECTIONS; i++)
12547   {
12548     int xx = x + xy[i][0];
12549     int yy = y + xy[i][1];
12550     int border_element;
12551
12552     border_element_old[i] = -1;
12553
12554     if (!IN_LEV_FIELD(xx, yy))
12555       continue;
12556
12557     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12558       border_element = Feld[xx][yy];    /* may be moving! */
12559     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12560       border_element = Feld[xx][yy];
12561     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12562       border_element = MovingOrBlocked2Element(xx, yy);
12563     else
12564       continue;                 /* center and border element do not touch */
12565
12566     border_element_old[i] = border_element;
12567   }
12568
12569   for (i = 0; i < NUM_DIRECTIONS; i++)
12570   {
12571     int xx = x + xy[i][0];
12572     int yy = y + xy[i][1];
12573     int center_side = trigger_sides[i][0];
12574     int border_element = border_element_old[i];
12575
12576     if (border_element == -1)
12577       continue;
12578
12579     /* check for change of border element */
12580     CheckElementChangeBySide(xx, yy, border_element, center_element,
12581                              CE_TOUCHING_X, center_side);
12582
12583     /* (center element cannot be player, so we dont have to check this here) */
12584   }
12585
12586   for (i = 0; i < NUM_DIRECTIONS; i++)
12587   {
12588     int xx = x + xy[i][0];
12589     int yy = y + xy[i][1];
12590     int border_side = trigger_sides[i][1];
12591     int border_element = border_element_old[i];
12592
12593     if (border_element == -1)
12594       continue;
12595
12596     /* check for change of center element (but change it only once) */
12597     if (!change_center_element)
12598       change_center_element =
12599         CheckElementChangeBySide(x, y, center_element, border_element,
12600                                  CE_TOUCHING_X, border_side);
12601
12602     if (IS_PLAYER(xx, yy))
12603     {
12604       /* use player element that is initially defined in the level playfield,
12605          not the player element that corresponds to the runtime player number
12606          (example: a level that contains EL_PLAYER_3 as the only player would
12607          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12608       int player_element = PLAYERINFO(xx, yy)->initial_element;
12609
12610       CheckElementChangeBySide(x, y, center_element, player_element,
12611                                CE_TOUCHING_X, border_side);
12612     }
12613   }
12614 }
12615
12616 void TestIfElementHitsCustomElement(int x, int y, int direction)
12617 {
12618   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12619   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12620   int hitx = x + dx, hity = y + dy;
12621   int hitting_element = Feld[x][y];
12622   int touched_element;
12623
12624   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12625     return;
12626
12627   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12628                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12629
12630   if (IN_LEV_FIELD(hitx, hity))
12631   {
12632     int opposite_direction = MV_DIR_OPPOSITE(direction);
12633     int hitting_side = direction;
12634     int touched_side = opposite_direction;
12635     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12636                           MovDir[hitx][hity] != direction ||
12637                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12638
12639     object_hit = TRUE;
12640
12641     if (object_hit)
12642     {
12643       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12644                                CE_HITTING_X, touched_side);
12645
12646       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12647                                CE_HIT_BY_X, hitting_side);
12648
12649       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12650                                CE_HIT_BY_SOMETHING, opposite_direction);
12651
12652       if (IS_PLAYER(hitx, hity))
12653       {
12654         /* use player element that is initially defined in the level playfield,
12655            not the player element that corresponds to the runtime player number
12656            (example: a level that contains EL_PLAYER_3 as the only player would
12657            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12658         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12659
12660         CheckElementChangeBySide(x, y, hitting_element, player_element,
12661                                  CE_HITTING_X, touched_side);
12662       }
12663     }
12664   }
12665
12666   /* "hitting something" is also true when hitting the playfield border */
12667   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12668                            CE_HITTING_SOMETHING, direction);
12669 }
12670
12671 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12672 {
12673   int i, kill_x = -1, kill_y = -1;
12674
12675   int bad_element = -1;
12676   static int test_xy[4][2] =
12677   {
12678     { 0, -1 },
12679     { -1, 0 },
12680     { +1, 0 },
12681     { 0, +1 }
12682   };
12683   static int test_dir[4] =
12684   {
12685     MV_UP,
12686     MV_LEFT,
12687     MV_RIGHT,
12688     MV_DOWN
12689   };
12690
12691   for (i = 0; i < NUM_DIRECTIONS; i++)
12692   {
12693     int test_x, test_y, test_move_dir, test_element;
12694
12695     test_x = good_x + test_xy[i][0];
12696     test_y = good_y + test_xy[i][1];
12697
12698     if (!IN_LEV_FIELD(test_x, test_y))
12699       continue;
12700
12701     test_move_dir =
12702       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12703
12704     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12705
12706     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12707        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12708     */
12709     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12710         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12711     {
12712       kill_x = test_x;
12713       kill_y = test_y;
12714       bad_element = test_element;
12715
12716       break;
12717     }
12718   }
12719
12720   if (kill_x != -1 || kill_y != -1)
12721   {
12722     if (IS_PLAYER(good_x, good_y))
12723     {
12724       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12725
12726       if (player->shield_deadly_time_left > 0 &&
12727           !IS_INDESTRUCTIBLE(bad_element))
12728         Bang(kill_x, kill_y);
12729       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12730         KillPlayer(player);
12731     }
12732     else
12733       Bang(good_x, good_y);
12734   }
12735 }
12736
12737 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12738 {
12739   int i, kill_x = -1, kill_y = -1;
12740   int bad_element = Feld[bad_x][bad_y];
12741   static int test_xy[4][2] =
12742   {
12743     { 0, -1 },
12744     { -1, 0 },
12745     { +1, 0 },
12746     { 0, +1 }
12747   };
12748   static int touch_dir[4] =
12749   {
12750     MV_LEFT | MV_RIGHT,
12751     MV_UP   | MV_DOWN,
12752     MV_UP   | MV_DOWN,
12753     MV_LEFT | MV_RIGHT
12754   };
12755   static int test_dir[4] =
12756   {
12757     MV_UP,
12758     MV_LEFT,
12759     MV_RIGHT,
12760     MV_DOWN
12761   };
12762
12763   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12764     return;
12765
12766   for (i = 0; i < NUM_DIRECTIONS; i++)
12767   {
12768     int test_x, test_y, test_move_dir, test_element;
12769
12770     test_x = bad_x + test_xy[i][0];
12771     test_y = bad_y + test_xy[i][1];
12772
12773     if (!IN_LEV_FIELD(test_x, test_y))
12774       continue;
12775
12776     test_move_dir =
12777       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12778
12779     test_element = Feld[test_x][test_y];
12780
12781     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12782        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12783     */
12784     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12785         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12786     {
12787       /* good thing is player or penguin that does not move away */
12788       if (IS_PLAYER(test_x, test_y))
12789       {
12790         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12791
12792         if (bad_element == EL_ROBOT && player->is_moving)
12793           continue;     /* robot does not kill player if he is moving */
12794
12795         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12796         {
12797           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12798             continue;           /* center and border element do not touch */
12799         }
12800
12801         kill_x = test_x;
12802         kill_y = test_y;
12803
12804         break;
12805       }
12806       else if (test_element == EL_PENGUIN)
12807       {
12808         kill_x = test_x;
12809         kill_y = test_y;
12810
12811         break;
12812       }
12813     }
12814   }
12815
12816   if (kill_x != -1 || kill_y != -1)
12817   {
12818     if (IS_PLAYER(kill_x, kill_y))
12819     {
12820       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12821
12822       if (player->shield_deadly_time_left > 0 &&
12823           !IS_INDESTRUCTIBLE(bad_element))
12824         Bang(bad_x, bad_y);
12825       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12826         KillPlayer(player);
12827     }
12828     else
12829       Bang(kill_x, kill_y);
12830   }
12831 }
12832
12833 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12834 {
12835   int bad_element = Feld[bad_x][bad_y];
12836   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12837   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12838   int test_x = bad_x + dx, test_y = bad_y + dy;
12839   int test_move_dir, test_element;
12840   int kill_x = -1, kill_y = -1;
12841
12842   if (!IN_LEV_FIELD(test_x, test_y))
12843     return;
12844
12845   test_move_dir =
12846     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12847
12848   test_element = Feld[test_x][test_y];
12849
12850   if (test_move_dir != bad_move_dir)
12851   {
12852     /* good thing can be player or penguin that does not move away */
12853     if (IS_PLAYER(test_x, test_y))
12854     {
12855       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12856
12857       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12858          player as being hit when he is moving towards the bad thing, because
12859          the "get hit by" condition would be lost after the player stops) */
12860       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12861         return;         /* player moves away from bad thing */
12862
12863       kill_x = test_x;
12864       kill_y = test_y;
12865     }
12866     else if (test_element == EL_PENGUIN)
12867     {
12868       kill_x = test_x;
12869       kill_y = test_y;
12870     }
12871   }
12872
12873   if (kill_x != -1 || kill_y != -1)
12874   {
12875     if (IS_PLAYER(kill_x, kill_y))
12876     {
12877       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12878
12879       if (player->shield_deadly_time_left > 0 &&
12880           !IS_INDESTRUCTIBLE(bad_element))
12881         Bang(bad_x, bad_y);
12882       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12883         KillPlayer(player);
12884     }
12885     else
12886       Bang(kill_x, kill_y);
12887   }
12888 }
12889
12890 void TestIfPlayerTouchesBadThing(int x, int y)
12891 {
12892   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12893 }
12894
12895 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12896 {
12897   TestIfGoodThingHitsBadThing(x, y, move_dir);
12898 }
12899
12900 void TestIfBadThingTouchesPlayer(int x, int y)
12901 {
12902   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12903 }
12904
12905 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12906 {
12907   TestIfBadThingHitsGoodThing(x, y, move_dir);
12908 }
12909
12910 void TestIfFriendTouchesBadThing(int x, int y)
12911 {
12912   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12913 }
12914
12915 void TestIfBadThingTouchesFriend(int x, int y)
12916 {
12917   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12918 }
12919
12920 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12921 {
12922   int i, kill_x = bad_x, kill_y = bad_y;
12923   static int xy[4][2] =
12924   {
12925     { 0, -1 },
12926     { -1, 0 },
12927     { +1, 0 },
12928     { 0, +1 }
12929   };
12930
12931   for (i = 0; i < NUM_DIRECTIONS; i++)
12932   {
12933     int x, y, element;
12934
12935     x = bad_x + xy[i][0];
12936     y = bad_y + xy[i][1];
12937     if (!IN_LEV_FIELD(x, y))
12938       continue;
12939
12940     element = Feld[x][y];
12941     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12942         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12943     {
12944       kill_x = x;
12945       kill_y = y;
12946       break;
12947     }
12948   }
12949
12950   if (kill_x != bad_x || kill_y != bad_y)
12951     Bang(bad_x, bad_y);
12952 }
12953
12954 void KillPlayer(struct PlayerInfo *player)
12955 {
12956   int jx = player->jx, jy = player->jy;
12957
12958   if (!player->active)
12959     return;
12960
12961 #if 0
12962   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12963          player->killed, player->active, player->reanimated);
12964 #endif
12965
12966   /* the following code was introduced to prevent an infinite loop when calling
12967      -> Bang()
12968      -> CheckTriggeredElementChangeExt()
12969      -> ExecuteCustomElementAction()
12970      -> KillPlayer()
12971      -> (infinitely repeating the above sequence of function calls)
12972      which occurs when killing the player while having a CE with the setting
12973      "kill player X when explosion of <player X>"; the solution using a new
12974      field "player->killed" was chosen for backwards compatibility, although
12975      clever use of the fields "player->active" etc. would probably also work */
12976 #if 1
12977   if (player->killed)
12978     return;
12979 #endif
12980
12981   player->killed = TRUE;
12982
12983   /* remove accessible field at the player's position */
12984   Feld[jx][jy] = EL_EMPTY;
12985
12986   /* deactivate shield (else Bang()/Explode() would not work right) */
12987   player->shield_normal_time_left = 0;
12988   player->shield_deadly_time_left = 0;
12989
12990 #if 0
12991   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12992          player->killed, player->active, player->reanimated);
12993 #endif
12994
12995   Bang(jx, jy);
12996
12997 #if 0
12998   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12999          player->killed, player->active, player->reanimated);
13000 #endif
13001
13002   if (player->reanimated)       /* killed player may have been reanimated */
13003     player->killed = player->reanimated = FALSE;
13004   else
13005     BuryPlayer(player);
13006 }
13007
13008 static void KillPlayerUnlessEnemyProtected(int x, int y)
13009 {
13010   if (!PLAYER_ENEMY_PROTECTED(x, y))
13011     KillPlayer(PLAYERINFO(x, y));
13012 }
13013
13014 static void KillPlayerUnlessExplosionProtected(int x, int y)
13015 {
13016   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13017     KillPlayer(PLAYERINFO(x, y));
13018 }
13019
13020 void BuryPlayer(struct PlayerInfo *player)
13021 {
13022   int jx = player->jx, jy = player->jy;
13023
13024   if (!player->active)
13025     return;
13026
13027   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13028   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13029
13030   player->GameOver = TRUE;
13031   RemovePlayer(player);
13032 }
13033
13034 void RemovePlayer(struct PlayerInfo *player)
13035 {
13036   int jx = player->jx, jy = player->jy;
13037   int i, found = FALSE;
13038
13039   player->present = FALSE;
13040   player->active = FALSE;
13041
13042   if (!ExplodeField[jx][jy])
13043     StorePlayer[jx][jy] = 0;
13044
13045   if (player->is_moving)
13046     TEST_DrawLevelField(player->last_jx, player->last_jy);
13047
13048   for (i = 0; i < MAX_PLAYERS; i++)
13049     if (stored_player[i].active)
13050       found = TRUE;
13051
13052   if (!found)
13053     AllPlayersGone = TRUE;
13054
13055   ExitX = ZX = jx;
13056   ExitY = ZY = jy;
13057 }
13058
13059 static void setFieldForSnapping(int x, int y, int element, int direction)
13060 {
13061   struct ElementInfo *ei = &element_info[element];
13062   int direction_bit = MV_DIR_TO_BIT(direction);
13063   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13064   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13065                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13066
13067   Feld[x][y] = EL_ELEMENT_SNAPPING;
13068   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13069
13070   ResetGfxAnimation(x, y);
13071
13072   GfxElement[x][y] = element;
13073   GfxAction[x][y] = action;
13074   GfxDir[x][y] = direction;
13075   GfxFrame[x][y] = -1;
13076 }
13077
13078 /*
13079   =============================================================================
13080   checkDiagonalPushing()
13081   -----------------------------------------------------------------------------
13082   check if diagonal input device direction results in pushing of object
13083   (by checking if the alternative direction is walkable, diggable, ...)
13084   =============================================================================
13085 */
13086
13087 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13088                                     int x, int y, int real_dx, int real_dy)
13089 {
13090   int jx, jy, dx, dy, xx, yy;
13091
13092   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13093     return TRUE;
13094
13095   /* diagonal direction: check alternative direction */
13096   jx = player->jx;
13097   jy = player->jy;
13098   dx = x - jx;
13099   dy = y - jy;
13100   xx = jx + (dx == 0 ? real_dx : 0);
13101   yy = jy + (dy == 0 ? real_dy : 0);
13102
13103   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13104 }
13105
13106 /*
13107   =============================================================================
13108   DigField()
13109   -----------------------------------------------------------------------------
13110   x, y:                 field next to player (non-diagonal) to try to dig to
13111   real_dx, real_dy:     direction as read from input device (can be diagonal)
13112   =============================================================================
13113 */
13114
13115 static int DigField(struct PlayerInfo *player,
13116                     int oldx, int oldy, int x, int y,
13117                     int real_dx, int real_dy, int mode)
13118 {
13119   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13120   boolean player_was_pushing = player->is_pushing;
13121   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13122   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13123   int jx = oldx, jy = oldy;
13124   int dx = x - jx, dy = y - jy;
13125   int nextx = x + dx, nexty = y + dy;
13126   int move_direction = (dx == -1 ? MV_LEFT  :
13127                         dx == +1 ? MV_RIGHT :
13128                         dy == -1 ? MV_UP    :
13129                         dy == +1 ? MV_DOWN  : MV_NONE);
13130   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13131   int dig_side = MV_DIR_OPPOSITE(move_direction);
13132   int old_element = Feld[jx][jy];
13133   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13134   int collect_count;
13135
13136   if (is_player)                /* function can also be called by EL_PENGUIN */
13137   {
13138     if (player->MovPos == 0)
13139     {
13140       player->is_digging = FALSE;
13141       player->is_collecting = FALSE;
13142     }
13143
13144     if (player->MovPos == 0)    /* last pushing move finished */
13145       player->is_pushing = FALSE;
13146
13147     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13148     {
13149       player->is_switching = FALSE;
13150       player->push_delay = -1;
13151
13152       return MP_NO_ACTION;
13153     }
13154   }
13155
13156   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13157     old_element = Back[jx][jy];
13158
13159   /* in case of element dropped at player position, check background */
13160   else if (Back[jx][jy] != EL_EMPTY &&
13161            game.engine_version >= VERSION_IDENT(2,2,0,0))
13162     old_element = Back[jx][jy];
13163
13164   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13165     return MP_NO_ACTION;        /* field has no opening in this direction */
13166
13167   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13168     return MP_NO_ACTION;        /* field has no opening in this direction */
13169
13170   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13171   {
13172     SplashAcid(x, y);
13173
13174     Feld[jx][jy] = player->artwork_element;
13175     InitMovingField(jx, jy, MV_DOWN);
13176     Store[jx][jy] = EL_ACID;
13177     ContinueMoving(jx, jy);
13178     BuryPlayer(player);
13179
13180     return MP_DONT_RUN_INTO;
13181   }
13182
13183   if (player_can_move && DONT_RUN_INTO(element))
13184   {
13185     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13186
13187     return MP_DONT_RUN_INTO;
13188   }
13189
13190   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13191     return MP_NO_ACTION;
13192
13193   collect_count = element_info[element].collect_count_initial;
13194
13195   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13196     return MP_NO_ACTION;
13197
13198   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13199     player_can_move = player_can_move_or_snap;
13200
13201   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13202       game.engine_version >= VERSION_IDENT(2,2,0,0))
13203   {
13204     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13205                                player->index_bit, dig_side);
13206     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13207                                         player->index_bit, dig_side);
13208
13209     if (element == EL_DC_LANDMINE)
13210       Bang(x, y);
13211
13212     if (Feld[x][y] != element)          /* field changed by snapping */
13213       return MP_ACTION;
13214
13215     return MP_NO_ACTION;
13216   }
13217
13218   if (player->gravity && is_player && !player->is_auto_moving &&
13219       canFallDown(player) && move_direction != MV_DOWN &&
13220       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13221     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13222
13223   if (player_can_move &&
13224       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13225   {
13226     int sound_element = SND_ELEMENT(element);
13227     int sound_action = ACTION_WALKING;
13228
13229     if (IS_RND_GATE(element))
13230     {
13231       if (!player->key[RND_GATE_NR(element)])
13232         return MP_NO_ACTION;
13233     }
13234     else if (IS_RND_GATE_GRAY(element))
13235     {
13236       if (!player->key[RND_GATE_GRAY_NR(element)])
13237         return MP_NO_ACTION;
13238     }
13239     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13240     {
13241       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13242         return MP_NO_ACTION;
13243     }
13244     else if (element == EL_EXIT_OPEN ||
13245              element == EL_EM_EXIT_OPEN ||
13246              element == EL_EM_EXIT_OPENING ||
13247              element == EL_STEEL_EXIT_OPEN ||
13248              element == EL_EM_STEEL_EXIT_OPEN ||
13249              element == EL_EM_STEEL_EXIT_OPENING ||
13250              element == EL_SP_EXIT_OPEN ||
13251              element == EL_SP_EXIT_OPENING)
13252     {
13253       sound_action = ACTION_PASSING;    /* player is passing exit */
13254     }
13255     else if (element == EL_EMPTY)
13256     {
13257       sound_action = ACTION_MOVING;             /* nothing to walk on */
13258     }
13259
13260     /* play sound from background or player, whatever is available */
13261     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13262       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13263     else
13264       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13265   }
13266   else if (player_can_move &&
13267            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13268   {
13269     if (!ACCESS_FROM(element, opposite_direction))
13270       return MP_NO_ACTION;      /* field not accessible from this direction */
13271
13272     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13273       return MP_NO_ACTION;
13274
13275     if (IS_EM_GATE(element))
13276     {
13277       if (!player->key[EM_GATE_NR(element)])
13278         return MP_NO_ACTION;
13279     }
13280     else if (IS_EM_GATE_GRAY(element))
13281     {
13282       if (!player->key[EM_GATE_GRAY_NR(element)])
13283         return MP_NO_ACTION;
13284     }
13285     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13286     {
13287       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13288         return MP_NO_ACTION;
13289     }
13290     else if (IS_EMC_GATE(element))
13291     {
13292       if (!player->key[EMC_GATE_NR(element)])
13293         return MP_NO_ACTION;
13294     }
13295     else if (IS_EMC_GATE_GRAY(element))
13296     {
13297       if (!player->key[EMC_GATE_GRAY_NR(element)])
13298         return MP_NO_ACTION;
13299     }
13300     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13301     {
13302       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13303         return MP_NO_ACTION;
13304     }
13305     else if (element == EL_DC_GATE_WHITE ||
13306              element == EL_DC_GATE_WHITE_GRAY ||
13307              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13308     {
13309       if (player->num_white_keys == 0)
13310         return MP_NO_ACTION;
13311
13312       player->num_white_keys--;
13313     }
13314     else if (IS_SP_PORT(element))
13315     {
13316       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13317           element == EL_SP_GRAVITY_PORT_RIGHT ||
13318           element == EL_SP_GRAVITY_PORT_UP ||
13319           element == EL_SP_GRAVITY_PORT_DOWN)
13320         player->gravity = !player->gravity;
13321       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13322                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13323                element == EL_SP_GRAVITY_ON_PORT_UP ||
13324                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13325         player->gravity = TRUE;
13326       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13327                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13328                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13329                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13330         player->gravity = FALSE;
13331     }
13332
13333     /* automatically move to the next field with double speed */
13334     player->programmed_action = move_direction;
13335
13336     if (player->move_delay_reset_counter == 0)
13337     {
13338       player->move_delay_reset_counter = 2;     /* two double speed steps */
13339
13340       DOUBLE_PLAYER_SPEED(player);
13341     }
13342
13343     PlayLevelSoundAction(x, y, ACTION_PASSING);
13344   }
13345   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13346   {
13347     RemoveField(x, y);
13348
13349     if (mode != DF_SNAP)
13350     {
13351       GfxElement[x][y] = GFX_ELEMENT(element);
13352       player->is_digging = TRUE;
13353     }
13354
13355     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13356
13357     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13358                                         player->index_bit, dig_side);
13359
13360     if (mode == DF_SNAP)
13361     {
13362       if (level.block_snap_field)
13363         setFieldForSnapping(x, y, element, move_direction);
13364       else
13365         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13366
13367       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13368                                           player->index_bit, dig_side);
13369     }
13370   }
13371   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13372   {
13373     RemoveField(x, y);
13374
13375     if (is_player && mode != DF_SNAP)
13376     {
13377       GfxElement[x][y] = element;
13378       player->is_collecting = TRUE;
13379     }
13380
13381     if (element == EL_SPEED_PILL)
13382     {
13383       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13384     }
13385     else if (element == EL_EXTRA_TIME && level.time > 0)
13386     {
13387       TimeLeft += level.extra_time;
13388
13389       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13390
13391       DisplayGameControlValues();
13392     }
13393     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13394     {
13395       player->shield_normal_time_left += level.shield_normal_time;
13396       if (element == EL_SHIELD_DEADLY)
13397         player->shield_deadly_time_left += level.shield_deadly_time;
13398     }
13399     else if (element == EL_DYNAMITE ||
13400              element == EL_EM_DYNAMITE ||
13401              element == EL_SP_DISK_RED)
13402     {
13403       if (player->inventory_size < MAX_INVENTORY_SIZE)
13404         player->inventory_element[player->inventory_size++] = element;
13405
13406       DrawGameDoorValues();
13407     }
13408     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13409     {
13410       player->dynabomb_count++;
13411       player->dynabombs_left++;
13412     }
13413     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13414     {
13415       player->dynabomb_size++;
13416     }
13417     else if (element == EL_DYNABOMB_INCREASE_POWER)
13418     {
13419       player->dynabomb_xl = TRUE;
13420     }
13421     else if (IS_KEY(element))
13422     {
13423       player->key[KEY_NR(element)] = TRUE;
13424
13425       DrawGameDoorValues();
13426     }
13427     else if (element == EL_DC_KEY_WHITE)
13428     {
13429       player->num_white_keys++;
13430
13431       /* display white keys? */
13432       /* DrawGameDoorValues(); */
13433     }
13434     else if (IS_ENVELOPE(element))
13435     {
13436       player->show_envelope = element;
13437     }
13438     else if (element == EL_EMC_LENSES)
13439     {
13440       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13441
13442       RedrawAllInvisibleElementsForLenses();
13443     }
13444     else if (element == EL_EMC_MAGNIFIER)
13445     {
13446       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13447
13448       RedrawAllInvisibleElementsForMagnifier();
13449     }
13450     else if (IS_DROPPABLE(element) ||
13451              IS_THROWABLE(element))     /* can be collected and dropped */
13452     {
13453       int i;
13454
13455       if (collect_count == 0)
13456         player->inventory_infinite_element = element;
13457       else
13458         for (i = 0; i < collect_count; i++)
13459           if (player->inventory_size < MAX_INVENTORY_SIZE)
13460             player->inventory_element[player->inventory_size++] = element;
13461
13462       DrawGameDoorValues();
13463     }
13464     else if (collect_count > 0)
13465     {
13466       local_player->gems_still_needed -= collect_count;
13467       if (local_player->gems_still_needed < 0)
13468         local_player->gems_still_needed = 0;
13469
13470       game.snapshot.collected_item = TRUE;
13471
13472       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13473
13474       DisplayGameControlValues();
13475     }
13476
13477     RaiseScoreElement(element);
13478     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13479
13480     if (is_player)
13481       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13482                                           player->index_bit, dig_side);
13483
13484     if (mode == DF_SNAP)
13485     {
13486       if (level.block_snap_field)
13487         setFieldForSnapping(x, y, element, move_direction);
13488       else
13489         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13490
13491       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13492                                           player->index_bit, dig_side);
13493     }
13494   }
13495   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13496   {
13497     if (mode == DF_SNAP && element != EL_BD_ROCK)
13498       return MP_NO_ACTION;
13499
13500     if (CAN_FALL(element) && dy)
13501       return MP_NO_ACTION;
13502
13503     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13504         !(element == EL_SPRING && level.use_spring_bug))
13505       return MP_NO_ACTION;
13506
13507     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13508         ((move_direction & MV_VERTICAL &&
13509           ((element_info[element].move_pattern & MV_LEFT &&
13510             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13511            (element_info[element].move_pattern & MV_RIGHT &&
13512             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13513          (move_direction & MV_HORIZONTAL &&
13514           ((element_info[element].move_pattern & MV_UP &&
13515             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13516            (element_info[element].move_pattern & MV_DOWN &&
13517             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13518       return MP_NO_ACTION;
13519
13520     /* do not push elements already moving away faster than player */
13521     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13522         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13523       return MP_NO_ACTION;
13524
13525     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13526     {
13527       if (player->push_delay_value == -1 || !player_was_pushing)
13528         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13529     }
13530     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13531     {
13532       if (player->push_delay_value == -1)
13533         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13534     }
13535     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13536     {
13537       if (!player->is_pushing)
13538         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13539     }
13540
13541     player->is_pushing = TRUE;
13542     player->is_active = TRUE;
13543
13544     if (!(IN_LEV_FIELD(nextx, nexty) &&
13545           (IS_FREE(nextx, nexty) ||
13546            (IS_SB_ELEMENT(element) &&
13547             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13548            (IS_CUSTOM_ELEMENT(element) &&
13549             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13550       return MP_NO_ACTION;
13551
13552     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13553       return MP_NO_ACTION;
13554
13555     if (player->push_delay == -1)       /* new pushing; restart delay */
13556       player->push_delay = 0;
13557
13558     if (player->push_delay < player->push_delay_value &&
13559         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13560         element != EL_SPRING && element != EL_BALLOON)
13561     {
13562       /* make sure that there is no move delay before next try to push */
13563       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13564         player->move_delay = 0;
13565
13566       return MP_NO_ACTION;
13567     }
13568
13569     if (IS_CUSTOM_ELEMENT(element) &&
13570         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13571     {
13572       if (!DigFieldByCE(nextx, nexty, element))
13573         return MP_NO_ACTION;
13574     }
13575
13576     if (IS_SB_ELEMENT(element))
13577     {
13578       if (element == EL_SOKOBAN_FIELD_FULL)
13579       {
13580         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13581         local_player->sokobanfields_still_needed++;
13582       }
13583
13584       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13585       {
13586         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13587         local_player->sokobanfields_still_needed--;
13588       }
13589
13590       Feld[x][y] = EL_SOKOBAN_OBJECT;
13591
13592       if (Back[x][y] == Back[nextx][nexty])
13593         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13594       else if (Back[x][y] != 0)
13595         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13596                                     ACTION_EMPTYING);
13597       else
13598         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13599                                     ACTION_FILLING);
13600
13601       if (local_player->sokobanfields_still_needed == 0 &&
13602           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13603       {
13604         PlayerWins(player);
13605
13606         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13607       }
13608     }
13609     else
13610       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13611
13612     InitMovingField(x, y, move_direction);
13613     GfxAction[x][y] = ACTION_PUSHING;
13614
13615     if (mode == DF_SNAP)
13616       ContinueMoving(x, y);
13617     else
13618       MovPos[x][y] = (dx != 0 ? dx : dy);
13619
13620     Pushed[x][y] = TRUE;
13621     Pushed[nextx][nexty] = TRUE;
13622
13623     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13624       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13625     else
13626       player->push_delay_value = -1;    /* get new value later */
13627
13628     /* check for element change _after_ element has been pushed */
13629     if (game.use_change_when_pushing_bug)
13630     {
13631       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13632                                  player->index_bit, dig_side);
13633       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13634                                           player->index_bit, dig_side);
13635     }
13636   }
13637   else if (IS_SWITCHABLE(element))
13638   {
13639     if (PLAYER_SWITCHING(player, x, y))
13640     {
13641       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13642                                           player->index_bit, dig_side);
13643
13644       return MP_ACTION;
13645     }
13646
13647     player->is_switching = TRUE;
13648     player->switch_x = x;
13649     player->switch_y = y;
13650
13651     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13652
13653     if (element == EL_ROBOT_WHEEL)
13654     {
13655       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13656       ZX = x;
13657       ZY = y;
13658
13659       game.robot_wheel_active = TRUE;
13660
13661       TEST_DrawLevelField(x, y);
13662     }
13663     else if (element == EL_SP_TERMINAL)
13664     {
13665       int xx, yy;
13666
13667       SCAN_PLAYFIELD(xx, yy)
13668       {
13669         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13670         {
13671           Bang(xx, yy);
13672         }
13673         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13674         {
13675           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13676
13677           ResetGfxAnimation(xx, yy);
13678           TEST_DrawLevelField(xx, yy);
13679         }
13680       }
13681     }
13682     else if (IS_BELT_SWITCH(element))
13683     {
13684       ToggleBeltSwitch(x, y);
13685     }
13686     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13687              element == EL_SWITCHGATE_SWITCH_DOWN ||
13688              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13689              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13690     {
13691       ToggleSwitchgateSwitch(x, y);
13692     }
13693     else if (element == EL_LIGHT_SWITCH ||
13694              element == EL_LIGHT_SWITCH_ACTIVE)
13695     {
13696       ToggleLightSwitch(x, y);
13697     }
13698     else if (element == EL_TIMEGATE_SWITCH ||
13699              element == EL_DC_TIMEGATE_SWITCH)
13700     {
13701       ActivateTimegateSwitch(x, y);
13702     }
13703     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13704              element == EL_BALLOON_SWITCH_RIGHT ||
13705              element == EL_BALLOON_SWITCH_UP    ||
13706              element == EL_BALLOON_SWITCH_DOWN  ||
13707              element == EL_BALLOON_SWITCH_NONE  ||
13708              element == EL_BALLOON_SWITCH_ANY)
13709     {
13710       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13711                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13712                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13713                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13714                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13715                              move_direction);
13716     }
13717     else if (element == EL_LAMP)
13718     {
13719       Feld[x][y] = EL_LAMP_ACTIVE;
13720       local_player->lights_still_needed--;
13721
13722       ResetGfxAnimation(x, y);
13723       TEST_DrawLevelField(x, y);
13724     }
13725     else if (element == EL_TIME_ORB_FULL)
13726     {
13727       Feld[x][y] = EL_TIME_ORB_EMPTY;
13728
13729       if (level.time > 0 || level.use_time_orb_bug)
13730       {
13731         TimeLeft += level.time_orb_time;
13732         game.no_time_limit = FALSE;
13733
13734         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13735
13736         DisplayGameControlValues();
13737       }
13738
13739       ResetGfxAnimation(x, y);
13740       TEST_DrawLevelField(x, y);
13741     }
13742     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13743              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13744     {
13745       int xx, yy;
13746
13747       game.ball_state = !game.ball_state;
13748
13749       SCAN_PLAYFIELD(xx, yy)
13750       {
13751         int e = Feld[xx][yy];
13752
13753         if (game.ball_state)
13754         {
13755           if (e == EL_EMC_MAGIC_BALL)
13756             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13757           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13758             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13759         }
13760         else
13761         {
13762           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13763             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13764           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13765             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13766         }
13767       }
13768     }
13769
13770     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13771                                         player->index_bit, dig_side);
13772
13773     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13774                                         player->index_bit, dig_side);
13775
13776     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13777                                         player->index_bit, dig_side);
13778
13779     return MP_ACTION;
13780   }
13781   else
13782   {
13783     if (!PLAYER_SWITCHING(player, x, y))
13784     {
13785       player->is_switching = TRUE;
13786       player->switch_x = x;
13787       player->switch_y = y;
13788
13789       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13790                                  player->index_bit, dig_side);
13791       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13792                                           player->index_bit, dig_side);
13793
13794       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13795                                  player->index_bit, dig_side);
13796       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13797                                           player->index_bit, dig_side);
13798     }
13799
13800     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13801                                player->index_bit, dig_side);
13802     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13803                                         player->index_bit, dig_side);
13804
13805     return MP_NO_ACTION;
13806   }
13807
13808   player->push_delay = -1;
13809
13810   if (is_player)                /* function can also be called by EL_PENGUIN */
13811   {
13812     if (Feld[x][y] != element)          /* really digged/collected something */
13813     {
13814       player->is_collecting = !player->is_digging;
13815       player->is_active = TRUE;
13816     }
13817   }
13818
13819   return MP_MOVING;
13820 }
13821
13822 static boolean DigFieldByCE(int x, int y, int digging_element)
13823 {
13824   int element = Feld[x][y];
13825
13826   if (!IS_FREE(x, y))
13827   {
13828     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13829                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13830                   ACTION_BREAKING);
13831
13832     /* no element can dig solid indestructible elements */
13833     if (IS_INDESTRUCTIBLE(element) &&
13834         !IS_DIGGABLE(element) &&
13835         !IS_COLLECTIBLE(element))
13836       return FALSE;
13837
13838     if (AmoebaNr[x][y] &&
13839         (element == EL_AMOEBA_FULL ||
13840          element == EL_BD_AMOEBA ||
13841          element == EL_AMOEBA_GROWING))
13842     {
13843       AmoebaCnt[AmoebaNr[x][y]]--;
13844       AmoebaCnt2[AmoebaNr[x][y]]--;
13845     }
13846
13847     if (IS_MOVING(x, y))
13848       RemoveMovingField(x, y);
13849     else
13850     {
13851       RemoveField(x, y);
13852       TEST_DrawLevelField(x, y);
13853     }
13854
13855     /* if digged element was about to explode, prevent the explosion */
13856     ExplodeField[x][y] = EX_TYPE_NONE;
13857
13858     PlayLevelSoundAction(x, y, action);
13859   }
13860
13861   Store[x][y] = EL_EMPTY;
13862
13863   /* this makes it possible to leave the removed element again */
13864   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13865     Store[x][y] = element;
13866
13867   return TRUE;
13868 }
13869
13870 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13871 {
13872   int jx = player->jx, jy = player->jy;
13873   int x = jx + dx, y = jy + dy;
13874   int snap_direction = (dx == -1 ? MV_LEFT  :
13875                         dx == +1 ? MV_RIGHT :
13876                         dy == -1 ? MV_UP    :
13877                         dy == +1 ? MV_DOWN  : MV_NONE);
13878   boolean can_continue_snapping = (level.continuous_snapping &&
13879                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13880
13881   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13882     return FALSE;
13883
13884   if (!player->active || !IN_LEV_FIELD(x, y))
13885     return FALSE;
13886
13887   if (dx && dy)
13888     return FALSE;
13889
13890   if (!dx && !dy)
13891   {
13892     if (player->MovPos == 0)
13893       player->is_pushing = FALSE;
13894
13895     player->is_snapping = FALSE;
13896
13897     if (player->MovPos == 0)
13898     {
13899       player->is_moving = FALSE;
13900       player->is_digging = FALSE;
13901       player->is_collecting = FALSE;
13902     }
13903
13904     return FALSE;
13905   }
13906
13907   /* prevent snapping with already pressed snap key when not allowed */
13908   if (player->is_snapping && !can_continue_snapping)
13909     return FALSE;
13910
13911   player->MovDir = snap_direction;
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   player->is_dropping = FALSE;
13921   player->is_dropping_pressed = FALSE;
13922   player->drop_pressed_delay = 0;
13923
13924   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13925     return FALSE;
13926
13927   player->is_snapping = TRUE;
13928   player->is_active = TRUE;
13929
13930   if (player->MovPos == 0)
13931   {
13932     player->is_moving = FALSE;
13933     player->is_digging = FALSE;
13934     player->is_collecting = FALSE;
13935   }
13936
13937   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13938     TEST_DrawLevelField(player->last_jx, player->last_jy);
13939
13940   TEST_DrawLevelField(x, y);
13941
13942   return TRUE;
13943 }
13944
13945 static boolean DropElement(struct PlayerInfo *player)
13946 {
13947   int old_element, new_element;
13948   int dropx = player->jx, dropy = player->jy;
13949   int drop_direction = player->MovDir;
13950   int drop_side = drop_direction;
13951   int drop_element = get_next_dropped_element(player);
13952
13953   player->is_dropping_pressed = TRUE;
13954
13955   /* do not drop an element on top of another element; when holding drop key
13956      pressed without moving, dropped element must move away before the next
13957      element can be dropped (this is especially important if the next element
13958      is dynamite, which can be placed on background for historical reasons) */
13959   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13960     return MP_ACTION;
13961
13962   if (IS_THROWABLE(drop_element))
13963   {
13964     dropx += GET_DX_FROM_DIR(drop_direction);
13965     dropy += GET_DY_FROM_DIR(drop_direction);
13966
13967     if (!IN_LEV_FIELD(dropx, dropy))
13968       return FALSE;
13969   }
13970
13971   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13972   new_element = drop_element;           /* default: no change when dropping */
13973
13974   /* check if player is active, not moving and ready to drop */
13975   if (!player->active || player->MovPos || player->drop_delay > 0)
13976     return FALSE;
13977
13978   /* check if player has anything that can be dropped */
13979   if (new_element == EL_UNDEFINED)
13980     return FALSE;
13981
13982   /* check if drop key was pressed long enough for EM style dynamite */
13983   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13984     return FALSE;
13985
13986   /* check if anything can be dropped at the current position */
13987   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13988     return FALSE;
13989
13990   /* collected custom elements can only be dropped on empty fields */
13991   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13992     return FALSE;
13993
13994   if (old_element != EL_EMPTY)
13995     Back[dropx][dropy] = old_element;   /* store old element on this field */
13996
13997   ResetGfxAnimation(dropx, dropy);
13998   ResetRandomAnimationValue(dropx, dropy);
13999
14000   if (player->inventory_size > 0 ||
14001       player->inventory_infinite_element != EL_UNDEFINED)
14002   {
14003     if (player->inventory_size > 0)
14004     {
14005       player->inventory_size--;
14006
14007       DrawGameDoorValues();
14008
14009       if (new_element == EL_DYNAMITE)
14010         new_element = EL_DYNAMITE_ACTIVE;
14011       else if (new_element == EL_EM_DYNAMITE)
14012         new_element = EL_EM_DYNAMITE_ACTIVE;
14013       else if (new_element == EL_SP_DISK_RED)
14014         new_element = EL_SP_DISK_RED_ACTIVE;
14015     }
14016
14017     Feld[dropx][dropy] = new_element;
14018
14019     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14020       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14021                           el2img(Feld[dropx][dropy]), 0);
14022
14023     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14024
14025     /* needed if previous element just changed to "empty" in the last frame */
14026     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14027
14028     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14029                                player->index_bit, drop_side);
14030     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14031                                         CE_PLAYER_DROPS_X,
14032                                         player->index_bit, drop_side);
14033
14034     TestIfElementTouchesCustomElement(dropx, dropy);
14035   }
14036   else          /* player is dropping a dyna bomb */
14037   {
14038     player->dynabombs_left--;
14039
14040     Feld[dropx][dropy] = new_element;
14041
14042     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14043       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14044                           el2img(Feld[dropx][dropy]), 0);
14045
14046     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14047   }
14048
14049   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14050     InitField_WithBug1(dropx, dropy, FALSE);
14051
14052   new_element = Feld[dropx][dropy];     /* element might have changed */
14053
14054   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14055       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14056   {
14057     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14058       MovDir[dropx][dropy] = drop_direction;
14059
14060     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14061
14062     /* do not cause impact style collision by dropping elements that can fall */
14063     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14064   }
14065
14066   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14067   player->is_dropping = TRUE;
14068
14069   player->drop_pressed_delay = 0;
14070   player->is_dropping_pressed = FALSE;
14071
14072   player->drop_x = dropx;
14073   player->drop_y = dropy;
14074
14075   return TRUE;
14076 }
14077
14078 /* ------------------------------------------------------------------------- */
14079 /* game sound playing functions                                              */
14080 /* ------------------------------------------------------------------------- */
14081
14082 static int *loop_sound_frame = NULL;
14083 static int *loop_sound_volume = NULL;
14084
14085 void InitPlayLevelSound()
14086 {
14087   int num_sounds = getSoundListSize();
14088
14089   checked_free(loop_sound_frame);
14090   checked_free(loop_sound_volume);
14091
14092   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14093   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14094 }
14095
14096 static void PlayLevelSound(int x, int y, int nr)
14097 {
14098   int sx = SCREENX(x), sy = SCREENY(y);
14099   int volume, stereo_position;
14100   int max_distance = 8;
14101   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14102
14103   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14104       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14105     return;
14106
14107   if (!IN_LEV_FIELD(x, y) ||
14108       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14109       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14110     return;
14111
14112   volume = SOUND_MAX_VOLUME;
14113
14114   if (!IN_SCR_FIELD(sx, sy))
14115   {
14116     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14117     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14118
14119     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14120   }
14121
14122   stereo_position = (SOUND_MAX_LEFT +
14123                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14124                      (SCR_FIELDX + 2 * max_distance));
14125
14126   if (IS_LOOP_SOUND(nr))
14127   {
14128     /* This assures that quieter loop sounds do not overwrite louder ones,
14129        while restarting sound volume comparison with each new game frame. */
14130
14131     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14132       return;
14133
14134     loop_sound_volume[nr] = volume;
14135     loop_sound_frame[nr] = FrameCounter;
14136   }
14137
14138   PlaySoundExt(nr, volume, stereo_position, type);
14139 }
14140
14141 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14142 {
14143   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14144                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14145                  y < LEVELY(BY1) ? LEVELY(BY1) :
14146                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14147                  sound_action);
14148 }
14149
14150 static void PlayLevelSoundAction(int x, int y, int action)
14151 {
14152   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14153 }
14154
14155 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14156 {
14157   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14158
14159   if (sound_effect != SND_UNDEFINED)
14160     PlayLevelSound(x, y, sound_effect);
14161 }
14162
14163 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14164                                               int action)
14165 {
14166   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14167
14168   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14169     PlayLevelSound(x, y, sound_effect);
14170 }
14171
14172 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14173 {
14174   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14175
14176   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14177     PlayLevelSound(x, y, sound_effect);
14178 }
14179
14180 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14181 {
14182   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14183
14184   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14185     StopSound(sound_effect);
14186 }
14187
14188 static void PlayLevelMusic()
14189 {
14190   if (levelset.music[level_nr] != MUS_UNDEFINED)
14191     PlayMusic(levelset.music[level_nr]);        /* from config file */
14192   else
14193     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14194 }
14195
14196 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14197 {
14198   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14199   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14200   int x = xx - 1 - offset;
14201   int y = yy - 1 - offset;
14202
14203   switch (sample)
14204   {
14205     case SAMPLE_blank:
14206       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14207       break;
14208
14209     case SAMPLE_roll:
14210       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14211       break;
14212
14213     case SAMPLE_stone:
14214       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14215       break;
14216
14217     case SAMPLE_nut:
14218       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14219       break;
14220
14221     case SAMPLE_crack:
14222       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14223       break;
14224
14225     case SAMPLE_bug:
14226       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14227       break;
14228
14229     case SAMPLE_tank:
14230       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14231       break;
14232
14233     case SAMPLE_android_clone:
14234       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14235       break;
14236
14237     case SAMPLE_android_move:
14238       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14239       break;
14240
14241     case SAMPLE_spring:
14242       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14243       break;
14244
14245     case SAMPLE_slurp:
14246       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14247       break;
14248
14249     case SAMPLE_eater:
14250       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14251       break;
14252
14253     case SAMPLE_eater_eat:
14254       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14255       break;
14256
14257     case SAMPLE_alien:
14258       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14259       break;
14260
14261     case SAMPLE_collect:
14262       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14263       break;
14264
14265     case SAMPLE_diamond:
14266       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14267       break;
14268
14269     case SAMPLE_squash:
14270       /* !!! CHECK THIS !!! */
14271 #if 1
14272       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14273 #else
14274       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14275 #endif
14276       break;
14277
14278     case SAMPLE_wonderfall:
14279       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14280       break;
14281
14282     case SAMPLE_drip:
14283       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14284       break;
14285
14286     case SAMPLE_push:
14287       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14288       break;
14289
14290     case SAMPLE_dirt:
14291       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14292       break;
14293
14294     case SAMPLE_acid:
14295       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14296       break;
14297
14298     case SAMPLE_ball:
14299       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14300       break;
14301
14302     case SAMPLE_grow:
14303       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14304       break;
14305
14306     case SAMPLE_wonder:
14307       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14308       break;
14309
14310     case SAMPLE_door:
14311       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14312       break;
14313
14314     case SAMPLE_exit_open:
14315       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14316       break;
14317
14318     case SAMPLE_exit_leave:
14319       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14320       break;
14321
14322     case SAMPLE_dynamite:
14323       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14324       break;
14325
14326     case SAMPLE_tick:
14327       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14328       break;
14329
14330     case SAMPLE_press:
14331       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14332       break;
14333
14334     case SAMPLE_wheel:
14335       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14336       break;
14337
14338     case SAMPLE_boom:
14339       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14340       break;
14341
14342     case SAMPLE_die:
14343       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14344       break;
14345
14346     case SAMPLE_time:
14347       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14348       break;
14349
14350     default:
14351       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14352       break;
14353   }
14354 }
14355
14356 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14357 {
14358   int element = map_element_SP_to_RND(element_sp);
14359   int action = map_action_SP_to_RND(action_sp);
14360   int offset = (setup.sp_show_border_elements ? 0 : 1);
14361   int x = xx - offset;
14362   int y = yy - offset;
14363
14364   PlayLevelSoundElementAction(x, y, element, action);
14365 }
14366
14367 void RaiseScore(int value)
14368 {
14369   local_player->score += value;
14370
14371   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14372
14373   DisplayGameControlValues();
14374 }
14375
14376 void RaiseScoreElement(int element)
14377 {
14378   switch (element)
14379   {
14380     case EL_EMERALD:
14381     case EL_BD_DIAMOND:
14382     case EL_EMERALD_YELLOW:
14383     case EL_EMERALD_RED:
14384     case EL_EMERALD_PURPLE:
14385     case EL_SP_INFOTRON:
14386       RaiseScore(level.score[SC_EMERALD]);
14387       break;
14388     case EL_DIAMOND:
14389       RaiseScore(level.score[SC_DIAMOND]);
14390       break;
14391     case EL_CRYSTAL:
14392       RaiseScore(level.score[SC_CRYSTAL]);
14393       break;
14394     case EL_PEARL:
14395       RaiseScore(level.score[SC_PEARL]);
14396       break;
14397     case EL_BUG:
14398     case EL_BD_BUTTERFLY:
14399     case EL_SP_ELECTRON:
14400       RaiseScore(level.score[SC_BUG]);
14401       break;
14402     case EL_SPACESHIP:
14403     case EL_BD_FIREFLY:
14404     case EL_SP_SNIKSNAK:
14405       RaiseScore(level.score[SC_SPACESHIP]);
14406       break;
14407     case EL_YAMYAM:
14408     case EL_DARK_YAMYAM:
14409       RaiseScore(level.score[SC_YAMYAM]);
14410       break;
14411     case EL_ROBOT:
14412       RaiseScore(level.score[SC_ROBOT]);
14413       break;
14414     case EL_PACMAN:
14415       RaiseScore(level.score[SC_PACMAN]);
14416       break;
14417     case EL_NUT:
14418       RaiseScore(level.score[SC_NUT]);
14419       break;
14420     case EL_DYNAMITE:
14421     case EL_EM_DYNAMITE:
14422     case EL_SP_DISK_RED:
14423     case EL_DYNABOMB_INCREASE_NUMBER:
14424     case EL_DYNABOMB_INCREASE_SIZE:
14425     case EL_DYNABOMB_INCREASE_POWER:
14426       RaiseScore(level.score[SC_DYNAMITE]);
14427       break;
14428     case EL_SHIELD_NORMAL:
14429     case EL_SHIELD_DEADLY:
14430       RaiseScore(level.score[SC_SHIELD]);
14431       break;
14432     case EL_EXTRA_TIME:
14433       RaiseScore(level.extra_time_score);
14434       break;
14435     case EL_KEY_1:
14436     case EL_KEY_2:
14437     case EL_KEY_3:
14438     case EL_KEY_4:
14439     case EL_EM_KEY_1:
14440     case EL_EM_KEY_2:
14441     case EL_EM_KEY_3:
14442     case EL_EM_KEY_4:
14443     case EL_EMC_KEY_5:
14444     case EL_EMC_KEY_6:
14445     case EL_EMC_KEY_7:
14446     case EL_EMC_KEY_8:
14447     case EL_DC_KEY_WHITE:
14448       RaiseScore(level.score[SC_KEY]);
14449       break;
14450     default:
14451       RaiseScore(element_info[element].collect_score);
14452       break;
14453   }
14454 }
14455
14456 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14457 {
14458   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14459   {
14460     /* closing door required in case of envelope style request dialogs */
14461     if (!skip_request)
14462       CloseDoor(DOOR_CLOSE_1);
14463
14464 #if defined(NETWORK_AVALIABLE)
14465     if (options.network)
14466       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14467     else
14468 #endif
14469     {
14470       if (quick_quit)
14471         FadeSkipNextFadeIn();
14472
14473       SetGameStatus(GAME_MODE_MAIN);
14474
14475       DrawMainMenu();
14476     }
14477   }
14478   else          /* continue playing the game */
14479   {
14480     if (tape.playing && tape.deactivate_display)
14481       TapeDeactivateDisplayOff(TRUE);
14482
14483     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14484
14485     if (tape.playing && tape.deactivate_display)
14486       TapeDeactivateDisplayOn();
14487   }
14488 }
14489
14490 void RequestQuitGame(boolean ask_if_really_quit)
14491 {
14492   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14493   boolean skip_request = AllPlayersGone || quick_quit;
14494
14495   RequestQuitGameExt(skip_request, quick_quit,
14496                      "Do you really want to quit the game?");
14497 }
14498
14499
14500 /* ------------------------------------------------------------------------- */
14501 /* random generator functions                                                */
14502 /* ------------------------------------------------------------------------- */
14503
14504 unsigned int InitEngineRandom_RND(int seed)
14505 {
14506   game.num_random_calls = 0;
14507
14508   return InitEngineRandom(seed);
14509 }
14510
14511 unsigned int RND(int max)
14512 {
14513   if (max > 0)
14514   {
14515     game.num_random_calls++;
14516
14517     return GetEngineRandom(max);
14518   }
14519
14520   return 0;
14521 }
14522
14523
14524 /* ------------------------------------------------------------------------- */
14525 /* game engine snapshot handling functions                                   */
14526 /* ------------------------------------------------------------------------- */
14527
14528 struct EngineSnapshotInfo
14529 {
14530   /* runtime values for custom element collect score */
14531   int collect_score[NUM_CUSTOM_ELEMENTS];
14532
14533   /* runtime values for group element choice position */
14534   int choice_pos[NUM_GROUP_ELEMENTS];
14535
14536   /* runtime values for belt position animations */
14537   int belt_graphic[4][NUM_BELT_PARTS];
14538   int belt_anim_mode[4][NUM_BELT_PARTS];
14539 };
14540
14541 static struct EngineSnapshotInfo engine_snapshot_rnd;
14542 static char *snapshot_level_identifier = NULL;
14543 static int snapshot_level_nr = -1;
14544
14545 static void SaveEngineSnapshotValues_RND()
14546 {
14547   static int belt_base_active_element[4] =
14548   {
14549     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14550     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14551     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14552     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14553   };
14554   int i, j;
14555
14556   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14557   {
14558     int element = EL_CUSTOM_START + i;
14559
14560     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14561   }
14562
14563   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14564   {
14565     int element = EL_GROUP_START + i;
14566
14567     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14568   }
14569
14570   for (i = 0; i < 4; i++)
14571   {
14572     for (j = 0; j < NUM_BELT_PARTS; j++)
14573     {
14574       int element = belt_base_active_element[i] + j;
14575       int graphic = el2img(element);
14576       int anim_mode = graphic_info[graphic].anim_mode;
14577
14578       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14579       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14580     }
14581   }
14582 }
14583
14584 static void LoadEngineSnapshotValues_RND()
14585 {
14586   unsigned int num_random_calls = game.num_random_calls;
14587   int i, j;
14588
14589   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14590   {
14591     int element = EL_CUSTOM_START + i;
14592
14593     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14594   }
14595
14596   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14597   {
14598     int element = EL_GROUP_START + i;
14599
14600     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14601   }
14602
14603   for (i = 0; i < 4; i++)
14604   {
14605     for (j = 0; j < NUM_BELT_PARTS; j++)
14606     {
14607       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14608       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14609
14610       graphic_info[graphic].anim_mode = anim_mode;
14611     }
14612   }
14613
14614   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14615   {
14616     InitRND(tape.random_seed);
14617     for (i = 0; i < num_random_calls; i++)
14618       RND(1);
14619   }
14620
14621   if (game.num_random_calls != num_random_calls)
14622   {
14623     Error(ERR_INFO, "number of random calls out of sync");
14624     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14625     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14626     Error(ERR_EXIT, "this should not happen -- please debug");
14627   }
14628 }
14629
14630 void FreeEngineSnapshotSingle()
14631 {
14632   FreeSnapshotSingle();
14633
14634   setString(&snapshot_level_identifier, NULL);
14635   snapshot_level_nr = -1;
14636 }
14637
14638 void FreeEngineSnapshotList()
14639 {
14640   FreeSnapshotList();
14641 }
14642
14643 ListNode *SaveEngineSnapshotBuffers()
14644 {
14645   ListNode *buffers = NULL;
14646
14647   /* copy some special values to a structure better suited for the snapshot */
14648
14649   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14650     SaveEngineSnapshotValues_RND();
14651   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14652     SaveEngineSnapshotValues_EM();
14653   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14654     SaveEngineSnapshotValues_SP(&buffers);
14655
14656   /* save values stored in special snapshot structure */
14657
14658   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14659     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14660   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14661     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14662   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14663     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14664
14665   /* save further RND engine values */
14666
14667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14670
14671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14675
14676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14681
14682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14685
14686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14687
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14689
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14692
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14711
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14714
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14718
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14721
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14727
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14730
14731 #if 0
14732   ListNode *node = engine_snapshot_list_rnd;
14733   int num_bytes = 0;
14734
14735   while (node != NULL)
14736   {
14737     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14738
14739     node = node->next;
14740   }
14741
14742   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14743 #endif
14744
14745   return buffers;
14746 }
14747
14748 void SaveEngineSnapshotSingle()
14749 {
14750   ListNode *buffers = SaveEngineSnapshotBuffers();
14751
14752   /* finally save all snapshot buffers to single snapshot */
14753   SaveSnapshotSingle(buffers);
14754
14755   /* save level identification information */
14756   setString(&snapshot_level_identifier, leveldir_current->identifier);
14757   snapshot_level_nr = level_nr;
14758 }
14759
14760 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14761 {
14762   boolean save_snapshot =
14763     (initial_snapshot ||
14764      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14765      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14766       game.snapshot.changed_action) ||
14767      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14768       game.snapshot.collected_item));
14769
14770   game.snapshot.changed_action = FALSE;
14771   game.snapshot.collected_item = FALSE;
14772
14773   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14774       tape.quick_resume ||
14775       !save_snapshot)
14776     return FALSE;
14777
14778   ListNode *buffers = SaveEngineSnapshotBuffers();
14779
14780   /* finally save all snapshot buffers to snapshot list */
14781   SaveSnapshotToList(buffers);
14782
14783   return TRUE;
14784 }
14785
14786 boolean SaveEngineSnapshotToList()
14787 {
14788   return SaveEngineSnapshotToListExt(FALSE);
14789 }
14790
14791 void SaveEngineSnapshotToListInitial()
14792 {
14793   FreeEngineSnapshotList();
14794
14795   SaveEngineSnapshotToListExt(TRUE);
14796 }
14797
14798 void LoadEngineSnapshotValues()
14799 {
14800   /* restore special values from snapshot structure */
14801
14802   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14803     LoadEngineSnapshotValues_RND();
14804   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14805     LoadEngineSnapshotValues_EM();
14806   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14807     LoadEngineSnapshotValues_SP();
14808 }
14809
14810 void LoadEngineSnapshotSingle()
14811 {
14812   LoadSnapshotSingle();
14813
14814   LoadEngineSnapshotValues();
14815 }
14816
14817 void LoadEngineSnapshot_Undo(int steps)
14818 {
14819   LoadSnapshotFromList_Older(steps);
14820
14821   LoadEngineSnapshotValues();
14822 }
14823
14824 void LoadEngineSnapshot_Redo(int steps)
14825 {
14826   LoadSnapshotFromList_Newer(steps);
14827
14828   LoadEngineSnapshotValues();
14829 }
14830
14831 boolean CheckEngineSnapshotSingle()
14832 {
14833   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14834           snapshot_level_nr == level_nr);
14835 }
14836
14837 boolean CheckEngineSnapshotList()
14838 {
14839   return CheckSnapshotList();
14840 }
14841
14842
14843 /* ---------- new game button stuff ---------------------------------------- */
14844
14845 static struct
14846 {
14847   int graphic;
14848   struct XY *pos;
14849   int gadget_id;
14850   char *infotext;
14851 } gamebutton_info[NUM_GAME_BUTTONS] =
14852 {
14853   {
14854     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14855     GAME_CTRL_ID_STOP,                  "stop game"
14856   },
14857   {
14858     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14859     GAME_CTRL_ID_PAUSE,                 "pause game"
14860   },
14861   {
14862     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14863     GAME_CTRL_ID_PLAY,                  "play game"
14864   },
14865   {
14866     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14867     GAME_CTRL_ID_UNDO,                  "undo step"
14868   },
14869   {
14870     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14871     GAME_CTRL_ID_REDO,                  "redo step"
14872   },
14873   {
14874     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14875     GAME_CTRL_ID_SAVE,                  "save game"
14876   },
14877   {
14878     IMG_GAME_BUTTON_GFX_PAUSE2,         &game.button.pause2,
14879     GAME_CTRL_ID_PAUSE2,                "pause game"
14880   },
14881   {
14882     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14883     GAME_CTRL_ID_LOAD,                  "load game"
14884   },
14885   {
14886     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14887     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14888   },
14889   {
14890     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14891     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14892   },
14893   {
14894     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14895     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14896   }
14897 };
14898
14899 void CreateGameButtons()
14900 {
14901   int i;
14902
14903   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14904   {
14905     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14906     struct XY *pos = gamebutton_info[i].pos;
14907     struct GadgetInfo *gi;
14908     int button_type;
14909     boolean checked;
14910     unsigned int event_mask;
14911     int base_x = (tape.show_game_buttons ? VX : DX);
14912     int base_y = (tape.show_game_buttons ? VY : DY);
14913     int gd_x   = gfx->src_x;
14914     int gd_y   = gfx->src_y;
14915     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14916     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14917     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14918     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14919     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14920     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14921     int id = i;
14922
14923     if (gfx->bitmap == NULL)
14924     {
14925       game_gadget[id] = NULL;
14926
14927       continue;
14928     }
14929
14930     if (id == GAME_CTRL_ID_STOP ||
14931         id == GAME_CTRL_ID_PLAY ||
14932         id == GAME_CTRL_ID_SAVE ||
14933         id == GAME_CTRL_ID_LOAD)
14934     {
14935       button_type = GD_TYPE_NORMAL_BUTTON;
14936       checked = FALSE;
14937       event_mask = GD_EVENT_RELEASED;
14938     }
14939     else if (id == GAME_CTRL_ID_UNDO ||
14940              id == GAME_CTRL_ID_REDO)
14941     {
14942       button_type = GD_TYPE_NORMAL_BUTTON;
14943       checked = FALSE;
14944       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14945     }
14946     else
14947     {
14948       button_type = GD_TYPE_CHECK_BUTTON;
14949       checked =
14950         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14951          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14952          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14953       event_mask = GD_EVENT_PRESSED;
14954     }
14955
14956     gi = CreateGadget(GDI_CUSTOM_ID, id,
14957                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14958                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14959                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14960                       GDI_WIDTH, gfx->width,
14961                       GDI_HEIGHT, gfx->height,
14962                       GDI_TYPE, button_type,
14963                       GDI_STATE, GD_BUTTON_UNPRESSED,
14964                       GDI_CHECKED, checked,
14965                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14966                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14967                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14968                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14969                       GDI_DIRECT_DRAW, FALSE,
14970                       GDI_EVENT_MASK, event_mask,
14971                       GDI_CALLBACK_ACTION, HandleGameButtons,
14972                       GDI_END);
14973
14974     if (gi == NULL)
14975       Error(ERR_EXIT, "cannot create gadget");
14976
14977     game_gadget[id] = gi;
14978   }
14979 }
14980
14981 void FreeGameButtons()
14982 {
14983   int i;
14984
14985   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14986     FreeGadget(game_gadget[i]);
14987 }
14988
14989 static void MapGameButtonsAtSamePosition(int id)
14990 {
14991   int i;
14992
14993   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14994     if (i != id &&
14995         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
14996         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
14997       MapGadget(game_gadget[i]);
14998 }
14999
15000 static void UnmapGameButtonsAtSamePosition(int id)
15001 {
15002   int i;
15003
15004   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15005     if (i != id &&
15006         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15007         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15008       UnmapGadget(game_gadget[i]);
15009 }
15010
15011 void MapUndoRedoButtons()
15012 {
15013   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15014   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15015
15016   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15017   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15018 }
15019
15020 void UnmapUndoRedoButtons()
15021 {
15022   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15023   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15024
15025   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15026   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15027 }
15028
15029 void MapGameButtons()
15030 {
15031   int i;
15032
15033   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15034     if (i != GAME_CTRL_ID_UNDO &&
15035         i != GAME_CTRL_ID_REDO)
15036       MapGadget(game_gadget[i]);
15037
15038   if (setup.show_snapshot_buttons)
15039   {
15040     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15041     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15042     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15043   }
15044   else
15045   {
15046     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15047     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15048     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15049   }
15050
15051   RedrawGameButtons();
15052 }
15053
15054 void UnmapGameButtons()
15055 {
15056   int i;
15057
15058   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15059     UnmapGadget(game_gadget[i]);
15060 }
15061
15062 void RedrawGameButtons()
15063 {
15064   int i;
15065
15066   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15067     RedrawGadget(game_gadget[i]);
15068
15069   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15070   redraw_mask &= ~REDRAW_ALL;
15071 }
15072
15073 void GameUndoRedoExt()
15074 {
15075   ClearPlayerAction();
15076
15077   tape.pausing = TRUE;
15078
15079   RedrawPlayfield();
15080   UpdateAndDisplayGameControlValues();
15081
15082   DrawCompleteVideoDisplay();
15083   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15084   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15085   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15086                     VIDEO_STATE_1STEP_OFF), 0);
15087
15088   BackToFront();
15089 }
15090
15091 void GameUndo(int steps)
15092 {
15093   if (!CheckEngineSnapshotList())
15094     return;
15095
15096   LoadEngineSnapshot_Undo(steps);
15097
15098   GameUndoRedoExt();
15099 }
15100
15101 void GameRedo(int steps)
15102 {
15103   if (!CheckEngineSnapshotList())
15104     return;
15105
15106   LoadEngineSnapshot_Redo(steps);
15107
15108   GameUndoRedoExt();
15109 }
15110
15111 static void HandleGameButtonsExt(int id, int button)
15112 {
15113   static boolean game_undo_executed = FALSE;
15114   int steps = BUTTON_STEPSIZE(button);
15115   boolean handle_game_buttons =
15116     (game_status == GAME_MODE_PLAYING ||
15117      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15118
15119   if (!handle_game_buttons)
15120     return;
15121
15122   switch (id)
15123   {
15124     case GAME_CTRL_ID_STOP:
15125       if (game_status == GAME_MODE_MAIN)
15126         break;
15127
15128       if (tape.playing)
15129         TapeStop();
15130       else
15131         RequestQuitGame(TRUE);
15132
15133       break;
15134
15135     case GAME_CTRL_ID_PAUSE:
15136     case GAME_CTRL_ID_PAUSE2:
15137       if (options.network && game_status == GAME_MODE_PLAYING)
15138       {
15139 #if defined(NETWORK_AVALIABLE)
15140         if (tape.pausing)
15141           SendToServer_ContinuePlaying();
15142         else
15143           SendToServer_PausePlaying();
15144 #endif
15145       }
15146       else
15147         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15148
15149       game_undo_executed = FALSE;
15150
15151       break;
15152
15153     case GAME_CTRL_ID_PLAY:
15154       if (game_status == GAME_MODE_MAIN)
15155       {
15156         StartGameActions(options.network, setup.autorecord, level.random_seed);
15157       }
15158       else if (tape.pausing)
15159       {
15160 #if defined(NETWORK_AVALIABLE)
15161         if (options.network)
15162           SendToServer_ContinuePlaying();
15163         else
15164 #endif
15165           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15166       }
15167       break;
15168
15169     case GAME_CTRL_ID_UNDO:
15170       // Important: When using "save snapshot when collecting an item" mode,
15171       // load last (current) snapshot for first "undo" after pressing "pause"
15172       // (else the last-but-one snapshot would be loaded, because the snapshot
15173       // pointer already points to the last snapshot when pressing "pause",
15174       // which is fine for "every step/move" mode, but not for "every collect")
15175       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15176           !game_undo_executed)
15177         steps--;
15178
15179       game_undo_executed = TRUE;
15180
15181       GameUndo(steps);
15182       break;
15183
15184     case GAME_CTRL_ID_REDO:
15185       GameRedo(steps);
15186       break;
15187
15188     case GAME_CTRL_ID_SAVE:
15189       TapeQuickSave();
15190       break;
15191
15192     case GAME_CTRL_ID_LOAD:
15193       TapeQuickLoad();
15194       break;
15195
15196     case SOUND_CTRL_ID_MUSIC:
15197       if (setup.sound_music)
15198       { 
15199         setup.sound_music = FALSE;
15200
15201         FadeMusic();
15202       }
15203       else if (audio.music_available)
15204       { 
15205         setup.sound = setup.sound_music = TRUE;
15206
15207         SetAudioMode(setup.sound);
15208
15209         PlayLevelMusic();
15210       }
15211       break;
15212
15213     case SOUND_CTRL_ID_LOOPS:
15214       if (setup.sound_loops)
15215         setup.sound_loops = FALSE;
15216       else if (audio.loops_available)
15217       {
15218         setup.sound = setup.sound_loops = TRUE;
15219
15220         SetAudioMode(setup.sound);
15221       }
15222       break;
15223
15224     case SOUND_CTRL_ID_SIMPLE:
15225       if (setup.sound_simple)
15226         setup.sound_simple = FALSE;
15227       else if (audio.sound_available)
15228       {
15229         setup.sound = setup.sound_simple = TRUE;
15230
15231         SetAudioMode(setup.sound);
15232       }
15233       break;
15234
15235     default:
15236       break;
15237   }
15238 }
15239
15240 static void HandleGameButtons(struct GadgetInfo *gi)
15241 {
15242   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15243 }
15244
15245 void HandleSoundButtonKeys(Key key)
15246 {
15247
15248   if (key == setup.shortcut.sound_simple)
15249     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15250   else if (key == setup.shortcut.sound_loops)
15251     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15252   else if (key == setup.shortcut.sound_music)
15253     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15254 }