fixed graphical bug when fading out screen after player has died
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_PAUSE2             6
978 #define GAME_CTRL_ID_LOAD               7
979 #define SOUND_CTRL_ID_MUSIC             8
980 #define SOUND_CTRL_ID_LOOPS             9
981 #define SOUND_CTRL_ID_SIMPLE            10
982
983 #define NUM_GAME_BUTTONS                11
984
985
986 /* forward declaration for internal use */
987
988 static void CreateField(int, int, int);
989
990 static void ResetGfxAnimation(int, int);
991
992 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
993 static void AdvanceFrameAndPlayerCounters(int);
994
995 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
996 static boolean MovePlayer(struct PlayerInfo *, int, int);
997 static void ScrollPlayer(struct PlayerInfo *, int);
998 static void ScrollScreen(struct PlayerInfo *, int);
999
1000 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1001 static boolean DigFieldByCE(int, int, int);
1002 static boolean SnapField(struct PlayerInfo *, int, int);
1003 static boolean DropElement(struct PlayerInfo *);
1004
1005 static void InitBeltMovement(void);
1006 static void CloseAllOpenTimegates(void);
1007 static void CheckGravityMovement(struct PlayerInfo *);
1008 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1009 static void KillPlayerUnlessEnemyProtected(int, int);
1010 static void KillPlayerUnlessExplosionProtected(int, int);
1011
1012 static void TestIfPlayerTouchesCustomElement(int, int);
1013 static void TestIfElementTouchesCustomElement(int, int);
1014 static void TestIfElementHitsCustomElement(int, int, int);
1015
1016 static void HandleElementChange(int, int, int);
1017 static void ExecuteCustomElementAction(int, int, int, int);
1018 static boolean ChangeElement(int, int, int, int);
1019
1020 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1021 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1022         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1023 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1024         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1025 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1026         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1027 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1028         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1029
1030 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1031 #define CheckElementChange(x, y, e, te, ev)                             \
1032         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1033 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1034         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1035 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1036         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1037
1038 static void PlayLevelSound(int, int, int);
1039 static void PlayLevelSoundNearest(int, int, int);
1040 static void PlayLevelSoundAction(int, int, int);
1041 static void PlayLevelSoundElementAction(int, int, int, int);
1042 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1043 static void PlayLevelSoundActionIfLoop(int, int, int);
1044 static void StopLevelSoundActionIfLoop(int, int, int);
1045 static void PlayLevelMusic();
1046
1047 static void HandleGameButtons(struct GadgetInfo *);
1048
1049 int AmoebeNachbarNr(int, int);
1050 void AmoebeUmwandeln(int, int);
1051 void ContinueMoving(int, int);
1052 void Bang(int, int);
1053 void InitMovDir(int, int);
1054 void InitAmoebaNr(int, int);
1055 int NewHiScore(void);
1056
1057 void TestIfGoodThingHitsBadThing(int, int, int);
1058 void TestIfBadThingHitsGoodThing(int, int, int);
1059 void TestIfPlayerTouchesBadThing(int, int);
1060 void TestIfPlayerRunsIntoBadThing(int, int, int);
1061 void TestIfBadThingTouchesPlayer(int, int);
1062 void TestIfBadThingRunsIntoPlayer(int, int, int);
1063 void TestIfFriendTouchesBadThing(int, int);
1064 void TestIfBadThingTouchesFriend(int, int);
1065 void TestIfBadThingTouchesOtherBadThing(int, int);
1066 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1067
1068 void KillPlayer(struct PlayerInfo *);
1069 void BuryPlayer(struct PlayerInfo *);
1070 void RemovePlayer(struct PlayerInfo *);
1071
1072 static int getInvisibleActiveFromInvisibleElement(int);
1073 static int getInvisibleFromInvisibleActiveElement(int);
1074
1075 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1076
1077 /* for detection of endless loops, caused by custom element programming */
1078 /* (using maximal playfield width x 10 is just a rough approximation) */
1079 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1080
1081 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1082 {                                                                       \
1083   if (recursion_loop_detected)                                          \
1084     return (rc);                                                        \
1085                                                                         \
1086   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1087   {                                                                     \
1088     recursion_loop_detected = TRUE;                                     \
1089     recursion_loop_element = (e);                                       \
1090   }                                                                     \
1091                                                                         \
1092   recursion_loop_depth++;                                               \
1093 }
1094
1095 #define RECURSION_LOOP_DETECTION_END()                                  \
1096 {                                                                       \
1097   recursion_loop_depth--;                                               \
1098 }
1099
1100 static int recursion_loop_depth;
1101 static boolean recursion_loop_detected;
1102 static boolean recursion_loop_element;
1103
1104 static int map_player_action[MAX_PLAYERS];
1105
1106
1107 /* ------------------------------------------------------------------------- */
1108 /* definition of elements that automatically change to other elements after  */
1109 /* a specified time, eventually calling a function when changing             */
1110 /* ------------------------------------------------------------------------- */
1111
1112 /* forward declaration for changer functions */
1113 static void InitBuggyBase(int, int);
1114 static void WarnBuggyBase(int, int);
1115
1116 static void InitTrap(int, int);
1117 static void ActivateTrap(int, int);
1118 static void ChangeActiveTrap(int, int);
1119
1120 static void InitRobotWheel(int, int);
1121 static void RunRobotWheel(int, int);
1122 static void StopRobotWheel(int, int);
1123
1124 static void InitTimegateWheel(int, int);
1125 static void RunTimegateWheel(int, int);
1126
1127 static void InitMagicBallDelay(int, int);
1128 static void ActivateMagicBall(int, int);
1129
1130 struct ChangingElementInfo
1131 {
1132   int element;
1133   int target_element;
1134   int change_delay;
1135   void (*pre_change_function)(int x, int y);
1136   void (*change_function)(int x, int y);
1137   void (*post_change_function)(int x, int y);
1138 };
1139
1140 static struct ChangingElementInfo change_delay_list[] =
1141 {
1142   {
1143     EL_NUT_BREAKING,
1144     EL_EMERALD,
1145     6,
1146     NULL,
1147     NULL,
1148     NULL
1149   },
1150   {
1151     EL_PEARL_BREAKING,
1152     EL_EMPTY,
1153     8,
1154     NULL,
1155     NULL,
1156     NULL
1157   },
1158   {
1159     EL_EXIT_OPENING,
1160     EL_EXIT_OPEN,
1161     29,
1162     NULL,
1163     NULL,
1164     NULL
1165   },
1166   {
1167     EL_EXIT_CLOSING,
1168     EL_EXIT_CLOSED,
1169     29,
1170     NULL,
1171     NULL,
1172     NULL
1173   },
1174   {
1175     EL_STEEL_EXIT_OPENING,
1176     EL_STEEL_EXIT_OPEN,
1177     29,
1178     NULL,
1179     NULL,
1180     NULL
1181   },
1182   {
1183     EL_STEEL_EXIT_CLOSING,
1184     EL_STEEL_EXIT_CLOSED,
1185     29,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_EM_EXIT_OPENING,
1192     EL_EM_EXIT_OPEN,
1193     29,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EM_EXIT_CLOSING,
1200     EL_EMPTY,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EM_STEEL_EXIT_OPENING,
1208     EL_EM_STEEL_EXIT_OPEN,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_EM_STEEL_EXIT_CLOSING,
1216     EL_STEELWALL,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_SP_EXIT_OPENING,
1224     EL_SP_EXIT_OPEN,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_SP_EXIT_CLOSING,
1232     EL_SP_EXIT_CLOSED,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_SWITCHGATE_OPENING,
1240     EL_SWITCHGATE_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_SWITCHGATE_CLOSING,
1248     EL_SWITCHGATE_CLOSED,
1249     29,
1250     NULL,
1251     NULL,
1252     NULL
1253   },
1254   {
1255     EL_TIMEGATE_OPENING,
1256     EL_TIMEGATE_OPEN,
1257     29,
1258     NULL,
1259     NULL,
1260     NULL
1261   },
1262   {
1263     EL_TIMEGATE_CLOSING,
1264     EL_TIMEGATE_CLOSED,
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270
1271   {
1272     EL_ACID_SPLASH_LEFT,
1273     EL_EMPTY,
1274     8,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_ACID_SPLASH_RIGHT,
1281     EL_EMPTY,
1282     8,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_BUGGY_BASE,
1289     EL_SP_BUGGY_BASE_ACTIVATING,
1290     0,
1291     InitBuggyBase,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SP_BUGGY_BASE_ACTIVATING,
1297     EL_SP_BUGGY_BASE_ACTIVE,
1298     0,
1299     InitBuggyBase,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_BUGGY_BASE_ACTIVE,
1305     EL_SP_BUGGY_BASE,
1306     0,
1307     InitBuggyBase,
1308     WarnBuggyBase,
1309     NULL
1310   },
1311   {
1312     EL_TRAP,
1313     EL_TRAP_ACTIVE,
1314     0,
1315     InitTrap,
1316     NULL,
1317     ActivateTrap
1318   },
1319   {
1320     EL_TRAP_ACTIVE,
1321     EL_TRAP,
1322     31,
1323     NULL,
1324     ChangeActiveTrap,
1325     NULL
1326   },
1327   {
1328     EL_ROBOT_WHEEL_ACTIVE,
1329     EL_ROBOT_WHEEL,
1330     0,
1331     InitRobotWheel,
1332     RunRobotWheel,
1333     StopRobotWheel
1334   },
1335   {
1336     EL_TIMEGATE_SWITCH_ACTIVE,
1337     EL_TIMEGATE_SWITCH,
1338     0,
1339     InitTimegateWheel,
1340     RunTimegateWheel,
1341     NULL
1342   },
1343   {
1344     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1345     EL_DC_TIMEGATE_SWITCH,
1346     0,
1347     InitTimegateWheel,
1348     RunTimegateWheel,
1349     NULL
1350   },
1351   {
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     0,
1355     InitMagicBallDelay,
1356     NULL,
1357     ActivateMagicBall
1358   },
1359   {
1360     EL_EMC_SPRING_BUMPER_ACTIVE,
1361     EL_EMC_SPRING_BUMPER,
1362     8,
1363     NULL,
1364     NULL,
1365     NULL
1366   },
1367   {
1368     EL_DIAGONAL_SHRINKING,
1369     EL_UNDEFINED,
1370     0,
1371     NULL,
1372     NULL,
1373     NULL
1374   },
1375   {
1376     EL_DIAGONAL_GROWING,
1377     EL_UNDEFINED,
1378     0,
1379     NULL,
1380     NULL,
1381     NULL,
1382   },
1383
1384   {
1385     EL_UNDEFINED,
1386     EL_UNDEFINED,
1387     -1,
1388     NULL,
1389     NULL,
1390     NULL
1391   }
1392 };
1393
1394 struct
1395 {
1396   int element;
1397   int push_delay_fixed, push_delay_random;
1398 }
1399 push_delay_list[] =
1400 {
1401   { EL_SPRING,                  0, 0 },
1402   { EL_BALLOON,                 0, 0 },
1403
1404   { EL_SOKOBAN_OBJECT,          2, 0 },
1405   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1406   { EL_SATELLITE,               2, 0 },
1407   { EL_SP_DISK_YELLOW,          2, 0 },
1408
1409   { EL_UNDEFINED,               0, 0 },
1410 };
1411
1412 struct
1413 {
1414   int element;
1415   int move_stepsize;
1416 }
1417 move_stepsize_list[] =
1418 {
1419   { EL_AMOEBA_DROP,             2 },
1420   { EL_AMOEBA_DROPPING,         2 },
1421   { EL_QUICKSAND_FILLING,       1 },
1422   { EL_QUICKSAND_EMPTYING,      1 },
1423   { EL_QUICKSAND_FAST_FILLING,  2 },
1424   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1425   { EL_MAGIC_WALL_FILLING,      2 },
1426   { EL_MAGIC_WALL_EMPTYING,     2 },
1427   { EL_BD_MAGIC_WALL_FILLING,   2 },
1428   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1429   { EL_DC_MAGIC_WALL_FILLING,   2 },
1430   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1431
1432   { EL_UNDEFINED,               0 },
1433 };
1434
1435 struct
1436 {
1437   int element;
1438   int count;
1439 }
1440 collect_count_list[] =
1441 {
1442   { EL_EMERALD,                 1 },
1443   { EL_BD_DIAMOND,              1 },
1444   { EL_EMERALD_YELLOW,          1 },
1445   { EL_EMERALD_RED,             1 },
1446   { EL_EMERALD_PURPLE,          1 },
1447   { EL_DIAMOND,                 3 },
1448   { EL_SP_INFOTRON,             1 },
1449   { EL_PEARL,                   5 },
1450   { EL_CRYSTAL,                 8 },
1451
1452   { EL_UNDEFINED,               0 },
1453 };
1454
1455 struct
1456 {
1457   int element;
1458   int direction;
1459 }
1460 access_direction_list[] =
1461 {
1462   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1463   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1464   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1465   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1466   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1467   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1468   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1469   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1470   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1471   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1472   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1473
1474   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1475   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1476   { EL_SP_PORT_UP,                                                   MV_DOWN },
1477   { EL_SP_PORT_DOWN,                                         MV_UP           },
1478   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1479   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1480   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1481   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1482   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1483   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1484   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1485   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1486   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1487   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1488   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1489   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1490   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1491   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1492   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1493
1494   { EL_UNDEFINED,                       MV_NONE                              }
1495 };
1496
1497 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1498
1499 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1500 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1501 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1502                                  IS_JUST_CHANGING(x, y))
1503
1504 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1505
1506 /* static variables for playfield scan mode (scanning forward or backward) */
1507 static int playfield_scan_start_x = 0;
1508 static int playfield_scan_start_y = 0;
1509 static int playfield_scan_delta_x = 1;
1510 static int playfield_scan_delta_y = 1;
1511
1512 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1513                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1514                                      (y) += playfield_scan_delta_y)     \
1515                                 for ((x) = playfield_scan_start_x;      \
1516                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1517                                      (x) += playfield_scan_delta_x)
1518
1519 #ifdef DEBUG
1520 void DEBUG_SetMaximumDynamite()
1521 {
1522   int i;
1523
1524   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1525     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1526       local_player->inventory_element[local_player->inventory_size++] =
1527         EL_DYNAMITE;
1528 }
1529 #endif
1530
1531 static void InitPlayfieldScanModeVars()
1532 {
1533   if (game.use_reverse_scan_direction)
1534   {
1535     playfield_scan_start_x = lev_fieldx - 1;
1536     playfield_scan_start_y = lev_fieldy - 1;
1537
1538     playfield_scan_delta_x = -1;
1539     playfield_scan_delta_y = -1;
1540   }
1541   else
1542   {
1543     playfield_scan_start_x = 0;
1544     playfield_scan_start_y = 0;
1545
1546     playfield_scan_delta_x = 1;
1547     playfield_scan_delta_y = 1;
1548   }
1549 }
1550
1551 static void InitPlayfieldScanMode(int mode)
1552 {
1553   game.use_reverse_scan_direction =
1554     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1555
1556   InitPlayfieldScanModeVars();
1557 }
1558
1559 static int get_move_delay_from_stepsize(int move_stepsize)
1560 {
1561   move_stepsize =
1562     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1563
1564   /* make sure that stepsize value is always a power of 2 */
1565   move_stepsize = (1 << log_2(move_stepsize));
1566
1567   return TILEX / move_stepsize;
1568 }
1569
1570 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1571                                boolean init_game)
1572 {
1573   int player_nr = player->index_nr;
1574   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1575   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1576
1577   /* do no immediately change move delay -- the player might just be moving */
1578   player->move_delay_value_next = move_delay;
1579
1580   /* information if player can move must be set separately */
1581   player->cannot_move = cannot_move;
1582
1583   if (init_game)
1584   {
1585     player->move_delay       = game.initial_move_delay[player_nr];
1586     player->move_delay_value = game.initial_move_delay_value[player_nr];
1587
1588     player->move_delay_value_next = -1;
1589
1590     player->move_delay_reset_counter = 0;
1591   }
1592 }
1593
1594 void GetPlayerConfig()
1595 {
1596   GameFrameDelay = setup.game_frame_delay;
1597
1598   if (!audio.sound_available)
1599     setup.sound_simple = FALSE;
1600
1601   if (!audio.loops_available)
1602     setup.sound_loops = FALSE;
1603
1604   if (!audio.music_available)
1605     setup.sound_music = FALSE;
1606
1607   if (!video.fullscreen_available)
1608     setup.fullscreen = FALSE;
1609
1610   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1611
1612   SetAudioMode(setup.sound);
1613   InitJoysticks();
1614 }
1615
1616 int GetElementFromGroupElement(int element)
1617 {
1618   if (IS_GROUP_ELEMENT(element))
1619   {
1620     struct ElementGroupInfo *group = element_info[element].group;
1621     int last_anim_random_frame = gfx.anim_random_frame;
1622     int element_pos;
1623
1624     if (group->choice_mode == ANIM_RANDOM)
1625       gfx.anim_random_frame = RND(group->num_elements_resolved);
1626
1627     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1628                                     group->choice_mode, 0,
1629                                     group->choice_pos);
1630
1631     if (group->choice_mode == ANIM_RANDOM)
1632       gfx.anim_random_frame = last_anim_random_frame;
1633
1634     group->choice_pos++;
1635
1636     element = group->element_resolved[element_pos];
1637   }
1638
1639   return element;
1640 }
1641
1642 static void InitPlayerField(int x, int y, int element, boolean init_game)
1643 {
1644   if (element == EL_SP_MURPHY)
1645   {
1646     if (init_game)
1647     {
1648       if (stored_player[0].present)
1649       {
1650         Feld[x][y] = EL_SP_MURPHY_CLONE;
1651
1652         return;
1653       }
1654       else
1655       {
1656         stored_player[0].initial_element = element;
1657         stored_player[0].use_murphy = TRUE;
1658
1659         if (!level.use_artwork_element[0])
1660           stored_player[0].artwork_element = EL_SP_MURPHY;
1661       }
1662
1663       Feld[x][y] = EL_PLAYER_1;
1664     }
1665   }
1666
1667   if (init_game)
1668   {
1669     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1670     int jx = player->jx, jy = player->jy;
1671
1672     player->present = TRUE;
1673
1674     player->block_last_field = (element == EL_SP_MURPHY ?
1675                                 level.sp_block_last_field :
1676                                 level.block_last_field);
1677
1678     /* ---------- initialize player's last field block delay --------------- */
1679
1680     /* always start with reliable default value (no adjustment needed) */
1681     player->block_delay_adjustment = 0;
1682
1683     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1684     if (player->block_last_field && element == EL_SP_MURPHY)
1685       player->block_delay_adjustment = 1;
1686
1687     /* special case 2: in game engines before 3.1.1, blocking was different */
1688     if (game.use_block_last_field_bug)
1689       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1690
1691     if (!options.network || player->connected)
1692     {
1693       player->active = TRUE;
1694
1695       /* remove potentially duplicate players */
1696       if (StorePlayer[jx][jy] == Feld[x][y])
1697         StorePlayer[jx][jy] = 0;
1698
1699       StorePlayer[x][y] = Feld[x][y];
1700
1701 #if DEBUG_INIT_PLAYER
1702       if (options.debug)
1703       {
1704         printf("- player element %d activated", player->element_nr);
1705         printf(" (local player is %d and currently %s)\n",
1706                local_player->element_nr,
1707                local_player->active ? "active" : "not active");
1708       }
1709     }
1710 #endif
1711
1712     Feld[x][y] = EL_EMPTY;
1713
1714     player->jx = player->last_jx = x;
1715     player->jy = player->last_jy = y;
1716   }
1717
1718   if (!init_game)
1719   {
1720     int player_nr = GET_PLAYER_NR(element);
1721     struct PlayerInfo *player = &stored_player[player_nr];
1722
1723     if (player->active && player->killed)
1724       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1725   }
1726 }
1727
1728 static void InitField(int x, int y, boolean init_game)
1729 {
1730   int element = Feld[x][y];
1731
1732   switch (element)
1733   {
1734     case EL_SP_MURPHY:
1735     case EL_PLAYER_1:
1736     case EL_PLAYER_2:
1737     case EL_PLAYER_3:
1738     case EL_PLAYER_4:
1739       InitPlayerField(x, y, element, init_game);
1740       break;
1741
1742     case EL_SOKOBAN_FIELD_PLAYER:
1743       element = Feld[x][y] = EL_PLAYER_1;
1744       InitField(x, y, init_game);
1745
1746       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1747       InitField(x, y, init_game);
1748       break;
1749
1750     case EL_SOKOBAN_FIELD_EMPTY:
1751       local_player->sokobanfields_still_needed++;
1752       break;
1753
1754     case EL_STONEBLOCK:
1755       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1756         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1757       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1758         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1759       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1760         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1761       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1762         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1763       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1764         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1765       break;
1766
1767     case EL_BUG:
1768     case EL_BUG_RIGHT:
1769     case EL_BUG_UP:
1770     case EL_BUG_LEFT:
1771     case EL_BUG_DOWN:
1772     case EL_SPACESHIP:
1773     case EL_SPACESHIP_RIGHT:
1774     case EL_SPACESHIP_UP:
1775     case EL_SPACESHIP_LEFT:
1776     case EL_SPACESHIP_DOWN:
1777     case EL_BD_BUTTERFLY:
1778     case EL_BD_BUTTERFLY_RIGHT:
1779     case EL_BD_BUTTERFLY_UP:
1780     case EL_BD_BUTTERFLY_LEFT:
1781     case EL_BD_BUTTERFLY_DOWN:
1782     case EL_BD_FIREFLY:
1783     case EL_BD_FIREFLY_RIGHT:
1784     case EL_BD_FIREFLY_UP:
1785     case EL_BD_FIREFLY_LEFT:
1786     case EL_BD_FIREFLY_DOWN:
1787     case EL_PACMAN_RIGHT:
1788     case EL_PACMAN_UP:
1789     case EL_PACMAN_LEFT:
1790     case EL_PACMAN_DOWN:
1791     case EL_YAMYAM:
1792     case EL_YAMYAM_LEFT:
1793     case EL_YAMYAM_RIGHT:
1794     case EL_YAMYAM_UP:
1795     case EL_YAMYAM_DOWN:
1796     case EL_DARK_YAMYAM:
1797     case EL_ROBOT:
1798     case EL_PACMAN:
1799     case EL_SP_SNIKSNAK:
1800     case EL_SP_ELECTRON:
1801     case EL_MOLE:
1802     case EL_MOLE_LEFT:
1803     case EL_MOLE_RIGHT:
1804     case EL_MOLE_UP:
1805     case EL_MOLE_DOWN:
1806       InitMovDir(x, y);
1807       break;
1808
1809     case EL_AMOEBA_FULL:
1810     case EL_BD_AMOEBA:
1811       InitAmoebaNr(x, y);
1812       break;
1813
1814     case EL_AMOEBA_DROP:
1815       if (y == lev_fieldy - 1)
1816       {
1817         Feld[x][y] = EL_AMOEBA_GROWING;
1818         Store[x][y] = EL_AMOEBA_WET;
1819       }
1820       break;
1821
1822     case EL_DYNAMITE_ACTIVE:
1823     case EL_SP_DISK_RED_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1828       MovDelay[x][y] = 96;
1829       break;
1830
1831     case EL_EM_DYNAMITE_ACTIVE:
1832       MovDelay[x][y] = 32;
1833       break;
1834
1835     case EL_LAMP:
1836       local_player->lights_still_needed++;
1837       break;
1838
1839     case EL_PENGUIN:
1840       local_player->friends_still_needed++;
1841       break;
1842
1843     case EL_PIG:
1844     case EL_DRAGON:
1845       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1846       break;
1847
1848     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1849     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1850     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1853     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1856     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1860       if (init_game)
1861       {
1862         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1865
1866         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1867         {
1868           game.belt_dir[belt_nr] = belt_dir;
1869           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1870         }
1871         else    /* more than one switch -- set it like the first switch */
1872         {
1873           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1874         }
1875       }
1876       break;
1877
1878     case EL_LIGHT_SWITCH_ACTIVE:
1879       if (init_game)
1880         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1881       break;
1882
1883     case EL_INVISIBLE_STEELWALL:
1884     case EL_INVISIBLE_WALL:
1885     case EL_INVISIBLE_SAND:
1886       if (game.light_time_left > 0 ||
1887           game.lenses_time_left > 0)
1888         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1889       break;
1890
1891     case EL_EMC_MAGIC_BALL:
1892       if (game.ball_state)
1893         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1894       break;
1895
1896     case EL_EMC_MAGIC_BALL_SWITCH:
1897       if (game.ball_state)
1898         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1899       break;
1900
1901     case EL_TRIGGER_PLAYER:
1902     case EL_TRIGGER_ELEMENT:
1903     case EL_TRIGGER_CE_VALUE:
1904     case EL_TRIGGER_CE_SCORE:
1905     case EL_SELF:
1906     case EL_ANY_ELEMENT:
1907     case EL_CURRENT_CE_VALUE:
1908     case EL_CURRENT_CE_SCORE:
1909     case EL_PREV_CE_1:
1910     case EL_PREV_CE_2:
1911     case EL_PREV_CE_3:
1912     case EL_PREV_CE_4:
1913     case EL_PREV_CE_5:
1914     case EL_PREV_CE_6:
1915     case EL_PREV_CE_7:
1916     case EL_PREV_CE_8:
1917     case EL_NEXT_CE_1:
1918     case EL_NEXT_CE_2:
1919     case EL_NEXT_CE_3:
1920     case EL_NEXT_CE_4:
1921     case EL_NEXT_CE_5:
1922     case EL_NEXT_CE_6:
1923     case EL_NEXT_CE_7:
1924     case EL_NEXT_CE_8:
1925       /* reference elements should not be used on the playfield */
1926       Feld[x][y] = EL_EMPTY;
1927       break;
1928
1929     default:
1930       if (IS_CUSTOM_ELEMENT(element))
1931       {
1932         if (CAN_MOVE(element))
1933           InitMovDir(x, y);
1934
1935         if (!element_info[element].use_last_ce_value || init_game)
1936           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1937       }
1938       else if (IS_GROUP_ELEMENT(element))
1939       {
1940         Feld[x][y] = GetElementFromGroupElement(element);
1941
1942         InitField(x, y, init_game);
1943       }
1944
1945       break;
1946   }
1947
1948   if (!init_game)
1949     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1950 }
1951
1952 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1953 {
1954   InitField(x, y, init_game);
1955
1956   /* not needed to call InitMovDir() -- already done by InitField()! */
1957   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1958       CAN_MOVE(Feld[x][y]))
1959     InitMovDir(x, y);
1960 }
1961
1962 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1963 {
1964   int old_element = Feld[x][y];
1965
1966   InitField(x, y, init_game);
1967
1968   /* not needed to call InitMovDir() -- already done by InitField()! */
1969   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1970       CAN_MOVE(old_element) &&
1971       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1972     InitMovDir(x, y);
1973
1974   /* this case is in fact a combination of not less than three bugs:
1975      first, it calls InitMovDir() for elements that can move, although this is
1976      already done by InitField(); then, it checks the element that was at this
1977      field _before_ the call to InitField() (which can change it); lastly, it
1978      was not called for "mole with direction" elements, which were treated as
1979      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1980   */
1981 }
1982
1983 static int get_key_element_from_nr(int key_nr)
1984 {
1985   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1986                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1987                           EL_EM_KEY_1 : EL_KEY_1);
1988
1989   return key_base_element + key_nr;
1990 }
1991
1992 static int get_next_dropped_element(struct PlayerInfo *player)
1993 {
1994   return (player->inventory_size > 0 ?
1995           player->inventory_element[player->inventory_size - 1] :
1996           player->inventory_infinite_element != EL_UNDEFINED ?
1997           player->inventory_infinite_element :
1998           player->dynabombs_left > 0 ?
1999           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2000           EL_UNDEFINED);
2001 }
2002
2003 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2004 {
2005   /* pos >= 0: get element from bottom of the stack;
2006      pos <  0: get element from top of the stack */
2007
2008   if (pos < 0)
2009   {
2010     int min_inventory_size = -pos;
2011     int inventory_pos = player->inventory_size - min_inventory_size;
2012     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2013
2014     return (player->inventory_size >= min_inventory_size ?
2015             player->inventory_element[inventory_pos] :
2016             player->inventory_infinite_element != EL_UNDEFINED ?
2017             player->inventory_infinite_element :
2018             player->dynabombs_left >= min_dynabombs_left ?
2019             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2020             EL_UNDEFINED);
2021   }
2022   else
2023   {
2024     int min_dynabombs_left = pos + 1;
2025     int min_inventory_size = pos + 1 - player->dynabombs_left;
2026     int inventory_pos = pos - player->dynabombs_left;
2027
2028     return (player->inventory_infinite_element != EL_UNDEFINED ?
2029             player->inventory_infinite_element :
2030             player->dynabombs_left >= min_dynabombs_left ?
2031             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032             player->inventory_size >= min_inventory_size ?
2033             player->inventory_element[inventory_pos] :
2034             EL_UNDEFINED);
2035   }
2036 }
2037
2038 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2039 {
2040   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2041   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2042   int compare_result;
2043
2044   if (gpo1->sort_priority != gpo2->sort_priority)
2045     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2046   else
2047     compare_result = gpo1->nr - gpo2->nr;
2048
2049   return compare_result;
2050 }
2051
2052 void InitGameControlValues()
2053 {
2054   int i;
2055
2056   for (i = 0; game_panel_controls[i].nr != -1; i++)
2057   {
2058     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2059     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2060     struct TextPosInfo *pos = gpc->pos;
2061     int nr = gpc->nr;
2062     int type = gpc->type;
2063
2064     if (nr != i)
2065     {
2066       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2067       Error(ERR_EXIT, "this should not happen -- please debug");
2068     }
2069
2070     /* force update of game controls after initialization */
2071     gpc->value = gpc->last_value = -1;
2072     gpc->frame = gpc->last_frame = -1;
2073     gpc->gfx_frame = -1;
2074
2075     /* determine panel value width for later calculation of alignment */
2076     if (type == TYPE_INTEGER || type == TYPE_STRING)
2077     {
2078       pos->width = pos->size * getFontWidth(pos->font);
2079       pos->height = getFontHeight(pos->font);
2080     }
2081     else if (type == TYPE_ELEMENT)
2082     {
2083       pos->width = pos->size;
2084       pos->height = pos->size;
2085     }
2086
2087     /* fill structure for game panel draw order */
2088     gpo->nr = gpc->nr;
2089     gpo->sort_priority = pos->sort_priority;
2090   }
2091
2092   /* sort game panel controls according to sort_priority and control number */
2093   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2094         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2095 }
2096
2097 void UpdatePlayfieldElementCount()
2098 {
2099   boolean use_element_count = FALSE;
2100   int i, j, x, y;
2101
2102   /* first check if it is needed at all to calculate playfield element count */
2103   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2104     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2105       use_element_count = TRUE;
2106
2107   if (!use_element_count)
2108     return;
2109
2110   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2111     element_info[i].element_count = 0;
2112
2113   SCAN_PLAYFIELD(x, y)
2114   {
2115     element_info[Feld[x][y]].element_count++;
2116   }
2117
2118   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2119     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2120       if (IS_IN_GROUP(j, i))
2121         element_info[EL_GROUP_START + i].element_count +=
2122           element_info[j].element_count;
2123 }
2124
2125 void UpdateGameControlValues()
2126 {
2127   int i, k;
2128   int time = (local_player->LevelSolved ?
2129               local_player->LevelSolved_CountingTime :
2130               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2131               level.native_em_level->lev->time :
2132               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2133               level.native_sp_level->game_sp->time_played :
2134               game.no_time_limit ? TimePlayed : TimeLeft);
2135   int score = (local_player->LevelSolved ?
2136                local_player->LevelSolved_CountingScore :
2137                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2138                level.native_em_level->lev->score :
2139                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2140                level.native_sp_level->game_sp->score :
2141                local_player->score);
2142   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2143               level.native_em_level->lev->required :
2144               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2145               level.native_sp_level->game_sp->infotrons_still_needed :
2146               local_player->gems_still_needed);
2147   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148                      level.native_em_level->lev->required > 0 :
2149                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2150                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2151                      local_player->gems_still_needed > 0 ||
2152                      local_player->sokobanfields_still_needed > 0 ||
2153                      local_player->lights_still_needed > 0);
2154
2155   UpdatePlayfieldElementCount();
2156
2157   /* update game panel control values */
2158
2159   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2160   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2161
2162   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2163   for (i = 0; i < MAX_NUM_KEYS; i++)
2164     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2167
2168   if (game.centered_player_nr == -1)
2169   {
2170     for (i = 0; i < MAX_PLAYERS; i++)
2171     {
2172       /* only one player in Supaplex game engine */
2173       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2174         break;
2175
2176       for (k = 0; k < MAX_NUM_KEYS; k++)
2177       {
2178         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179         {
2180           if (level.native_em_level->ply[i]->keys & (1 << k))
2181             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182               get_key_element_from_nr(k);
2183         }
2184         else if (stored_player[i].key[k])
2185           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2186             get_key_element_from_nr(k);
2187       }
2188
2189       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2190         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2191           level.native_em_level->ply[i]->dynamite;
2192       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2193         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194           level.native_sp_level->game_sp->red_disk_count;
2195       else
2196         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197           stored_player[i].inventory_size;
2198
2199       if (stored_player[i].num_white_keys > 0)
2200         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2201           EL_DC_KEY_WHITE;
2202
2203       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204         stored_player[i].num_white_keys;
2205     }
2206   }
2207   else
2208   {
2209     int player_nr = game.centered_player_nr;
2210
2211     for (k = 0; k < MAX_NUM_KEYS; k++)
2212     {
2213       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214       {
2215         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217             get_key_element_from_nr(k);
2218       }
2219       else if (stored_player[player_nr].key[k])
2220         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221           get_key_element_from_nr(k);
2222     }
2223
2224     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226         level.native_em_level->ply[player_nr]->dynamite;
2227     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2228       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229         level.native_sp_level->game_sp->red_disk_count;
2230     else
2231       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232         stored_player[player_nr].inventory_size;
2233
2234     if (stored_player[player_nr].num_white_keys > 0)
2235       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2236
2237     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2238       stored_player[player_nr].num_white_keys;
2239   }
2240
2241   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2242   {
2243     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2244       get_inventory_element_from_pos(local_player, i);
2245     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2246       get_inventory_element_from_pos(local_player, -i - 1);
2247   }
2248
2249   game_panel_controls[GAME_PANEL_SCORE].value = score;
2250   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2251
2252   game_panel_controls[GAME_PANEL_TIME].value = time;
2253
2254   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2255   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2256   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2257
2258   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2259
2260   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2261     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2262      EL_EMPTY);
2263   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2264     local_player->shield_normal_time_left;
2265   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2266     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2269     local_player->shield_deadly_time_left;
2270
2271   game_panel_controls[GAME_PANEL_EXIT].value =
2272     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2273
2274   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2275     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2276   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2277     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2278      EL_EMC_MAGIC_BALL_SWITCH);
2279
2280   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2281     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2282   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2283     game.light_time_left;
2284
2285   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2286     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2287   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2288     game.timegate_time_left;
2289
2290   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2291     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2292
2293   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2294     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2295   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2296     game.lenses_time_left;
2297
2298   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2299     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2301     game.magnify_time_left;
2302
2303   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2304     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2305      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2306      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2307      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2308      EL_BALLOON_SWITCH_NONE);
2309
2310   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2311     local_player->dynabomb_count;
2312   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2313     local_player->dynabomb_size;
2314   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2315     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2316
2317   game_panel_controls[GAME_PANEL_PENGUINS].value =
2318     local_player->friends_still_needed;
2319
2320   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2321     local_player->sokobanfields_still_needed;
2322   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2323     local_player->sokobanfields_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2326     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2327
2328   for (i = 0; i < NUM_BELTS; i++)
2329   {
2330     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2331       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2332        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2333     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2334       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2338     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2339   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2340     game.magic_wall_time_left;
2341
2342   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2343     local_player->gravity;
2344
2345   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2346     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2347
2348   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2349     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2350       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2351        game.panel.element[i].id : EL_UNDEFINED);
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2355       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2356        element_info[game.panel.element_count[i].id].element_count : 0);
2357
2358   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2359     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2360       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2361        element_info[game.panel.ce_score[i].id].collect_score : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2366        element_info[game.panel.ce_score_element[i].id].collect_score :
2367        EL_UNDEFINED);
2368
2369   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2372
2373   /* update game panel control frames */
2374
2375   for (i = 0; game_panel_controls[i].nr != -1; i++)
2376   {
2377     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2378
2379     if (gpc->type == TYPE_ELEMENT)
2380     {
2381       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2382       {
2383         int last_anim_random_frame = gfx.anim_random_frame;
2384         int element = gpc->value;
2385         int graphic = el2panelimg(element);
2386
2387         if (gpc->value != gpc->last_value)
2388         {
2389           gpc->gfx_frame = 0;
2390           gpc->gfx_random = INIT_GFX_RANDOM();
2391         }
2392         else
2393         {
2394           gpc->gfx_frame++;
2395
2396           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2397               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2398             gpc->gfx_random = INIT_GFX_RANDOM();
2399         }
2400
2401         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2402           gfx.anim_random_frame = gpc->gfx_random;
2403
2404         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2405           gpc->gfx_frame = element_info[element].collect_score;
2406
2407         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2408                                               gpc->gfx_frame);
2409
2410         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2411           gfx.anim_random_frame = last_anim_random_frame;
2412       }
2413     }
2414   }
2415 }
2416
2417 void DisplayGameControlValues()
2418 {
2419   boolean redraw_panel = FALSE;
2420   int i;
2421
2422   for (i = 0; game_panel_controls[i].nr != -1; i++)
2423   {
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2425
2426     if (PANEL_DEACTIVATED(gpc->pos))
2427       continue;
2428
2429     if (gpc->value == gpc->last_value &&
2430         gpc->frame == gpc->last_frame)
2431       continue;
2432
2433     redraw_panel = TRUE;
2434   }
2435
2436   if (!redraw_panel)
2437     return;
2438
2439   /* copy default game door content to main double buffer */
2440
2441   /* !!! CHECK AGAIN !!! */
2442   SetPanelBackground();
2443   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2444   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2445
2446   /* redraw game control buttons */
2447   RedrawGameButtons();
2448
2449   game_status = GAME_MODE_PSEUDO_PANEL;
2450
2451   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2452   {
2453     int nr = game_panel_order[i].nr;
2454     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2455     struct TextPosInfo *pos = gpc->pos;
2456     int type = gpc->type;
2457     int value = gpc->value;
2458     int frame = gpc->frame;
2459     int size = pos->size;
2460     int font = pos->font;
2461     boolean draw_masked = pos->draw_masked;
2462     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2463
2464     if (PANEL_DEACTIVATED(pos))
2465       continue;
2466
2467     gpc->last_value = value;
2468     gpc->last_frame = frame;
2469
2470     if (type == TYPE_INTEGER)
2471     {
2472       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2473           nr == GAME_PANEL_TIME)
2474       {
2475         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2476
2477         if (use_dynamic_size)           /* use dynamic number of digits */
2478         {
2479           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2480           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2481           int size2 = size1 + 1;
2482           int font1 = pos->font;
2483           int font2 = pos->font_alt;
2484
2485           size = (value < value_change ? size1 : size2);
2486           font = (value < value_change ? font1 : font2);
2487         }
2488       }
2489
2490       /* correct text size if "digits" is zero or less */
2491       if (size <= 0)
2492         size = strlen(int2str(value, size));
2493
2494       /* dynamically correct text alignment */
2495       pos->width = size * getFontWidth(font);
2496
2497       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2498                   int2str(value, size), font, mask_mode);
2499     }
2500     else if (type == TYPE_ELEMENT)
2501     {
2502       int element, graphic;
2503       Bitmap *src_bitmap;
2504       int src_x, src_y;
2505       int width, height;
2506       int dst_x = PANEL_XPOS(pos);
2507       int dst_y = PANEL_YPOS(pos);
2508
2509       if (value != EL_UNDEFINED && value != EL_EMPTY)
2510       {
2511         element = value;
2512         graphic = el2panelimg(value);
2513
2514         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2515
2516         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2517           size = TILESIZE;
2518
2519         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2520                               &src_x, &src_y);
2521
2522         width  = graphic_info[graphic].width  * size / TILESIZE;
2523         height = graphic_info[graphic].height * size / TILESIZE;
2524
2525         if (draw_masked)
2526           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2527                            dst_x, dst_y);
2528         else
2529           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2530                      dst_x, dst_y);
2531       }
2532     }
2533     else if (type == TYPE_STRING)
2534     {
2535       boolean active = (value != 0);
2536       char *state_normal = "off";
2537       char *state_active = "on";
2538       char *state = (active ? state_active : state_normal);
2539       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2540                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2541                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2542                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2543
2544       if (nr == GAME_PANEL_GRAVITY_STATE)
2545       {
2546         int font1 = pos->font;          /* (used for normal state) */
2547         int font2 = pos->font_alt;      /* (used for active state) */
2548
2549         font = (active ? font2 : font1);
2550       }
2551
2552       if (s != NULL)
2553       {
2554         char *s_cut;
2555
2556         if (size <= 0)
2557         {
2558           /* don't truncate output if "chars" is zero or less */
2559           size = strlen(s);
2560
2561           /* dynamically correct text alignment */
2562           pos->width = size * getFontWidth(font);
2563         }
2564
2565         s_cut = getStringCopyN(s, size);
2566
2567         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2568                     s_cut, font, mask_mode);
2569
2570         free(s_cut);
2571       }
2572     }
2573
2574     redraw_mask |= REDRAW_DOOR_1;
2575   }
2576
2577   game_status = GAME_MODE_PLAYING;
2578 }
2579
2580 void UpdateAndDisplayGameControlValues()
2581 {
2582   if (tape.deactivate_display)
2583     return;
2584
2585   UpdateGameControlValues();
2586   DisplayGameControlValues();
2587 }
2588
2589 void UpdateGameDoorValues()
2590 {
2591   UpdateGameControlValues();
2592 }
2593
2594 void DrawGameDoorValues()
2595 {
2596   DisplayGameControlValues();
2597 }
2598
2599
2600 /*
2601   =============================================================================
2602   InitGameEngine()
2603   -----------------------------------------------------------------------------
2604   initialize game engine due to level / tape version number
2605   =============================================================================
2606 */
2607
2608 static void InitGameEngine()
2609 {
2610   int i, j, k, l, x, y;
2611
2612   /* set game engine from tape file when re-playing, else from level file */
2613   game.engine_version = (tape.playing ? tape.engine_version :
2614                          level.game_version);
2615
2616   /* set single or multi-player game mode (needed for re-playing tapes) */
2617   game.team_mode = setup.team_mode;
2618
2619   if (tape.playing)
2620   {
2621     int num_players = 0;
2622
2623     for (i = 0; i < MAX_PLAYERS; i++)
2624       if (tape.player_participates[i])
2625         num_players++;
2626
2627     /* multi-player tapes contain input data for more than one player */
2628     game.team_mode = (num_players > 1);
2629   }
2630
2631   /* ---------------------------------------------------------------------- */
2632   /* set flags for bugs and changes according to active game engine version */
2633   /* ---------------------------------------------------------------------- */
2634
2635   /*
2636     Summary of bugfix/change:
2637     Fixed handling for custom elements that change when pushed by the player.
2638
2639     Fixed/changed in version:
2640     3.1.0
2641
2642     Description:
2643     Before 3.1.0, custom elements that "change when pushing" changed directly
2644     after the player started pushing them (until then handled in "DigField()").
2645     Since 3.1.0, these custom elements are not changed until the "pushing"
2646     move of the element is finished (now handled in "ContinueMoving()").
2647
2648     Affected levels/tapes:
2649     The first condition is generally needed for all levels/tapes before version
2650     3.1.0, which might use the old behaviour before it was changed; known tapes
2651     that are affected are some tapes from the level set "Walpurgis Gardens" by
2652     Jamie Cullen.
2653     The second condition is an exception from the above case and is needed for
2654     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2655     above (including some development versions of 3.1.0), but before it was
2656     known that this change would break tapes like the above and was fixed in
2657     3.1.1, so that the changed behaviour was active although the engine version
2658     while recording maybe was before 3.1.0. There is at least one tape that is
2659     affected by this exception, which is the tape for the one-level set "Bug
2660     Machine" by Juergen Bonhagen.
2661   */
2662
2663   game.use_change_when_pushing_bug =
2664     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2665      !(tape.playing &&
2666        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2667        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2668
2669   /*
2670     Summary of bugfix/change:
2671     Fixed handling for blocking the field the player leaves when moving.
2672
2673     Fixed/changed in version:
2674     3.1.1
2675
2676     Description:
2677     Before 3.1.1, when "block last field when moving" was enabled, the field
2678     the player is leaving when moving was blocked for the time of the move,
2679     and was directly unblocked afterwards. This resulted in the last field
2680     being blocked for exactly one less than the number of frames of one player
2681     move. Additionally, even when blocking was disabled, the last field was
2682     blocked for exactly one frame.
2683     Since 3.1.1, due to changes in player movement handling, the last field
2684     is not blocked at all when blocking is disabled. When blocking is enabled,
2685     the last field is blocked for exactly the number of frames of one player
2686     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2687     last field is blocked for exactly one more than the number of frames of
2688     one player move.
2689
2690     Affected levels/tapes:
2691     (!!! yet to be determined -- probably many !!!)
2692   */
2693
2694   game.use_block_last_field_bug =
2695     (game.engine_version < VERSION_IDENT(3,1,1,0));
2696
2697   /* ---------------------------------------------------------------------- */
2698
2699   /* set maximal allowed number of custom element changes per game frame */
2700   game.max_num_changes_per_frame = 1;
2701
2702   /* default scan direction: scan playfield from top/left to bottom/right */
2703   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2704
2705   /* dynamically adjust element properties according to game engine version */
2706   InitElementPropertiesEngine(game.engine_version);
2707
2708 #if 0
2709   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2710   printf("          tape version == %06d [%s] [file: %06d]\n",
2711          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2712          tape.file_version);
2713   printf("       => game.engine_version == %06d\n", game.engine_version);
2714 #endif
2715
2716   /* ---------- initialize player's initial move delay --------------------- */
2717
2718   /* dynamically adjust player properties according to level information */
2719   for (i = 0; i < MAX_PLAYERS; i++)
2720     game.initial_move_delay_value[i] =
2721       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2722
2723   /* dynamically adjust player properties according to game engine version */
2724   for (i = 0; i < MAX_PLAYERS; i++)
2725     game.initial_move_delay[i] =
2726       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2727        game.initial_move_delay_value[i] : 0);
2728
2729   /* ---------- initialize player's initial push delay --------------------- */
2730
2731   /* dynamically adjust player properties according to game engine version */
2732   game.initial_push_delay_value =
2733     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2734
2735   /* ---------- initialize changing elements ------------------------------- */
2736
2737   /* initialize changing elements information */
2738   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2739   {
2740     struct ElementInfo *ei = &element_info[i];
2741
2742     /* this pointer might have been changed in the level editor */
2743     ei->change = &ei->change_page[0];
2744
2745     if (!IS_CUSTOM_ELEMENT(i))
2746     {
2747       ei->change->target_element = EL_EMPTY_SPACE;
2748       ei->change->delay_fixed = 0;
2749       ei->change->delay_random = 0;
2750       ei->change->delay_frames = 1;
2751     }
2752
2753     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2754     {
2755       ei->has_change_event[j] = FALSE;
2756
2757       ei->event_page_nr[j] = 0;
2758       ei->event_page[j] = &ei->change_page[0];
2759     }
2760   }
2761
2762   /* add changing elements from pre-defined list */
2763   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2764   {
2765     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2766     struct ElementInfo *ei = &element_info[ch_delay->element];
2767
2768     ei->change->target_element       = ch_delay->target_element;
2769     ei->change->delay_fixed          = ch_delay->change_delay;
2770
2771     ei->change->pre_change_function  = ch_delay->pre_change_function;
2772     ei->change->change_function      = ch_delay->change_function;
2773     ei->change->post_change_function = ch_delay->post_change_function;
2774
2775     ei->change->can_change = TRUE;
2776     ei->change->can_change_or_has_action = TRUE;
2777
2778     ei->has_change_event[CE_DELAY] = TRUE;
2779
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2782   }
2783
2784   /* ---------- initialize internal run-time variables --------------------- */
2785
2786   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2787   {
2788     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2789
2790     for (j = 0; j < ei->num_change_pages; j++)
2791     {
2792       ei->change_page[j].can_change_or_has_action =
2793         (ei->change_page[j].can_change |
2794          ei->change_page[j].has_action);
2795     }
2796   }
2797
2798   /* add change events from custom element configuration */
2799   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2800   {
2801     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2802
2803     for (j = 0; j < ei->num_change_pages; j++)
2804     {
2805       if (!ei->change_page[j].can_change_or_has_action)
2806         continue;
2807
2808       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2809       {
2810         /* only add event page for the first page found with this event */
2811         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2812         {
2813           ei->has_change_event[k] = TRUE;
2814
2815           ei->event_page_nr[k] = j;
2816           ei->event_page[k] = &ei->change_page[j];
2817         }
2818       }
2819     }
2820   }
2821
2822   /* ---------- initialize reference elements in change conditions --------- */
2823
2824   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2825   {
2826     int element = EL_CUSTOM_START + i;
2827     struct ElementInfo *ei = &element_info[element];
2828
2829     for (j = 0; j < ei->num_change_pages; j++)
2830     {
2831       int trigger_element = ei->change_page[j].initial_trigger_element;
2832
2833       if (trigger_element >= EL_PREV_CE_8 &&
2834           trigger_element <= EL_NEXT_CE_8)
2835         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2836
2837       ei->change_page[j].trigger_element = trigger_element;
2838     }
2839   }
2840
2841   /* ---------- initialize run-time trigger player and element ------------- */
2842
2843   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2844   {
2845     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2846
2847     for (j = 0; j < ei->num_change_pages; j++)
2848     {
2849       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2852       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2853       ei->change_page[j].actual_trigger_ce_value = 0;
2854       ei->change_page[j].actual_trigger_ce_score = 0;
2855     }
2856   }
2857
2858   /* ---------- initialize trigger events ---------------------------------- */
2859
2860   /* initialize trigger events information */
2861   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2862     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2863       trigger_events[i][j] = FALSE;
2864
2865   /* add trigger events from element change event properties */
2866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867   {
2868     struct ElementInfo *ei = &element_info[i];
2869
2870     for (j = 0; j < ei->num_change_pages; j++)
2871     {
2872       if (!ei->change_page[j].can_change_or_has_action)
2873         continue;
2874
2875       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2876       {
2877         int trigger_element = ei->change_page[j].trigger_element;
2878
2879         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2880         {
2881           if (ei->change_page[j].has_event[k])
2882           {
2883             if (IS_GROUP_ELEMENT(trigger_element))
2884             {
2885               struct ElementGroupInfo *group =
2886                 element_info[trigger_element].group;
2887
2888               for (l = 0; l < group->num_elements_resolved; l++)
2889                 trigger_events[group->element_resolved[l]][k] = TRUE;
2890             }
2891             else if (trigger_element == EL_ANY_ELEMENT)
2892               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2893                 trigger_events[l][k] = TRUE;
2894             else
2895               trigger_events[trigger_element][k] = TRUE;
2896           }
2897         }
2898       }
2899     }
2900   }
2901
2902   /* ---------- initialize push delay -------------------------------------- */
2903
2904   /* initialize push delay values to default */
2905   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2906   {
2907     if (!IS_CUSTOM_ELEMENT(i))
2908     {
2909       /* set default push delay values (corrected since version 3.0.7-1) */
2910       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2911       {
2912         element_info[i].push_delay_fixed = 2;
2913         element_info[i].push_delay_random = 8;
2914       }
2915       else
2916       {
2917         element_info[i].push_delay_fixed = 8;
2918         element_info[i].push_delay_random = 8;
2919       }
2920     }
2921   }
2922
2923   /* set push delay value for certain elements from pre-defined list */
2924   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2925   {
2926     int e = push_delay_list[i].element;
2927
2928     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2929     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2930   }
2931
2932   /* set push delay value for Supaplex elements for newer engine versions */
2933   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2934   {
2935     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2936     {
2937       if (IS_SP_ELEMENT(i))
2938       {
2939         /* set SP push delay to just enough to push under a falling zonk */
2940         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2941
2942         element_info[i].push_delay_fixed  = delay;
2943         element_info[i].push_delay_random = 0;
2944       }
2945     }
2946   }
2947
2948   /* ---------- initialize move stepsize ----------------------------------- */
2949
2950   /* initialize move stepsize values to default */
2951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2952     if (!IS_CUSTOM_ELEMENT(i))
2953       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2954
2955   /* set move stepsize value for certain elements from pre-defined list */
2956   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2957   {
2958     int e = move_stepsize_list[i].element;
2959
2960     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2961   }
2962
2963   /* ---------- initialize collect score ----------------------------------- */
2964
2965   /* initialize collect score values for custom elements from initial value */
2966   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2967     if (IS_CUSTOM_ELEMENT(i))
2968       element_info[i].collect_score = element_info[i].collect_score_initial;
2969
2970   /* ---------- initialize collect count ----------------------------------- */
2971
2972   /* initialize collect count values for non-custom elements */
2973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974     if (!IS_CUSTOM_ELEMENT(i))
2975       element_info[i].collect_count_initial = 0;
2976
2977   /* add collect count values for all elements from pre-defined list */
2978   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2979     element_info[collect_count_list[i].element].collect_count_initial =
2980       collect_count_list[i].count;
2981
2982   /* ---------- initialize access direction -------------------------------- */
2983
2984   /* initialize access direction values to default (access from every side) */
2985   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2986     if (!IS_CUSTOM_ELEMENT(i))
2987       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2988
2989   /* set access direction value for certain elements from pre-defined list */
2990   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2991     element_info[access_direction_list[i].element].access_direction =
2992       access_direction_list[i].direction;
2993
2994   /* ---------- initialize explosion content ------------------------------- */
2995   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2996   {
2997     if (IS_CUSTOM_ELEMENT(i))
2998       continue;
2999
3000     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3001     {
3002       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3003
3004       element_info[i].content.e[x][y] =
3005         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3006          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3007          i == EL_PLAYER_3 ? EL_EMERALD :
3008          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3009          i == EL_MOLE ? EL_EMERALD_RED :
3010          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3011          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3012          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3013          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3014          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3015          i == EL_WALL_EMERALD ? EL_EMERALD :
3016          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3017          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3018          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3019          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3020          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3021          i == EL_WALL_PEARL ? EL_PEARL :
3022          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3023          EL_EMPTY);
3024     }
3025   }
3026
3027   /* ---------- initialize recursion detection ------------------------------ */
3028   recursion_loop_depth = 0;
3029   recursion_loop_detected = FALSE;
3030   recursion_loop_element = EL_UNDEFINED;
3031
3032   /* ---------- initialize graphics engine ---------------------------------- */
3033   game.scroll_delay_value =
3034     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3035      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3036   game.scroll_delay_value =
3037     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3038
3039   /* ---------- initialize game engine snapshots ---------------------------- */
3040   for (i = 0; i < MAX_PLAYERS; i++)
3041     game.snapshot.last_action[i] = 0;
3042   game.snapshot.changed_action = FALSE;
3043   game.snapshot.mode =
3044     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3045      SNAPSHOT_MODE_EVERY_STEP :
3046      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3047      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3048 }
3049
3050 int get_num_special_action(int element, int action_first, int action_last)
3051 {
3052   int num_special_action = 0;
3053   int i, j;
3054
3055   for (i = action_first; i <= action_last; i++)
3056   {
3057     boolean found = FALSE;
3058
3059     for (j = 0; j < NUM_DIRECTIONS; j++)
3060       if (el_act_dir2img(element, i, j) !=
3061           el_act_dir2img(element, ACTION_DEFAULT, j))
3062         found = TRUE;
3063
3064     if (found)
3065       num_special_action++;
3066     else
3067       break;
3068   }
3069
3070   return num_special_action;
3071 }
3072
3073
3074 /*
3075   =============================================================================
3076   InitGame()
3077   -----------------------------------------------------------------------------
3078   initialize and start new game
3079   =============================================================================
3080 */
3081
3082 void InitGame()
3083 {
3084   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3085   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3086   int fade_mask = REDRAW_FIELD;
3087
3088   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3089   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3090   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3091   int initial_move_dir = MV_DOWN;
3092   int i, j, x, y;
3093
3094   // required here to update video display before fading (FIX THIS)
3095   DrawMaskedBorder(REDRAW_DOOR_2);
3096
3097   game_status = GAME_MODE_PLAYING;
3098
3099   if (!game.restart_level)
3100     CloseDoor(DOOR_CLOSE_1);
3101
3102   /* needed if different viewport properties defined for playing */
3103   ChangeViewportPropertiesIfNeeded();
3104
3105   if (level_editor_test_game)
3106     FadeSkipNextFadeIn();
3107   else
3108     FadeSetEnterScreen();
3109
3110   if (CheckIfGlobalBorderHasChanged())
3111     fade_mask = REDRAW_ALL;
3112
3113   FadeOut(fade_mask);
3114
3115   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3116
3117   ClearField();
3118
3119   DrawCompleteVideoDisplay();
3120
3121   InitGameEngine();
3122   InitGameControlValues();
3123
3124   /* don't play tapes over network */
3125   network_playing = (options.network && !tape.playing);
3126
3127   for (i = 0; i < MAX_PLAYERS; i++)
3128   {
3129     struct PlayerInfo *player = &stored_player[i];
3130
3131     player->index_nr = i;
3132     player->index_bit = (1 << i);
3133     player->element_nr = EL_PLAYER_1 + i;
3134
3135     player->present = FALSE;
3136     player->active = FALSE;
3137     player->mapped = FALSE;
3138
3139     player->killed = FALSE;
3140     player->reanimated = FALSE;
3141
3142     player->action = 0;
3143     player->effective_action = 0;
3144     player->programmed_action = 0;
3145
3146     player->score = 0;
3147     player->score_final = 0;
3148
3149     player->gems_still_needed = level.gems_needed;
3150     player->sokobanfields_still_needed = 0;
3151     player->lights_still_needed = 0;
3152     player->friends_still_needed = 0;
3153
3154     for (j = 0; j < MAX_NUM_KEYS; j++)
3155       player->key[j] = FALSE;
3156
3157     player->num_white_keys = 0;
3158
3159     player->dynabomb_count = 0;
3160     player->dynabomb_size = 1;
3161     player->dynabombs_left = 0;
3162     player->dynabomb_xl = FALSE;
3163
3164     player->MovDir = initial_move_dir;
3165     player->MovPos = 0;
3166     player->GfxPos = 0;
3167     player->GfxDir = initial_move_dir;
3168     player->GfxAction = ACTION_DEFAULT;
3169     player->Frame = 0;
3170     player->StepFrame = 0;
3171
3172     player->initial_element = player->element_nr;
3173     player->artwork_element =
3174       (level.use_artwork_element[i] ? level.artwork_element[i] :
3175        player->element_nr);
3176     player->use_murphy = FALSE;
3177
3178     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3179     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3180
3181     player->gravity = level.initial_player_gravity[i];
3182
3183     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3184
3185     player->actual_frame_counter = 0;
3186
3187     player->step_counter = 0;
3188
3189     player->last_move_dir = initial_move_dir;
3190
3191     player->is_active = FALSE;
3192
3193     player->is_waiting = FALSE;
3194     player->is_moving = FALSE;
3195     player->is_auto_moving = FALSE;
3196     player->is_digging = FALSE;
3197     player->is_snapping = FALSE;
3198     player->is_collecting = FALSE;
3199     player->is_pushing = FALSE;
3200     player->is_switching = FALSE;
3201     player->is_dropping = FALSE;
3202     player->is_dropping_pressed = FALSE;
3203
3204     player->is_bored = FALSE;
3205     player->is_sleeping = FALSE;
3206
3207     player->frame_counter_bored = -1;
3208     player->frame_counter_sleeping = -1;
3209
3210     player->anim_delay_counter = 0;
3211     player->post_delay_counter = 0;
3212
3213     player->dir_waiting = initial_move_dir;
3214     player->action_waiting = ACTION_DEFAULT;
3215     player->last_action_waiting = ACTION_DEFAULT;
3216     player->special_action_bored = ACTION_DEFAULT;
3217     player->special_action_sleeping = ACTION_DEFAULT;
3218
3219     player->switch_x = -1;
3220     player->switch_y = -1;
3221
3222     player->drop_x = -1;
3223     player->drop_y = -1;
3224
3225     player->show_envelope = 0;
3226
3227     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3228
3229     player->push_delay       = -1;      /* initialized when pushing starts */
3230     player->push_delay_value = game.initial_push_delay_value;
3231
3232     player->drop_delay = 0;
3233     player->drop_pressed_delay = 0;
3234
3235     player->last_jx = -1;
3236     player->last_jy = -1;
3237     player->jx = -1;
3238     player->jy = -1;
3239
3240     player->shield_normal_time_left = 0;
3241     player->shield_deadly_time_left = 0;
3242
3243     player->inventory_infinite_element = EL_UNDEFINED;
3244     player->inventory_size = 0;
3245
3246     if (level.use_initial_inventory[i])
3247     {
3248       for (j = 0; j < level.initial_inventory_size[i]; j++)
3249       {
3250         int element = level.initial_inventory_content[i][j];
3251         int collect_count = element_info[element].collect_count_initial;
3252         int k;
3253
3254         if (!IS_CUSTOM_ELEMENT(element))
3255           collect_count = 1;
3256
3257         if (collect_count == 0)
3258           player->inventory_infinite_element = element;
3259         else
3260           for (k = 0; k < collect_count; k++)
3261             if (player->inventory_size < MAX_INVENTORY_SIZE)
3262               player->inventory_element[player->inventory_size++] = element;
3263       }
3264     }
3265
3266     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3267     SnapField(player, 0, 0);
3268
3269     player->LevelSolved = FALSE;
3270     player->GameOver = FALSE;
3271
3272     player->LevelSolved_GameWon = FALSE;
3273     player->LevelSolved_GameEnd = FALSE;
3274     player->LevelSolved_PanelOff = FALSE;
3275     player->LevelSolved_SaveTape = FALSE;
3276     player->LevelSolved_SaveScore = FALSE;
3277     player->LevelSolved_CountingTime = 0;
3278     player->LevelSolved_CountingScore = 0;
3279
3280     map_player_action[i] = i;
3281   }
3282
3283   network_player_action_received = FALSE;
3284
3285 #if defined(NETWORK_AVALIABLE)
3286   /* initial null action */
3287   if (network_playing)
3288     SendToServer_MovePlayer(MV_NONE);
3289 #endif
3290
3291   ZX = ZY = -1;
3292   ExitX = ExitY = -1;
3293
3294   FrameCounter = 0;
3295   TimeFrames = 0;
3296   TimePlayed = 0;
3297   TimeLeft = level.time;
3298   TapeTime = 0;
3299
3300   ScreenMovDir = MV_NONE;
3301   ScreenMovPos = 0;
3302   ScreenGfxPos = 0;
3303
3304   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3305
3306   AllPlayersGone = FALSE;
3307
3308   game.no_time_limit = (level.time == 0);
3309
3310   game.yamyam_content_nr = 0;
3311   game.robot_wheel_active = FALSE;
3312   game.magic_wall_active = FALSE;
3313   game.magic_wall_time_left = 0;
3314   game.light_time_left = 0;
3315   game.timegate_time_left = 0;
3316   game.switchgate_pos = 0;
3317   game.wind_direction = level.wind_direction_initial;
3318
3319   game.lenses_time_left = 0;
3320   game.magnify_time_left = 0;
3321
3322   game.ball_state = level.ball_state_initial;
3323   game.ball_content_nr = 0;
3324
3325   game.envelope_active = FALSE;
3326
3327   /* set focus to local player for network games, else to all players */
3328   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3329   game.centered_player_nr_next = game.centered_player_nr;
3330   game.set_centered_player = FALSE;
3331
3332   if (network_playing && tape.recording)
3333   {
3334     /* store client dependent player focus when recording network games */
3335     tape.centered_player_nr_next = game.centered_player_nr_next;
3336     tape.set_centered_player = TRUE;
3337   }
3338
3339   for (i = 0; i < NUM_BELTS; i++)
3340   {
3341     game.belt_dir[i] = MV_NONE;
3342     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3343   }
3344
3345   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3346     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3347
3348 #if DEBUG_INIT_PLAYER
3349   if (options.debug)
3350   {
3351     printf("Player status at level initialization:\n");
3352   }
3353 #endif
3354
3355   SCAN_PLAYFIELD(x, y)
3356   {
3357     Feld[x][y] = level.field[x][y];
3358     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3359     ChangeDelay[x][y] = 0;
3360     ChangePage[x][y] = -1;
3361     CustomValue[x][y] = 0;              /* initialized in InitField() */
3362     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3363     AmoebaNr[x][y] = 0;
3364     WasJustMoving[x][y] = 0;
3365     WasJustFalling[x][y] = 0;
3366     CheckCollision[x][y] = 0;
3367     CheckImpact[x][y] = 0;
3368     Stop[x][y] = FALSE;
3369     Pushed[x][y] = FALSE;
3370
3371     ChangeCount[x][y] = 0;
3372     ChangeEvent[x][y] = -1;
3373
3374     ExplodePhase[x][y] = 0;
3375     ExplodeDelay[x][y] = 0;
3376     ExplodeField[x][y] = EX_TYPE_NONE;
3377
3378     RunnerVisit[x][y] = 0;
3379     PlayerVisit[x][y] = 0;
3380
3381     GfxFrame[x][y] = 0;
3382     GfxRandom[x][y] = INIT_GFX_RANDOM();
3383     GfxElement[x][y] = EL_UNDEFINED;
3384     GfxAction[x][y] = ACTION_DEFAULT;
3385     GfxDir[x][y] = MV_NONE;
3386     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3387   }
3388
3389   SCAN_PLAYFIELD(x, y)
3390   {
3391     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3392       emulate_bd = FALSE;
3393     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3394       emulate_sb = FALSE;
3395     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3396       emulate_sp = FALSE;
3397
3398     InitField(x, y, TRUE);
3399
3400     ResetGfxAnimation(x, y);
3401   }
3402
3403   InitBeltMovement();
3404
3405   for (i = 0; i < MAX_PLAYERS; i++)
3406   {
3407     struct PlayerInfo *player = &stored_player[i];
3408
3409     /* set number of special actions for bored and sleeping animation */
3410     player->num_special_action_bored =
3411       get_num_special_action(player->artwork_element,
3412                              ACTION_BORING_1, ACTION_BORING_LAST);
3413     player->num_special_action_sleeping =
3414       get_num_special_action(player->artwork_element,
3415                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3416   }
3417
3418   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3419                     emulate_sb ? EMU_SOKOBAN :
3420                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3421
3422   /* initialize type of slippery elements */
3423   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3424   {
3425     if (!IS_CUSTOM_ELEMENT(i))
3426     {
3427       /* default: elements slip down either to the left or right randomly */
3428       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3429
3430       /* SP style elements prefer to slip down on the left side */
3431       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3432         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3433
3434       /* BD style elements prefer to slip down on the left side */
3435       if (game.emulation == EMU_BOULDERDASH)
3436         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3437     }
3438   }
3439
3440   /* initialize explosion and ignition delay */
3441   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3442   {
3443     if (!IS_CUSTOM_ELEMENT(i))
3444     {
3445       int num_phase = 8;
3446       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3447                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3448                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3449       int last_phase = (num_phase + 1) * delay;
3450       int half_phase = (num_phase / 2) * delay;
3451
3452       element_info[i].explosion_delay = last_phase - 1;
3453       element_info[i].ignition_delay = half_phase;
3454
3455       if (i == EL_BLACK_ORB)
3456         element_info[i].ignition_delay = 1;
3457     }
3458   }
3459
3460   /* correct non-moving belts to start moving left */
3461   for (i = 0; i < NUM_BELTS; i++)
3462     if (game.belt_dir[i] == MV_NONE)
3463       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3464
3465 #if USE_NEW_PLAYER_ASSIGNMENTS
3466   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3467   /* choose default local player */
3468   local_player = &stored_player[0];
3469
3470   for (i = 0; i < MAX_PLAYERS; i++)
3471     stored_player[i].connected = FALSE;
3472
3473   local_player->connected = TRUE;
3474   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3475
3476   if (tape.playing)
3477   {
3478     for (i = 0; i < MAX_PLAYERS; i++)
3479       stored_player[i].connected = tape.player_participates[i];
3480   }
3481   else if (game.team_mode && !options.network)
3482   {
3483     /* try to guess locally connected team mode players (needed for correct
3484        assignment of player figures from level to locally playing players) */
3485
3486     for (i = 0; i < MAX_PLAYERS; i++)
3487       if (setup.input[i].use_joystick ||
3488           setup.input[i].key.left != KSYM_UNDEFINED)
3489         stored_player[i].connected = TRUE;
3490   }
3491
3492 #if DEBUG_INIT_PLAYER
3493   if (options.debug)
3494   {
3495     printf("Player status after level initialization:\n");
3496
3497     for (i = 0; i < MAX_PLAYERS; i++)
3498     {
3499       struct PlayerInfo *player = &stored_player[i];
3500
3501       printf("- player %d: present == %d, connected == %d, active == %d",
3502              i + 1,
3503              player->present,
3504              player->connected,
3505              player->active);
3506
3507       if (local_player == player)
3508         printf(" (local player)");
3509
3510       printf("\n");
3511     }
3512   }
3513 #endif
3514
3515 #if DEBUG_INIT_PLAYER
3516   if (options.debug)
3517     printf("Reassigning players ...\n");
3518 #endif
3519
3520   /* check if any connected player was not found in playfield */
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     if (player->connected && !player->present)
3526     {
3527       struct PlayerInfo *field_player = NULL;
3528
3529 #if DEBUG_INIT_PLAYER
3530       if (options.debug)
3531         printf("- looking for field player for player %d ...\n", i + 1);
3532 #endif
3533
3534       /* assign first free player found that is present in the playfield */
3535
3536       /* first try: look for unmapped playfield player that is not connected */
3537       for (j = 0; j < MAX_PLAYERS; j++)
3538         if (field_player == NULL &&
3539             stored_player[j].present &&
3540             !stored_player[j].mapped &&
3541             !stored_player[j].connected)
3542           field_player = &stored_player[j];
3543
3544       /* second try: look for *any* unmapped playfield player */
3545       for (j = 0; j < MAX_PLAYERS; j++)
3546         if (field_player == NULL &&
3547             stored_player[j].present &&
3548             !stored_player[j].mapped)
3549           field_player = &stored_player[j];
3550
3551       if (field_player != NULL)
3552       {
3553         int jx = field_player->jx, jy = field_player->jy;
3554
3555 #if DEBUG_INIT_PLAYER
3556         if (options.debug)
3557           printf("- found player %d\n", field_player->index_nr + 1);
3558 #endif
3559
3560         player->present = FALSE;
3561         player->active = FALSE;
3562
3563         field_player->present = TRUE;
3564         field_player->active = TRUE;
3565
3566         /*
3567         player->initial_element = field_player->initial_element;
3568         player->artwork_element = field_player->artwork_element;
3569
3570         player->block_last_field       = field_player->block_last_field;
3571         player->block_delay_adjustment = field_player->block_delay_adjustment;
3572         */
3573
3574         StorePlayer[jx][jy] = field_player->element_nr;
3575
3576         field_player->jx = field_player->last_jx = jx;
3577         field_player->jy = field_player->last_jy = jy;
3578
3579         if (local_player == player)
3580           local_player = field_player;
3581
3582         map_player_action[field_player->index_nr] = i;
3583
3584         field_player->mapped = TRUE;
3585
3586 #if DEBUG_INIT_PLAYER
3587         if (options.debug)
3588           printf("- map_player_action[%d] == %d\n",
3589                  field_player->index_nr + 1, i + 1);
3590 #endif
3591       }
3592     }
3593
3594     if (player->connected && player->present)
3595       player->mapped = TRUE;
3596   }
3597
3598 #if DEBUG_INIT_PLAYER
3599   if (options.debug)
3600   {
3601     printf("Player status after player assignment (first stage):\n");
3602
3603     for (i = 0; i < MAX_PLAYERS; i++)
3604     {
3605       struct PlayerInfo *player = &stored_player[i];
3606
3607       printf("- player %d: present == %d, connected == %d, active == %d",
3608              i + 1,
3609              player->present,
3610              player->connected,
3611              player->active);
3612
3613       if (local_player == player)
3614         printf(" (local player)");
3615
3616       printf("\n");
3617     }
3618   }
3619 #endif
3620
3621 #else
3622
3623   /* check if any connected player was not found in playfield */
3624   for (i = 0; i < MAX_PLAYERS; i++)
3625   {
3626     struct PlayerInfo *player = &stored_player[i];
3627
3628     if (player->connected && !player->present)
3629     {
3630       for (j = 0; j < MAX_PLAYERS; j++)
3631       {
3632         struct PlayerInfo *field_player = &stored_player[j];
3633         int jx = field_player->jx, jy = field_player->jy;
3634
3635         /* assign first free player found that is present in the playfield */
3636         if (field_player->present && !field_player->connected)
3637         {
3638           player->present = TRUE;
3639           player->active = TRUE;
3640
3641           field_player->present = FALSE;
3642           field_player->active = FALSE;
3643
3644           player->initial_element = field_player->initial_element;
3645           player->artwork_element = field_player->artwork_element;
3646
3647           player->block_last_field       = field_player->block_last_field;
3648           player->block_delay_adjustment = field_player->block_delay_adjustment;
3649
3650           StorePlayer[jx][jy] = player->element_nr;
3651
3652           player->jx = player->last_jx = jx;
3653           player->jy = player->last_jy = jy;
3654
3655           break;
3656         }
3657       }
3658     }
3659   }
3660 #endif
3661
3662 #if 0
3663   printf("::: local_player->present == %d\n", local_player->present);
3664 #endif
3665
3666   if (tape.playing)
3667   {
3668     /* when playing a tape, eliminate all players who do not participate */
3669
3670 #if USE_NEW_PLAYER_ASSIGNMENTS
3671
3672     if (!game.team_mode)
3673     {
3674       for (i = 0; i < MAX_PLAYERS; i++)
3675       {
3676         if (stored_player[i].active &&
3677             !tape.player_participates[map_player_action[i]])
3678         {
3679           struct PlayerInfo *player = &stored_player[i];
3680           int jx = player->jx, jy = player->jy;
3681
3682 #if DEBUG_INIT_PLAYER
3683           if (options.debug)
3684             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3685 #endif
3686
3687           player->active = FALSE;
3688           StorePlayer[jx][jy] = 0;
3689           Feld[jx][jy] = EL_EMPTY;
3690         }
3691       }
3692     }
3693
3694 #else
3695
3696     for (i = 0; i < MAX_PLAYERS; i++)
3697     {
3698       if (stored_player[i].active &&
3699           !tape.player_participates[i])
3700       {
3701         struct PlayerInfo *player = &stored_player[i];
3702         int jx = player->jx, jy = player->jy;
3703
3704         player->active = FALSE;
3705         StorePlayer[jx][jy] = 0;
3706         Feld[jx][jy] = EL_EMPTY;
3707       }
3708     }
3709 #endif
3710   }
3711   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3712   {
3713     /* when in single player mode, eliminate all but the first active player */
3714
3715     for (i = 0; i < MAX_PLAYERS; i++)
3716     {
3717       if (stored_player[i].active)
3718       {
3719         for (j = i + 1; j < MAX_PLAYERS; j++)
3720         {
3721           if (stored_player[j].active)
3722           {
3723             struct PlayerInfo *player = &stored_player[j];
3724             int jx = player->jx, jy = player->jy;
3725
3726             player->active = FALSE;
3727             player->present = FALSE;
3728
3729             StorePlayer[jx][jy] = 0;
3730             Feld[jx][jy] = EL_EMPTY;
3731           }
3732         }
3733       }
3734     }
3735   }
3736
3737   /* when recording the game, store which players take part in the game */
3738   if (tape.recording)
3739   {
3740 #if USE_NEW_PLAYER_ASSIGNMENTS
3741     for (i = 0; i < MAX_PLAYERS; i++)
3742       if (stored_player[i].connected)
3743         tape.player_participates[i] = TRUE;
3744 #else
3745     for (i = 0; i < MAX_PLAYERS; i++)
3746       if (stored_player[i].active)
3747         tape.player_participates[i] = TRUE;
3748 #endif
3749   }
3750
3751 #if DEBUG_INIT_PLAYER
3752   if (options.debug)
3753   {
3754     printf("Player status after player assignment (final stage):\n");
3755
3756     for (i = 0; i < MAX_PLAYERS; i++)
3757     {
3758       struct PlayerInfo *player = &stored_player[i];
3759
3760       printf("- player %d: present == %d, connected == %d, active == %d",
3761              i + 1,
3762              player->present,
3763              player->connected,
3764              player->active);
3765
3766       if (local_player == player)
3767         printf(" (local player)");
3768
3769       printf("\n");
3770     }
3771   }
3772 #endif
3773
3774   if (BorderElement == EL_EMPTY)
3775   {
3776     SBX_Left = 0;
3777     SBX_Right = lev_fieldx - SCR_FIELDX;
3778     SBY_Upper = 0;
3779     SBY_Lower = lev_fieldy - SCR_FIELDY;
3780   }
3781   else
3782   {
3783     SBX_Left = -1;
3784     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3785     SBY_Upper = -1;
3786     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3787   }
3788
3789   if (full_lev_fieldx <= SCR_FIELDX)
3790     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3791   if (full_lev_fieldy <= SCR_FIELDY)
3792     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3793
3794   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3795     SBX_Left--;
3796   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3797     SBY_Upper--;
3798
3799   /* if local player not found, look for custom element that might create
3800      the player (make some assumptions about the right custom element) */
3801   if (!local_player->present)
3802   {
3803     int start_x = 0, start_y = 0;
3804     int found_rating = 0;
3805     int found_element = EL_UNDEFINED;
3806     int player_nr = local_player->index_nr;
3807
3808     SCAN_PLAYFIELD(x, y)
3809     {
3810       int element = Feld[x][y];
3811       int content;
3812       int xx, yy;
3813       boolean is_player;
3814
3815       if (level.use_start_element[player_nr] &&
3816           level.start_element[player_nr] == element &&
3817           found_rating < 4)
3818       {
3819         start_x = x;
3820         start_y = y;
3821
3822         found_rating = 4;
3823         found_element = element;
3824       }
3825
3826       if (!IS_CUSTOM_ELEMENT(element))
3827         continue;
3828
3829       if (CAN_CHANGE(element))
3830       {
3831         for (i = 0; i < element_info[element].num_change_pages; i++)
3832         {
3833           /* check for player created from custom element as single target */
3834           content = element_info[element].change_page[i].target_element;
3835           is_player = ELEM_IS_PLAYER(content);
3836
3837           if (is_player && (found_rating < 3 ||
3838                             (found_rating == 3 && element < found_element)))
3839           {
3840             start_x = x;
3841             start_y = y;
3842
3843             found_rating = 3;
3844             found_element = element;
3845           }
3846         }
3847       }
3848
3849       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3850       {
3851         /* check for player created from custom element as explosion content */
3852         content = element_info[element].content.e[xx][yy];
3853         is_player = ELEM_IS_PLAYER(content);
3854
3855         if (is_player && (found_rating < 2 ||
3856                           (found_rating == 2 && element < found_element)))
3857         {
3858           start_x = x + xx - 1;
3859           start_y = y + yy - 1;
3860
3861           found_rating = 2;
3862           found_element = element;
3863         }
3864
3865         if (!CAN_CHANGE(element))
3866           continue;
3867
3868         for (i = 0; i < element_info[element].num_change_pages; i++)
3869         {
3870           /* check for player created from custom element as extended target */
3871           content =
3872             element_info[element].change_page[i].target_content.e[xx][yy];
3873
3874           is_player = ELEM_IS_PLAYER(content);
3875
3876           if (is_player && (found_rating < 1 ||
3877                             (found_rating == 1 && element < found_element)))
3878           {
3879             start_x = x + xx - 1;
3880             start_y = y + yy - 1;
3881
3882             found_rating = 1;
3883             found_element = element;
3884           }
3885         }
3886       }
3887     }
3888
3889     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3890                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3891                 start_x - MIDPOSX);
3892
3893     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3894                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3895                 start_y - MIDPOSY);
3896   }
3897   else
3898   {
3899     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3900                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3901                 local_player->jx - MIDPOSX);
3902
3903     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3904                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3905                 local_player->jy - MIDPOSY);
3906   }
3907
3908   /* !!! FIX THIS (START) !!! */
3909   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3910   {
3911     InitGameEngine_EM();
3912   }
3913   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3914   {
3915     InitGameEngine_SP();
3916   }
3917   else
3918   {
3919     DrawLevel(REDRAW_FIELD);
3920     DrawAllPlayers();
3921
3922     /* after drawing the level, correct some elements */
3923     if (game.timegate_time_left == 0)
3924       CloseAllOpenTimegates();
3925   }
3926
3927   /* blit playfield from scroll buffer to normal back buffer for fading in */
3928   BlitScreenToBitmap(backbuffer);
3929   /* !!! FIX THIS (END) !!! */
3930
3931   DrawMaskedBorder(fade_mask);
3932
3933   FadeIn(fade_mask);
3934
3935 #if 1
3936   // full screen redraw is required at this point in the following cases:
3937   // - special editor door undrawn when game was started from level editor
3938   // - drawing area (playfield) was changed and has to be removed completely
3939   redraw_mask = REDRAW_ALL;
3940   BackToFront();
3941 #endif
3942
3943   if (!game.restart_level)
3944   {
3945     /* copy default game door content to main double buffer */
3946
3947     /* !!! CHECK AGAIN !!! */
3948     SetPanelBackground();
3949     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3950     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3951   }
3952
3953   SetPanelBackground();
3954   SetDrawBackgroundMask(REDRAW_DOOR_1);
3955
3956   UpdateAndDisplayGameControlValues();
3957
3958   if (!game.restart_level)
3959   {
3960     UnmapGameButtons();
3961     UnmapTapeButtons();
3962
3963     FreeGameButtons();
3964     CreateGameButtons();
3965
3966     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3967     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3968     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3969
3970     MapGameButtons();
3971     MapTapeButtons();
3972
3973     /* copy actual game door content to door double buffer for OpenDoor() */
3974     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3975
3976     OpenDoor(DOOR_OPEN_ALL);
3977
3978     PlaySound(SND_GAME_STARTING);
3979
3980     if (setup.sound_music)
3981       PlayLevelMusic();
3982
3983     KeyboardAutoRepeatOffUnlessAutoplay();
3984
3985 #if DEBUG_INIT_PLAYER
3986     if (options.debug)
3987     {
3988       printf("Player status (final):\n");
3989
3990       for (i = 0; i < MAX_PLAYERS; i++)
3991       {
3992         struct PlayerInfo *player = &stored_player[i];
3993
3994         printf("- player %d: present == %d, connected == %d, active == %d",
3995                i + 1,
3996                player->present,
3997                player->connected,
3998                player->active);
3999
4000         if (local_player == player)
4001           printf(" (local player)");
4002
4003         printf("\n");
4004       }
4005     }
4006 #endif
4007   }
4008
4009   UnmapAllGadgets();
4010
4011   MapGameButtons();
4012   MapTapeButtons();
4013
4014   if (!game.restart_level && !tape.playing)
4015   {
4016     LevelStats_incPlayed(level_nr);
4017
4018     SaveLevelSetup_SeriesInfo();
4019   }
4020
4021   game.restart_level = FALSE;
4022
4023   SaveEngineSnapshotToListInitial();
4024 }
4025
4026 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4027 {
4028   /* this is used for non-R'n'D game engines to update certain engine values */
4029
4030   /* needed to determine if sounds are played within the visible screen area */
4031   scroll_x = actual_scroll_x;
4032   scroll_y = actual_scroll_y;
4033 }
4034
4035 void InitMovDir(int x, int y)
4036 {
4037   int i, element = Feld[x][y];
4038   static int xy[4][2] =
4039   {
4040     {  0, +1 },
4041     { +1,  0 },
4042     {  0, -1 },
4043     { -1,  0 }
4044   };
4045   static int direction[3][4] =
4046   {
4047     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4048     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4049     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4050   };
4051
4052   switch (element)
4053   {
4054     case EL_BUG_RIGHT:
4055     case EL_BUG_UP:
4056     case EL_BUG_LEFT:
4057     case EL_BUG_DOWN:
4058       Feld[x][y] = EL_BUG;
4059       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4060       break;
4061
4062     case EL_SPACESHIP_RIGHT:
4063     case EL_SPACESHIP_UP:
4064     case EL_SPACESHIP_LEFT:
4065     case EL_SPACESHIP_DOWN:
4066       Feld[x][y] = EL_SPACESHIP;
4067       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4068       break;
4069
4070     case EL_BD_BUTTERFLY_RIGHT:
4071     case EL_BD_BUTTERFLY_UP:
4072     case EL_BD_BUTTERFLY_LEFT:
4073     case EL_BD_BUTTERFLY_DOWN:
4074       Feld[x][y] = EL_BD_BUTTERFLY;
4075       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4076       break;
4077
4078     case EL_BD_FIREFLY_RIGHT:
4079     case EL_BD_FIREFLY_UP:
4080     case EL_BD_FIREFLY_LEFT:
4081     case EL_BD_FIREFLY_DOWN:
4082       Feld[x][y] = EL_BD_FIREFLY;
4083       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4084       break;
4085
4086     case EL_PACMAN_RIGHT:
4087     case EL_PACMAN_UP:
4088     case EL_PACMAN_LEFT:
4089     case EL_PACMAN_DOWN:
4090       Feld[x][y] = EL_PACMAN;
4091       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4092       break;
4093
4094     case EL_YAMYAM_LEFT:
4095     case EL_YAMYAM_RIGHT:
4096     case EL_YAMYAM_UP:
4097     case EL_YAMYAM_DOWN:
4098       Feld[x][y] = EL_YAMYAM;
4099       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4100       break;
4101
4102     case EL_SP_SNIKSNAK:
4103       MovDir[x][y] = MV_UP;
4104       break;
4105
4106     case EL_SP_ELECTRON:
4107       MovDir[x][y] = MV_LEFT;
4108       break;
4109
4110     case EL_MOLE_LEFT:
4111     case EL_MOLE_RIGHT:
4112     case EL_MOLE_UP:
4113     case EL_MOLE_DOWN:
4114       Feld[x][y] = EL_MOLE;
4115       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4116       break;
4117
4118     default:
4119       if (IS_CUSTOM_ELEMENT(element))
4120       {
4121         struct ElementInfo *ei = &element_info[element];
4122         int move_direction_initial = ei->move_direction_initial;
4123         int move_pattern = ei->move_pattern;
4124
4125         if (move_direction_initial == MV_START_PREVIOUS)
4126         {
4127           if (MovDir[x][y] != MV_NONE)
4128             return;
4129
4130           move_direction_initial = MV_START_AUTOMATIC;
4131         }
4132
4133         if (move_direction_initial == MV_START_RANDOM)
4134           MovDir[x][y] = 1 << RND(4);
4135         else if (move_direction_initial & MV_ANY_DIRECTION)
4136           MovDir[x][y] = move_direction_initial;
4137         else if (move_pattern == MV_ALL_DIRECTIONS ||
4138                  move_pattern == MV_TURNING_LEFT ||
4139                  move_pattern == MV_TURNING_RIGHT ||
4140                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4141                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4142                  move_pattern == MV_TURNING_RANDOM)
4143           MovDir[x][y] = 1 << RND(4);
4144         else if (move_pattern == MV_HORIZONTAL)
4145           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4146         else if (move_pattern == MV_VERTICAL)
4147           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4148         else if (move_pattern & MV_ANY_DIRECTION)
4149           MovDir[x][y] = element_info[element].move_pattern;
4150         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4151                  move_pattern == MV_ALONG_RIGHT_SIDE)
4152         {
4153           /* use random direction as default start direction */
4154           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4155             MovDir[x][y] = 1 << RND(4);
4156
4157           for (i = 0; i < NUM_DIRECTIONS; i++)
4158           {
4159             int x1 = x + xy[i][0];
4160             int y1 = y + xy[i][1];
4161
4162             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4163             {
4164               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4165                 MovDir[x][y] = direction[0][i];
4166               else
4167                 MovDir[x][y] = direction[1][i];
4168
4169               break;
4170             }
4171           }
4172         }                
4173       }
4174       else
4175       {
4176         MovDir[x][y] = 1 << RND(4);
4177
4178         if (element != EL_BUG &&
4179             element != EL_SPACESHIP &&
4180             element != EL_BD_BUTTERFLY &&
4181             element != EL_BD_FIREFLY)
4182           break;
4183
4184         for (i = 0; i < NUM_DIRECTIONS; i++)
4185         {
4186           int x1 = x + xy[i][0];
4187           int y1 = y + xy[i][1];
4188
4189           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4190           {
4191             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4192             {
4193               MovDir[x][y] = direction[0][i];
4194               break;
4195             }
4196             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4197                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4198             {
4199               MovDir[x][y] = direction[1][i];
4200               break;
4201             }
4202           }
4203         }
4204       }
4205       break;
4206   }
4207
4208   GfxDir[x][y] = MovDir[x][y];
4209 }
4210
4211 void InitAmoebaNr(int x, int y)
4212 {
4213   int i;
4214   int group_nr = AmoebeNachbarNr(x, y);
4215
4216   if (group_nr == 0)
4217   {
4218     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4219     {
4220       if (AmoebaCnt[i] == 0)
4221       {
4222         group_nr = i;
4223         break;
4224       }
4225     }
4226   }
4227
4228   AmoebaNr[x][y] = group_nr;
4229   AmoebaCnt[group_nr]++;
4230   AmoebaCnt2[group_nr]++;
4231 }
4232
4233 static void PlayerWins(struct PlayerInfo *player)
4234 {
4235   player->LevelSolved = TRUE;
4236   player->GameOver = TRUE;
4237
4238   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4239                          level.native_em_level->lev->score : player->score);
4240
4241   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4242                                       TimeLeft);
4243   player->LevelSolved_CountingScore = player->score_final;
4244 }
4245
4246 void GameWon()
4247 {
4248   static int time, time_final;
4249   static int score, score_final;
4250   static int game_over_delay_1 = 0;
4251   static int game_over_delay_2 = 0;
4252   int game_over_delay_value_1 = 50;
4253   int game_over_delay_value_2 = 50;
4254
4255   if (!local_player->LevelSolved_GameWon)
4256   {
4257     int i;
4258
4259     /* do not start end game actions before the player stops moving (to exit) */
4260     if (local_player->MovPos)
4261       return;
4262
4263     local_player->LevelSolved_GameWon = TRUE;
4264     local_player->LevelSolved_SaveTape = tape.recording;
4265     local_player->LevelSolved_SaveScore = !tape.playing;
4266
4267     if (!tape.playing)
4268     {
4269       LevelStats_incSolved(level_nr);
4270
4271       SaveLevelSetup_SeriesInfo();
4272     }
4273
4274     if (tape.auto_play)         /* tape might already be stopped here */
4275       tape.auto_play_level_solved = TRUE;
4276
4277     TapeStop();
4278
4279     game_over_delay_1 = game_over_delay_value_1;
4280     game_over_delay_2 = game_over_delay_value_2;
4281
4282     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4283     score = score_final = local_player->score_final;
4284
4285     if (TimeLeft > 0)
4286     {
4287       time_final = 0;
4288       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4289     }
4290     else if (game.no_time_limit && TimePlayed < 999)
4291     {
4292       time_final = 999;
4293       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4294     }
4295
4296     local_player->score_final = score_final;
4297
4298     if (level_editor_test_game)
4299     {
4300       time = time_final;
4301       score = score_final;
4302
4303       local_player->LevelSolved_CountingTime = time;
4304       local_player->LevelSolved_CountingScore = score;
4305
4306       game_panel_controls[GAME_PANEL_TIME].value = time;
4307       game_panel_controls[GAME_PANEL_SCORE].value = score;
4308
4309       DisplayGameControlValues();
4310     }
4311
4312     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4313     {
4314       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4315       {
4316         /* close exit door after last player */
4317         if ((AllPlayersGone &&
4318              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4319               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4320               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4321             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4322             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4323         {
4324           int element = Feld[ExitX][ExitY];
4325
4326           Feld[ExitX][ExitY] =
4327             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4328              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4329              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4330              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4331              EL_EM_STEEL_EXIT_CLOSING);
4332
4333           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4334         }
4335
4336         /* player disappears */
4337         DrawLevelField(ExitX, ExitY);
4338       }
4339
4340       for (i = 0; i < MAX_PLAYERS; i++)
4341       {
4342         struct PlayerInfo *player = &stored_player[i];
4343
4344         if (player->present)
4345         {
4346           RemovePlayer(player);
4347
4348           /* player disappears */
4349           DrawLevelField(player->jx, player->jy);
4350         }
4351       }
4352     }
4353
4354     PlaySound(SND_GAME_WINNING);
4355   }
4356
4357   if (game_over_delay_1 > 0)
4358   {
4359     game_over_delay_1--;
4360
4361     return;
4362   }
4363
4364   if (time != time_final)
4365   {
4366     int time_to_go = ABS(time_final - time);
4367     int time_count_dir = (time < time_final ? +1 : -1);
4368     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4369
4370     time  += time_count_steps * time_count_dir;
4371     score += time_count_steps * level.score[SC_TIME_BONUS];
4372
4373     local_player->LevelSolved_CountingTime = time;
4374     local_player->LevelSolved_CountingScore = score;
4375
4376     game_panel_controls[GAME_PANEL_TIME].value = time;
4377     game_panel_controls[GAME_PANEL_SCORE].value = score;
4378
4379     DisplayGameControlValues();
4380
4381     if (time == time_final)
4382       StopSound(SND_GAME_LEVELTIME_BONUS);
4383     else if (setup.sound_loops)
4384       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4385     else
4386       PlaySound(SND_GAME_LEVELTIME_BONUS);
4387
4388     return;
4389   }
4390
4391   local_player->LevelSolved_PanelOff = TRUE;
4392
4393   if (game_over_delay_2 > 0)
4394   {
4395     game_over_delay_2--;
4396
4397     return;
4398   }
4399
4400   GameEnd();
4401 }
4402
4403 void GameEnd()
4404 {
4405   int hi_pos;
4406   boolean raise_level = FALSE;
4407
4408   local_player->LevelSolved_GameEnd = TRUE;
4409
4410   if (!global.use_envelope_request)
4411     CloseDoor(DOOR_CLOSE_1);
4412
4413   if (local_player->LevelSolved_SaveTape)
4414   {
4415     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4416   }
4417
4418   CloseDoor(DOOR_CLOSE_ALL);
4419
4420   if (level_editor_test_game)
4421   {
4422     game_status = GAME_MODE_MAIN;
4423
4424     DrawMainMenu();
4425
4426     return;
4427   }
4428
4429   if (!local_player->LevelSolved_SaveScore)
4430   {
4431     game_status = GAME_MODE_MAIN;
4432
4433     DrawMainMenu();
4434
4435     return;
4436   }
4437
4438   if (level_nr == leveldir_current->handicap_level)
4439   {
4440     leveldir_current->handicap_level++;
4441
4442     SaveLevelSetup_SeriesInfo();
4443   }
4444
4445   if (level_nr < leveldir_current->last_level)
4446     raise_level = TRUE;                 /* advance to next level */
4447
4448   if ((hi_pos = NewHiScore()) >= 0) 
4449   {
4450     game_status = GAME_MODE_SCORES;
4451
4452     DrawHallOfFame(hi_pos);
4453
4454     if (raise_level)
4455     {
4456       level_nr++;
4457       TapeErase();
4458     }
4459   }
4460   else
4461   {
4462     game_status = GAME_MODE_MAIN;
4463
4464     if (raise_level)
4465     {
4466       level_nr++;
4467       TapeErase();
4468     }
4469
4470     DrawMainMenu();
4471   }
4472 }
4473
4474 int NewHiScore()
4475 {
4476   int k, l;
4477   int position = -1;
4478
4479   LoadScore(level_nr);
4480
4481   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4482       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4483     return -1;
4484
4485   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4486   {
4487     if (local_player->score_final > highscore[k].Score)
4488     {
4489       /* player has made it to the hall of fame */
4490
4491       if (k < MAX_SCORE_ENTRIES - 1)
4492       {
4493         int m = MAX_SCORE_ENTRIES - 1;
4494
4495 #ifdef ONE_PER_NAME
4496         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4497           if (strEqual(setup.player_name, highscore[l].Name))
4498             m = l;
4499         if (m == k)     /* player's new highscore overwrites his old one */
4500           goto put_into_list;
4501 #endif
4502
4503         for (l = m; l > k; l--)
4504         {
4505           strcpy(highscore[l].Name, highscore[l - 1].Name);
4506           highscore[l].Score = highscore[l - 1].Score;
4507         }
4508       }
4509
4510 #ifdef ONE_PER_NAME
4511       put_into_list:
4512 #endif
4513       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4514       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4515       highscore[k].Score = local_player->score_final; 
4516       position = k;
4517       break;
4518     }
4519
4520 #ifdef ONE_PER_NAME
4521     else if (!strncmp(setup.player_name, highscore[k].Name,
4522                       MAX_PLAYER_NAME_LEN))
4523       break;    /* player already there with a higher score */
4524 #endif
4525
4526   }
4527
4528   if (position >= 0) 
4529     SaveScore(level_nr);
4530
4531   return position;
4532 }
4533
4534 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4535 {
4536   int element = Feld[x][y];
4537   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4538   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4539   int horiz_move = (dx != 0);
4540   int sign = (horiz_move ? dx : dy);
4541   int step = sign * element_info[element].move_stepsize;
4542
4543   /* special values for move stepsize for spring and things on conveyor belt */
4544   if (horiz_move)
4545   {
4546     if (CAN_FALL(element) &&
4547         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4548       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4549     else if (element == EL_SPRING)
4550       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4551   }
4552
4553   return step;
4554 }
4555
4556 inline static int getElementMoveStepsize(int x, int y)
4557 {
4558   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4559 }
4560
4561 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4562 {
4563   if (player->GfxAction != action || player->GfxDir != dir)
4564   {
4565     player->GfxAction = action;
4566     player->GfxDir = dir;
4567     player->Frame = 0;
4568     player->StepFrame = 0;
4569   }
4570 }
4571
4572 static void ResetGfxFrame(int x, int y, boolean redraw)
4573 {
4574   int element = Feld[x][y];
4575   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4576   int last_gfx_frame = GfxFrame[x][y];
4577
4578   if (graphic_info[graphic].anim_global_sync)
4579     GfxFrame[x][y] = FrameCounter;
4580   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4581     GfxFrame[x][y] = CustomValue[x][y];
4582   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4583     GfxFrame[x][y] = element_info[element].collect_score;
4584   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4585     GfxFrame[x][y] = ChangeDelay[x][y];
4586
4587   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4588     DrawLevelGraphicAnimation(x, y, graphic);
4589 }
4590
4591 static void ResetGfxAnimation(int x, int y)
4592 {
4593   GfxAction[x][y] = ACTION_DEFAULT;
4594   GfxDir[x][y] = MovDir[x][y];
4595   GfxFrame[x][y] = 0;
4596
4597   ResetGfxFrame(x, y, FALSE);
4598 }
4599
4600 static void ResetRandomAnimationValue(int x, int y)
4601 {
4602   GfxRandom[x][y] = INIT_GFX_RANDOM();
4603 }
4604
4605 void InitMovingField(int x, int y, int direction)
4606 {
4607   int element = Feld[x][y];
4608   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4609   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4610   int newx = x + dx;
4611   int newy = y + dy;
4612   boolean is_moving_before, is_moving_after;
4613
4614   /* check if element was/is moving or being moved before/after mode change */
4615   is_moving_before = (WasJustMoving[x][y] != 0);
4616   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4617
4618   /* reset animation only for moving elements which change direction of moving
4619      or which just started or stopped moving
4620      (else CEs with property "can move" / "not moving" are reset each frame) */
4621   if (is_moving_before != is_moving_after ||
4622       direction != MovDir[x][y])
4623     ResetGfxAnimation(x, y);
4624
4625   MovDir[x][y] = direction;
4626   GfxDir[x][y] = direction;
4627
4628   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4629                      direction == MV_DOWN && CAN_FALL(element) ?
4630                      ACTION_FALLING : ACTION_MOVING);
4631
4632   /* this is needed for CEs with property "can move" / "not moving" */
4633
4634   if (is_moving_after)
4635   {
4636     if (Feld[newx][newy] == EL_EMPTY)
4637       Feld[newx][newy] = EL_BLOCKED;
4638
4639     MovDir[newx][newy] = MovDir[x][y];
4640
4641     CustomValue[newx][newy] = CustomValue[x][y];
4642
4643     GfxFrame[newx][newy] = GfxFrame[x][y];
4644     GfxRandom[newx][newy] = GfxRandom[x][y];
4645     GfxAction[newx][newy] = GfxAction[x][y];
4646     GfxDir[newx][newy] = GfxDir[x][y];
4647   }
4648 }
4649
4650 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4651 {
4652   int direction = MovDir[x][y];
4653   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4654   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4655
4656   *goes_to_x = newx;
4657   *goes_to_y = newy;
4658 }
4659
4660 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4661 {
4662   int oldx = x, oldy = y;
4663   int direction = MovDir[x][y];
4664
4665   if (direction == MV_LEFT)
4666     oldx++;
4667   else if (direction == MV_RIGHT)
4668     oldx--;
4669   else if (direction == MV_UP)
4670     oldy++;
4671   else if (direction == MV_DOWN)
4672     oldy--;
4673
4674   *comes_from_x = oldx;
4675   *comes_from_y = oldy;
4676 }
4677
4678 int MovingOrBlocked2Element(int x, int y)
4679 {
4680   int element = Feld[x][y];
4681
4682   if (element == EL_BLOCKED)
4683   {
4684     int oldx, oldy;
4685
4686     Blocked2Moving(x, y, &oldx, &oldy);
4687     return Feld[oldx][oldy];
4688   }
4689   else
4690     return element;
4691 }
4692
4693 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4694 {
4695   /* like MovingOrBlocked2Element(), but if element is moving
4696      and (x,y) is the field the moving element is just leaving,
4697      return EL_BLOCKED instead of the element value */
4698   int element = Feld[x][y];
4699
4700   if (IS_MOVING(x, y))
4701   {
4702     if (element == EL_BLOCKED)
4703     {
4704       int oldx, oldy;
4705
4706       Blocked2Moving(x, y, &oldx, &oldy);
4707       return Feld[oldx][oldy];
4708     }
4709     else
4710       return EL_BLOCKED;
4711   }
4712   else
4713     return element;
4714 }
4715
4716 static void RemoveField(int x, int y)
4717 {
4718   Feld[x][y] = EL_EMPTY;
4719
4720   MovPos[x][y] = 0;
4721   MovDir[x][y] = 0;
4722   MovDelay[x][y] = 0;
4723
4724   CustomValue[x][y] = 0;
4725
4726   AmoebaNr[x][y] = 0;
4727   ChangeDelay[x][y] = 0;
4728   ChangePage[x][y] = -1;
4729   Pushed[x][y] = FALSE;
4730
4731   GfxElement[x][y] = EL_UNDEFINED;
4732   GfxAction[x][y] = ACTION_DEFAULT;
4733   GfxDir[x][y] = MV_NONE;
4734 }
4735
4736 void RemoveMovingField(int x, int y)
4737 {
4738   int oldx = x, oldy = y, newx = x, newy = y;
4739   int element = Feld[x][y];
4740   int next_element = EL_UNDEFINED;
4741
4742   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4743     return;
4744
4745   if (IS_MOVING(x, y))
4746   {
4747     Moving2Blocked(x, y, &newx, &newy);
4748
4749     if (Feld[newx][newy] != EL_BLOCKED)
4750     {
4751       /* element is moving, but target field is not free (blocked), but
4752          already occupied by something different (example: acid pool);
4753          in this case, only remove the moving field, but not the target */
4754
4755       RemoveField(oldx, oldy);
4756
4757       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4758
4759       TEST_DrawLevelField(oldx, oldy);
4760
4761       return;
4762     }
4763   }
4764   else if (element == EL_BLOCKED)
4765   {
4766     Blocked2Moving(x, y, &oldx, &oldy);
4767     if (!IS_MOVING(oldx, oldy))
4768       return;
4769   }
4770
4771   if (element == EL_BLOCKED &&
4772       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4773        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4774        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4775        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4776        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4777        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4778     next_element = get_next_element(Feld[oldx][oldy]);
4779
4780   RemoveField(oldx, oldy);
4781   RemoveField(newx, newy);
4782
4783   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4784
4785   if (next_element != EL_UNDEFINED)
4786     Feld[oldx][oldy] = next_element;
4787
4788   TEST_DrawLevelField(oldx, oldy);
4789   TEST_DrawLevelField(newx, newy);
4790 }
4791
4792 void DrawDynamite(int x, int y)
4793 {
4794   int sx = SCREENX(x), sy = SCREENY(y);
4795   int graphic = el2img(Feld[x][y]);
4796   int frame;
4797
4798   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4799     return;
4800
4801   if (IS_WALKABLE_INSIDE(Back[x][y]))
4802     return;
4803
4804   if (Back[x][y])
4805     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4806   else if (Store[x][y])
4807     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4808
4809   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4810
4811   if (Back[x][y] || Store[x][y])
4812     DrawGraphicThruMask(sx, sy, graphic, frame);
4813   else
4814     DrawGraphic(sx, sy, graphic, frame);
4815 }
4816
4817 void CheckDynamite(int x, int y)
4818 {
4819   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4820   {
4821     MovDelay[x][y]--;
4822
4823     if (MovDelay[x][y] != 0)
4824     {
4825       DrawDynamite(x, y);
4826       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4827
4828       return;
4829     }
4830   }
4831
4832   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4833
4834   Bang(x, y);
4835 }
4836
4837 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4838 {
4839   boolean num_checked_players = 0;
4840   int i;
4841
4842   for (i = 0; i < MAX_PLAYERS; i++)
4843   {
4844     if (stored_player[i].active)
4845     {
4846       int sx = stored_player[i].jx;
4847       int sy = stored_player[i].jy;
4848
4849       if (num_checked_players == 0)
4850       {
4851         *sx1 = *sx2 = sx;
4852         *sy1 = *sy2 = sy;
4853       }
4854       else
4855       {
4856         *sx1 = MIN(*sx1, sx);
4857         *sy1 = MIN(*sy1, sy);
4858         *sx2 = MAX(*sx2, sx);
4859         *sy2 = MAX(*sy2, sy);
4860       }
4861
4862       num_checked_players++;
4863     }
4864   }
4865 }
4866
4867 static boolean checkIfAllPlayersFitToScreen_RND()
4868 {
4869   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4870
4871   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4872
4873   return (sx2 - sx1 < SCR_FIELDX &&
4874           sy2 - sy1 < SCR_FIELDY);
4875 }
4876
4877 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4878 {
4879   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4880
4881   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4882
4883   *sx = (sx1 + sx2) / 2;
4884   *sy = (sy1 + sy2) / 2;
4885 }
4886
4887 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4888                         boolean center_screen, boolean quick_relocation)
4889 {
4890   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4891   boolean no_delay = (tape.warp_forward);
4892   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4893   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4894
4895   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4896   {
4897     RedrawPlayfield();
4898   }
4899   else if (quick_relocation)
4900   {
4901     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4902     {
4903       if (!level.shifted_relocation || center_screen)
4904       {
4905         /* quick relocation (without scrolling), with centering of screen */
4906
4907         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4908                     x > SBX_Right + MIDPOSX ? SBX_Right :
4909                     x - MIDPOSX);
4910
4911         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4912                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4913                     y - MIDPOSY);
4914       }
4915       else
4916       {
4917         /* quick relocation (without scrolling), but do not center screen */
4918
4919         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4920                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4921                                old_x - MIDPOSX);
4922
4923         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4924                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4925                                old_y - MIDPOSY);
4926
4927         int offset_x = x + (scroll_x - center_scroll_x);
4928         int offset_y = y + (scroll_y - center_scroll_y);
4929
4930         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4931                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4932                     offset_x - MIDPOSX);
4933
4934         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4935                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4936                     offset_y - MIDPOSY);
4937       }
4938     }
4939     else
4940     {
4941       if (!level.shifted_relocation || center_screen)
4942       {
4943         /* quick relocation (without scrolling), with centering of screen */
4944
4945         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4946                     x > SBX_Right + MIDPOSX ? SBX_Right :
4947                     x - MIDPOSX);
4948
4949         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4950                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4951                     y - MIDPOSY);
4952       }
4953       else
4954       {
4955         /* quick relocation (without scrolling), but do not center screen */
4956
4957         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4958                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4959                                old_x - MIDPOSX);
4960
4961         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4962                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4963                                old_y - MIDPOSY);
4964
4965         int offset_x = x + (scroll_x - center_scroll_x);
4966         int offset_y = y + (scroll_y - center_scroll_y);
4967
4968         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4969                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4970                     offset_x - MIDPOSX);
4971
4972         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4973                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4974                     offset_y - MIDPOSY);
4975       }
4976     }
4977
4978     RedrawPlayfield();
4979   }
4980   else
4981   {
4982     int scroll_xx, scroll_yy;
4983
4984     if (!level.shifted_relocation || center_screen)
4985     {
4986       /* visible relocation (with scrolling), with centering of screen */
4987
4988       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4989                    x > SBX_Right + MIDPOSX ? SBX_Right :
4990                    x - MIDPOSX);
4991
4992       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4993                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4994                    y - MIDPOSY);
4995     }
4996     else
4997     {
4998       /* visible relocation (with scrolling), but do not center screen */
4999
5000       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5001                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5002                              old_x - MIDPOSX);
5003
5004       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5005                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5006                              old_y - MIDPOSY);
5007
5008       int offset_x = x + (scroll_x - center_scroll_x);
5009       int offset_y = y + (scroll_y - center_scroll_y);
5010
5011       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5012                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5013                    offset_x - MIDPOSX);
5014
5015       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5016                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5017                    offset_y - MIDPOSY);
5018     }
5019
5020     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5021
5022     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5023     {
5024       int dx = 0, dy = 0;
5025       int fx = FX, fy = FY;
5026
5027       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5028       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5029
5030       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5031         break;
5032
5033       scroll_x -= dx;
5034       scroll_y -= dy;
5035
5036       fx += dx * TILEX / 2;
5037       fy += dy * TILEY / 2;
5038
5039       ScrollLevel(dx, dy);
5040       DrawAllPlayers();
5041
5042       /* scroll in two steps of half tile size to make things smoother */
5043       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5044       Delay(wait_delay_value);
5045
5046       /* scroll second step to align at full tile size */
5047       BackToFront();
5048       Delay(wait_delay_value);
5049     }
5050
5051     DrawAllPlayers();
5052     BackToFront();
5053     Delay(wait_delay_value);
5054   }
5055 }
5056
5057 void RelocatePlayer(int jx, int jy, int el_player_raw)
5058 {
5059   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5060   int player_nr = GET_PLAYER_NR(el_player);
5061   struct PlayerInfo *player = &stored_player[player_nr];
5062   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5063   boolean no_delay = (tape.warp_forward);
5064   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5065   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5066   int old_jx = player->jx;
5067   int old_jy = player->jy;
5068   int old_element = Feld[old_jx][old_jy];
5069   int element = Feld[jx][jy];
5070   boolean player_relocated = (old_jx != jx || old_jy != jy);
5071
5072   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5073   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5074   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5075   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5076   int leave_side_horiz = move_dir_horiz;
5077   int leave_side_vert  = move_dir_vert;
5078   int enter_side = enter_side_horiz | enter_side_vert;
5079   int leave_side = leave_side_horiz | leave_side_vert;
5080
5081   if (player->GameOver)         /* do not reanimate dead player */
5082     return;
5083
5084   if (!player_relocated)        /* no need to relocate the player */
5085     return;
5086
5087   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5088   {
5089     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5090     DrawLevelField(jx, jy);
5091   }
5092
5093   if (player->present)
5094   {
5095     while (player->MovPos)
5096     {
5097       ScrollPlayer(player, SCROLL_GO_ON);
5098       ScrollScreen(NULL, SCROLL_GO_ON);
5099
5100       AdvanceFrameAndPlayerCounters(player->index_nr);
5101
5102       DrawPlayer(player);
5103
5104       BackToFront();
5105       Delay(wait_delay_value);
5106     }
5107
5108     DrawPlayer(player);         /* needed here only to cleanup last field */
5109     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5110
5111     player->is_moving = FALSE;
5112   }
5113
5114   if (IS_CUSTOM_ELEMENT(old_element))
5115     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5116                                CE_LEFT_BY_PLAYER,
5117                                player->index_bit, leave_side);
5118
5119   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5120                                       CE_PLAYER_LEAVES_X,
5121                                       player->index_bit, leave_side);
5122
5123   Feld[jx][jy] = el_player;
5124   InitPlayerField(jx, jy, el_player, TRUE);
5125
5126   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5127      possible that the relocation target field did not contain a player element,
5128      but a walkable element, to which the new player was relocated -- in this
5129      case, restore that (already initialized!) element on the player field */
5130   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5131   {
5132     Feld[jx][jy] = element;     /* restore previously existing element */
5133   }
5134
5135   /* only visually relocate centered player */
5136   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5137                      FALSE, level.instant_relocation);
5138
5139   TestIfPlayerTouchesBadThing(jx, jy);
5140   TestIfPlayerTouchesCustomElement(jx, jy);
5141
5142   if (IS_CUSTOM_ELEMENT(element))
5143     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5144                                player->index_bit, enter_side);
5145
5146   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5147                                       player->index_bit, enter_side);
5148
5149   if (player->is_switching)
5150   {
5151     /* ensure that relocation while still switching an element does not cause
5152        a new element to be treated as also switched directly after relocation
5153        (this is important for teleporter switches that teleport the player to
5154        a place where another teleporter switch is in the same direction, which
5155        would then incorrectly be treated as immediately switched before the
5156        direction key that caused the switch was released) */
5157
5158     player->switch_x += jx - old_jx;
5159     player->switch_y += jy - old_jy;
5160   }
5161 }
5162
5163 void Explode(int ex, int ey, int phase, int mode)
5164 {
5165   int x, y;
5166   int last_phase;
5167   int border_element;
5168
5169   /* !!! eliminate this variable !!! */
5170   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5171
5172   if (game.explosions_delayed)
5173   {
5174     ExplodeField[ex][ey] = mode;
5175     return;
5176   }
5177
5178   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5179   {
5180     int center_element = Feld[ex][ey];
5181     int artwork_element, explosion_element;     /* set these values later */
5182
5183     /* remove things displayed in background while burning dynamite */
5184     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5185       Back[ex][ey] = 0;
5186
5187     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5188     {
5189       /* put moving element to center field (and let it explode there) */
5190       center_element = MovingOrBlocked2Element(ex, ey);
5191       RemoveMovingField(ex, ey);
5192       Feld[ex][ey] = center_element;
5193     }
5194
5195     /* now "center_element" is finally determined -- set related values now */
5196     artwork_element = center_element;           /* for custom player artwork */
5197     explosion_element = center_element;         /* for custom player artwork */
5198
5199     if (IS_PLAYER(ex, ey))
5200     {
5201       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5202
5203       artwork_element = stored_player[player_nr].artwork_element;
5204
5205       if (level.use_explosion_element[player_nr])
5206       {
5207         explosion_element = level.explosion_element[player_nr];
5208         artwork_element = explosion_element;
5209       }
5210     }
5211
5212     if (mode == EX_TYPE_NORMAL ||
5213         mode == EX_TYPE_CENTER ||
5214         mode == EX_TYPE_CROSS)
5215       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5216
5217     last_phase = element_info[explosion_element].explosion_delay + 1;
5218
5219     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5220     {
5221       int xx = x - ex + 1;
5222       int yy = y - ey + 1;
5223       int element;
5224
5225       if (!IN_LEV_FIELD(x, y) ||
5226           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5227           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5228         continue;
5229
5230       element = Feld[x][y];
5231
5232       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5233       {
5234         element = MovingOrBlocked2Element(x, y);
5235
5236         if (!IS_EXPLOSION_PROOF(element))
5237           RemoveMovingField(x, y);
5238       }
5239
5240       /* indestructible elements can only explode in center (but not flames) */
5241       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5242                                            mode == EX_TYPE_BORDER)) ||
5243           element == EL_FLAMES)
5244         continue;
5245
5246       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5247          behaviour, for example when touching a yamyam that explodes to rocks
5248          with active deadly shield, a rock is created under the player !!! */
5249       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5250 #if 0
5251       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5252           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5253            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5254 #else
5255       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5256 #endif
5257       {
5258         if (IS_ACTIVE_BOMB(element))
5259         {
5260           /* re-activate things under the bomb like gate or penguin */
5261           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5262           Back[x][y] = 0;
5263         }
5264
5265         continue;
5266       }
5267
5268       /* save walkable background elements while explosion on same tile */
5269       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5270           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5271         Back[x][y] = element;
5272
5273       /* ignite explodable elements reached by other explosion */
5274       if (element == EL_EXPLOSION)
5275         element = Store2[x][y];
5276
5277       if (AmoebaNr[x][y] &&
5278           (element == EL_AMOEBA_FULL ||
5279            element == EL_BD_AMOEBA ||
5280            element == EL_AMOEBA_GROWING))
5281       {
5282         AmoebaCnt[AmoebaNr[x][y]]--;
5283         AmoebaCnt2[AmoebaNr[x][y]]--;
5284       }
5285
5286       RemoveField(x, y);
5287
5288       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5289       {
5290         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5291
5292         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5293
5294         if (PLAYERINFO(ex, ey)->use_murphy)
5295           Store[x][y] = EL_EMPTY;
5296       }
5297
5298       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5299          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5300       else if (ELEM_IS_PLAYER(center_element))
5301         Store[x][y] = EL_EMPTY;
5302       else if (center_element == EL_YAMYAM)
5303         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5304       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5305         Store[x][y] = element_info[center_element].content.e[xx][yy];
5306 #if 1
5307       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5308          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5309          otherwise) -- FIX THIS !!! */
5310       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5311         Store[x][y] = element_info[element].content.e[1][1];
5312 #else
5313       else if (!CAN_EXPLODE(element))
5314         Store[x][y] = element_info[element].content.e[1][1];
5315 #endif
5316       else
5317         Store[x][y] = EL_EMPTY;
5318
5319       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5320           center_element == EL_AMOEBA_TO_DIAMOND)
5321         Store2[x][y] = element;
5322
5323       Feld[x][y] = EL_EXPLOSION;
5324       GfxElement[x][y] = artwork_element;
5325
5326       ExplodePhase[x][y] = 1;
5327       ExplodeDelay[x][y] = last_phase;
5328
5329       Stop[x][y] = TRUE;
5330     }
5331
5332     if (center_element == EL_YAMYAM)
5333       game.yamyam_content_nr =
5334         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5335
5336     return;
5337   }
5338
5339   if (Stop[ex][ey])
5340     return;
5341
5342   x = ex;
5343   y = ey;
5344
5345   if (phase == 1)
5346     GfxFrame[x][y] = 0;         /* restart explosion animation */
5347
5348   last_phase = ExplodeDelay[x][y];
5349
5350   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5351
5352   /* this can happen if the player leaves an explosion just in time */
5353   if (GfxElement[x][y] == EL_UNDEFINED)
5354     GfxElement[x][y] = EL_EMPTY;
5355
5356   border_element = Store2[x][y];
5357   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5358     border_element = StorePlayer[x][y];
5359
5360   if (phase == element_info[border_element].ignition_delay ||
5361       phase == last_phase)
5362   {
5363     boolean border_explosion = FALSE;
5364
5365     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5366         !PLAYER_EXPLOSION_PROTECTED(x, y))
5367     {
5368       KillPlayerUnlessExplosionProtected(x, y);
5369       border_explosion = TRUE;
5370     }
5371     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5372     {
5373       Feld[x][y] = Store2[x][y];
5374       Store2[x][y] = 0;
5375       Bang(x, y);
5376       border_explosion = TRUE;
5377     }
5378     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5379     {
5380       AmoebeUmwandeln(x, y);
5381       Store2[x][y] = 0;
5382       border_explosion = TRUE;
5383     }
5384
5385     /* if an element just explodes due to another explosion (chain-reaction),
5386        do not immediately end the new explosion when it was the last frame of
5387        the explosion (as it would be done in the following "if"-statement!) */
5388     if (border_explosion && phase == last_phase)
5389       return;
5390   }
5391
5392   if (phase == last_phase)
5393   {
5394     int element;
5395
5396     element = Feld[x][y] = Store[x][y];
5397     Store[x][y] = Store2[x][y] = 0;
5398     GfxElement[x][y] = EL_UNDEFINED;
5399
5400     /* player can escape from explosions and might therefore be still alive */
5401     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5402         element <= EL_PLAYER_IS_EXPLODING_4)
5403     {
5404       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5405       int explosion_element = EL_PLAYER_1 + player_nr;
5406       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5407       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5408
5409       if (level.use_explosion_element[player_nr])
5410         explosion_element = level.explosion_element[player_nr];
5411
5412       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5413                     element_info[explosion_element].content.e[xx][yy]);
5414     }
5415
5416     /* restore probably existing indestructible background element */
5417     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5418       element = Feld[x][y] = Back[x][y];
5419     Back[x][y] = 0;
5420
5421     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5422     GfxDir[x][y] = MV_NONE;
5423     ChangeDelay[x][y] = 0;
5424     ChangePage[x][y] = -1;
5425
5426     CustomValue[x][y] = 0;
5427
5428     InitField_WithBug2(x, y, FALSE);
5429
5430     TEST_DrawLevelField(x, y);
5431
5432     TestIfElementTouchesCustomElement(x, y);
5433
5434     if (GFX_CRUMBLED(element))
5435       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5436
5437     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5438       StorePlayer[x][y] = 0;
5439
5440     if (ELEM_IS_PLAYER(element))
5441       RelocatePlayer(x, y, element);
5442   }
5443   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5444   {
5445     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5446     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5447
5448     if (phase == delay)
5449       TEST_DrawLevelFieldCrumbled(x, y);
5450
5451     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5452     {
5453       DrawLevelElement(x, y, Back[x][y]);
5454       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5455     }
5456     else if (IS_WALKABLE_UNDER(Back[x][y]))
5457     {
5458       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5459       DrawLevelElementThruMask(x, y, Back[x][y]);
5460     }
5461     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5462       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5463   }
5464 }
5465
5466 void DynaExplode(int ex, int ey)
5467 {
5468   int i, j;
5469   int dynabomb_element = Feld[ex][ey];
5470   int dynabomb_size = 1;
5471   boolean dynabomb_xl = FALSE;
5472   struct PlayerInfo *player;
5473   static int xy[4][2] =
5474   {
5475     { 0, -1 },
5476     { -1, 0 },
5477     { +1, 0 },
5478     { 0, +1 }
5479   };
5480
5481   if (IS_ACTIVE_BOMB(dynabomb_element))
5482   {
5483     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5484     dynabomb_size = player->dynabomb_size;
5485     dynabomb_xl = player->dynabomb_xl;
5486     player->dynabombs_left++;
5487   }
5488
5489   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5490
5491   for (i = 0; i < NUM_DIRECTIONS; i++)
5492   {
5493     for (j = 1; j <= dynabomb_size; j++)
5494     {
5495       int x = ex + j * xy[i][0];
5496       int y = ey + j * xy[i][1];
5497       int element;
5498
5499       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5500         break;
5501
5502       element = Feld[x][y];
5503
5504       /* do not restart explosions of fields with active bombs */
5505       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5506         continue;
5507
5508       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5509
5510       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5511           !IS_DIGGABLE(element) && !dynabomb_xl)
5512         break;
5513     }
5514   }
5515 }
5516
5517 void Bang(int x, int y)
5518 {
5519   int element = MovingOrBlocked2Element(x, y);
5520   int explosion_type = EX_TYPE_NORMAL;
5521
5522   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5523   {
5524     struct PlayerInfo *player = PLAYERINFO(x, y);
5525
5526     element = Feld[x][y] = player->initial_element;
5527
5528     if (level.use_explosion_element[player->index_nr])
5529     {
5530       int explosion_element = level.explosion_element[player->index_nr];
5531
5532       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5533         explosion_type = EX_TYPE_CROSS;
5534       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5535         explosion_type = EX_TYPE_CENTER;
5536     }
5537   }
5538
5539   switch (element)
5540   {
5541     case EL_BUG:
5542     case EL_SPACESHIP:
5543     case EL_BD_BUTTERFLY:
5544     case EL_BD_FIREFLY:
5545     case EL_YAMYAM:
5546     case EL_DARK_YAMYAM:
5547     case EL_ROBOT:
5548     case EL_PACMAN:
5549     case EL_MOLE:
5550       RaiseScoreElement(element);
5551       break;
5552
5553     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5554     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5555     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5556     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5557     case EL_DYNABOMB_INCREASE_NUMBER:
5558     case EL_DYNABOMB_INCREASE_SIZE:
5559     case EL_DYNABOMB_INCREASE_POWER:
5560       explosion_type = EX_TYPE_DYNA;
5561       break;
5562
5563     case EL_DC_LANDMINE:
5564       explosion_type = EX_TYPE_CENTER;
5565       break;
5566
5567     case EL_PENGUIN:
5568     case EL_LAMP:
5569     case EL_LAMP_ACTIVE:
5570     case EL_AMOEBA_TO_DIAMOND:
5571       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5572         explosion_type = EX_TYPE_CENTER;
5573       break;
5574
5575     default:
5576       if (element_info[element].explosion_type == EXPLODES_CROSS)
5577         explosion_type = EX_TYPE_CROSS;
5578       else if (element_info[element].explosion_type == EXPLODES_1X1)
5579         explosion_type = EX_TYPE_CENTER;
5580       break;
5581   }
5582
5583   if (explosion_type == EX_TYPE_DYNA)
5584     DynaExplode(x, y);
5585   else
5586     Explode(x, y, EX_PHASE_START, explosion_type);
5587
5588   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5589 }
5590
5591 void SplashAcid(int x, int y)
5592 {
5593   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5594       (!IN_LEV_FIELD(x - 1, y - 2) ||
5595        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5596     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5597
5598   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5599       (!IN_LEV_FIELD(x + 1, y - 2) ||
5600        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5601     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5602
5603   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5604 }
5605
5606 static void InitBeltMovement()
5607 {
5608   static int belt_base_element[4] =
5609   {
5610     EL_CONVEYOR_BELT_1_LEFT,
5611     EL_CONVEYOR_BELT_2_LEFT,
5612     EL_CONVEYOR_BELT_3_LEFT,
5613     EL_CONVEYOR_BELT_4_LEFT
5614   };
5615   static int belt_base_active_element[4] =
5616   {
5617     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5618     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5619     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5620     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5621   };
5622
5623   int x, y, i, j;
5624
5625   /* set frame order for belt animation graphic according to belt direction */
5626   for (i = 0; i < NUM_BELTS; i++)
5627   {
5628     int belt_nr = i;
5629
5630     for (j = 0; j < NUM_BELT_PARTS; j++)
5631     {
5632       int element = belt_base_active_element[belt_nr] + j;
5633       int graphic_1 = el2img(element);
5634       int graphic_2 = el2panelimg(element);
5635
5636       if (game.belt_dir[i] == MV_LEFT)
5637       {
5638         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5639         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5640       }
5641       else
5642       {
5643         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5644         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5645       }
5646     }
5647   }
5648
5649   SCAN_PLAYFIELD(x, y)
5650   {
5651     int element = Feld[x][y];
5652
5653     for (i = 0; i < NUM_BELTS; i++)
5654     {
5655       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5656       {
5657         int e_belt_nr = getBeltNrFromBeltElement(element);
5658         int belt_nr = i;
5659
5660         if (e_belt_nr == belt_nr)
5661         {
5662           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5663
5664           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5665         }
5666       }
5667     }
5668   }
5669 }
5670
5671 static void ToggleBeltSwitch(int x, int y)
5672 {
5673   static int belt_base_element[4] =
5674   {
5675     EL_CONVEYOR_BELT_1_LEFT,
5676     EL_CONVEYOR_BELT_2_LEFT,
5677     EL_CONVEYOR_BELT_3_LEFT,
5678     EL_CONVEYOR_BELT_4_LEFT
5679   };
5680   static int belt_base_active_element[4] =
5681   {
5682     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5683     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5684     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5685     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5686   };
5687   static int belt_base_switch_element[4] =
5688   {
5689     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5690     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5691     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5692     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5693   };
5694   static int belt_move_dir[4] =
5695   {
5696     MV_LEFT,
5697     MV_NONE,
5698     MV_RIGHT,
5699     MV_NONE,
5700   };
5701
5702   int element = Feld[x][y];
5703   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5704   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5705   int belt_dir = belt_move_dir[belt_dir_nr];
5706   int xx, yy, i;
5707
5708   if (!IS_BELT_SWITCH(element))
5709     return;
5710
5711   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5712   game.belt_dir[belt_nr] = belt_dir;
5713
5714   if (belt_dir_nr == 3)
5715     belt_dir_nr = 1;
5716
5717   /* set frame order for belt animation graphic according to belt direction */
5718   for (i = 0; i < NUM_BELT_PARTS; i++)
5719   {
5720     int element = belt_base_active_element[belt_nr] + i;
5721     int graphic_1 = el2img(element);
5722     int graphic_2 = el2panelimg(element);
5723
5724     if (belt_dir == MV_LEFT)
5725     {
5726       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5727       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5728     }
5729     else
5730     {
5731       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5732       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5733     }
5734   }
5735
5736   SCAN_PLAYFIELD(xx, yy)
5737   {
5738     int element = Feld[xx][yy];
5739
5740     if (IS_BELT_SWITCH(element))
5741     {
5742       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5743
5744       if (e_belt_nr == belt_nr)
5745       {
5746         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5747         TEST_DrawLevelField(xx, yy);
5748       }
5749     }
5750     else if (IS_BELT(element) && belt_dir != MV_NONE)
5751     {
5752       int e_belt_nr = getBeltNrFromBeltElement(element);
5753
5754       if (e_belt_nr == belt_nr)
5755       {
5756         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5757
5758         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5759         TEST_DrawLevelField(xx, yy);
5760       }
5761     }
5762     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5763     {
5764       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5765
5766       if (e_belt_nr == belt_nr)
5767       {
5768         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5769
5770         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5771         TEST_DrawLevelField(xx, yy);
5772       }
5773     }
5774   }
5775 }
5776
5777 static void ToggleSwitchgateSwitch(int x, int y)
5778 {
5779   int xx, yy;
5780
5781   game.switchgate_pos = !game.switchgate_pos;
5782
5783   SCAN_PLAYFIELD(xx, yy)
5784   {
5785     int element = Feld[xx][yy];
5786
5787     if (element == EL_SWITCHGATE_SWITCH_UP)
5788     {
5789       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5790       TEST_DrawLevelField(xx, yy);
5791     }
5792     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5793     {
5794       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5795       TEST_DrawLevelField(xx, yy);
5796     }
5797     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5798     {
5799       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5800       TEST_DrawLevelField(xx, yy);
5801     }
5802     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5803     {
5804       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5805       TEST_DrawLevelField(xx, yy);
5806     }
5807     else if (element == EL_SWITCHGATE_OPEN ||
5808              element == EL_SWITCHGATE_OPENING)
5809     {
5810       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5811
5812       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5813     }
5814     else if (element == EL_SWITCHGATE_CLOSED ||
5815              element == EL_SWITCHGATE_CLOSING)
5816     {
5817       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5818
5819       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5820     }
5821   }
5822 }
5823
5824 static int getInvisibleActiveFromInvisibleElement(int element)
5825 {
5826   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5827           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5828           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5829           element);
5830 }
5831
5832 static int getInvisibleFromInvisibleActiveElement(int element)
5833 {
5834   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5835           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5836           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5837           element);
5838 }
5839
5840 static void RedrawAllLightSwitchesAndInvisibleElements()
5841 {
5842   int x, y;
5843
5844   SCAN_PLAYFIELD(x, y)
5845   {
5846     int element = Feld[x][y];
5847
5848     if (element == EL_LIGHT_SWITCH &&
5849         game.light_time_left > 0)
5850     {
5851       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5852       TEST_DrawLevelField(x, y);
5853     }
5854     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5855              game.light_time_left == 0)
5856     {
5857       Feld[x][y] = EL_LIGHT_SWITCH;
5858       TEST_DrawLevelField(x, y);
5859     }
5860     else if (element == EL_EMC_DRIPPER &&
5861              game.light_time_left > 0)
5862     {
5863       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5864       TEST_DrawLevelField(x, y);
5865     }
5866     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5867              game.light_time_left == 0)
5868     {
5869       Feld[x][y] = EL_EMC_DRIPPER;
5870       TEST_DrawLevelField(x, y);
5871     }
5872     else if (element == EL_INVISIBLE_STEELWALL ||
5873              element == EL_INVISIBLE_WALL ||
5874              element == EL_INVISIBLE_SAND)
5875     {
5876       if (game.light_time_left > 0)
5877         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5878
5879       TEST_DrawLevelField(x, y);
5880
5881       /* uncrumble neighbour fields, if needed */
5882       if (element == EL_INVISIBLE_SAND)
5883         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5884     }
5885     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5886              element == EL_INVISIBLE_WALL_ACTIVE ||
5887              element == EL_INVISIBLE_SAND_ACTIVE)
5888     {
5889       if (game.light_time_left == 0)
5890         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5891
5892       TEST_DrawLevelField(x, y);
5893
5894       /* re-crumble neighbour fields, if needed */
5895       if (element == EL_INVISIBLE_SAND)
5896         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5897     }
5898   }
5899 }
5900
5901 static void RedrawAllInvisibleElementsForLenses()
5902 {
5903   int x, y;
5904
5905   SCAN_PLAYFIELD(x, y)
5906   {
5907     int element = Feld[x][y];
5908
5909     if (element == EL_EMC_DRIPPER &&
5910         game.lenses_time_left > 0)
5911     {
5912       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5913       TEST_DrawLevelField(x, y);
5914     }
5915     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5916              game.lenses_time_left == 0)
5917     {
5918       Feld[x][y] = EL_EMC_DRIPPER;
5919       TEST_DrawLevelField(x, y);
5920     }
5921     else if (element == EL_INVISIBLE_STEELWALL ||
5922              element == EL_INVISIBLE_WALL ||
5923              element == EL_INVISIBLE_SAND)
5924     {
5925       if (game.lenses_time_left > 0)
5926         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5927
5928       TEST_DrawLevelField(x, y);
5929
5930       /* uncrumble neighbour fields, if needed */
5931       if (element == EL_INVISIBLE_SAND)
5932         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5933     }
5934     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5935              element == EL_INVISIBLE_WALL_ACTIVE ||
5936              element == EL_INVISIBLE_SAND_ACTIVE)
5937     {
5938       if (game.lenses_time_left == 0)
5939         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5940
5941       TEST_DrawLevelField(x, y);
5942
5943       /* re-crumble neighbour fields, if needed */
5944       if (element == EL_INVISIBLE_SAND)
5945         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5946     }
5947   }
5948 }
5949
5950 static void RedrawAllInvisibleElementsForMagnifier()
5951 {
5952   int x, y;
5953
5954   SCAN_PLAYFIELD(x, y)
5955   {
5956     int element = Feld[x][y];
5957
5958     if (element == EL_EMC_FAKE_GRASS &&
5959         game.magnify_time_left > 0)
5960     {
5961       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5962       TEST_DrawLevelField(x, y);
5963     }
5964     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5965              game.magnify_time_left == 0)
5966     {
5967       Feld[x][y] = EL_EMC_FAKE_GRASS;
5968       TEST_DrawLevelField(x, y);
5969     }
5970     else if (IS_GATE_GRAY(element) &&
5971              game.magnify_time_left > 0)
5972     {
5973       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5974                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5975                     IS_EM_GATE_GRAY(element) ?
5976                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5977                     IS_EMC_GATE_GRAY(element) ?
5978                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5979                     IS_DC_GATE_GRAY(element) ?
5980                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5981                     element);
5982       TEST_DrawLevelField(x, y);
5983     }
5984     else if (IS_GATE_GRAY_ACTIVE(element) &&
5985              game.magnify_time_left == 0)
5986     {
5987       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5988                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5989                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5990                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5991                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5992                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5993                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5994                     EL_DC_GATE_WHITE_GRAY :
5995                     element);
5996       TEST_DrawLevelField(x, y);
5997     }
5998   }
5999 }
6000
6001 static void ToggleLightSwitch(int x, int y)
6002 {
6003   int element = Feld[x][y];
6004
6005   game.light_time_left =
6006     (element == EL_LIGHT_SWITCH ?
6007      level.time_light * FRAMES_PER_SECOND : 0);
6008
6009   RedrawAllLightSwitchesAndInvisibleElements();
6010 }
6011
6012 static void ActivateTimegateSwitch(int x, int y)
6013 {
6014   int xx, yy;
6015
6016   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6017
6018   SCAN_PLAYFIELD(xx, yy)
6019   {
6020     int element = Feld[xx][yy];
6021
6022     if (element == EL_TIMEGATE_CLOSED ||
6023         element == EL_TIMEGATE_CLOSING)
6024     {
6025       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6026       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6027     }
6028
6029     /*
6030     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6031     {
6032       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6033       TEST_DrawLevelField(xx, yy);
6034     }
6035     */
6036
6037   }
6038
6039   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6040                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6041 }
6042
6043 void Impact(int x, int y)
6044 {
6045   boolean last_line = (y == lev_fieldy - 1);
6046   boolean object_hit = FALSE;
6047   boolean impact = (last_line || object_hit);
6048   int element = Feld[x][y];
6049   int smashed = EL_STEELWALL;
6050
6051   if (!last_line)       /* check if element below was hit */
6052   {
6053     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6054       return;
6055
6056     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6057                                          MovDir[x][y + 1] != MV_DOWN ||
6058                                          MovPos[x][y + 1] <= TILEY / 2));
6059
6060     /* do not smash moving elements that left the smashed field in time */
6061     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6062         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6063       object_hit = FALSE;
6064
6065 #if USE_QUICKSAND_IMPACT_BUGFIX
6066     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6067     {
6068       RemoveMovingField(x, y + 1);
6069       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6070       Feld[x][y + 2] = EL_ROCK;
6071       TEST_DrawLevelField(x, y + 2);
6072
6073       object_hit = TRUE;
6074     }
6075
6076     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6077     {
6078       RemoveMovingField(x, y + 1);
6079       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6080       Feld[x][y + 2] = EL_ROCK;
6081       TEST_DrawLevelField(x, y + 2);
6082
6083       object_hit = TRUE;
6084     }
6085 #endif
6086
6087     if (object_hit)
6088       smashed = MovingOrBlocked2Element(x, y + 1);
6089
6090     impact = (last_line || object_hit);
6091   }
6092
6093   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6094   {
6095     SplashAcid(x, y + 1);
6096     return;
6097   }
6098
6099   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6100   /* only reset graphic animation if graphic really changes after impact */
6101   if (impact &&
6102       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6103   {
6104     ResetGfxAnimation(x, y);
6105     TEST_DrawLevelField(x, y);
6106   }
6107
6108   if (impact && CAN_EXPLODE_IMPACT(element))
6109   {
6110     Bang(x, y);
6111     return;
6112   }
6113   else if (impact && element == EL_PEARL &&
6114            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6115   {
6116     ResetGfxAnimation(x, y);
6117
6118     Feld[x][y] = EL_PEARL_BREAKING;
6119     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6120     return;
6121   }
6122   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6123   {
6124     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6125
6126     return;
6127   }
6128
6129   if (impact && element == EL_AMOEBA_DROP)
6130   {
6131     if (object_hit && IS_PLAYER(x, y + 1))
6132       KillPlayerUnlessEnemyProtected(x, y + 1);
6133     else if (object_hit && smashed == EL_PENGUIN)
6134       Bang(x, y + 1);
6135     else
6136     {
6137       Feld[x][y] = EL_AMOEBA_GROWING;
6138       Store[x][y] = EL_AMOEBA_WET;
6139
6140       ResetRandomAnimationValue(x, y);
6141     }
6142     return;
6143   }
6144
6145   if (object_hit)               /* check which object was hit */
6146   {
6147     if ((CAN_PASS_MAGIC_WALL(element) && 
6148          (smashed == EL_MAGIC_WALL ||
6149           smashed == EL_BD_MAGIC_WALL)) ||
6150         (CAN_PASS_DC_MAGIC_WALL(element) &&
6151          smashed == EL_DC_MAGIC_WALL))
6152     {
6153       int xx, yy;
6154       int activated_magic_wall =
6155         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6156          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6157          EL_DC_MAGIC_WALL_ACTIVE);
6158
6159       /* activate magic wall / mill */
6160       SCAN_PLAYFIELD(xx, yy)
6161       {
6162         if (Feld[xx][yy] == smashed)
6163           Feld[xx][yy] = activated_magic_wall;
6164       }
6165
6166       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6167       game.magic_wall_active = TRUE;
6168
6169       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6170                             SND_MAGIC_WALL_ACTIVATING :
6171                             smashed == EL_BD_MAGIC_WALL ?
6172                             SND_BD_MAGIC_WALL_ACTIVATING :
6173                             SND_DC_MAGIC_WALL_ACTIVATING));
6174     }
6175
6176     if (IS_PLAYER(x, y + 1))
6177     {
6178       if (CAN_SMASH_PLAYER(element))
6179       {
6180         KillPlayerUnlessEnemyProtected(x, y + 1);
6181         return;
6182       }
6183     }
6184     else if (smashed == EL_PENGUIN)
6185     {
6186       if (CAN_SMASH_PLAYER(element))
6187       {
6188         Bang(x, y + 1);
6189         return;
6190       }
6191     }
6192     else if (element == EL_BD_DIAMOND)
6193     {
6194       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6195       {
6196         Bang(x, y + 1);
6197         return;
6198       }
6199     }
6200     else if (((element == EL_SP_INFOTRON ||
6201                element == EL_SP_ZONK) &&
6202               (smashed == EL_SP_SNIKSNAK ||
6203                smashed == EL_SP_ELECTRON ||
6204                smashed == EL_SP_DISK_ORANGE)) ||
6205              (element == EL_SP_INFOTRON &&
6206               smashed == EL_SP_DISK_YELLOW))
6207     {
6208       Bang(x, y + 1);
6209       return;
6210     }
6211     else if (CAN_SMASH_EVERYTHING(element))
6212     {
6213       if (IS_CLASSIC_ENEMY(smashed) ||
6214           CAN_EXPLODE_SMASHED(smashed))
6215       {
6216         Bang(x, y + 1);
6217         return;
6218       }
6219       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6220       {
6221         if (smashed == EL_LAMP ||
6222             smashed == EL_LAMP_ACTIVE)
6223         {
6224           Bang(x, y + 1);
6225           return;
6226         }
6227         else if (smashed == EL_NUT)
6228         {
6229           Feld[x][y + 1] = EL_NUT_BREAKING;
6230           PlayLevelSound(x, y, SND_NUT_BREAKING);
6231           RaiseScoreElement(EL_NUT);
6232           return;
6233         }
6234         else if (smashed == EL_PEARL)
6235         {
6236           ResetGfxAnimation(x, y);
6237
6238           Feld[x][y + 1] = EL_PEARL_BREAKING;
6239           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6240           return;
6241         }
6242         else if (smashed == EL_DIAMOND)
6243         {
6244           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6245           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6246           return;
6247         }
6248         else if (IS_BELT_SWITCH(smashed))
6249         {
6250           ToggleBeltSwitch(x, y + 1);
6251         }
6252         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6253                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6254                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6255                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6256         {
6257           ToggleSwitchgateSwitch(x, y + 1);
6258         }
6259         else if (smashed == EL_LIGHT_SWITCH ||
6260                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6261         {
6262           ToggleLightSwitch(x, y + 1);
6263         }
6264         else
6265         {
6266           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6267
6268           CheckElementChangeBySide(x, y + 1, smashed, element,
6269                                    CE_SWITCHED, CH_SIDE_TOP);
6270           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6271                                             CH_SIDE_TOP);
6272         }
6273       }
6274       else
6275       {
6276         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6277       }
6278     }
6279   }
6280
6281   /* play sound of magic wall / mill */
6282   if (!last_line &&
6283       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6284        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6285        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6286   {
6287     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6288       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6289     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6290       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6291     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6292       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6293
6294     return;
6295   }
6296
6297   /* play sound of object that hits the ground */
6298   if (last_line || object_hit)
6299     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6300 }
6301
6302 inline static void TurnRoundExt(int x, int y)
6303 {
6304   static struct
6305   {
6306     int dx, dy;
6307   } move_xy[] =
6308   {
6309     {  0,  0 },
6310     { -1,  0 },
6311     { +1,  0 },
6312     {  0,  0 },
6313     {  0, -1 },
6314     {  0,  0 }, { 0, 0 }, { 0, 0 },
6315     {  0, +1 }
6316   };
6317   static struct
6318   {
6319     int left, right, back;
6320   } turn[] =
6321   {
6322     { 0,        0,              0        },
6323     { MV_DOWN,  MV_UP,          MV_RIGHT },
6324     { MV_UP,    MV_DOWN,        MV_LEFT  },
6325     { 0,        0,              0        },
6326     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6327     { 0,        0,              0        },
6328     { 0,        0,              0        },
6329     { 0,        0,              0        },
6330     { MV_RIGHT, MV_LEFT,        MV_UP    }
6331   };
6332
6333   int element = Feld[x][y];
6334   int move_pattern = element_info[element].move_pattern;
6335
6336   int old_move_dir = MovDir[x][y];
6337   int left_dir  = turn[old_move_dir].left;
6338   int right_dir = turn[old_move_dir].right;
6339   int back_dir  = turn[old_move_dir].back;
6340
6341   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6342   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6343   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6344   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6345
6346   int left_x  = x + left_dx,  left_y  = y + left_dy;
6347   int right_x = x + right_dx, right_y = y + right_dy;
6348   int move_x  = x + move_dx,  move_y  = y + move_dy;
6349
6350   int xx, yy;
6351
6352   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6353   {
6354     TestIfBadThingTouchesOtherBadThing(x, y);
6355
6356     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6357       MovDir[x][y] = right_dir;
6358     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6359       MovDir[x][y] = left_dir;
6360
6361     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6362       MovDelay[x][y] = 9;
6363     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6364       MovDelay[x][y] = 1;
6365   }
6366   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6367   {
6368     TestIfBadThingTouchesOtherBadThing(x, y);
6369
6370     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6371       MovDir[x][y] = left_dir;
6372     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6373       MovDir[x][y] = right_dir;
6374
6375     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6376       MovDelay[x][y] = 9;
6377     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6378       MovDelay[x][y] = 1;
6379   }
6380   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6381   {
6382     TestIfBadThingTouchesOtherBadThing(x, y);
6383
6384     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6385       MovDir[x][y] = left_dir;
6386     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6387       MovDir[x][y] = right_dir;
6388
6389     if (MovDir[x][y] != old_move_dir)
6390       MovDelay[x][y] = 9;
6391   }
6392   else if (element == EL_YAMYAM)
6393   {
6394     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6395     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6396
6397     if (can_turn_left && can_turn_right)
6398       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6399     else if (can_turn_left)
6400       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6401     else if (can_turn_right)
6402       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6403     else
6404       MovDir[x][y] = back_dir;
6405
6406     MovDelay[x][y] = 16 + 16 * RND(3);
6407   }
6408   else if (element == EL_DARK_YAMYAM)
6409   {
6410     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6411                                                          left_x, left_y);
6412     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6413                                                          right_x, right_y);
6414
6415     if (can_turn_left && can_turn_right)
6416       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6417     else if (can_turn_left)
6418       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6419     else if (can_turn_right)
6420       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6421     else
6422       MovDir[x][y] = back_dir;
6423
6424     MovDelay[x][y] = 16 + 16 * RND(3);
6425   }
6426   else if (element == EL_PACMAN)
6427   {
6428     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6429     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6430
6431     if (can_turn_left && can_turn_right)
6432       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6433     else if (can_turn_left)
6434       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6435     else if (can_turn_right)
6436       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6437     else
6438       MovDir[x][y] = back_dir;
6439
6440     MovDelay[x][y] = 6 + RND(40);
6441   }
6442   else if (element == EL_PIG)
6443   {
6444     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6445     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6446     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6447     boolean should_turn_left, should_turn_right, should_move_on;
6448     int rnd_value = 24;
6449     int rnd = RND(rnd_value);
6450
6451     should_turn_left = (can_turn_left &&
6452                         (!can_move_on ||
6453                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6454                                                    y + back_dy + left_dy)));
6455     should_turn_right = (can_turn_right &&
6456                          (!can_move_on ||
6457                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6458                                                     y + back_dy + right_dy)));
6459     should_move_on = (can_move_on &&
6460                       (!can_turn_left ||
6461                        !can_turn_right ||
6462                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6463                                                  y + move_dy + left_dy) ||
6464                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6465                                                  y + move_dy + right_dy)));
6466
6467     if (should_turn_left || should_turn_right || should_move_on)
6468     {
6469       if (should_turn_left && should_turn_right && should_move_on)
6470         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6471                         rnd < 2 * rnd_value / 3 ? right_dir :
6472                         old_move_dir);
6473       else if (should_turn_left && should_turn_right)
6474         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6475       else if (should_turn_left && should_move_on)
6476         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6477       else if (should_turn_right && should_move_on)
6478         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6479       else if (should_turn_left)
6480         MovDir[x][y] = left_dir;
6481       else if (should_turn_right)
6482         MovDir[x][y] = right_dir;
6483       else if (should_move_on)
6484         MovDir[x][y] = old_move_dir;
6485     }
6486     else if (can_move_on && rnd > rnd_value / 8)
6487       MovDir[x][y] = old_move_dir;
6488     else if (can_turn_left && can_turn_right)
6489       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6490     else if (can_turn_left && rnd > rnd_value / 8)
6491       MovDir[x][y] = left_dir;
6492     else if (can_turn_right && rnd > rnd_value/8)
6493       MovDir[x][y] = right_dir;
6494     else
6495       MovDir[x][y] = back_dir;
6496
6497     xx = x + move_xy[MovDir[x][y]].dx;
6498     yy = y + move_xy[MovDir[x][y]].dy;
6499
6500     if (!IN_LEV_FIELD(xx, yy) ||
6501         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6502       MovDir[x][y] = old_move_dir;
6503
6504     MovDelay[x][y] = 0;
6505   }
6506   else if (element == EL_DRAGON)
6507   {
6508     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6509     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6510     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6511     int rnd_value = 24;
6512     int rnd = RND(rnd_value);
6513
6514     if (can_move_on && rnd > rnd_value / 8)
6515       MovDir[x][y] = old_move_dir;
6516     else if (can_turn_left && can_turn_right)
6517       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6518     else if (can_turn_left && rnd > rnd_value / 8)
6519       MovDir[x][y] = left_dir;
6520     else if (can_turn_right && rnd > rnd_value / 8)
6521       MovDir[x][y] = right_dir;
6522     else
6523       MovDir[x][y] = back_dir;
6524
6525     xx = x + move_xy[MovDir[x][y]].dx;
6526     yy = y + move_xy[MovDir[x][y]].dy;
6527
6528     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6529       MovDir[x][y] = old_move_dir;
6530
6531     MovDelay[x][y] = 0;
6532   }
6533   else if (element == EL_MOLE)
6534   {
6535     boolean can_move_on =
6536       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6537                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6538                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6539     if (!can_move_on)
6540     {
6541       boolean can_turn_left =
6542         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6543                               IS_AMOEBOID(Feld[left_x][left_y])));
6544
6545       boolean can_turn_right =
6546         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6547                               IS_AMOEBOID(Feld[right_x][right_y])));
6548
6549       if (can_turn_left && can_turn_right)
6550         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6551       else if (can_turn_left)
6552         MovDir[x][y] = left_dir;
6553       else
6554         MovDir[x][y] = right_dir;
6555     }
6556
6557     if (MovDir[x][y] != old_move_dir)
6558       MovDelay[x][y] = 9;
6559   }
6560   else if (element == EL_BALLOON)
6561   {
6562     MovDir[x][y] = game.wind_direction;
6563     MovDelay[x][y] = 0;
6564   }
6565   else if (element == EL_SPRING)
6566   {
6567     if (MovDir[x][y] & MV_HORIZONTAL)
6568     {
6569       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6570           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6571       {
6572         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6573         ResetGfxAnimation(move_x, move_y);
6574         TEST_DrawLevelField(move_x, move_y);
6575
6576         MovDir[x][y] = back_dir;
6577       }
6578       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6579                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6580         MovDir[x][y] = MV_NONE;
6581     }
6582
6583     MovDelay[x][y] = 0;
6584   }
6585   else if (element == EL_ROBOT ||
6586            element == EL_SATELLITE ||
6587            element == EL_PENGUIN ||
6588            element == EL_EMC_ANDROID)
6589   {
6590     int attr_x = -1, attr_y = -1;
6591
6592     if (AllPlayersGone)
6593     {
6594       attr_x = ExitX;
6595       attr_y = ExitY;
6596     }
6597     else
6598     {
6599       int i;
6600
6601       for (i = 0; i < MAX_PLAYERS; i++)
6602       {
6603         struct PlayerInfo *player = &stored_player[i];
6604         int jx = player->jx, jy = player->jy;
6605
6606         if (!player->active)
6607           continue;
6608
6609         if (attr_x == -1 ||
6610             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6611         {
6612           attr_x = jx;
6613           attr_y = jy;
6614         }
6615       }
6616     }
6617
6618     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6619         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6620          game.engine_version < VERSION_IDENT(3,1,0,0)))
6621     {
6622       attr_x = ZX;
6623       attr_y = ZY;
6624     }
6625
6626     if (element == EL_PENGUIN)
6627     {
6628       int i;
6629       static int xy[4][2] =
6630       {
6631         { 0, -1 },
6632         { -1, 0 },
6633         { +1, 0 },
6634         { 0, +1 }
6635       };
6636
6637       for (i = 0; i < NUM_DIRECTIONS; i++)
6638       {
6639         int ex = x + xy[i][0];
6640         int ey = y + xy[i][1];
6641
6642         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6643                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6644                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6645                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6646         {
6647           attr_x = ex;
6648           attr_y = ey;
6649           break;
6650         }
6651       }
6652     }
6653
6654     MovDir[x][y] = MV_NONE;
6655     if (attr_x < x)
6656       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6657     else if (attr_x > x)
6658       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6659     if (attr_y < y)
6660       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6661     else if (attr_y > y)
6662       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6663
6664     if (element == EL_ROBOT)
6665     {
6666       int newx, newy;
6667
6668       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6669         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6670       Moving2Blocked(x, y, &newx, &newy);
6671
6672       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6673         MovDelay[x][y] = 8 + 8 * !RND(3);
6674       else
6675         MovDelay[x][y] = 16;
6676     }
6677     else if (element == EL_PENGUIN)
6678     {
6679       int newx, newy;
6680
6681       MovDelay[x][y] = 1;
6682
6683       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6684       {
6685         boolean first_horiz = RND(2);
6686         int new_move_dir = MovDir[x][y];
6687
6688         MovDir[x][y] =
6689           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6690         Moving2Blocked(x, y, &newx, &newy);
6691
6692         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6693           return;
6694
6695         MovDir[x][y] =
6696           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6697         Moving2Blocked(x, y, &newx, &newy);
6698
6699         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6700           return;
6701
6702         MovDir[x][y] = old_move_dir;
6703         return;
6704       }
6705     }
6706     else if (element == EL_SATELLITE)
6707     {
6708       int newx, newy;
6709
6710       MovDelay[x][y] = 1;
6711
6712       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6713       {
6714         boolean first_horiz = RND(2);
6715         int new_move_dir = MovDir[x][y];
6716
6717         MovDir[x][y] =
6718           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6719         Moving2Blocked(x, y, &newx, &newy);
6720
6721         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6722           return;
6723
6724         MovDir[x][y] =
6725           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6726         Moving2Blocked(x, y, &newx, &newy);
6727
6728         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6729           return;
6730
6731         MovDir[x][y] = old_move_dir;
6732         return;
6733       }
6734     }
6735     else if (element == EL_EMC_ANDROID)
6736     {
6737       static int check_pos[16] =
6738       {
6739         -1,             /*  0 => (invalid)          */
6740         7,              /*  1 => MV_LEFT            */
6741         3,              /*  2 => MV_RIGHT           */
6742         -1,             /*  3 => (invalid)          */
6743         1,              /*  4 =>            MV_UP   */
6744         0,              /*  5 => MV_LEFT  | MV_UP   */
6745         2,              /*  6 => MV_RIGHT | MV_UP   */
6746         -1,             /*  7 => (invalid)          */
6747         5,              /*  8 =>            MV_DOWN */
6748         6,              /*  9 => MV_LEFT  | MV_DOWN */
6749         4,              /* 10 => MV_RIGHT | MV_DOWN */
6750         -1,             /* 11 => (invalid)          */
6751         -1,             /* 12 => (invalid)          */
6752         -1,             /* 13 => (invalid)          */
6753         -1,             /* 14 => (invalid)          */
6754         -1,             /* 15 => (invalid)          */
6755       };
6756       static struct
6757       {
6758         int dx, dy;
6759         int dir;
6760       } check_xy[8] =
6761       {
6762         { -1, -1,       MV_LEFT  | MV_UP   },
6763         {  0, -1,                  MV_UP   },
6764         { +1, -1,       MV_RIGHT | MV_UP   },
6765         { +1,  0,       MV_RIGHT           },
6766         { +1, +1,       MV_RIGHT | MV_DOWN },
6767         {  0, +1,                  MV_DOWN },
6768         { -1, +1,       MV_LEFT  | MV_DOWN },
6769         { -1,  0,       MV_LEFT            },
6770       };
6771       int start_pos, check_order;
6772       boolean can_clone = FALSE;
6773       int i;
6774
6775       /* check if there is any free field around current position */
6776       for (i = 0; i < 8; i++)
6777       {
6778         int newx = x + check_xy[i].dx;
6779         int newy = y + check_xy[i].dy;
6780
6781         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6782         {
6783           can_clone = TRUE;
6784
6785           break;
6786         }
6787       }
6788
6789       if (can_clone)            /* randomly find an element to clone */
6790       {
6791         can_clone = FALSE;
6792
6793         start_pos = check_pos[RND(8)];
6794         check_order = (RND(2) ? -1 : +1);
6795
6796         for (i = 0; i < 8; i++)
6797         {
6798           int pos_raw = start_pos + i * check_order;
6799           int pos = (pos_raw + 8) % 8;
6800           int newx = x + check_xy[pos].dx;
6801           int newy = y + check_xy[pos].dy;
6802
6803           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6804           {
6805             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6806             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6807
6808             Store[x][y] = Feld[newx][newy];
6809
6810             can_clone = TRUE;
6811
6812             break;
6813           }
6814         }
6815       }
6816
6817       if (can_clone)            /* randomly find a direction to move */
6818       {
6819         can_clone = FALSE;
6820
6821         start_pos = check_pos[RND(8)];
6822         check_order = (RND(2) ? -1 : +1);
6823
6824         for (i = 0; i < 8; i++)
6825         {
6826           int pos_raw = start_pos + i * check_order;
6827           int pos = (pos_raw + 8) % 8;
6828           int newx = x + check_xy[pos].dx;
6829           int newy = y + check_xy[pos].dy;
6830           int new_move_dir = check_xy[pos].dir;
6831
6832           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6833           {
6834             MovDir[x][y] = new_move_dir;
6835             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6836
6837             can_clone = TRUE;
6838
6839             break;
6840           }
6841         }
6842       }
6843
6844       if (can_clone)            /* cloning and moving successful */
6845         return;
6846
6847       /* cannot clone -- try to move towards player */
6848
6849       start_pos = check_pos[MovDir[x][y] & 0x0f];
6850       check_order = (RND(2) ? -1 : +1);
6851
6852       for (i = 0; i < 3; i++)
6853       {
6854         /* first check start_pos, then previous/next or (next/previous) pos */
6855         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6856         int pos = (pos_raw + 8) % 8;
6857         int newx = x + check_xy[pos].dx;
6858         int newy = y + check_xy[pos].dy;
6859         int new_move_dir = check_xy[pos].dir;
6860
6861         if (IS_PLAYER(newx, newy))
6862           break;
6863
6864         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6865         {
6866           MovDir[x][y] = new_move_dir;
6867           MovDelay[x][y] = level.android_move_time * 8 + 1;
6868
6869           break;
6870         }
6871       }
6872     }
6873   }
6874   else if (move_pattern == MV_TURNING_LEFT ||
6875            move_pattern == MV_TURNING_RIGHT ||
6876            move_pattern == MV_TURNING_LEFT_RIGHT ||
6877            move_pattern == MV_TURNING_RIGHT_LEFT ||
6878            move_pattern == MV_TURNING_RANDOM ||
6879            move_pattern == MV_ALL_DIRECTIONS)
6880   {
6881     boolean can_turn_left =
6882       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6883     boolean can_turn_right =
6884       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6885
6886     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6887       return;
6888
6889     if (move_pattern == MV_TURNING_LEFT)
6890       MovDir[x][y] = left_dir;
6891     else if (move_pattern == MV_TURNING_RIGHT)
6892       MovDir[x][y] = right_dir;
6893     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6894       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6895     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6896       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6897     else if (move_pattern == MV_TURNING_RANDOM)
6898       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6899                       can_turn_right && !can_turn_left ? right_dir :
6900                       RND(2) ? left_dir : right_dir);
6901     else if (can_turn_left && can_turn_right)
6902       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6903     else if (can_turn_left)
6904       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6905     else if (can_turn_right)
6906       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6907     else
6908       MovDir[x][y] = back_dir;
6909
6910     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6911   }
6912   else if (move_pattern == MV_HORIZONTAL ||
6913            move_pattern == MV_VERTICAL)
6914   {
6915     if (move_pattern & old_move_dir)
6916       MovDir[x][y] = back_dir;
6917     else if (move_pattern == MV_HORIZONTAL)
6918       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6919     else if (move_pattern == MV_VERTICAL)
6920       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6921
6922     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6923   }
6924   else if (move_pattern & MV_ANY_DIRECTION)
6925   {
6926     MovDir[x][y] = move_pattern;
6927     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6928   }
6929   else if (move_pattern & MV_WIND_DIRECTION)
6930   {
6931     MovDir[x][y] = game.wind_direction;
6932     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6933   }
6934   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6935   {
6936     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6937       MovDir[x][y] = left_dir;
6938     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6939       MovDir[x][y] = right_dir;
6940
6941     if (MovDir[x][y] != old_move_dir)
6942       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6943   }
6944   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6945   {
6946     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6947       MovDir[x][y] = right_dir;
6948     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6949       MovDir[x][y] = left_dir;
6950
6951     if (MovDir[x][y] != old_move_dir)
6952       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6953   }
6954   else if (move_pattern == MV_TOWARDS_PLAYER ||
6955            move_pattern == MV_AWAY_FROM_PLAYER)
6956   {
6957     int attr_x = -1, attr_y = -1;
6958     int newx, newy;
6959     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6960
6961     if (AllPlayersGone)
6962     {
6963       attr_x = ExitX;
6964       attr_y = ExitY;
6965     }
6966     else
6967     {
6968       int i;
6969
6970       for (i = 0; i < MAX_PLAYERS; i++)
6971       {
6972         struct PlayerInfo *player = &stored_player[i];
6973         int jx = player->jx, jy = player->jy;
6974
6975         if (!player->active)
6976           continue;
6977
6978         if (attr_x == -1 ||
6979             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6980         {
6981           attr_x = jx;
6982           attr_y = jy;
6983         }
6984       }
6985     }
6986
6987     MovDir[x][y] = MV_NONE;
6988     if (attr_x < x)
6989       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6990     else if (attr_x > x)
6991       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6992     if (attr_y < y)
6993       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6994     else if (attr_y > y)
6995       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6996
6997     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6998
6999     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7000     {
7001       boolean first_horiz = RND(2);
7002       int new_move_dir = MovDir[x][y];
7003
7004       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7005       {
7006         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7007         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7008
7009         return;
7010       }
7011
7012       MovDir[x][y] =
7013         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7014       Moving2Blocked(x, y, &newx, &newy);
7015
7016       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7017         return;
7018
7019       MovDir[x][y] =
7020         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7021       Moving2Blocked(x, y, &newx, &newy);
7022
7023       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7024         return;
7025
7026       MovDir[x][y] = old_move_dir;
7027     }
7028   }
7029   else if (move_pattern == MV_WHEN_PUSHED ||
7030            move_pattern == MV_WHEN_DROPPED)
7031   {
7032     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7033       MovDir[x][y] = MV_NONE;
7034
7035     MovDelay[x][y] = 0;
7036   }
7037   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7038   {
7039     static int test_xy[7][2] =
7040     {
7041       { 0, -1 },
7042       { -1, 0 },
7043       { +1, 0 },
7044       { 0, +1 },
7045       { 0, -1 },
7046       { -1, 0 },
7047       { +1, 0 },
7048     };
7049     static int test_dir[7] =
7050     {
7051       MV_UP,
7052       MV_LEFT,
7053       MV_RIGHT,
7054       MV_DOWN,
7055       MV_UP,
7056       MV_LEFT,
7057       MV_RIGHT,
7058     };
7059     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7060     int move_preference = -1000000;     /* start with very low preference */
7061     int new_move_dir = MV_NONE;
7062     int start_test = RND(4);
7063     int i;
7064
7065     for (i = 0; i < NUM_DIRECTIONS; i++)
7066     {
7067       int move_dir = test_dir[start_test + i];
7068       int move_dir_preference;
7069
7070       xx = x + test_xy[start_test + i][0];
7071       yy = y + test_xy[start_test + i][1];
7072
7073       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7074           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7075       {
7076         new_move_dir = move_dir;
7077
7078         break;
7079       }
7080
7081       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7082         continue;
7083
7084       move_dir_preference = -1 * RunnerVisit[xx][yy];
7085       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7086         move_dir_preference = PlayerVisit[xx][yy];
7087
7088       if (move_dir_preference > move_preference)
7089       {
7090         /* prefer field that has not been visited for the longest time */
7091         move_preference = move_dir_preference;
7092         new_move_dir = move_dir;
7093       }
7094       else if (move_dir_preference == move_preference &&
7095                move_dir == old_move_dir)
7096       {
7097         /* prefer last direction when all directions are preferred equally */
7098         move_preference = move_dir_preference;
7099         new_move_dir = move_dir;
7100       }
7101     }
7102
7103     MovDir[x][y] = new_move_dir;
7104     if (old_move_dir != new_move_dir)
7105       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7106   }
7107 }
7108
7109 static void TurnRound(int x, int y)
7110 {
7111   int direction = MovDir[x][y];
7112
7113   TurnRoundExt(x, y);
7114
7115   GfxDir[x][y] = MovDir[x][y];
7116
7117   if (direction != MovDir[x][y])
7118     GfxFrame[x][y] = 0;
7119
7120   if (MovDelay[x][y])
7121     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7122
7123   ResetGfxFrame(x, y, FALSE);
7124 }
7125
7126 static boolean JustBeingPushed(int x, int y)
7127 {
7128   int i;
7129
7130   for (i = 0; i < MAX_PLAYERS; i++)
7131   {
7132     struct PlayerInfo *player = &stored_player[i];
7133
7134     if (player->active && player->is_pushing && player->MovPos)
7135     {
7136       int next_jx = player->jx + (player->jx - player->last_jx);
7137       int next_jy = player->jy + (player->jy - player->last_jy);
7138
7139       if (x == next_jx && y == next_jy)
7140         return TRUE;
7141     }
7142   }
7143
7144   return FALSE;
7145 }
7146
7147 void StartMoving(int x, int y)
7148 {
7149   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7150   int element = Feld[x][y];
7151
7152   if (Stop[x][y])
7153     return;
7154
7155   if (MovDelay[x][y] == 0)
7156     GfxAction[x][y] = ACTION_DEFAULT;
7157
7158   if (CAN_FALL(element) && y < lev_fieldy - 1)
7159   {
7160     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7161         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7162       if (JustBeingPushed(x, y))
7163         return;
7164
7165     if (element == EL_QUICKSAND_FULL)
7166     {
7167       if (IS_FREE(x, y + 1))
7168       {
7169         InitMovingField(x, y, MV_DOWN);
7170         started_moving = TRUE;
7171
7172         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7173 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7174         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7175           Store[x][y] = EL_ROCK;
7176 #else
7177         Store[x][y] = EL_ROCK;
7178 #endif
7179
7180         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7181       }
7182       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7183       {
7184         if (!MovDelay[x][y])
7185         {
7186           MovDelay[x][y] = TILEY + 1;
7187
7188           ResetGfxAnimation(x, y);
7189           ResetGfxAnimation(x, y + 1);
7190         }
7191
7192         if (MovDelay[x][y])
7193         {
7194           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7195           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7196
7197           MovDelay[x][y]--;
7198           if (MovDelay[x][y])
7199             return;
7200         }
7201
7202         Feld[x][y] = EL_QUICKSAND_EMPTY;
7203         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7204         Store[x][y + 1] = Store[x][y];
7205         Store[x][y] = 0;
7206
7207         PlayLevelSoundAction(x, y, ACTION_FILLING);
7208       }
7209       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7210       {
7211         if (!MovDelay[x][y])
7212         {
7213           MovDelay[x][y] = TILEY + 1;
7214
7215           ResetGfxAnimation(x, y);
7216           ResetGfxAnimation(x, y + 1);
7217         }
7218
7219         if (MovDelay[x][y])
7220         {
7221           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7222           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7223
7224           MovDelay[x][y]--;
7225           if (MovDelay[x][y])
7226             return;
7227         }
7228
7229         Feld[x][y] = EL_QUICKSAND_EMPTY;
7230         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7231         Store[x][y + 1] = Store[x][y];
7232         Store[x][y] = 0;
7233
7234         PlayLevelSoundAction(x, y, ACTION_FILLING);
7235       }
7236     }
7237     else if (element == EL_QUICKSAND_FAST_FULL)
7238     {
7239       if (IS_FREE(x, y + 1))
7240       {
7241         InitMovingField(x, y, MV_DOWN);
7242         started_moving = TRUE;
7243
7244         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7245 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7246         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7247           Store[x][y] = EL_ROCK;
7248 #else
7249         Store[x][y] = EL_ROCK;
7250 #endif
7251
7252         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7253       }
7254       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7255       {
7256         if (!MovDelay[x][y])
7257         {
7258           MovDelay[x][y] = TILEY + 1;
7259
7260           ResetGfxAnimation(x, y);
7261           ResetGfxAnimation(x, y + 1);
7262         }
7263
7264         if (MovDelay[x][y])
7265         {
7266           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7267           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7268
7269           MovDelay[x][y]--;
7270           if (MovDelay[x][y])
7271             return;
7272         }
7273
7274         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7275         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7276         Store[x][y + 1] = Store[x][y];
7277         Store[x][y] = 0;
7278
7279         PlayLevelSoundAction(x, y, ACTION_FILLING);
7280       }
7281       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7282       {
7283         if (!MovDelay[x][y])
7284         {
7285           MovDelay[x][y] = TILEY + 1;
7286
7287           ResetGfxAnimation(x, y);
7288           ResetGfxAnimation(x, y + 1);
7289         }
7290
7291         if (MovDelay[x][y])
7292         {
7293           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7294           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7295
7296           MovDelay[x][y]--;
7297           if (MovDelay[x][y])
7298             return;
7299         }
7300
7301         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7302         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7303         Store[x][y + 1] = Store[x][y];
7304         Store[x][y] = 0;
7305
7306         PlayLevelSoundAction(x, y, ACTION_FILLING);
7307       }
7308     }
7309     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7310              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7311     {
7312       InitMovingField(x, y, MV_DOWN);
7313       started_moving = TRUE;
7314
7315       Feld[x][y] = EL_QUICKSAND_FILLING;
7316       Store[x][y] = element;
7317
7318       PlayLevelSoundAction(x, y, ACTION_FILLING);
7319     }
7320     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7321              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7322     {
7323       InitMovingField(x, y, MV_DOWN);
7324       started_moving = TRUE;
7325
7326       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7327       Store[x][y] = element;
7328
7329       PlayLevelSoundAction(x, y, ACTION_FILLING);
7330     }
7331     else if (element == EL_MAGIC_WALL_FULL)
7332     {
7333       if (IS_FREE(x, y + 1))
7334       {
7335         InitMovingField(x, y, MV_DOWN);
7336         started_moving = TRUE;
7337
7338         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7339         Store[x][y] = EL_CHANGED(Store[x][y]);
7340       }
7341       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7342       {
7343         if (!MovDelay[x][y])
7344           MovDelay[x][y] = TILEY / 4 + 1;
7345
7346         if (MovDelay[x][y])
7347         {
7348           MovDelay[x][y]--;
7349           if (MovDelay[x][y])
7350             return;
7351         }
7352
7353         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7354         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7355         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7356         Store[x][y] = 0;
7357       }
7358     }
7359     else if (element == EL_BD_MAGIC_WALL_FULL)
7360     {
7361       if (IS_FREE(x, y + 1))
7362       {
7363         InitMovingField(x, y, MV_DOWN);
7364         started_moving = TRUE;
7365
7366         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7367         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7368       }
7369       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7370       {
7371         if (!MovDelay[x][y])
7372           MovDelay[x][y] = TILEY / 4 + 1;
7373
7374         if (MovDelay[x][y])
7375         {
7376           MovDelay[x][y]--;
7377           if (MovDelay[x][y])
7378             return;
7379         }
7380
7381         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7382         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7383         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7384         Store[x][y] = 0;
7385       }
7386     }
7387     else if (element == EL_DC_MAGIC_WALL_FULL)
7388     {
7389       if (IS_FREE(x, y + 1))
7390       {
7391         InitMovingField(x, y, MV_DOWN);
7392         started_moving = TRUE;
7393
7394         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7395         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7396       }
7397       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7398       {
7399         if (!MovDelay[x][y])
7400           MovDelay[x][y] = TILEY / 4 + 1;
7401
7402         if (MovDelay[x][y])
7403         {
7404           MovDelay[x][y]--;
7405           if (MovDelay[x][y])
7406             return;
7407         }
7408
7409         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7410         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7411         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7412         Store[x][y] = 0;
7413       }
7414     }
7415     else if ((CAN_PASS_MAGIC_WALL(element) &&
7416               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7417                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7418              (CAN_PASS_DC_MAGIC_WALL(element) &&
7419               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7420
7421     {
7422       InitMovingField(x, y, MV_DOWN);
7423       started_moving = TRUE;
7424
7425       Feld[x][y] =
7426         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7427          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7428          EL_DC_MAGIC_WALL_FILLING);
7429       Store[x][y] = element;
7430     }
7431     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7432     {
7433       SplashAcid(x, y + 1);
7434
7435       InitMovingField(x, y, MV_DOWN);
7436       started_moving = TRUE;
7437
7438       Store[x][y] = EL_ACID;
7439     }
7440     else if (
7441              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7442               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7443              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7444               CAN_FALL(element) && WasJustFalling[x][y] &&
7445               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7446
7447              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7448               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7449               (Feld[x][y + 1] == EL_BLOCKED)))
7450     {
7451       /* this is needed for a special case not covered by calling "Impact()"
7452          from "ContinueMoving()": if an element moves to a tile directly below
7453          another element which was just falling on that tile (which was empty
7454          in the previous frame), the falling element above would just stop
7455          instead of smashing the element below (in previous version, the above
7456          element was just checked for "moving" instead of "falling", resulting
7457          in incorrect smashes caused by horizontal movement of the above
7458          element; also, the case of the player being the element to smash was
7459          simply not covered here... :-/ ) */
7460
7461       CheckCollision[x][y] = 0;
7462       CheckImpact[x][y] = 0;
7463
7464       Impact(x, y);
7465     }
7466     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7467     {
7468       if (MovDir[x][y] == MV_NONE)
7469       {
7470         InitMovingField(x, y, MV_DOWN);
7471         started_moving = TRUE;
7472       }
7473     }
7474     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7475     {
7476       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7477         MovDir[x][y] = MV_DOWN;
7478
7479       InitMovingField(x, y, MV_DOWN);
7480       started_moving = TRUE;
7481     }
7482     else if (element == EL_AMOEBA_DROP)
7483     {
7484       Feld[x][y] = EL_AMOEBA_GROWING;
7485       Store[x][y] = EL_AMOEBA_WET;
7486     }
7487     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7488               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7489              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7490              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7491     {
7492       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7493                                 (IS_FREE(x - 1, y + 1) ||
7494                                  Feld[x - 1][y + 1] == EL_ACID));
7495       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7496                                 (IS_FREE(x + 1, y + 1) ||
7497                                  Feld[x + 1][y + 1] == EL_ACID));
7498       boolean can_fall_any  = (can_fall_left || can_fall_right);
7499       boolean can_fall_both = (can_fall_left && can_fall_right);
7500       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7501
7502       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7503       {
7504         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7505           can_fall_right = FALSE;
7506         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7507           can_fall_left = FALSE;
7508         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7509           can_fall_right = FALSE;
7510         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7511           can_fall_left = FALSE;
7512
7513         can_fall_any  = (can_fall_left || can_fall_right);
7514         can_fall_both = FALSE;
7515       }
7516
7517       if (can_fall_both)
7518       {
7519         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7520           can_fall_right = FALSE;       /* slip down on left side */
7521         else
7522           can_fall_left = !(can_fall_right = RND(2));
7523
7524         can_fall_both = FALSE;
7525       }
7526
7527       if (can_fall_any)
7528       {
7529         /* if not determined otherwise, prefer left side for slipping down */
7530         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7531         started_moving = TRUE;
7532       }
7533     }
7534     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7535     {
7536       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7537       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7538       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7539       int belt_dir = game.belt_dir[belt_nr];
7540
7541       if ((belt_dir == MV_LEFT  && left_is_free) ||
7542           (belt_dir == MV_RIGHT && right_is_free))
7543       {
7544         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7545
7546         InitMovingField(x, y, belt_dir);
7547         started_moving = TRUE;
7548
7549         Pushed[x][y] = TRUE;
7550         Pushed[nextx][y] = TRUE;
7551
7552         GfxAction[x][y] = ACTION_DEFAULT;
7553       }
7554       else
7555       {
7556         MovDir[x][y] = 0;       /* if element was moving, stop it */
7557       }
7558     }
7559   }
7560
7561   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7562   if (CAN_MOVE(element) && !started_moving)
7563   {
7564     int move_pattern = element_info[element].move_pattern;
7565     int newx, newy;
7566
7567     Moving2Blocked(x, y, &newx, &newy);
7568
7569     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7570       return;
7571
7572     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7573         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7574     {
7575       WasJustMoving[x][y] = 0;
7576       CheckCollision[x][y] = 0;
7577
7578       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7579
7580       if (Feld[x][y] != element)        /* element has changed */
7581         return;
7582     }
7583
7584     if (!MovDelay[x][y])        /* start new movement phase */
7585     {
7586       /* all objects that can change their move direction after each step
7587          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7588
7589       if (element != EL_YAMYAM &&
7590           element != EL_DARK_YAMYAM &&
7591           element != EL_PACMAN &&
7592           !(move_pattern & MV_ANY_DIRECTION) &&
7593           move_pattern != MV_TURNING_LEFT &&
7594           move_pattern != MV_TURNING_RIGHT &&
7595           move_pattern != MV_TURNING_LEFT_RIGHT &&
7596           move_pattern != MV_TURNING_RIGHT_LEFT &&
7597           move_pattern != MV_TURNING_RANDOM)
7598       {
7599         TurnRound(x, y);
7600
7601         if (MovDelay[x][y] && (element == EL_BUG ||
7602                                element == EL_SPACESHIP ||
7603                                element == EL_SP_SNIKSNAK ||
7604                                element == EL_SP_ELECTRON ||
7605                                element == EL_MOLE))
7606           TEST_DrawLevelField(x, y);
7607       }
7608     }
7609
7610     if (MovDelay[x][y])         /* wait some time before next movement */
7611     {
7612       MovDelay[x][y]--;
7613
7614       if (element == EL_ROBOT ||
7615           element == EL_YAMYAM ||
7616           element == EL_DARK_YAMYAM)
7617       {
7618         DrawLevelElementAnimationIfNeeded(x, y, element);
7619         PlayLevelSoundAction(x, y, ACTION_WAITING);
7620       }
7621       else if (element == EL_SP_ELECTRON)
7622         DrawLevelElementAnimationIfNeeded(x, y, element);
7623       else if (element == EL_DRAGON)
7624       {
7625         int i;
7626         int dir = MovDir[x][y];
7627         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7628         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7629         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7630                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7631                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7632                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7633         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7634
7635         GfxAction[x][y] = ACTION_ATTACKING;
7636
7637         if (IS_PLAYER(x, y))
7638           DrawPlayerField(x, y);
7639         else
7640           TEST_DrawLevelField(x, y);
7641
7642         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7643
7644         for (i = 1; i <= 3; i++)
7645         {
7646           int xx = x + i * dx;
7647           int yy = y + i * dy;
7648           int sx = SCREENX(xx);
7649           int sy = SCREENY(yy);
7650           int flame_graphic = graphic + (i - 1);
7651
7652           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7653             break;
7654
7655           if (MovDelay[x][y])
7656           {
7657             int flamed = MovingOrBlocked2Element(xx, yy);
7658
7659             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7660               Bang(xx, yy);
7661             else
7662               RemoveMovingField(xx, yy);
7663
7664             ChangeDelay[xx][yy] = 0;
7665
7666             Feld[xx][yy] = EL_FLAMES;
7667
7668             if (IN_SCR_FIELD(sx, sy))
7669             {
7670               TEST_DrawLevelFieldCrumbled(xx, yy);
7671               DrawGraphic(sx, sy, flame_graphic, frame);
7672             }
7673           }
7674           else
7675           {
7676             if (Feld[xx][yy] == EL_FLAMES)
7677               Feld[xx][yy] = EL_EMPTY;
7678             TEST_DrawLevelField(xx, yy);
7679           }
7680         }
7681       }
7682
7683       if (MovDelay[x][y])       /* element still has to wait some time */
7684       {
7685         PlayLevelSoundAction(x, y, ACTION_WAITING);
7686
7687         return;
7688       }
7689     }
7690
7691     /* now make next step */
7692
7693     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7694
7695     if (DONT_COLLIDE_WITH(element) &&
7696         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7697         !PLAYER_ENEMY_PROTECTED(newx, newy))
7698     {
7699       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7700
7701       return;
7702     }
7703
7704     else if (CAN_MOVE_INTO_ACID(element) &&
7705              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7706              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7707              (MovDir[x][y] == MV_DOWN ||
7708               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7709     {
7710       SplashAcid(newx, newy);
7711       Store[x][y] = EL_ACID;
7712     }
7713     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7714     {
7715       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7716           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7717           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7718           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7719       {
7720         RemoveField(x, y);
7721         TEST_DrawLevelField(x, y);
7722
7723         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7724         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7725           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7726
7727         local_player->friends_still_needed--;
7728         if (!local_player->friends_still_needed &&
7729             !local_player->GameOver && AllPlayersGone)
7730           PlayerWins(local_player);
7731
7732         return;
7733       }
7734       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7735       {
7736         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7737           TEST_DrawLevelField(newx, newy);
7738         else
7739           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7740       }
7741       else if (!IS_FREE(newx, newy))
7742       {
7743         GfxAction[x][y] = ACTION_WAITING;
7744
7745         if (IS_PLAYER(x, y))
7746           DrawPlayerField(x, y);
7747         else
7748           TEST_DrawLevelField(x, y);
7749
7750         return;
7751       }
7752     }
7753     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7754     {
7755       if (IS_FOOD_PIG(Feld[newx][newy]))
7756       {
7757         if (IS_MOVING(newx, newy))
7758           RemoveMovingField(newx, newy);
7759         else
7760         {
7761           Feld[newx][newy] = EL_EMPTY;
7762           TEST_DrawLevelField(newx, newy);
7763         }
7764
7765         PlayLevelSound(x, y, SND_PIG_DIGGING);
7766       }
7767       else if (!IS_FREE(newx, newy))
7768       {
7769         if (IS_PLAYER(x, y))
7770           DrawPlayerField(x, y);
7771         else
7772           TEST_DrawLevelField(x, y);
7773
7774         return;
7775       }
7776     }
7777     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7778     {
7779       if (Store[x][y] != EL_EMPTY)
7780       {
7781         boolean can_clone = FALSE;
7782         int xx, yy;
7783
7784         /* check if element to clone is still there */
7785         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7786         {
7787           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7788           {
7789             can_clone = TRUE;
7790
7791             break;
7792           }
7793         }
7794
7795         /* cannot clone or target field not free anymore -- do not clone */
7796         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7797           Store[x][y] = EL_EMPTY;
7798       }
7799
7800       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7801       {
7802         if (IS_MV_DIAGONAL(MovDir[x][y]))
7803         {
7804           int diagonal_move_dir = MovDir[x][y];
7805           int stored = Store[x][y];
7806           int change_delay = 8;
7807           int graphic;
7808
7809           /* android is moving diagonally */
7810
7811           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7812
7813           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7814           GfxElement[x][y] = EL_EMC_ANDROID;
7815           GfxAction[x][y] = ACTION_SHRINKING;
7816           GfxDir[x][y] = diagonal_move_dir;
7817           ChangeDelay[x][y] = change_delay;
7818
7819           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7820                                    GfxDir[x][y]);
7821
7822           DrawLevelGraphicAnimation(x, y, graphic);
7823           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7824
7825           if (Feld[newx][newy] == EL_ACID)
7826           {
7827             SplashAcid(newx, newy);
7828
7829             return;
7830           }
7831
7832           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7833
7834           Store[newx][newy] = EL_EMC_ANDROID;
7835           GfxElement[newx][newy] = EL_EMC_ANDROID;
7836           GfxAction[newx][newy] = ACTION_GROWING;
7837           GfxDir[newx][newy] = diagonal_move_dir;
7838           ChangeDelay[newx][newy] = change_delay;
7839
7840           graphic = el_act_dir2img(GfxElement[newx][newy],
7841                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7842
7843           DrawLevelGraphicAnimation(newx, newy, graphic);
7844           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7845
7846           return;
7847         }
7848         else
7849         {
7850           Feld[newx][newy] = EL_EMPTY;
7851           TEST_DrawLevelField(newx, newy);
7852
7853           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7854         }
7855       }
7856       else if (!IS_FREE(newx, newy))
7857       {
7858         return;
7859       }
7860     }
7861     else if (IS_CUSTOM_ELEMENT(element) &&
7862              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7863     {
7864       if (!DigFieldByCE(newx, newy, element))
7865         return;
7866
7867       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7868       {
7869         RunnerVisit[x][y] = FrameCounter;
7870         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7871       }
7872     }
7873     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7874     {
7875       if (!IS_FREE(newx, newy))
7876       {
7877         if (IS_PLAYER(x, y))
7878           DrawPlayerField(x, y);
7879         else
7880           TEST_DrawLevelField(x, y);
7881
7882         return;
7883       }
7884       else
7885       {
7886         boolean wanna_flame = !RND(10);
7887         int dx = newx - x, dy = newy - y;
7888         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7889         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7890         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7891                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7892         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7893                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7894
7895         if ((wanna_flame ||
7896              IS_CLASSIC_ENEMY(element1) ||
7897              IS_CLASSIC_ENEMY(element2)) &&
7898             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7899             element1 != EL_FLAMES && element2 != EL_FLAMES)
7900         {
7901           ResetGfxAnimation(x, y);
7902           GfxAction[x][y] = ACTION_ATTACKING;
7903
7904           if (IS_PLAYER(x, y))
7905             DrawPlayerField(x, y);
7906           else
7907             TEST_DrawLevelField(x, y);
7908
7909           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7910
7911           MovDelay[x][y] = 50;
7912
7913           Feld[newx][newy] = EL_FLAMES;
7914           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7915             Feld[newx1][newy1] = EL_FLAMES;
7916           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7917             Feld[newx2][newy2] = EL_FLAMES;
7918
7919           return;
7920         }
7921       }
7922     }
7923     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7924              Feld[newx][newy] == EL_DIAMOND)
7925     {
7926       if (IS_MOVING(newx, newy))
7927         RemoveMovingField(newx, newy);
7928       else
7929       {
7930         Feld[newx][newy] = EL_EMPTY;
7931         TEST_DrawLevelField(newx, newy);
7932       }
7933
7934       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7935     }
7936     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7937              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7938     {
7939       if (AmoebaNr[newx][newy])
7940       {
7941         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7942         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7943             Feld[newx][newy] == EL_BD_AMOEBA)
7944           AmoebaCnt[AmoebaNr[newx][newy]]--;
7945       }
7946
7947       if (IS_MOVING(newx, newy))
7948       {
7949         RemoveMovingField(newx, newy);
7950       }
7951       else
7952       {
7953         Feld[newx][newy] = EL_EMPTY;
7954         TEST_DrawLevelField(newx, newy);
7955       }
7956
7957       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7958     }
7959     else if ((element == EL_PACMAN || element == EL_MOLE)
7960              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7961     {
7962       if (AmoebaNr[newx][newy])
7963       {
7964         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7965         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7966             Feld[newx][newy] == EL_BD_AMOEBA)
7967           AmoebaCnt[AmoebaNr[newx][newy]]--;
7968       }
7969
7970       if (element == EL_MOLE)
7971       {
7972         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7973         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7974
7975         ResetGfxAnimation(x, y);
7976         GfxAction[x][y] = ACTION_DIGGING;
7977         TEST_DrawLevelField(x, y);
7978
7979         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7980
7981         return;                         /* wait for shrinking amoeba */
7982       }
7983       else      /* element == EL_PACMAN */
7984       {
7985         Feld[newx][newy] = EL_EMPTY;
7986         TEST_DrawLevelField(newx, newy);
7987         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7988       }
7989     }
7990     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7991              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7992               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7993     {
7994       /* wait for shrinking amoeba to completely disappear */
7995       return;
7996     }
7997     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7998     {
7999       /* object was running against a wall */
8000
8001       TurnRound(x, y);
8002
8003       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8004         DrawLevelElementAnimation(x, y, element);
8005
8006       if (DONT_TOUCH(element))
8007         TestIfBadThingTouchesPlayer(x, y);
8008
8009       return;
8010     }
8011
8012     InitMovingField(x, y, MovDir[x][y]);
8013
8014     PlayLevelSoundAction(x, y, ACTION_MOVING);
8015   }
8016
8017   if (MovDir[x][y])
8018     ContinueMoving(x, y);
8019 }
8020
8021 void ContinueMoving(int x, int y)
8022 {
8023   int element = Feld[x][y];
8024   struct ElementInfo *ei = &element_info[element];
8025   int direction = MovDir[x][y];
8026   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8027   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8028   int newx = x + dx, newy = y + dy;
8029   int stored = Store[x][y];
8030   int stored_new = Store[newx][newy];
8031   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8032   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8033   boolean last_line = (newy == lev_fieldy - 1);
8034
8035   MovPos[x][y] += getElementMoveStepsize(x, y);
8036
8037   if (pushed_by_player) /* special case: moving object pushed by player */
8038     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8039
8040   if (ABS(MovPos[x][y]) < TILEX)
8041   {
8042     TEST_DrawLevelField(x, y);
8043
8044     return;     /* element is still moving */
8045   }
8046
8047   /* element reached destination field */
8048
8049   Feld[x][y] = EL_EMPTY;
8050   Feld[newx][newy] = element;
8051   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8052
8053   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8054   {
8055     element = Feld[newx][newy] = EL_ACID;
8056   }
8057   else if (element == EL_MOLE)
8058   {
8059     Feld[x][y] = EL_SAND;
8060
8061     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8062   }
8063   else if (element == EL_QUICKSAND_FILLING)
8064   {
8065     element = Feld[newx][newy] = get_next_element(element);
8066     Store[newx][newy] = Store[x][y];
8067   }
8068   else if (element == EL_QUICKSAND_EMPTYING)
8069   {
8070     Feld[x][y] = get_next_element(element);
8071     element = Feld[newx][newy] = Store[x][y];
8072   }
8073   else if (element == EL_QUICKSAND_FAST_FILLING)
8074   {
8075     element = Feld[newx][newy] = get_next_element(element);
8076     Store[newx][newy] = Store[x][y];
8077   }
8078   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8079   {
8080     Feld[x][y] = get_next_element(element);
8081     element = Feld[newx][newy] = Store[x][y];
8082   }
8083   else if (element == EL_MAGIC_WALL_FILLING)
8084   {
8085     element = Feld[newx][newy] = get_next_element(element);
8086     if (!game.magic_wall_active)
8087       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8088     Store[newx][newy] = Store[x][y];
8089   }
8090   else if (element == EL_MAGIC_WALL_EMPTYING)
8091   {
8092     Feld[x][y] = get_next_element(element);
8093     if (!game.magic_wall_active)
8094       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8095     element = Feld[newx][newy] = Store[x][y];
8096
8097     InitField(newx, newy, FALSE);
8098   }
8099   else if (element == EL_BD_MAGIC_WALL_FILLING)
8100   {
8101     element = Feld[newx][newy] = get_next_element(element);
8102     if (!game.magic_wall_active)
8103       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8104     Store[newx][newy] = Store[x][y];
8105   }
8106   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8107   {
8108     Feld[x][y] = get_next_element(element);
8109     if (!game.magic_wall_active)
8110       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8111     element = Feld[newx][newy] = Store[x][y];
8112
8113     InitField(newx, newy, FALSE);
8114   }
8115   else if (element == EL_DC_MAGIC_WALL_FILLING)
8116   {
8117     element = Feld[newx][newy] = get_next_element(element);
8118     if (!game.magic_wall_active)
8119       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8120     Store[newx][newy] = Store[x][y];
8121   }
8122   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8123   {
8124     Feld[x][y] = get_next_element(element);
8125     if (!game.magic_wall_active)
8126       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8127     element = Feld[newx][newy] = Store[x][y];
8128
8129     InitField(newx, newy, FALSE);
8130   }
8131   else if (element == EL_AMOEBA_DROPPING)
8132   {
8133     Feld[x][y] = get_next_element(element);
8134     element = Feld[newx][newy] = Store[x][y];
8135   }
8136   else if (element == EL_SOKOBAN_OBJECT)
8137   {
8138     if (Back[x][y])
8139       Feld[x][y] = Back[x][y];
8140
8141     if (Back[newx][newy])
8142       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8143
8144     Back[x][y] = Back[newx][newy] = 0;
8145   }
8146
8147   Store[x][y] = EL_EMPTY;
8148   MovPos[x][y] = 0;
8149   MovDir[x][y] = 0;
8150   MovDelay[x][y] = 0;
8151
8152   MovDelay[newx][newy] = 0;
8153
8154   if (CAN_CHANGE_OR_HAS_ACTION(element))
8155   {
8156     /* copy element change control values to new field */
8157     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8158     ChangePage[newx][newy]  = ChangePage[x][y];
8159     ChangeCount[newx][newy] = ChangeCount[x][y];
8160     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8161   }
8162
8163   CustomValue[newx][newy] = CustomValue[x][y];
8164
8165   ChangeDelay[x][y] = 0;
8166   ChangePage[x][y] = -1;
8167   ChangeCount[x][y] = 0;
8168   ChangeEvent[x][y] = -1;
8169
8170   CustomValue[x][y] = 0;
8171
8172   /* copy animation control values to new field */
8173   GfxFrame[newx][newy]  = GfxFrame[x][y];
8174   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8175   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8176   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8177
8178   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8179
8180   /* some elements can leave other elements behind after moving */
8181   if (ei->move_leave_element != EL_EMPTY &&
8182       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8183       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8184   {
8185     int move_leave_element = ei->move_leave_element;
8186
8187     /* this makes it possible to leave the removed element again */
8188     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8189       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8190
8191     Feld[x][y] = move_leave_element;
8192
8193     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8194       MovDir[x][y] = direction;
8195
8196     InitField(x, y, FALSE);
8197
8198     if (GFX_CRUMBLED(Feld[x][y]))
8199       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8200
8201     if (ELEM_IS_PLAYER(move_leave_element))
8202       RelocatePlayer(x, y, move_leave_element);
8203   }
8204
8205   /* do this after checking for left-behind element */
8206   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8207
8208   if (!CAN_MOVE(element) ||
8209       (CAN_FALL(element) && direction == MV_DOWN &&
8210        (element == EL_SPRING ||
8211         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8212         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8213     GfxDir[x][y] = MovDir[newx][newy] = 0;
8214
8215   TEST_DrawLevelField(x, y);
8216   TEST_DrawLevelField(newx, newy);
8217
8218   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8219
8220   /* prevent pushed element from moving on in pushed direction */
8221   if (pushed_by_player && CAN_MOVE(element) &&
8222       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8223       !(element_info[element].move_pattern & direction))
8224     TurnRound(newx, newy);
8225
8226   /* prevent elements on conveyor belt from moving on in last direction */
8227   if (pushed_by_conveyor && CAN_FALL(element) &&
8228       direction & MV_HORIZONTAL)
8229     MovDir[newx][newy] = 0;
8230
8231   if (!pushed_by_player)
8232   {
8233     int nextx = newx + dx, nexty = newy + dy;
8234     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8235
8236     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8237
8238     if (CAN_FALL(element) && direction == MV_DOWN)
8239       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8240
8241     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8242       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8243
8244     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8245       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8246   }
8247
8248   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8249   {
8250     TestIfBadThingTouchesPlayer(newx, newy);
8251     TestIfBadThingTouchesFriend(newx, newy);
8252
8253     if (!IS_CUSTOM_ELEMENT(element))
8254       TestIfBadThingTouchesOtherBadThing(newx, newy);
8255   }
8256   else if (element == EL_PENGUIN)
8257     TestIfFriendTouchesBadThing(newx, newy);
8258
8259   if (DONT_GET_HIT_BY(element))
8260   {
8261     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8262   }
8263
8264   /* give the player one last chance (one more frame) to move away */
8265   if (CAN_FALL(element) && direction == MV_DOWN &&
8266       (last_line || (!IS_FREE(x, newy + 1) &&
8267                      (!IS_PLAYER(x, newy + 1) ||
8268                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8269     Impact(x, newy);
8270
8271   if (pushed_by_player && !game.use_change_when_pushing_bug)
8272   {
8273     int push_side = MV_DIR_OPPOSITE(direction);
8274     struct PlayerInfo *player = PLAYERINFO(x, y);
8275
8276     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8277                                player->index_bit, push_side);
8278     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8279                                         player->index_bit, push_side);
8280   }
8281
8282   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8283     MovDelay[newx][newy] = 1;
8284
8285   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8286
8287   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8288   TestIfElementHitsCustomElement(newx, newy, direction);
8289   TestIfPlayerTouchesCustomElement(newx, newy);
8290   TestIfElementTouchesCustomElement(newx, newy);
8291
8292   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8293       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8294     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8295                              MV_DIR_OPPOSITE(direction));
8296 }
8297
8298 int AmoebeNachbarNr(int ax, int ay)
8299 {
8300   int i;
8301   int element = Feld[ax][ay];
8302   int group_nr = 0;
8303   static int xy[4][2] =
8304   {
8305     { 0, -1 },
8306     { -1, 0 },
8307     { +1, 0 },
8308     { 0, +1 }
8309   };
8310
8311   for (i = 0; i < NUM_DIRECTIONS; i++)
8312   {
8313     int x = ax + xy[i][0];
8314     int y = ay + xy[i][1];
8315
8316     if (!IN_LEV_FIELD(x, y))
8317       continue;
8318
8319     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8320       group_nr = AmoebaNr[x][y];
8321   }
8322
8323   return group_nr;
8324 }
8325
8326 void AmoebenVereinigen(int ax, int ay)
8327 {
8328   int i, x, y, xx, yy;
8329   int new_group_nr = AmoebaNr[ax][ay];
8330   static int xy[4][2] =
8331   {
8332     { 0, -1 },
8333     { -1, 0 },
8334     { +1, 0 },
8335     { 0, +1 }
8336   };
8337
8338   if (new_group_nr == 0)
8339     return;
8340
8341   for (i = 0; i < NUM_DIRECTIONS; i++)
8342   {
8343     x = ax + xy[i][0];
8344     y = ay + xy[i][1];
8345
8346     if (!IN_LEV_FIELD(x, y))
8347       continue;
8348
8349     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8350          Feld[x][y] == EL_BD_AMOEBA ||
8351          Feld[x][y] == EL_AMOEBA_DEAD) &&
8352         AmoebaNr[x][y] != new_group_nr)
8353     {
8354       int old_group_nr = AmoebaNr[x][y];
8355
8356       if (old_group_nr == 0)
8357         return;
8358
8359       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8360       AmoebaCnt[old_group_nr] = 0;
8361       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8362       AmoebaCnt2[old_group_nr] = 0;
8363
8364       SCAN_PLAYFIELD(xx, yy)
8365       {
8366         if (AmoebaNr[xx][yy] == old_group_nr)
8367           AmoebaNr[xx][yy] = new_group_nr;
8368       }
8369     }
8370   }
8371 }
8372
8373 void AmoebeUmwandeln(int ax, int ay)
8374 {
8375   int i, x, y;
8376
8377   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8378   {
8379     int group_nr = AmoebaNr[ax][ay];
8380
8381 #ifdef DEBUG
8382     if (group_nr == 0)
8383     {
8384       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8385       printf("AmoebeUmwandeln(): This should never happen!\n");
8386       return;
8387     }
8388 #endif
8389
8390     SCAN_PLAYFIELD(x, y)
8391     {
8392       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8393       {
8394         AmoebaNr[x][y] = 0;
8395         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8396       }
8397     }
8398
8399     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8400                             SND_AMOEBA_TURNING_TO_GEM :
8401                             SND_AMOEBA_TURNING_TO_ROCK));
8402     Bang(ax, ay);
8403   }
8404   else
8405   {
8406     static int xy[4][2] =
8407     {
8408       { 0, -1 },
8409       { -1, 0 },
8410       { +1, 0 },
8411       { 0, +1 }
8412     };
8413
8414     for (i = 0; i < NUM_DIRECTIONS; i++)
8415     {
8416       x = ax + xy[i][0];
8417       y = ay + xy[i][1];
8418
8419       if (!IN_LEV_FIELD(x, y))
8420         continue;
8421
8422       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8423       {
8424         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8425                               SND_AMOEBA_TURNING_TO_GEM :
8426                               SND_AMOEBA_TURNING_TO_ROCK));
8427         Bang(x, y);
8428       }
8429     }
8430   }
8431 }
8432
8433 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8434 {
8435   int x, y;
8436   int group_nr = AmoebaNr[ax][ay];
8437   boolean done = FALSE;
8438
8439 #ifdef DEBUG
8440   if (group_nr == 0)
8441   {
8442     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8443     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8444     return;
8445   }
8446 #endif
8447
8448   SCAN_PLAYFIELD(x, y)
8449   {
8450     if (AmoebaNr[x][y] == group_nr &&
8451         (Feld[x][y] == EL_AMOEBA_DEAD ||
8452          Feld[x][y] == EL_BD_AMOEBA ||
8453          Feld[x][y] == EL_AMOEBA_GROWING))
8454     {
8455       AmoebaNr[x][y] = 0;
8456       Feld[x][y] = new_element;
8457       InitField(x, y, FALSE);
8458       TEST_DrawLevelField(x, y);
8459       done = TRUE;
8460     }
8461   }
8462
8463   if (done)
8464     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8465                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8466                             SND_BD_AMOEBA_TURNING_TO_GEM));
8467 }
8468
8469 void AmoebeWaechst(int x, int y)
8470 {
8471   static unsigned int sound_delay = 0;
8472   static unsigned int sound_delay_value = 0;
8473
8474   if (!MovDelay[x][y])          /* start new growing cycle */
8475   {
8476     MovDelay[x][y] = 7;
8477
8478     if (DelayReached(&sound_delay, sound_delay_value))
8479     {
8480       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8481       sound_delay_value = 30;
8482     }
8483   }
8484
8485   if (MovDelay[x][y])           /* wait some time before growing bigger */
8486   {
8487     MovDelay[x][y]--;
8488     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8489     {
8490       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8491                                            6 - MovDelay[x][y]);
8492
8493       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8494     }
8495
8496     if (!MovDelay[x][y])
8497     {
8498       Feld[x][y] = Store[x][y];
8499       Store[x][y] = 0;
8500       TEST_DrawLevelField(x, y);
8501     }
8502   }
8503 }
8504
8505 void AmoebaDisappearing(int x, int y)
8506 {
8507   static unsigned int sound_delay = 0;
8508   static unsigned int sound_delay_value = 0;
8509
8510   if (!MovDelay[x][y])          /* start new shrinking cycle */
8511   {
8512     MovDelay[x][y] = 7;
8513
8514     if (DelayReached(&sound_delay, sound_delay_value))
8515       sound_delay_value = 30;
8516   }
8517
8518   if (MovDelay[x][y])           /* wait some time before shrinking */
8519   {
8520     MovDelay[x][y]--;
8521     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8522     {
8523       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8524                                            6 - MovDelay[x][y]);
8525
8526       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8527     }
8528
8529     if (!MovDelay[x][y])
8530     {
8531       Feld[x][y] = EL_EMPTY;
8532       TEST_DrawLevelField(x, y);
8533
8534       /* don't let mole enter this field in this cycle;
8535          (give priority to objects falling to this field from above) */
8536       Stop[x][y] = TRUE;
8537     }
8538   }
8539 }
8540
8541 void AmoebeAbleger(int ax, int ay)
8542 {
8543   int i;
8544   int element = Feld[ax][ay];
8545   int graphic = el2img(element);
8546   int newax = ax, neway = ay;
8547   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8548   static int xy[4][2] =
8549   {
8550     { 0, -1 },
8551     { -1, 0 },
8552     { +1, 0 },
8553     { 0, +1 }
8554   };
8555
8556   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8557   {
8558     Feld[ax][ay] = EL_AMOEBA_DEAD;
8559     TEST_DrawLevelField(ax, ay);
8560     return;
8561   }
8562
8563   if (IS_ANIMATED(graphic))
8564     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8565
8566   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8567     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8568
8569   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8570   {
8571     MovDelay[ax][ay]--;
8572     if (MovDelay[ax][ay])
8573       return;
8574   }
8575
8576   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8577   {
8578     int start = RND(4);
8579     int x = ax + xy[start][0];
8580     int y = ay + xy[start][1];
8581
8582     if (!IN_LEV_FIELD(x, y))
8583       return;
8584
8585     if (IS_FREE(x, y) ||
8586         CAN_GROW_INTO(Feld[x][y]) ||
8587         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8588         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8589     {
8590       newax = x;
8591       neway = y;
8592     }
8593
8594     if (newax == ax && neway == ay)
8595       return;
8596   }
8597   else                          /* normal or "filled" (BD style) amoeba */
8598   {
8599     int start = RND(4);
8600     boolean waiting_for_player = FALSE;
8601
8602     for (i = 0; i < NUM_DIRECTIONS; i++)
8603     {
8604       int j = (start + i) % 4;
8605       int x = ax + xy[j][0];
8606       int y = ay + xy[j][1];
8607
8608       if (!IN_LEV_FIELD(x, y))
8609         continue;
8610
8611       if (IS_FREE(x, y) ||
8612           CAN_GROW_INTO(Feld[x][y]) ||
8613           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8614           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8615       {
8616         newax = x;
8617         neway = y;
8618         break;
8619       }
8620       else if (IS_PLAYER(x, y))
8621         waiting_for_player = TRUE;
8622     }
8623
8624     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8625     {
8626       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8627       {
8628         Feld[ax][ay] = EL_AMOEBA_DEAD;
8629         TEST_DrawLevelField(ax, ay);
8630         AmoebaCnt[AmoebaNr[ax][ay]]--;
8631
8632         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8633         {
8634           if (element == EL_AMOEBA_FULL)
8635             AmoebeUmwandeln(ax, ay);
8636           else if (element == EL_BD_AMOEBA)
8637             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8638         }
8639       }
8640       return;
8641     }
8642     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8643     {
8644       /* amoeba gets larger by growing in some direction */
8645
8646       int new_group_nr = AmoebaNr[ax][ay];
8647
8648 #ifdef DEBUG
8649   if (new_group_nr == 0)
8650   {
8651     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8652     printf("AmoebeAbleger(): This should never happen!\n");
8653     return;
8654   }
8655 #endif
8656
8657       AmoebaNr[newax][neway] = new_group_nr;
8658       AmoebaCnt[new_group_nr]++;
8659       AmoebaCnt2[new_group_nr]++;
8660
8661       /* if amoeba touches other amoeba(s) after growing, unify them */
8662       AmoebenVereinigen(newax, neway);
8663
8664       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8665       {
8666         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8667         return;
8668       }
8669     }
8670   }
8671
8672   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8673       (neway == lev_fieldy - 1 && newax != ax))
8674   {
8675     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8676     Store[newax][neway] = element;
8677   }
8678   else if (neway == ay || element == EL_EMC_DRIPPER)
8679   {
8680     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8681
8682     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8683   }
8684   else
8685   {
8686     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8687     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8688     Store[ax][ay] = EL_AMOEBA_DROP;
8689     ContinueMoving(ax, ay);
8690     return;
8691   }
8692
8693   TEST_DrawLevelField(newax, neway);
8694 }
8695
8696 void Life(int ax, int ay)
8697 {
8698   int x1, y1, x2, y2;
8699   int life_time = 40;
8700   int element = Feld[ax][ay];
8701   int graphic = el2img(element);
8702   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8703                          level.biomaze);
8704   boolean changed = FALSE;
8705
8706   if (IS_ANIMATED(graphic))
8707     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8708
8709   if (Stop[ax][ay])
8710     return;
8711
8712   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8713     MovDelay[ax][ay] = life_time;
8714
8715   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8716   {
8717     MovDelay[ax][ay]--;
8718     if (MovDelay[ax][ay])
8719       return;
8720   }
8721
8722   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8723   {
8724     int xx = ax+x1, yy = ay+y1;
8725     int nachbarn = 0;
8726
8727     if (!IN_LEV_FIELD(xx, yy))
8728       continue;
8729
8730     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8731     {
8732       int x = xx+x2, y = yy+y2;
8733
8734       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8735         continue;
8736
8737       if (((Feld[x][y] == element ||
8738             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8739            !Stop[x][y]) ||
8740           (IS_FREE(x, y) && Stop[x][y]))
8741         nachbarn++;
8742     }
8743
8744     if (xx == ax && yy == ay)           /* field in the middle */
8745     {
8746       if (nachbarn < life_parameter[0] ||
8747           nachbarn > life_parameter[1])
8748       {
8749         Feld[xx][yy] = EL_EMPTY;
8750         if (!Stop[xx][yy])
8751           TEST_DrawLevelField(xx, yy);
8752         Stop[xx][yy] = TRUE;
8753         changed = TRUE;
8754       }
8755     }
8756     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8757     {                                   /* free border field */
8758       if (nachbarn >= life_parameter[2] &&
8759           nachbarn <= life_parameter[3])
8760       {
8761         Feld[xx][yy] = element;
8762         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8763         if (!Stop[xx][yy])
8764           TEST_DrawLevelField(xx, yy);
8765         Stop[xx][yy] = TRUE;
8766         changed = TRUE;
8767       }
8768     }
8769   }
8770
8771   if (changed)
8772     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8773                    SND_GAME_OF_LIFE_GROWING);
8774 }
8775
8776 static void InitRobotWheel(int x, int y)
8777 {
8778   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8779 }
8780
8781 static void RunRobotWheel(int x, int y)
8782 {
8783   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8784 }
8785
8786 static void StopRobotWheel(int x, int y)
8787 {
8788   if (ZX == x && ZY == y)
8789   {
8790     ZX = ZY = -1;
8791
8792     game.robot_wheel_active = FALSE;
8793   }
8794 }
8795
8796 static void InitTimegateWheel(int x, int y)
8797 {
8798   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8799 }
8800
8801 static void RunTimegateWheel(int x, int y)
8802 {
8803   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8804 }
8805
8806 static void InitMagicBallDelay(int x, int y)
8807 {
8808   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8809 }
8810
8811 static void ActivateMagicBall(int bx, int by)
8812 {
8813   int x, y;
8814
8815   if (level.ball_random)
8816   {
8817     int pos_border = RND(8);    /* select one of the eight border elements */
8818     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8819     int xx = pos_content % 3;
8820     int yy = pos_content / 3;
8821
8822     x = bx - 1 + xx;
8823     y = by - 1 + yy;
8824
8825     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8826       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8827   }
8828   else
8829   {
8830     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8831     {
8832       int xx = x - bx + 1;
8833       int yy = y - by + 1;
8834
8835       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8836         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8837     }
8838   }
8839
8840   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8841 }
8842
8843 void CheckExit(int x, int y)
8844 {
8845   if (local_player->gems_still_needed > 0 ||
8846       local_player->sokobanfields_still_needed > 0 ||
8847       local_player->lights_still_needed > 0)
8848   {
8849     int element = Feld[x][y];
8850     int graphic = el2img(element);
8851
8852     if (IS_ANIMATED(graphic))
8853       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8854
8855     return;
8856   }
8857
8858   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8859     return;
8860
8861   Feld[x][y] = EL_EXIT_OPENING;
8862
8863   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8864 }
8865
8866 void CheckExitEM(int x, int y)
8867 {
8868   if (local_player->gems_still_needed > 0 ||
8869       local_player->sokobanfields_still_needed > 0 ||
8870       local_player->lights_still_needed > 0)
8871   {
8872     int element = Feld[x][y];
8873     int graphic = el2img(element);
8874
8875     if (IS_ANIMATED(graphic))
8876       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8877
8878     return;
8879   }
8880
8881   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8882     return;
8883
8884   Feld[x][y] = EL_EM_EXIT_OPENING;
8885
8886   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8887 }
8888
8889 void CheckExitSteel(int x, int y)
8890 {
8891   if (local_player->gems_still_needed > 0 ||
8892       local_player->sokobanfields_still_needed > 0 ||
8893       local_player->lights_still_needed > 0)
8894   {
8895     int element = Feld[x][y];
8896     int graphic = el2img(element);
8897
8898     if (IS_ANIMATED(graphic))
8899       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8900
8901     return;
8902   }
8903
8904   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8905     return;
8906
8907   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8908
8909   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8910 }
8911
8912 void CheckExitSteelEM(int x, int y)
8913 {
8914   if (local_player->gems_still_needed > 0 ||
8915       local_player->sokobanfields_still_needed > 0 ||
8916       local_player->lights_still_needed > 0)
8917   {
8918     int element = Feld[x][y];
8919     int graphic = el2img(element);
8920
8921     if (IS_ANIMATED(graphic))
8922       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8923
8924     return;
8925   }
8926
8927   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8928     return;
8929
8930   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8931
8932   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8933 }
8934
8935 void CheckExitSP(int x, int y)
8936 {
8937   if (local_player->gems_still_needed > 0)
8938   {
8939     int element = Feld[x][y];
8940     int graphic = el2img(element);
8941
8942     if (IS_ANIMATED(graphic))
8943       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8944
8945     return;
8946   }
8947
8948   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8949     return;
8950
8951   Feld[x][y] = EL_SP_EXIT_OPENING;
8952
8953   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8954 }
8955
8956 static void CloseAllOpenTimegates()
8957 {
8958   int x, y;
8959
8960   SCAN_PLAYFIELD(x, y)
8961   {
8962     int element = Feld[x][y];
8963
8964     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8965     {
8966       Feld[x][y] = EL_TIMEGATE_CLOSING;
8967
8968       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8969     }
8970   }
8971 }
8972
8973 void DrawTwinkleOnField(int x, int y)
8974 {
8975   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8976     return;
8977
8978   if (Feld[x][y] == EL_BD_DIAMOND)
8979     return;
8980
8981   if (MovDelay[x][y] == 0)      /* next animation frame */
8982     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8983
8984   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8985   {
8986     MovDelay[x][y]--;
8987
8988     DrawLevelElementAnimation(x, y, Feld[x][y]);
8989
8990     if (MovDelay[x][y] != 0)
8991     {
8992       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8993                                            10 - MovDelay[x][y]);
8994
8995       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8996     }
8997   }
8998 }
8999
9000 void MauerWaechst(int x, int y)
9001 {
9002   int delay = 6;
9003
9004   if (!MovDelay[x][y])          /* next animation frame */
9005     MovDelay[x][y] = 3 * delay;
9006
9007   if (MovDelay[x][y])           /* wait some time before next frame */
9008   {
9009     MovDelay[x][y]--;
9010
9011     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9012     {
9013       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9014       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9015
9016       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9017     }
9018
9019     if (!MovDelay[x][y])
9020     {
9021       if (MovDir[x][y] == MV_LEFT)
9022       {
9023         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9024           TEST_DrawLevelField(x - 1, y);
9025       }
9026       else if (MovDir[x][y] == MV_RIGHT)
9027       {
9028         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9029           TEST_DrawLevelField(x + 1, y);
9030       }
9031       else if (MovDir[x][y] == MV_UP)
9032       {
9033         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9034           TEST_DrawLevelField(x, y - 1);
9035       }
9036       else
9037       {
9038         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9039           TEST_DrawLevelField(x, y + 1);
9040       }
9041
9042       Feld[x][y] = Store[x][y];
9043       Store[x][y] = 0;
9044       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9045       TEST_DrawLevelField(x, y);
9046     }
9047   }
9048 }
9049
9050 void MauerAbleger(int ax, int ay)
9051 {
9052   int element = Feld[ax][ay];
9053   int graphic = el2img(element);
9054   boolean oben_frei = FALSE, unten_frei = FALSE;
9055   boolean links_frei = FALSE, rechts_frei = FALSE;
9056   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9057   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9058   boolean new_wall = FALSE;
9059
9060   if (IS_ANIMATED(graphic))
9061     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9062
9063   if (!MovDelay[ax][ay])        /* start building new wall */
9064     MovDelay[ax][ay] = 6;
9065
9066   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9067   {
9068     MovDelay[ax][ay]--;
9069     if (MovDelay[ax][ay])
9070       return;
9071   }
9072
9073   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9074     oben_frei = TRUE;
9075   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9076     unten_frei = TRUE;
9077   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9078     links_frei = TRUE;
9079   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9080     rechts_frei = TRUE;
9081
9082   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9083       element == EL_EXPANDABLE_WALL_ANY)
9084   {
9085     if (oben_frei)
9086     {
9087       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9088       Store[ax][ay-1] = element;
9089       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9090       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9091         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9092                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9093       new_wall = TRUE;
9094     }
9095     if (unten_frei)
9096     {
9097       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9098       Store[ax][ay+1] = element;
9099       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9100       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9101         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9102                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9103       new_wall = TRUE;
9104     }
9105   }
9106
9107   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9108       element == EL_EXPANDABLE_WALL_ANY ||
9109       element == EL_EXPANDABLE_WALL ||
9110       element == EL_BD_EXPANDABLE_WALL)
9111   {
9112     if (links_frei)
9113     {
9114       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9115       Store[ax-1][ay] = element;
9116       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9117       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9118         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9119                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9120       new_wall = TRUE;
9121     }
9122
9123     if (rechts_frei)
9124     {
9125       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9126       Store[ax+1][ay] = element;
9127       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9128       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9129         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9130                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9131       new_wall = TRUE;
9132     }
9133   }
9134
9135   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9136     TEST_DrawLevelField(ax, ay);
9137
9138   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9139     oben_massiv = TRUE;
9140   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9141     unten_massiv = TRUE;
9142   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9143     links_massiv = TRUE;
9144   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9145     rechts_massiv = TRUE;
9146
9147   if (((oben_massiv && unten_massiv) ||
9148        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9149        element == EL_EXPANDABLE_WALL) &&
9150       ((links_massiv && rechts_massiv) ||
9151        element == EL_EXPANDABLE_WALL_VERTICAL))
9152     Feld[ax][ay] = EL_WALL;
9153
9154   if (new_wall)
9155     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9156 }
9157
9158 void MauerAblegerStahl(int ax, int ay)
9159 {
9160   int element = Feld[ax][ay];
9161   int graphic = el2img(element);
9162   boolean oben_frei = FALSE, unten_frei = FALSE;
9163   boolean links_frei = FALSE, rechts_frei = FALSE;
9164   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9165   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9166   boolean new_wall = FALSE;
9167
9168   if (IS_ANIMATED(graphic))
9169     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9170
9171   if (!MovDelay[ax][ay])        /* start building new wall */
9172     MovDelay[ax][ay] = 6;
9173
9174   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9175   {
9176     MovDelay[ax][ay]--;
9177     if (MovDelay[ax][ay])
9178       return;
9179   }
9180
9181   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9182     oben_frei = TRUE;
9183   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9184     unten_frei = TRUE;
9185   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9186     links_frei = TRUE;
9187   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9188     rechts_frei = TRUE;
9189
9190   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9191       element == EL_EXPANDABLE_STEELWALL_ANY)
9192   {
9193     if (oben_frei)
9194     {
9195       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9196       Store[ax][ay-1] = element;
9197       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9198       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9199         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9200                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9201       new_wall = TRUE;
9202     }
9203     if (unten_frei)
9204     {
9205       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9206       Store[ax][ay+1] = element;
9207       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9208       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9209         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9210                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9211       new_wall = TRUE;
9212     }
9213   }
9214
9215   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9216       element == EL_EXPANDABLE_STEELWALL_ANY)
9217   {
9218     if (links_frei)
9219     {
9220       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9221       Store[ax-1][ay] = element;
9222       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9223       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9224         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9225                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9226       new_wall = TRUE;
9227     }
9228
9229     if (rechts_frei)
9230     {
9231       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9232       Store[ax+1][ay] = element;
9233       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9234       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9235         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9236                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9237       new_wall = TRUE;
9238     }
9239   }
9240
9241   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9242     oben_massiv = TRUE;
9243   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9244     unten_massiv = TRUE;
9245   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9246     links_massiv = TRUE;
9247   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9248     rechts_massiv = TRUE;
9249
9250   if (((oben_massiv && unten_massiv) ||
9251        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9252       ((links_massiv && rechts_massiv) ||
9253        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9254     Feld[ax][ay] = EL_STEELWALL;
9255
9256   if (new_wall)
9257     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9258 }
9259
9260 void CheckForDragon(int x, int y)
9261 {
9262   int i, j;
9263   boolean dragon_found = FALSE;
9264   static int xy[4][2] =
9265   {
9266     { 0, -1 },
9267     { -1, 0 },
9268     { +1, 0 },
9269     { 0, +1 }
9270   };
9271
9272   for (i = 0; i < NUM_DIRECTIONS; i++)
9273   {
9274     for (j = 0; j < 4; j++)
9275     {
9276       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9277
9278       if (IN_LEV_FIELD(xx, yy) &&
9279           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9280       {
9281         if (Feld[xx][yy] == EL_DRAGON)
9282           dragon_found = TRUE;
9283       }
9284       else
9285         break;
9286     }
9287   }
9288
9289   if (!dragon_found)
9290   {
9291     for (i = 0; i < NUM_DIRECTIONS; i++)
9292     {
9293       for (j = 0; j < 3; j++)
9294       {
9295         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9296   
9297         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9298         {
9299           Feld[xx][yy] = EL_EMPTY;
9300           TEST_DrawLevelField(xx, yy);
9301         }
9302         else
9303           break;
9304       }
9305     }
9306   }
9307 }
9308
9309 static void InitBuggyBase(int x, int y)
9310 {
9311   int element = Feld[x][y];
9312   int activating_delay = FRAMES_PER_SECOND / 4;
9313
9314   ChangeDelay[x][y] =
9315     (element == EL_SP_BUGGY_BASE ?
9316      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9317      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9318      activating_delay :
9319      element == EL_SP_BUGGY_BASE_ACTIVE ?
9320      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9321 }
9322
9323 static void WarnBuggyBase(int x, int y)
9324 {
9325   int i;
9326   static int xy[4][2] =
9327   {
9328     { 0, -1 },
9329     { -1, 0 },
9330     { +1, 0 },
9331     { 0, +1 }
9332   };
9333
9334   for (i = 0; i < NUM_DIRECTIONS; i++)
9335   {
9336     int xx = x + xy[i][0];
9337     int yy = y + xy[i][1];
9338
9339     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9340     {
9341       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9342
9343       break;
9344     }
9345   }
9346 }
9347
9348 static void InitTrap(int x, int y)
9349 {
9350   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9351 }
9352
9353 static void ActivateTrap(int x, int y)
9354 {
9355   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9356 }
9357
9358 static void ChangeActiveTrap(int x, int y)
9359 {
9360   int graphic = IMG_TRAP_ACTIVE;
9361
9362   /* if new animation frame was drawn, correct crumbled sand border */
9363   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9364     TEST_DrawLevelFieldCrumbled(x, y);
9365 }
9366
9367 static int getSpecialActionElement(int element, int number, int base_element)
9368 {
9369   return (element != EL_EMPTY ? element :
9370           number != -1 ? base_element + number - 1 :
9371           EL_EMPTY);
9372 }
9373
9374 static int getModifiedActionNumber(int value_old, int operator, int operand,
9375                                    int value_min, int value_max)
9376 {
9377   int value_new = (operator == CA_MODE_SET      ? operand :
9378                    operator == CA_MODE_ADD      ? value_old + operand :
9379                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9380                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9381                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9382                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9383                    value_old);
9384
9385   return (value_new < value_min ? value_min :
9386           value_new > value_max ? value_max :
9387           value_new);
9388 }
9389
9390 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9391 {
9392   struct ElementInfo *ei = &element_info[element];
9393   struct ElementChangeInfo *change = &ei->change_page[page];
9394   int target_element = change->target_element;
9395   int action_type = change->action_type;
9396   int action_mode = change->action_mode;
9397   int action_arg = change->action_arg;
9398   int action_element = change->action_element;
9399   int i;
9400
9401   if (!change->has_action)
9402     return;
9403
9404   /* ---------- determine action paramater values -------------------------- */
9405
9406   int level_time_value =
9407     (level.time > 0 ? TimeLeft :
9408      TimePlayed);
9409
9410   int action_arg_element_raw =
9411     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9412      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9413      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9414      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9415      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9416      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9417      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9418      EL_EMPTY);
9419   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9420
9421   int action_arg_direction =
9422     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9423      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9424      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9425      change->actual_trigger_side :
9426      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9427      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9428      MV_NONE);
9429
9430   int action_arg_number_min =
9431     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9432      CA_ARG_MIN);
9433
9434   int action_arg_number_max =
9435     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9436      action_type == CA_SET_LEVEL_GEMS ? 999 :
9437      action_type == CA_SET_LEVEL_TIME ? 9999 :
9438      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9439      action_type == CA_SET_CE_VALUE ? 9999 :
9440      action_type == CA_SET_CE_SCORE ? 9999 :
9441      CA_ARG_MAX);
9442
9443   int action_arg_number_reset =
9444     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9445      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9446      action_type == CA_SET_LEVEL_TIME ? level.time :
9447      action_type == CA_SET_LEVEL_SCORE ? 0 :
9448      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9449      action_type == CA_SET_CE_SCORE ? 0 :
9450      0);
9451
9452   int action_arg_number =
9453     (action_arg <= CA_ARG_MAX ? action_arg :
9454      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9455      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9456      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9457      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9458      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9459      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9460      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9461      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9462      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9463      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9464      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9465      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9466      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9467      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9468      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9469      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9470      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9471      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9472      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9473      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9474      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9475      -1);
9476
9477   int action_arg_number_old =
9478     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9479      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9480      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9481      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9482      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9483      0);
9484
9485   int action_arg_number_new =
9486     getModifiedActionNumber(action_arg_number_old,
9487                             action_mode, action_arg_number,
9488                             action_arg_number_min, action_arg_number_max);
9489
9490   int trigger_player_bits =
9491     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9492      change->actual_trigger_player_bits : change->trigger_player);
9493
9494   int action_arg_player_bits =
9495     (action_arg >= CA_ARG_PLAYER_1 &&
9496      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9497      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9498      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9499      PLAYER_BITS_ANY);
9500
9501   /* ---------- execute action  -------------------------------------------- */
9502
9503   switch (action_type)
9504   {
9505     case CA_NO_ACTION:
9506     {
9507       return;
9508     }
9509
9510     /* ---------- level actions  ------------------------------------------- */
9511
9512     case CA_RESTART_LEVEL:
9513     {
9514       game.restart_level = TRUE;
9515
9516       break;
9517     }
9518
9519     case CA_SHOW_ENVELOPE:
9520     {
9521       int element = getSpecialActionElement(action_arg_element,
9522                                             action_arg_number, EL_ENVELOPE_1);
9523
9524       if (IS_ENVELOPE(element))
9525         local_player->show_envelope = element;
9526
9527       break;
9528     }
9529
9530     case CA_SET_LEVEL_TIME:
9531     {
9532       if (level.time > 0)       /* only modify limited time value */
9533       {
9534         TimeLeft = action_arg_number_new;
9535
9536         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9537
9538         DisplayGameControlValues();
9539
9540         if (!TimeLeft && setup.time_limit)
9541           for (i = 0; i < MAX_PLAYERS; i++)
9542             KillPlayer(&stored_player[i]);
9543       }
9544
9545       break;
9546     }
9547
9548     case CA_SET_LEVEL_SCORE:
9549     {
9550       local_player->score = action_arg_number_new;
9551
9552       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9553
9554       DisplayGameControlValues();
9555
9556       break;
9557     }
9558
9559     case CA_SET_LEVEL_GEMS:
9560     {
9561       local_player->gems_still_needed = action_arg_number_new;
9562
9563       game_panel_controls[GAME_PANEL_GEMS].value =
9564         local_player->gems_still_needed;
9565
9566       DisplayGameControlValues();
9567
9568       break;
9569     }
9570
9571     case CA_SET_LEVEL_WIND:
9572     {
9573       game.wind_direction = action_arg_direction;
9574
9575       break;
9576     }
9577
9578     case CA_SET_LEVEL_RANDOM_SEED:
9579     {
9580       /* ensure that setting a new random seed while playing is predictable */
9581       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9582
9583       break;
9584     }
9585
9586     /* ---------- player actions  ------------------------------------------ */
9587
9588     case CA_MOVE_PLAYER:
9589     {
9590       /* automatically move to the next field in specified direction */
9591       for (i = 0; i < MAX_PLAYERS; i++)
9592         if (trigger_player_bits & (1 << i))
9593           stored_player[i].programmed_action = action_arg_direction;
9594
9595       break;
9596     }
9597
9598     case CA_EXIT_PLAYER:
9599     {
9600       for (i = 0; i < MAX_PLAYERS; i++)
9601         if (action_arg_player_bits & (1 << i))
9602           PlayerWins(&stored_player[i]);
9603
9604       break;
9605     }
9606
9607     case CA_KILL_PLAYER:
9608     {
9609       for (i = 0; i < MAX_PLAYERS; i++)
9610         if (action_arg_player_bits & (1 << i))
9611           KillPlayer(&stored_player[i]);
9612
9613       break;
9614     }
9615
9616     case CA_SET_PLAYER_KEYS:
9617     {
9618       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9619       int element = getSpecialActionElement(action_arg_element,
9620                                             action_arg_number, EL_KEY_1);
9621
9622       if (IS_KEY(element))
9623       {
9624         for (i = 0; i < MAX_PLAYERS; i++)
9625         {
9626           if (trigger_player_bits & (1 << i))
9627           {
9628             stored_player[i].key[KEY_NR(element)] = key_state;
9629
9630             DrawGameDoorValues();
9631           }
9632         }
9633       }
9634
9635       break;
9636     }
9637
9638     case CA_SET_PLAYER_SPEED:
9639     {
9640       for (i = 0; i < MAX_PLAYERS; i++)
9641       {
9642         if (trigger_player_bits & (1 << i))
9643         {
9644           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9645
9646           if (action_arg == CA_ARG_SPEED_FASTER &&
9647               stored_player[i].cannot_move)
9648           {
9649             action_arg_number = STEPSIZE_VERY_SLOW;
9650           }
9651           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9652                    action_arg == CA_ARG_SPEED_FASTER)
9653           {
9654             action_arg_number = 2;
9655             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9656                            CA_MODE_MULTIPLY);
9657           }
9658           else if (action_arg == CA_ARG_NUMBER_RESET)
9659           {
9660             action_arg_number = level.initial_player_stepsize[i];
9661           }
9662
9663           move_stepsize =
9664             getModifiedActionNumber(move_stepsize,
9665                                     action_mode,
9666                                     action_arg_number,
9667                                     action_arg_number_min,
9668                                     action_arg_number_max);
9669
9670           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9671         }
9672       }
9673
9674       break;
9675     }
9676
9677     case CA_SET_PLAYER_SHIELD:
9678     {
9679       for (i = 0; i < MAX_PLAYERS; i++)
9680       {
9681         if (trigger_player_bits & (1 << i))
9682         {
9683           if (action_arg == CA_ARG_SHIELD_OFF)
9684           {
9685             stored_player[i].shield_normal_time_left = 0;
9686             stored_player[i].shield_deadly_time_left = 0;
9687           }
9688           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9689           {
9690             stored_player[i].shield_normal_time_left = 999999;
9691           }
9692           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9693           {
9694             stored_player[i].shield_normal_time_left = 999999;
9695             stored_player[i].shield_deadly_time_left = 999999;
9696           }
9697         }
9698       }
9699
9700       break;
9701     }
9702
9703     case CA_SET_PLAYER_GRAVITY:
9704     {
9705       for (i = 0; i < MAX_PLAYERS; i++)
9706       {
9707         if (trigger_player_bits & (1 << i))
9708         {
9709           stored_player[i].gravity =
9710             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9711              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9712              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9713              stored_player[i].gravity);
9714         }
9715       }
9716
9717       break;
9718     }
9719
9720     case CA_SET_PLAYER_ARTWORK:
9721     {
9722       for (i = 0; i < MAX_PLAYERS; i++)
9723       {
9724         if (trigger_player_bits & (1 << i))
9725         {
9726           int artwork_element = action_arg_element;
9727
9728           if (action_arg == CA_ARG_ELEMENT_RESET)
9729             artwork_element =
9730               (level.use_artwork_element[i] ? level.artwork_element[i] :
9731                stored_player[i].element_nr);
9732
9733           if (stored_player[i].artwork_element != artwork_element)
9734             stored_player[i].Frame = 0;
9735
9736           stored_player[i].artwork_element = artwork_element;
9737
9738           SetPlayerWaiting(&stored_player[i], FALSE);
9739
9740           /* set number of special actions for bored and sleeping animation */
9741           stored_player[i].num_special_action_bored =
9742             get_num_special_action(artwork_element,
9743                                    ACTION_BORING_1, ACTION_BORING_LAST);
9744           stored_player[i].num_special_action_sleeping =
9745             get_num_special_action(artwork_element,
9746                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9747         }
9748       }
9749
9750       break;
9751     }
9752
9753     case CA_SET_PLAYER_INVENTORY:
9754     {
9755       for (i = 0; i < MAX_PLAYERS; i++)
9756       {
9757         struct PlayerInfo *player = &stored_player[i];
9758         int j, k;
9759
9760         if (trigger_player_bits & (1 << i))
9761         {
9762           int inventory_element = action_arg_element;
9763
9764           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9765               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9766               action_arg == CA_ARG_ELEMENT_ACTION)
9767           {
9768             int element = inventory_element;
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           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9783                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9784                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9785           {
9786             if (player->inventory_infinite_element != EL_UNDEFINED &&
9787                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9788                                      action_arg_element_raw))
9789               player->inventory_infinite_element = EL_UNDEFINED;
9790
9791             for (k = 0, j = 0; j < player->inventory_size; j++)
9792             {
9793               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9794                                         action_arg_element_raw))
9795                 player->inventory_element[k++] = player->inventory_element[j];
9796             }
9797
9798             player->inventory_size = k;
9799           }
9800           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9801           {
9802             if (player->inventory_size > 0)
9803             {
9804               for (j = 0; j < player->inventory_size - 1; j++)
9805                 player->inventory_element[j] = player->inventory_element[j + 1];
9806
9807               player->inventory_size--;
9808             }
9809           }
9810           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9811           {
9812             if (player->inventory_size > 0)
9813               player->inventory_size--;
9814           }
9815           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9816           {
9817             player->inventory_infinite_element = EL_UNDEFINED;
9818             player->inventory_size = 0;
9819           }
9820           else if (action_arg == CA_ARG_INVENTORY_RESET)
9821           {
9822             player->inventory_infinite_element = EL_UNDEFINED;
9823             player->inventory_size = 0;
9824
9825             if (level.use_initial_inventory[i])
9826             {
9827               for (j = 0; j < level.initial_inventory_size[i]; j++)
9828               {
9829                 int element = level.initial_inventory_content[i][j];
9830                 int collect_count = element_info[element].collect_count_initial;
9831
9832                 if (!IS_CUSTOM_ELEMENT(element))
9833                   collect_count = 1;
9834
9835                 if (collect_count == 0)
9836                   player->inventory_infinite_element = element;
9837                 else
9838                   for (k = 0; k < collect_count; k++)
9839                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9840                       player->inventory_element[player->inventory_size++] =
9841                         element;
9842               }
9843             }
9844           }
9845         }
9846       }
9847
9848       break;
9849     }
9850
9851     /* ---------- CE actions  ---------------------------------------------- */
9852
9853     case CA_SET_CE_VALUE:
9854     {
9855       int last_ce_value = CustomValue[x][y];
9856
9857       CustomValue[x][y] = action_arg_number_new;
9858
9859       if (CustomValue[x][y] != last_ce_value)
9860       {
9861         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9862         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9863
9864         if (CustomValue[x][y] == 0)
9865         {
9866           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9867           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9868         }
9869       }
9870
9871       break;
9872     }
9873
9874     case CA_SET_CE_SCORE:
9875     {
9876       int last_ce_score = ei->collect_score;
9877
9878       ei->collect_score = action_arg_number_new;
9879
9880       if (ei->collect_score != last_ce_score)
9881       {
9882         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9883         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9884
9885         if (ei->collect_score == 0)
9886         {
9887           int xx, yy;
9888
9889           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9890           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9891
9892           /*
9893             This is a very special case that seems to be a mixture between
9894             CheckElementChange() and CheckTriggeredElementChange(): while
9895             the first one only affects single elements that are triggered
9896             directly, the second one affects multiple elements in the playfield
9897             that are triggered indirectly by another element. This is a third
9898             case: Changing the CE score always affects multiple identical CEs,
9899             so every affected CE must be checked, not only the single CE for
9900             which the CE score was changed in the first place (as every instance
9901             of that CE shares the same CE score, and therefore also can change)!
9902           */
9903           SCAN_PLAYFIELD(xx, yy)
9904           {
9905             if (Feld[xx][yy] == element)
9906               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9907                                  CE_SCORE_GETS_ZERO);
9908           }
9909         }
9910       }
9911
9912       break;
9913     }
9914
9915     case CA_SET_CE_ARTWORK:
9916     {
9917       int artwork_element = action_arg_element;
9918       boolean reset_frame = FALSE;
9919       int xx, yy;
9920
9921       if (action_arg == CA_ARG_ELEMENT_RESET)
9922         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9923                            element);
9924
9925       if (ei->gfx_element != artwork_element)
9926         reset_frame = TRUE;
9927
9928       ei->gfx_element = artwork_element;
9929
9930       SCAN_PLAYFIELD(xx, yy)
9931       {
9932         if (Feld[xx][yy] == element)
9933         {
9934           if (reset_frame)
9935           {
9936             ResetGfxAnimation(xx, yy);
9937             ResetRandomAnimationValue(xx, yy);
9938           }
9939
9940           TEST_DrawLevelField(xx, yy);
9941         }
9942       }
9943
9944       break;
9945     }
9946
9947     /* ---------- engine actions  ------------------------------------------ */
9948
9949     case CA_SET_ENGINE_SCAN_MODE:
9950     {
9951       InitPlayfieldScanMode(action_arg);
9952
9953       break;
9954     }
9955
9956     default:
9957       break;
9958   }
9959 }
9960
9961 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9962 {
9963   int old_element = Feld[x][y];
9964   int new_element = GetElementFromGroupElement(element);
9965   int previous_move_direction = MovDir[x][y];
9966   int last_ce_value = CustomValue[x][y];
9967   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9968   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9969   boolean add_player_onto_element = (new_element_is_player &&
9970                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9971                                      IS_WALKABLE(old_element));
9972
9973   if (!add_player_onto_element)
9974   {
9975     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9976       RemoveMovingField(x, y);
9977     else
9978       RemoveField(x, y);
9979
9980     Feld[x][y] = new_element;
9981
9982     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9983       MovDir[x][y] = previous_move_direction;
9984
9985     if (element_info[new_element].use_last_ce_value)
9986       CustomValue[x][y] = last_ce_value;
9987
9988     InitField_WithBug1(x, y, FALSE);
9989
9990     new_element = Feld[x][y];   /* element may have changed */
9991
9992     ResetGfxAnimation(x, y);
9993     ResetRandomAnimationValue(x, y);
9994
9995     TEST_DrawLevelField(x, y);
9996
9997     if (GFX_CRUMBLED(new_element))
9998       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9999   }
10000
10001   /* check if element under the player changes from accessible to unaccessible
10002      (needed for special case of dropping element which then changes) */
10003   /* (must be checked after creating new element for walkable group elements) */
10004   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10005       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10006   {
10007     Bang(x, y);
10008
10009     return;
10010   }
10011
10012   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10013   if (new_element_is_player)
10014     RelocatePlayer(x, y, new_element);
10015
10016   if (is_change)
10017     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10018
10019   TestIfBadThingTouchesPlayer(x, y);
10020   TestIfPlayerTouchesCustomElement(x, y);
10021   TestIfElementTouchesCustomElement(x, y);
10022 }
10023
10024 static void CreateField(int x, int y, int element)
10025 {
10026   CreateFieldExt(x, y, element, FALSE);
10027 }
10028
10029 static void CreateElementFromChange(int x, int y, int element)
10030 {
10031   element = GET_VALID_RUNTIME_ELEMENT(element);
10032
10033   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10034   {
10035     int old_element = Feld[x][y];
10036
10037     /* prevent changed element from moving in same engine frame
10038        unless both old and new element can either fall or move */
10039     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10040         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10041       Stop[x][y] = TRUE;
10042   }
10043
10044   CreateFieldExt(x, y, element, TRUE);
10045 }
10046
10047 static boolean ChangeElement(int x, int y, int element, int page)
10048 {
10049   struct ElementInfo *ei = &element_info[element];
10050   struct ElementChangeInfo *change = &ei->change_page[page];
10051   int ce_value = CustomValue[x][y];
10052   int ce_score = ei->collect_score;
10053   int target_element;
10054   int old_element = Feld[x][y];
10055
10056   /* always use default change event to prevent running into a loop */
10057   if (ChangeEvent[x][y] == -1)
10058     ChangeEvent[x][y] = CE_DELAY;
10059
10060   if (ChangeEvent[x][y] == CE_DELAY)
10061   {
10062     /* reset actual trigger element, trigger player and action element */
10063     change->actual_trigger_element = EL_EMPTY;
10064     change->actual_trigger_player = EL_EMPTY;
10065     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10066     change->actual_trigger_side = CH_SIDE_NONE;
10067     change->actual_trigger_ce_value = 0;
10068     change->actual_trigger_ce_score = 0;
10069   }
10070
10071   /* do not change elements more than a specified maximum number of changes */
10072   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10073     return FALSE;
10074
10075   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10076
10077   if (change->explode)
10078   {
10079     Bang(x, y);
10080
10081     return TRUE;
10082   }
10083
10084   if (change->use_target_content)
10085   {
10086     boolean complete_replace = TRUE;
10087     boolean can_replace[3][3];
10088     int xx, yy;
10089
10090     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10091     {
10092       boolean is_empty;
10093       boolean is_walkable;
10094       boolean is_diggable;
10095       boolean is_collectible;
10096       boolean is_removable;
10097       boolean is_destructible;
10098       int ex = x + xx - 1;
10099       int ey = y + yy - 1;
10100       int content_element = change->target_content.e[xx][yy];
10101       int e;
10102
10103       can_replace[xx][yy] = TRUE;
10104
10105       if (ex == x && ey == y)   /* do not check changing element itself */
10106         continue;
10107
10108       if (content_element == EL_EMPTY_SPACE)
10109       {
10110         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10111
10112         continue;
10113       }
10114
10115       if (!IN_LEV_FIELD(ex, ey))
10116       {
10117         can_replace[xx][yy] = FALSE;
10118         complete_replace = FALSE;
10119
10120         continue;
10121       }
10122
10123       e = Feld[ex][ey];
10124
10125       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10126         e = MovingOrBlocked2Element(ex, ey);
10127
10128       is_empty = (IS_FREE(ex, ey) ||
10129                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10130
10131       is_walkable     = (is_empty || IS_WALKABLE(e));
10132       is_diggable     = (is_empty || IS_DIGGABLE(e));
10133       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10134       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10135       is_removable    = (is_diggable || is_collectible);
10136
10137       can_replace[xx][yy] =
10138         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10139           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10140           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10141           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10142           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10143           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10144          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10145
10146       if (!can_replace[xx][yy])
10147         complete_replace = FALSE;
10148     }
10149
10150     if (!change->only_if_complete || complete_replace)
10151     {
10152       boolean something_has_changed = FALSE;
10153
10154       if (change->only_if_complete && change->use_random_replace &&
10155           RND(100) < change->random_percentage)
10156         return FALSE;
10157
10158       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10159       {
10160         int ex = x + xx - 1;
10161         int ey = y + yy - 1;
10162         int content_element;
10163
10164         if (can_replace[xx][yy] && (!change->use_random_replace ||
10165                                     RND(100) < change->random_percentage))
10166         {
10167           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10168             RemoveMovingField(ex, ey);
10169
10170           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10171
10172           content_element = change->target_content.e[xx][yy];
10173           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10174                                               ce_value, ce_score);
10175
10176           CreateElementFromChange(ex, ey, target_element);
10177
10178           something_has_changed = TRUE;
10179
10180           /* for symmetry reasons, freeze newly created border elements */
10181           if (ex != x || ey != y)
10182             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10183         }
10184       }
10185
10186       if (something_has_changed)
10187       {
10188         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10189         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10190       }
10191     }
10192   }
10193   else
10194   {
10195     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10196                                         ce_value, ce_score);
10197
10198     if (element == EL_DIAGONAL_GROWING ||
10199         element == EL_DIAGONAL_SHRINKING)
10200     {
10201       target_element = Store[x][y];
10202
10203       Store[x][y] = EL_EMPTY;
10204     }
10205
10206     CreateElementFromChange(x, y, target_element);
10207
10208     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10209     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10210   }
10211
10212   /* this uses direct change before indirect change */
10213   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10214
10215   return TRUE;
10216 }
10217
10218 static void HandleElementChange(int x, int y, int page)
10219 {
10220   int element = MovingOrBlocked2Element(x, y);
10221   struct ElementInfo *ei = &element_info[element];
10222   struct ElementChangeInfo *change = &ei->change_page[page];
10223   boolean handle_action_before_change = FALSE;
10224
10225 #ifdef DEBUG
10226   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10227       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10228   {
10229     printf("\n\n");
10230     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10231            x, y, element, element_info[element].token_name);
10232     printf("HandleElementChange(): This should never happen!\n");
10233     printf("\n\n");
10234   }
10235 #endif
10236
10237   /* this can happen with classic bombs on walkable, changing elements */
10238   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10239   {
10240     return;
10241   }
10242
10243   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10244   {
10245     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10246
10247     if (change->can_change)
10248     {
10249       /* !!! not clear why graphic animation should be reset at all here !!! */
10250       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10251       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10252
10253       /*
10254         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10255
10256         When using an animation frame delay of 1 (this only happens with
10257         "sp_zonk.moving.left/right" in the classic graphics), the default
10258         (non-moving) animation shows wrong animation frames (while the
10259         moving animation, like "sp_zonk.moving.left/right", is correct,
10260         so this graphical bug never shows up with the classic graphics).
10261         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10262         be drawn instead of the correct frames 0,1,2,3. This is caused by
10263         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10264         an element change: First when the change delay ("ChangeDelay[][]")
10265         counter has reached zero after decrementing, then a second time in
10266         the next frame (after "GfxFrame[][]" was already incremented) when
10267         "ChangeDelay[][]" is reset to the initial delay value again.
10268
10269         This causes frame 0 to be drawn twice, while the last frame won't
10270         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10271
10272         As some animations may already be cleverly designed around this bug
10273         (at least the "Snake Bite" snake tail animation does this), it cannot
10274         simply be fixed here without breaking such existing animations.
10275         Unfortunately, it cannot easily be detected if a graphics set was
10276         designed "before" or "after" the bug was fixed. As a workaround,
10277         a new graphics set option "game.graphics_engine_version" was added
10278         to be able to specify the game's major release version for which the
10279         graphics set was designed, which can then be used to decide if the
10280         bugfix should be used (version 4 and above) or not (version 3 or
10281         below, or if no version was specified at all, as with old sets).
10282
10283         (The wrong/fixed animation frames can be tested with the test level set
10284         "test_gfxframe" and level "000", which contains a specially prepared
10285         custom element at level position (x/y) == (11/9) which uses the zonk
10286         animation mentioned above. Using "game.graphics_engine_version: 4"
10287         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10288         This can also be seen from the debug output for this test element.)
10289       */
10290
10291       /* when a custom element is about to change (for example by change delay),
10292          do not reset graphic animation when the custom element is moving */
10293       if (game.graphics_engine_version < 4 &&
10294           !IS_MOVING(x, y))
10295       {
10296         ResetGfxAnimation(x, y);
10297         ResetRandomAnimationValue(x, y);
10298       }
10299
10300       if (change->pre_change_function)
10301         change->pre_change_function(x, y);
10302     }
10303   }
10304
10305   ChangeDelay[x][y]--;
10306
10307   if (ChangeDelay[x][y] != 0)           /* continue element change */
10308   {
10309     if (change->can_change)
10310     {
10311       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10312
10313       if (IS_ANIMATED(graphic))
10314         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10315
10316       if (change->change_function)
10317         change->change_function(x, y);
10318     }
10319   }
10320   else                                  /* finish element change */
10321   {
10322     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10323     {
10324       page = ChangePage[x][y];
10325       ChangePage[x][y] = -1;
10326
10327       change = &ei->change_page[page];
10328     }
10329
10330     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10331     {
10332       ChangeDelay[x][y] = 1;            /* try change after next move step */
10333       ChangePage[x][y] = page;          /* remember page to use for change */
10334
10335       return;
10336     }
10337
10338     /* special case: set new level random seed before changing element */
10339     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10340       handle_action_before_change = TRUE;
10341
10342     if (change->has_action && handle_action_before_change)
10343       ExecuteCustomElementAction(x, y, element, page);
10344
10345     if (change->can_change)
10346     {
10347       if (ChangeElement(x, y, element, page))
10348       {
10349         if (change->post_change_function)
10350           change->post_change_function(x, y);
10351       }
10352     }
10353
10354     if (change->has_action && !handle_action_before_change)
10355       ExecuteCustomElementAction(x, y, element, page);
10356   }
10357 }
10358
10359 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10360                                               int trigger_element,
10361                                               int trigger_event,
10362                                               int trigger_player,
10363                                               int trigger_side,
10364                                               int trigger_page)
10365 {
10366   boolean change_done_any = FALSE;
10367   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10368   int i;
10369
10370   if (!(trigger_events[trigger_element][trigger_event]))
10371     return FALSE;
10372
10373   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10374
10375   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10376   {
10377     int element = EL_CUSTOM_START + i;
10378     boolean change_done = FALSE;
10379     int p;
10380
10381     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10382         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10383       continue;
10384
10385     for (p = 0; p < element_info[element].num_change_pages; p++)
10386     {
10387       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10388
10389       if (change->can_change_or_has_action &&
10390           change->has_event[trigger_event] &&
10391           change->trigger_side & trigger_side &&
10392           change->trigger_player & trigger_player &&
10393           change->trigger_page & trigger_page_bits &&
10394           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10395       {
10396         change->actual_trigger_element = trigger_element;
10397         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10398         change->actual_trigger_player_bits = trigger_player;
10399         change->actual_trigger_side = trigger_side;
10400         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10401         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10402
10403         if ((change->can_change && !change_done) || change->has_action)
10404         {
10405           int x, y;
10406
10407           SCAN_PLAYFIELD(x, y)
10408           {
10409             if (Feld[x][y] == element)
10410             {
10411               if (change->can_change && !change_done)
10412               {
10413                 /* if element already changed in this frame, not only prevent
10414                    another element change (checked in ChangeElement()), but
10415                    also prevent additional element actions for this element */
10416
10417                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10418                     !level.use_action_after_change_bug)
10419                   continue;
10420
10421                 ChangeDelay[x][y] = 1;
10422                 ChangeEvent[x][y] = trigger_event;
10423
10424                 HandleElementChange(x, y, p);
10425               }
10426               else if (change->has_action)
10427               {
10428                 /* if element already changed in this frame, not only prevent
10429                    another element change (checked in ChangeElement()), but
10430                    also prevent additional element actions for this element */
10431
10432                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10433                     !level.use_action_after_change_bug)
10434                   continue;
10435
10436                 ExecuteCustomElementAction(x, y, element, p);
10437                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10438               }
10439             }
10440           }
10441
10442           if (change->can_change)
10443           {
10444             change_done = TRUE;
10445             change_done_any = TRUE;
10446           }
10447         }
10448       }
10449     }
10450   }
10451
10452   RECURSION_LOOP_DETECTION_END();
10453
10454   return change_done_any;
10455 }
10456
10457 static boolean CheckElementChangeExt(int x, int y,
10458                                      int element,
10459                                      int trigger_element,
10460                                      int trigger_event,
10461                                      int trigger_player,
10462                                      int trigger_side)
10463 {
10464   boolean change_done = FALSE;
10465   int p;
10466
10467   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10468       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10469     return FALSE;
10470
10471   if (Feld[x][y] == EL_BLOCKED)
10472   {
10473     Blocked2Moving(x, y, &x, &y);
10474     element = Feld[x][y];
10475   }
10476
10477   /* check if element has already changed or is about to change after moving */
10478   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10479        Feld[x][y] != element) ||
10480
10481       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10482        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10483         ChangePage[x][y] != -1)))
10484     return FALSE;
10485
10486   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10487
10488   for (p = 0; p < element_info[element].num_change_pages; p++)
10489   {
10490     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10491
10492     /* check trigger element for all events where the element that is checked
10493        for changing interacts with a directly adjacent element -- this is
10494        different to element changes that affect other elements to change on the
10495        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10496     boolean check_trigger_element =
10497       (trigger_event == CE_TOUCHING_X ||
10498        trigger_event == CE_HITTING_X ||
10499        trigger_event == CE_HIT_BY_X ||
10500        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10501
10502     if (change->can_change_or_has_action &&
10503         change->has_event[trigger_event] &&
10504         change->trigger_side & trigger_side &&
10505         change->trigger_player & trigger_player &&
10506         (!check_trigger_element ||
10507          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10508     {
10509       change->actual_trigger_element = trigger_element;
10510       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10511       change->actual_trigger_player_bits = trigger_player;
10512       change->actual_trigger_side = trigger_side;
10513       change->actual_trigger_ce_value = CustomValue[x][y];
10514       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10515
10516       /* special case: trigger element not at (x,y) position for some events */
10517       if (check_trigger_element)
10518       {
10519         static struct
10520         {
10521           int dx, dy;
10522         } move_xy[] =
10523           {
10524             {  0,  0 },
10525             { -1,  0 },
10526             { +1,  0 },
10527             {  0,  0 },
10528             {  0, -1 },
10529             {  0,  0 }, { 0, 0 }, { 0, 0 },
10530             {  0, +1 }
10531           };
10532
10533         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10534         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10535
10536         change->actual_trigger_ce_value = CustomValue[xx][yy];
10537         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10538       }
10539
10540       if (change->can_change && !change_done)
10541       {
10542         ChangeDelay[x][y] = 1;
10543         ChangeEvent[x][y] = trigger_event;
10544
10545         HandleElementChange(x, y, p);
10546
10547         change_done = TRUE;
10548       }
10549       else if (change->has_action)
10550       {
10551         ExecuteCustomElementAction(x, y, element, p);
10552         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10553       }
10554     }
10555   }
10556
10557   RECURSION_LOOP_DETECTION_END();
10558
10559   return change_done;
10560 }
10561
10562 static void PlayPlayerSound(struct PlayerInfo *player)
10563 {
10564   int jx = player->jx, jy = player->jy;
10565   int sound_element = player->artwork_element;
10566   int last_action = player->last_action_waiting;
10567   int action = player->action_waiting;
10568
10569   if (player->is_waiting)
10570   {
10571     if (action != last_action)
10572       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10573     else
10574       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10575   }
10576   else
10577   {
10578     if (action != last_action)
10579       StopSound(element_info[sound_element].sound[last_action]);
10580
10581     if (last_action == ACTION_SLEEPING)
10582       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10583   }
10584 }
10585
10586 static void PlayAllPlayersSound()
10587 {
10588   int i;
10589
10590   for (i = 0; i < MAX_PLAYERS; i++)
10591     if (stored_player[i].active)
10592       PlayPlayerSound(&stored_player[i]);
10593 }
10594
10595 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10596 {
10597   boolean last_waiting = player->is_waiting;
10598   int move_dir = player->MovDir;
10599
10600   player->dir_waiting = move_dir;
10601   player->last_action_waiting = player->action_waiting;
10602
10603   if (is_waiting)
10604   {
10605     if (!last_waiting)          /* not waiting -> waiting */
10606     {
10607       player->is_waiting = TRUE;
10608
10609       player->frame_counter_bored =
10610         FrameCounter +
10611         game.player_boring_delay_fixed +
10612         GetSimpleRandom(game.player_boring_delay_random);
10613       player->frame_counter_sleeping =
10614         FrameCounter +
10615         game.player_sleeping_delay_fixed +
10616         GetSimpleRandom(game.player_sleeping_delay_random);
10617
10618       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10619     }
10620
10621     if (game.player_sleeping_delay_fixed +
10622         game.player_sleeping_delay_random > 0 &&
10623         player->anim_delay_counter == 0 &&
10624         player->post_delay_counter == 0 &&
10625         FrameCounter >= player->frame_counter_sleeping)
10626       player->is_sleeping = TRUE;
10627     else if (game.player_boring_delay_fixed +
10628              game.player_boring_delay_random > 0 &&
10629              FrameCounter >= player->frame_counter_bored)
10630       player->is_bored = TRUE;
10631
10632     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10633                               player->is_bored ? ACTION_BORING :
10634                               ACTION_WAITING);
10635
10636     if (player->is_sleeping && player->use_murphy)
10637     {
10638       /* special case for sleeping Murphy when leaning against non-free tile */
10639
10640       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10641           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10642            !IS_MOVING(player->jx - 1, player->jy)))
10643         move_dir = MV_LEFT;
10644       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10645                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10646                 !IS_MOVING(player->jx + 1, player->jy)))
10647         move_dir = MV_RIGHT;
10648       else
10649         player->is_sleeping = FALSE;
10650
10651       player->dir_waiting = move_dir;
10652     }
10653
10654     if (player->is_sleeping)
10655     {
10656       if (player->num_special_action_sleeping > 0)
10657       {
10658         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10659         {
10660           int last_special_action = player->special_action_sleeping;
10661           int num_special_action = player->num_special_action_sleeping;
10662           int special_action =
10663             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10664              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10665              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10666              last_special_action + 1 : ACTION_SLEEPING);
10667           int special_graphic =
10668             el_act_dir2img(player->artwork_element, special_action, move_dir);
10669
10670           player->anim_delay_counter =
10671             graphic_info[special_graphic].anim_delay_fixed +
10672             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10673           player->post_delay_counter =
10674             graphic_info[special_graphic].post_delay_fixed +
10675             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10676
10677           player->special_action_sleeping = special_action;
10678         }
10679
10680         if (player->anim_delay_counter > 0)
10681         {
10682           player->action_waiting = player->special_action_sleeping;
10683           player->anim_delay_counter--;
10684         }
10685         else if (player->post_delay_counter > 0)
10686         {
10687           player->post_delay_counter--;
10688         }
10689       }
10690     }
10691     else if (player->is_bored)
10692     {
10693       if (player->num_special_action_bored > 0)
10694       {
10695         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10696         {
10697           int special_action =
10698             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10699           int special_graphic =
10700             el_act_dir2img(player->artwork_element, special_action, move_dir);
10701
10702           player->anim_delay_counter =
10703             graphic_info[special_graphic].anim_delay_fixed +
10704             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10705           player->post_delay_counter =
10706             graphic_info[special_graphic].post_delay_fixed +
10707             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10708
10709           player->special_action_bored = special_action;
10710         }
10711
10712         if (player->anim_delay_counter > 0)
10713         {
10714           player->action_waiting = player->special_action_bored;
10715           player->anim_delay_counter--;
10716         }
10717         else if (player->post_delay_counter > 0)
10718         {
10719           player->post_delay_counter--;
10720         }
10721       }
10722     }
10723   }
10724   else if (last_waiting)        /* waiting -> not waiting */
10725   {
10726     player->is_waiting = FALSE;
10727     player->is_bored = FALSE;
10728     player->is_sleeping = FALSE;
10729
10730     player->frame_counter_bored = -1;
10731     player->frame_counter_sleeping = -1;
10732
10733     player->anim_delay_counter = 0;
10734     player->post_delay_counter = 0;
10735
10736     player->dir_waiting = player->MovDir;
10737     player->action_waiting = ACTION_DEFAULT;
10738
10739     player->special_action_bored = ACTION_DEFAULT;
10740     player->special_action_sleeping = ACTION_DEFAULT;
10741   }
10742 }
10743
10744 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10745 {
10746   static boolean player_was_moving = FALSE;
10747   static boolean player_was_snapping = FALSE;
10748   static boolean player_was_dropping = FALSE;
10749
10750   if ((!player->is_moving  && player_was_moving) ||
10751       (player->MovPos == 0 && player_was_moving) ||
10752       (player->is_snapping && !player_was_snapping) ||
10753       (player->is_dropping && !player_was_dropping))
10754   {
10755     if (!SaveEngineSnapshotToList())
10756       return;
10757
10758     player_was_moving = FALSE;
10759     player_was_snapping = TRUE;
10760     player_was_dropping = TRUE;
10761   }
10762   else
10763   {
10764     if (player->is_moving)
10765       player_was_moving = TRUE;
10766
10767     if (!player->is_snapping)
10768       player_was_snapping = FALSE;
10769
10770     if (!player->is_dropping)
10771       player_was_dropping = FALSE;
10772   }
10773 }
10774
10775 static void CheckSingleStepMode(struct PlayerInfo *player)
10776 {
10777   if (tape.single_step && tape.recording && !tape.pausing)
10778   {
10779     /* as it is called "single step mode", just return to pause mode when the
10780        player stopped moving after one tile (or never starts moving at all) */
10781     if (!player->is_moving && !player->is_pushing)
10782     {
10783       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10784       SnapField(player, 0, 0);                  /* stop snapping */
10785     }
10786   }
10787
10788   CheckSaveEngineSnapshot(player);
10789 }
10790
10791 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10792 {
10793   int left      = player_action & JOY_LEFT;
10794   int right     = player_action & JOY_RIGHT;
10795   int up        = player_action & JOY_UP;
10796   int down      = player_action & JOY_DOWN;
10797   int button1   = player_action & JOY_BUTTON_1;
10798   int button2   = player_action & JOY_BUTTON_2;
10799   int dx        = (left ? -1 : right ? 1 : 0);
10800   int dy        = (up   ? -1 : down  ? 1 : 0);
10801
10802   if (!player->active || tape.pausing)
10803     return 0;
10804
10805   if (player_action)
10806   {
10807     if (button1)
10808       SnapField(player, dx, dy);
10809     else
10810     {
10811       if (button2)
10812         DropElement(player);
10813
10814       MovePlayer(player, dx, dy);
10815     }
10816
10817     CheckSingleStepMode(player);
10818
10819     SetPlayerWaiting(player, FALSE);
10820
10821     return player_action;
10822   }
10823   else
10824   {
10825     /* no actions for this player (no input at player's configured device) */
10826
10827     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10828     SnapField(player, 0, 0);
10829     CheckGravityMovementWhenNotMoving(player);
10830
10831     if (player->MovPos == 0)
10832       SetPlayerWaiting(player, TRUE);
10833
10834     if (player->MovPos == 0)    /* needed for tape.playing */
10835       player->is_moving = FALSE;
10836
10837     player->is_dropping = FALSE;
10838     player->is_dropping_pressed = FALSE;
10839     player->drop_pressed_delay = 0;
10840
10841     CheckSingleStepMode(player);
10842
10843     return 0;
10844   }
10845 }
10846
10847 static void CheckLevelTime()
10848 {
10849   int i;
10850
10851   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10852   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10853   {
10854     if (level.native_em_level->lev->home == 0)  /* all players at home */
10855     {
10856       PlayerWins(local_player);
10857
10858       AllPlayersGone = TRUE;
10859
10860       level.native_em_level->lev->home = -1;
10861     }
10862
10863     if (level.native_em_level->ply[0]->alive == 0 &&
10864         level.native_em_level->ply[1]->alive == 0 &&
10865         level.native_em_level->ply[2]->alive == 0 &&
10866         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10867       AllPlayersGone = TRUE;
10868   }
10869   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10870   {
10871     if (game_sp.LevelSolved &&
10872         !game_sp.GameOver)                              /* game won */
10873     {
10874       PlayerWins(local_player);
10875
10876       game_sp.GameOver = TRUE;
10877
10878       AllPlayersGone = TRUE;
10879     }
10880
10881     if (game_sp.GameOver)                               /* game lost */
10882       AllPlayersGone = TRUE;
10883   }
10884
10885   if (TimeFrames >= FRAMES_PER_SECOND)
10886   {
10887     TimeFrames = 0;
10888     TapeTime++;
10889
10890     for (i = 0; i < MAX_PLAYERS; i++)
10891     {
10892       struct PlayerInfo *player = &stored_player[i];
10893
10894       if (SHIELD_ON(player))
10895       {
10896         player->shield_normal_time_left--;
10897
10898         if (player->shield_deadly_time_left > 0)
10899           player->shield_deadly_time_left--;
10900       }
10901     }
10902
10903     if (!local_player->LevelSolved && !level.use_step_counter)
10904     {
10905       TimePlayed++;
10906
10907       if (TimeLeft > 0)
10908       {
10909         TimeLeft--;
10910
10911         if (TimeLeft <= 10 && setup.time_limit)
10912           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10913
10914         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10915            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10916
10917         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10918
10919         if (!TimeLeft && setup.time_limit)
10920         {
10921           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10922             level.native_em_level->lev->killed_out_of_time = TRUE;
10923           else
10924             for (i = 0; i < MAX_PLAYERS; i++)
10925               KillPlayer(&stored_player[i]);
10926         }
10927       }
10928       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10929       {
10930         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10931       }
10932
10933       level.native_em_level->lev->time =
10934         (game.no_time_limit ? TimePlayed : TimeLeft);
10935     }
10936
10937     if (tape.recording || tape.playing)
10938       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10939   }
10940
10941   if (tape.recording || tape.playing)
10942     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10943
10944   UpdateAndDisplayGameControlValues();
10945 }
10946
10947 void AdvanceFrameAndPlayerCounters(int player_nr)
10948 {
10949   int i;
10950
10951   /* advance frame counters (global frame counter and time frame counter) */
10952   FrameCounter++;
10953   TimeFrames++;
10954
10955   /* advance player counters (counters for move delay, move animation etc.) */
10956   for (i = 0; i < MAX_PLAYERS; i++)
10957   {
10958     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10959     int move_delay_value = stored_player[i].move_delay_value;
10960     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10961
10962     if (!advance_player_counters)       /* not all players may be affected */
10963       continue;
10964
10965     if (move_frames == 0)       /* less than one move per game frame */
10966     {
10967       int stepsize = TILEX / move_delay_value;
10968       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10969       int count = (stored_player[i].is_moving ?
10970                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10971
10972       if (count % delay == 0)
10973         move_frames = 1;
10974     }
10975
10976     stored_player[i].Frame += move_frames;
10977
10978     if (stored_player[i].MovPos != 0)
10979       stored_player[i].StepFrame += move_frames;
10980
10981     if (stored_player[i].move_delay > 0)
10982       stored_player[i].move_delay--;
10983
10984     /* due to bugs in previous versions, counter must count up, not down */
10985     if (stored_player[i].push_delay != -1)
10986       stored_player[i].push_delay++;
10987
10988     if (stored_player[i].drop_delay > 0)
10989       stored_player[i].drop_delay--;
10990
10991     if (stored_player[i].is_dropping_pressed)
10992       stored_player[i].drop_pressed_delay++;
10993   }
10994 }
10995
10996 void StartGameActions(boolean init_network_game, boolean record_tape,
10997                       int random_seed)
10998 {
10999   unsigned int new_random_seed = InitRND(random_seed);
11000
11001   if (record_tape)
11002     TapeStartRecording(new_random_seed);
11003
11004 #if defined(NETWORK_AVALIABLE)
11005   if (init_network_game)
11006   {
11007     SendToServer_StartPlaying();
11008
11009     return;
11010   }
11011 #endif
11012
11013   InitGame();
11014 }
11015
11016 void GameActions()
11017 {
11018   static unsigned int game_frame_delay = 0;
11019   unsigned int game_frame_delay_value;
11020   byte *recorded_player_action;
11021   byte summarized_player_action = 0;
11022   byte tape_action[MAX_PLAYERS];
11023   int i;
11024
11025   /* detect endless loops, caused by custom element programming */
11026   if (recursion_loop_detected && recursion_loop_depth == 0)
11027   {
11028     char *message = getStringCat3("Internal Error! Element ",
11029                                   EL_NAME(recursion_loop_element),
11030                                   " caused endless loop! Quit the game?");
11031
11032     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11033           EL_NAME(recursion_loop_element));
11034
11035     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11036
11037     recursion_loop_detected = FALSE;    /* if game should be continued */
11038
11039     free(message);
11040
11041     return;
11042   }
11043
11044   if (game.restart_level)
11045     StartGameActions(options.network, setup.autorecord, level.random_seed);
11046
11047   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11048   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11049   {
11050     if (level.native_em_level->lev->home == 0)  /* all players at home */
11051     {
11052       PlayerWins(local_player);
11053
11054       AllPlayersGone = TRUE;
11055
11056       level.native_em_level->lev->home = -1;
11057     }
11058
11059     if (level.native_em_level->ply[0]->alive == 0 &&
11060         level.native_em_level->ply[1]->alive == 0 &&
11061         level.native_em_level->ply[2]->alive == 0 &&
11062         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11063       AllPlayersGone = TRUE;
11064   }
11065   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11066   {
11067     if (game_sp.LevelSolved &&
11068         !game_sp.GameOver)                              /* game won */
11069     {
11070       PlayerWins(local_player);
11071
11072       game_sp.GameOver = TRUE;
11073
11074       AllPlayersGone = TRUE;
11075     }
11076
11077     if (game_sp.GameOver)                               /* game lost */
11078       AllPlayersGone = TRUE;
11079   }
11080
11081   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11082     GameWon();
11083
11084   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11085     TapeStop();
11086
11087   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11088     return;
11089
11090   game_frame_delay_value =
11091     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11092
11093   if (tape.playing && tape.warp_forward && !tape.pausing)
11094     game_frame_delay_value = 0;
11095
11096 #if 0
11097   /* ---------- main game synchronization point ---------- */
11098
11099   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11100
11101   printf("::: skip == %d\n", skip);
11102
11103 #else
11104   /* ---------- main game synchronization point ---------- */
11105
11106   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11107 #endif
11108
11109   if (network_playing && !network_player_action_received)
11110   {
11111     /* try to get network player actions in time */
11112
11113 #if defined(NETWORK_AVALIABLE)
11114     /* last chance to get network player actions without main loop delay */
11115     HandleNetworking();
11116 #endif
11117
11118     /* game was quit by network peer */
11119     if (game_status != GAME_MODE_PLAYING)
11120       return;
11121
11122     if (!network_player_action_received)
11123       return;           /* failed to get network player actions in time */
11124
11125     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11126   }
11127
11128   if (tape.pausing)
11129     return;
11130
11131   /* at this point we know that we really continue executing the game */
11132
11133   network_player_action_received = FALSE;
11134
11135   /* when playing tape, read previously recorded player input from tape data */
11136   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11137
11138   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11139   if (tape.pausing)
11140     return;
11141
11142   if (tape.set_centered_player)
11143   {
11144     game.centered_player_nr_next = tape.centered_player_nr_next;
11145     game.set_centered_player = TRUE;
11146   }
11147
11148   for (i = 0; i < MAX_PLAYERS; i++)
11149   {
11150     summarized_player_action |= stored_player[i].action;
11151
11152     if (!network_playing && (game.team_mode || tape.playing))
11153       stored_player[i].effective_action = stored_player[i].action;
11154   }
11155
11156 #if defined(NETWORK_AVALIABLE)
11157   if (network_playing)
11158     SendToServer_MovePlayer(summarized_player_action);
11159 #endif
11160
11161   if (!options.network && !game.team_mode)
11162     local_player->effective_action = summarized_player_action;
11163
11164   if (tape.recording &&
11165       setup.team_mode &&
11166       setup.input_on_focus &&
11167       game.centered_player_nr != -1)
11168   {
11169     for (i = 0; i < MAX_PLAYERS; i++)
11170       stored_player[i].effective_action =
11171         (i == game.centered_player_nr ? summarized_player_action : 0);
11172   }
11173
11174   if (recorded_player_action != NULL)
11175     for (i = 0; i < MAX_PLAYERS; i++)
11176       stored_player[i].effective_action = recorded_player_action[i];
11177
11178   for (i = 0; i < MAX_PLAYERS; i++)
11179   {
11180     tape_action[i] = stored_player[i].effective_action;
11181
11182     /* (this may happen in the RND game engine if a player was not present on
11183        the playfield on level start, but appeared later from a custom element */
11184     if (setup.team_mode &&
11185         tape.recording &&
11186         tape_action[i] &&
11187         !tape.player_participates[i])
11188       tape.player_participates[i] = TRUE;
11189   }
11190
11191   /* only record actions from input devices, but not programmed actions */
11192   if (tape.recording)
11193     TapeRecordAction(tape_action);
11194
11195 #if USE_NEW_PLAYER_ASSIGNMENTS
11196   // !!! also map player actions in single player mode !!!
11197   // if (game.team_mode)
11198   {
11199     byte mapped_action[MAX_PLAYERS];
11200
11201 #if DEBUG_PLAYER_ACTIONS
11202     printf(":::");
11203     for (i = 0; i < MAX_PLAYERS; i++)
11204       printf(" %d, ", stored_player[i].effective_action);
11205 #endif
11206
11207     for (i = 0; i < MAX_PLAYERS; i++)
11208       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11209
11210     for (i = 0; i < MAX_PLAYERS; i++)
11211       stored_player[i].effective_action = mapped_action[i];
11212
11213 #if DEBUG_PLAYER_ACTIONS
11214     printf(" =>");
11215     for (i = 0; i < MAX_PLAYERS; i++)
11216       printf(" %d, ", stored_player[i].effective_action);
11217     printf("\n");
11218 #endif
11219   }
11220 #if DEBUG_PLAYER_ACTIONS
11221   else
11222   {
11223     printf(":::");
11224     for (i = 0; i < MAX_PLAYERS; i++)
11225       printf(" %d, ", stored_player[i].effective_action);
11226     printf("\n");
11227   }
11228 #endif
11229 #endif
11230
11231   for (i = 0; i < MAX_PLAYERS; i++)
11232   {
11233     // allow engine snapshot in case of changed movement attempt
11234     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11235         (stored_player[i].effective_action & KEY_MOTION))
11236       game.snapshot.changed_action = TRUE;
11237
11238     // allow engine snapshot in case of snapping/dropping attempt
11239     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11240         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11241       game.snapshot.changed_action = TRUE;
11242
11243     game.snapshot.last_action[i] = stored_player[i].effective_action;
11244   }
11245
11246   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11247   {
11248     GameActions_EM_Main();
11249   }
11250   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11251   {
11252     GameActions_SP_Main();
11253   }
11254   else
11255   {
11256     GameActions_RND_Main();
11257   }
11258
11259   BlitScreenToBitmap(backbuffer);
11260
11261   CheckLevelTime();
11262
11263   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11264
11265   if (options.debug)                    /* calculate frames per second */
11266   {
11267     static unsigned int fps_counter = 0;
11268     static int fps_frames = 0;
11269     unsigned int fps_delay_ms = Counter() - fps_counter;
11270
11271     fps_frames++;
11272
11273     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11274     {
11275       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11276
11277       fps_frames = 0;
11278       fps_counter = Counter();
11279     }
11280
11281     redraw_mask |= REDRAW_FPS;
11282   }
11283 }
11284
11285 void GameActions_EM_Main()
11286 {
11287   byte effective_action[MAX_PLAYERS];
11288   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11289   int i;
11290
11291   for (i = 0; i < MAX_PLAYERS; i++)
11292     effective_action[i] = stored_player[i].effective_action;
11293
11294   GameActions_EM(effective_action, warp_mode);
11295 }
11296
11297 void GameActions_SP_Main()
11298 {
11299   byte effective_action[MAX_PLAYERS];
11300   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11301   int i;
11302
11303   for (i = 0; i < MAX_PLAYERS; i++)
11304     effective_action[i] = stored_player[i].effective_action;
11305
11306   GameActions_SP(effective_action, warp_mode);
11307 }
11308
11309 void GameActions_RND_Main()
11310 {
11311   GameActions_RND();
11312 }
11313
11314 void GameActions_RND()
11315 {
11316   int magic_wall_x = 0, magic_wall_y = 0;
11317   int i, x, y, element, graphic;
11318
11319   InitPlayfieldScanModeVars();
11320
11321   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11322   {
11323     SCAN_PLAYFIELD(x, y)
11324     {
11325       ChangeCount[x][y] = 0;
11326       ChangeEvent[x][y] = -1;
11327     }
11328   }
11329
11330   if (game.set_centered_player)
11331   {
11332     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11333
11334     /* switching to "all players" only possible if all players fit to screen */
11335     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11336     {
11337       game.centered_player_nr_next = game.centered_player_nr;
11338       game.set_centered_player = FALSE;
11339     }
11340
11341     /* do not switch focus to non-existing (or non-active) player */
11342     if (game.centered_player_nr_next >= 0 &&
11343         !stored_player[game.centered_player_nr_next].active)
11344     {
11345       game.centered_player_nr_next = game.centered_player_nr;
11346       game.set_centered_player = FALSE;
11347     }
11348   }
11349
11350   if (game.set_centered_player &&
11351       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11352   {
11353     int sx, sy;
11354
11355     if (game.centered_player_nr_next == -1)
11356     {
11357       setScreenCenteredToAllPlayers(&sx, &sy);
11358     }
11359     else
11360     {
11361       sx = stored_player[game.centered_player_nr_next].jx;
11362       sy = stored_player[game.centered_player_nr_next].jy;
11363     }
11364
11365     game.centered_player_nr = game.centered_player_nr_next;
11366     game.set_centered_player = FALSE;
11367
11368     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11369     DrawGameDoorValues();
11370   }
11371
11372   for (i = 0; i < MAX_PLAYERS; i++)
11373   {
11374     int actual_player_action = stored_player[i].effective_action;
11375
11376 #if 1
11377     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11378        - rnd_equinox_tetrachloride 048
11379        - rnd_equinox_tetrachloride_ii 096
11380        - rnd_emanuel_schmieg 002
11381        - doctor_sloan_ww 001, 020
11382     */
11383     if (stored_player[i].MovPos == 0)
11384       CheckGravityMovement(&stored_player[i]);
11385 #endif
11386
11387     /* overwrite programmed action with tape action */
11388     if (stored_player[i].programmed_action)
11389       actual_player_action = stored_player[i].programmed_action;
11390
11391     PlayerActions(&stored_player[i], actual_player_action);
11392
11393     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11394   }
11395
11396   ScrollScreen(NULL, SCROLL_GO_ON);
11397
11398   /* for backwards compatibility, the following code emulates a fixed bug that
11399      occured when pushing elements (causing elements that just made their last
11400      pushing step to already (if possible) make their first falling step in the
11401      same game frame, which is bad); this code is also needed to use the famous
11402      "spring push bug" which is used in older levels and might be wanted to be
11403      used also in newer levels, but in this case the buggy pushing code is only
11404      affecting the "spring" element and no other elements */
11405
11406   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11407   {
11408     for (i = 0; i < MAX_PLAYERS; i++)
11409     {
11410       struct PlayerInfo *player = &stored_player[i];
11411       int x = player->jx;
11412       int y = player->jy;
11413
11414       if (player->active && player->is_pushing && player->is_moving &&
11415           IS_MOVING(x, y) &&
11416           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11417            Feld[x][y] == EL_SPRING))
11418       {
11419         ContinueMoving(x, y);
11420
11421         /* continue moving after pushing (this is actually a bug) */
11422         if (!IS_MOVING(x, y))
11423           Stop[x][y] = FALSE;
11424       }
11425     }
11426   }
11427
11428   SCAN_PLAYFIELD(x, y)
11429   {
11430     ChangeCount[x][y] = 0;
11431     ChangeEvent[x][y] = -1;
11432
11433     /* this must be handled before main playfield loop */
11434     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11435     {
11436       MovDelay[x][y]--;
11437       if (MovDelay[x][y] <= 0)
11438         RemoveField(x, y);
11439     }
11440
11441     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11442     {
11443       MovDelay[x][y]--;
11444       if (MovDelay[x][y] <= 0)
11445       {
11446         RemoveField(x, y);
11447         TEST_DrawLevelField(x, y);
11448
11449         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11450       }
11451     }
11452
11453 #if DEBUG
11454     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11455     {
11456       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11457       printf("GameActions(): This should never happen!\n");
11458
11459       ChangePage[x][y] = -1;
11460     }
11461 #endif
11462
11463     Stop[x][y] = FALSE;
11464     if (WasJustMoving[x][y] > 0)
11465       WasJustMoving[x][y]--;
11466     if (WasJustFalling[x][y] > 0)
11467       WasJustFalling[x][y]--;
11468     if (CheckCollision[x][y] > 0)
11469       CheckCollision[x][y]--;
11470     if (CheckImpact[x][y] > 0)
11471       CheckImpact[x][y]--;
11472
11473     GfxFrame[x][y]++;
11474
11475     /* reset finished pushing action (not done in ContinueMoving() to allow
11476        continuous pushing animation for elements with zero push delay) */
11477     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11478     {
11479       ResetGfxAnimation(x, y);
11480       TEST_DrawLevelField(x, y);
11481     }
11482
11483 #if DEBUG
11484     if (IS_BLOCKED(x, y))
11485     {
11486       int oldx, oldy;
11487
11488       Blocked2Moving(x, y, &oldx, &oldy);
11489       if (!IS_MOVING(oldx, oldy))
11490       {
11491         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11492         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11493         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11494         printf("GameActions(): This should never happen!\n");
11495       }
11496     }
11497 #endif
11498   }
11499
11500   SCAN_PLAYFIELD(x, y)
11501   {
11502     element = Feld[x][y];
11503     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11504
11505     ResetGfxFrame(x, y, TRUE);
11506
11507     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11508         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11509       ResetRandomAnimationValue(x, y);
11510
11511     SetRandomAnimationValue(x, y);
11512
11513     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11514
11515     if (IS_INACTIVE(element))
11516     {
11517       if (IS_ANIMATED(graphic))
11518         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11519
11520       continue;
11521     }
11522
11523     /* this may take place after moving, so 'element' may have changed */
11524     if (IS_CHANGING(x, y) &&
11525         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11526     {
11527       int page = element_info[element].event_page_nr[CE_DELAY];
11528
11529       HandleElementChange(x, y, page);
11530
11531       element = Feld[x][y];
11532       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11533     }
11534
11535     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11536     {
11537       StartMoving(x, y);
11538
11539       element = Feld[x][y];
11540       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11541
11542       if (IS_ANIMATED(graphic) &&
11543           !IS_MOVING(x, y) &&
11544           !Stop[x][y])
11545         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11546
11547       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11548         TEST_DrawTwinkleOnField(x, y);
11549     }
11550     else if ((element == EL_ACID ||
11551               element == EL_EXIT_OPEN ||
11552               element == EL_EM_EXIT_OPEN ||
11553               element == EL_SP_EXIT_OPEN ||
11554               element == EL_STEEL_EXIT_OPEN ||
11555               element == EL_EM_STEEL_EXIT_OPEN ||
11556               element == EL_SP_TERMINAL ||
11557               element == EL_SP_TERMINAL_ACTIVE ||
11558               element == EL_EXTRA_TIME ||
11559               element == EL_SHIELD_NORMAL ||
11560               element == EL_SHIELD_DEADLY) &&
11561              IS_ANIMATED(graphic))
11562       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11563     else if (IS_MOVING(x, y))
11564       ContinueMoving(x, y);
11565     else if (IS_ACTIVE_BOMB(element))
11566       CheckDynamite(x, y);
11567     else if (element == EL_AMOEBA_GROWING)
11568       AmoebeWaechst(x, y);
11569     else if (element == EL_AMOEBA_SHRINKING)
11570       AmoebaDisappearing(x, y);
11571
11572 #if !USE_NEW_AMOEBA_CODE
11573     else if (IS_AMOEBALIVE(element))
11574       AmoebeAbleger(x, y);
11575 #endif
11576
11577     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11578       Life(x, y);
11579     else if (element == EL_EXIT_CLOSED)
11580       CheckExit(x, y);
11581     else if (element == EL_EM_EXIT_CLOSED)
11582       CheckExitEM(x, y);
11583     else if (element == EL_STEEL_EXIT_CLOSED)
11584       CheckExitSteel(x, y);
11585     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11586       CheckExitSteelEM(x, y);
11587     else if (element == EL_SP_EXIT_CLOSED)
11588       CheckExitSP(x, y);
11589     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11590              element == EL_EXPANDABLE_STEELWALL_GROWING)
11591       MauerWaechst(x, y);
11592     else if (element == EL_EXPANDABLE_WALL ||
11593              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11594              element == EL_EXPANDABLE_WALL_VERTICAL ||
11595              element == EL_EXPANDABLE_WALL_ANY ||
11596              element == EL_BD_EXPANDABLE_WALL)
11597       MauerAbleger(x, y);
11598     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11599              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11600              element == EL_EXPANDABLE_STEELWALL_ANY)
11601       MauerAblegerStahl(x, y);
11602     else if (element == EL_FLAMES)
11603       CheckForDragon(x, y);
11604     else if (element == EL_EXPLOSION)
11605       ; /* drawing of correct explosion animation is handled separately */
11606     else if (element == EL_ELEMENT_SNAPPING ||
11607              element == EL_DIAGONAL_SHRINKING ||
11608              element == EL_DIAGONAL_GROWING)
11609     {
11610       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11611
11612       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11613     }
11614     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11615       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11616
11617     if (IS_BELT_ACTIVE(element))
11618       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11619
11620     if (game.magic_wall_active)
11621     {
11622       int jx = local_player->jx, jy = local_player->jy;
11623
11624       /* play the element sound at the position nearest to the player */
11625       if ((element == EL_MAGIC_WALL_FULL ||
11626            element == EL_MAGIC_WALL_ACTIVE ||
11627            element == EL_MAGIC_WALL_EMPTYING ||
11628            element == EL_BD_MAGIC_WALL_FULL ||
11629            element == EL_BD_MAGIC_WALL_ACTIVE ||
11630            element == EL_BD_MAGIC_WALL_EMPTYING ||
11631            element == EL_DC_MAGIC_WALL_FULL ||
11632            element == EL_DC_MAGIC_WALL_ACTIVE ||
11633            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11634           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11635       {
11636         magic_wall_x = x;
11637         magic_wall_y = y;
11638       }
11639     }
11640   }
11641
11642 #if USE_NEW_AMOEBA_CODE
11643   /* new experimental amoeba growth stuff */
11644   if (!(FrameCounter % 8))
11645   {
11646     static unsigned int random = 1684108901;
11647
11648     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11649     {
11650       x = RND(lev_fieldx);
11651       y = RND(lev_fieldy);
11652       element = Feld[x][y];
11653
11654       if (!IS_PLAYER(x,y) &&
11655           (element == EL_EMPTY ||
11656            CAN_GROW_INTO(element) ||
11657            element == EL_QUICKSAND_EMPTY ||
11658            element == EL_QUICKSAND_FAST_EMPTY ||
11659            element == EL_ACID_SPLASH_LEFT ||
11660            element == EL_ACID_SPLASH_RIGHT))
11661       {
11662         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11663             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11664             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11665             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11666           Feld[x][y] = EL_AMOEBA_DROP;
11667       }
11668
11669       random = random * 129 + 1;
11670     }
11671   }
11672 #endif
11673
11674   game.explosions_delayed = FALSE;
11675
11676   SCAN_PLAYFIELD(x, y)
11677   {
11678     element = Feld[x][y];
11679
11680     if (ExplodeField[x][y])
11681       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11682     else if (element == EL_EXPLOSION)
11683       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11684
11685     ExplodeField[x][y] = EX_TYPE_NONE;
11686   }
11687
11688   game.explosions_delayed = TRUE;
11689
11690   if (game.magic_wall_active)
11691   {
11692     if (!(game.magic_wall_time_left % 4))
11693     {
11694       int element = Feld[magic_wall_x][magic_wall_y];
11695
11696       if (element == EL_BD_MAGIC_WALL_FULL ||
11697           element == EL_BD_MAGIC_WALL_ACTIVE ||
11698           element == EL_BD_MAGIC_WALL_EMPTYING)
11699         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11700       else if (element == EL_DC_MAGIC_WALL_FULL ||
11701                element == EL_DC_MAGIC_WALL_ACTIVE ||
11702                element == EL_DC_MAGIC_WALL_EMPTYING)
11703         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11704       else
11705         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11706     }
11707
11708     if (game.magic_wall_time_left > 0)
11709     {
11710       game.magic_wall_time_left--;
11711
11712       if (!game.magic_wall_time_left)
11713       {
11714         SCAN_PLAYFIELD(x, y)
11715         {
11716           element = Feld[x][y];
11717
11718           if (element == EL_MAGIC_WALL_ACTIVE ||
11719               element == EL_MAGIC_WALL_FULL)
11720           {
11721             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11722             TEST_DrawLevelField(x, y);
11723           }
11724           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11725                    element == EL_BD_MAGIC_WALL_FULL)
11726           {
11727             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11728             TEST_DrawLevelField(x, y);
11729           }
11730           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11731                    element == EL_DC_MAGIC_WALL_FULL)
11732           {
11733             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11734             TEST_DrawLevelField(x, y);
11735           }
11736         }
11737
11738         game.magic_wall_active = FALSE;
11739       }
11740     }
11741   }
11742
11743   if (game.light_time_left > 0)
11744   {
11745     game.light_time_left--;
11746
11747     if (game.light_time_left == 0)
11748       RedrawAllLightSwitchesAndInvisibleElements();
11749   }
11750
11751   if (game.timegate_time_left > 0)
11752   {
11753     game.timegate_time_left--;
11754
11755     if (game.timegate_time_left == 0)
11756       CloseAllOpenTimegates();
11757   }
11758
11759   if (game.lenses_time_left > 0)
11760   {
11761     game.lenses_time_left--;
11762
11763     if (game.lenses_time_left == 0)
11764       RedrawAllInvisibleElementsForLenses();
11765   }
11766
11767   if (game.magnify_time_left > 0)
11768   {
11769     game.magnify_time_left--;
11770
11771     if (game.magnify_time_left == 0)
11772       RedrawAllInvisibleElementsForMagnifier();
11773   }
11774
11775   for (i = 0; i < MAX_PLAYERS; i++)
11776   {
11777     struct PlayerInfo *player = &stored_player[i];
11778
11779     if (SHIELD_ON(player))
11780     {
11781       if (player->shield_deadly_time_left)
11782         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11783       else if (player->shield_normal_time_left)
11784         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11785     }
11786   }
11787
11788 #if USE_DELAYED_GFX_REDRAW
11789   SCAN_PLAYFIELD(x, y)
11790   {
11791     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11792     {
11793       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11794          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11795
11796       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11797         DrawLevelField(x, y);
11798
11799       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11800         DrawLevelFieldCrumbled(x, y);
11801
11802       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11803         DrawLevelFieldCrumbledNeighbours(x, y);
11804
11805       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11806         DrawTwinkleOnField(x, y);
11807     }
11808
11809     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11810   }
11811 #endif
11812
11813   DrawAllPlayers();
11814   PlayAllPlayersSound();
11815
11816   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11817   {
11818     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11819
11820     local_player->show_envelope = 0;
11821   }
11822
11823   /* use random number generator in every frame to make it less predictable */
11824   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11825     RND(1);
11826 }
11827
11828 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11829 {
11830   int min_x = x, min_y = y, max_x = x, max_y = y;
11831   int i;
11832
11833   for (i = 0; i < MAX_PLAYERS; i++)
11834   {
11835     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11836
11837     if (!stored_player[i].active || &stored_player[i] == player)
11838       continue;
11839
11840     min_x = MIN(min_x, jx);
11841     min_y = MIN(min_y, jy);
11842     max_x = MAX(max_x, jx);
11843     max_y = MAX(max_y, jy);
11844   }
11845
11846   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11847 }
11848
11849 static boolean AllPlayersInVisibleScreen()
11850 {
11851   int i;
11852
11853   for (i = 0; i < MAX_PLAYERS; i++)
11854   {
11855     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11856
11857     if (!stored_player[i].active)
11858       continue;
11859
11860     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11861       return FALSE;
11862   }
11863
11864   return TRUE;
11865 }
11866
11867 void ScrollLevel(int dx, int dy)
11868 {
11869   int scroll_offset = 2 * TILEX_VAR;
11870   int x, y;
11871
11872   BlitBitmap(drawto_field, drawto_field,
11873              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11874              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11875              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11876              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11877              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11878              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11879
11880   if (dx != 0)
11881   {
11882     x = (dx == 1 ? BX1 : BX2);
11883     for (y = BY1; y <= BY2; y++)
11884       DrawScreenField(x, y);
11885   }
11886
11887   if (dy != 0)
11888   {
11889     y = (dy == 1 ? BY1 : BY2);
11890     for (x = BX1; x <= BX2; x++)
11891       DrawScreenField(x, y);
11892   }
11893
11894   redraw_mask |= REDRAW_FIELD;
11895 }
11896
11897 static boolean canFallDown(struct PlayerInfo *player)
11898 {
11899   int jx = player->jx, jy = player->jy;
11900
11901   return (IN_LEV_FIELD(jx, jy + 1) &&
11902           (IS_FREE(jx, jy + 1) ||
11903            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11904           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11905           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11906 }
11907
11908 static boolean canPassField(int x, int y, int move_dir)
11909 {
11910   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11911   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11912   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11913   int nextx = x + dx;
11914   int nexty = y + dy;
11915   int element = Feld[x][y];
11916
11917   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11918           !CAN_MOVE(element) &&
11919           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11920           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11921           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11922 }
11923
11924 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11925 {
11926   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11927   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11928   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11929   int newx = x + dx;
11930   int newy = y + dy;
11931
11932   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11933           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11934           (IS_DIGGABLE(Feld[newx][newy]) ||
11935            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11936            canPassField(newx, newy, move_dir)));
11937 }
11938
11939 static void CheckGravityMovement(struct PlayerInfo *player)
11940 {
11941   if (player->gravity && !player->programmed_action)
11942   {
11943     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11944     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11945     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11946     int jx = player->jx, jy = player->jy;
11947     boolean player_is_moving_to_valid_field =
11948       (!player_is_snapping &&
11949        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11950         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11951     boolean player_can_fall_down = canFallDown(player);
11952
11953     if (player_can_fall_down &&
11954         !player_is_moving_to_valid_field)
11955       player->programmed_action = MV_DOWN;
11956   }
11957 }
11958
11959 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11960 {
11961   return CheckGravityMovement(player);
11962
11963   if (player->gravity && !player->programmed_action)
11964   {
11965     int jx = player->jx, jy = player->jy;
11966     boolean field_under_player_is_free =
11967       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11968     boolean player_is_standing_on_valid_field =
11969       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11970        (IS_WALKABLE(Feld[jx][jy]) &&
11971         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11972
11973     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11974       player->programmed_action = MV_DOWN;
11975   }
11976 }
11977
11978 /*
11979   MovePlayerOneStep()
11980   -----------------------------------------------------------------------------
11981   dx, dy:               direction (non-diagonal) to try to move the player to
11982   real_dx, real_dy:     direction as read from input device (can be diagonal)
11983 */
11984
11985 boolean MovePlayerOneStep(struct PlayerInfo *player,
11986                           int dx, int dy, int real_dx, int real_dy)
11987 {
11988   int jx = player->jx, jy = player->jy;
11989   int new_jx = jx + dx, new_jy = jy + dy;
11990   int can_move;
11991   boolean player_can_move = !player->cannot_move;
11992
11993   if (!player->active || (!dx && !dy))
11994     return MP_NO_ACTION;
11995
11996   player->MovDir = (dx < 0 ? MV_LEFT :
11997                     dx > 0 ? MV_RIGHT :
11998                     dy < 0 ? MV_UP :
11999                     dy > 0 ? MV_DOWN :  MV_NONE);
12000
12001   if (!IN_LEV_FIELD(new_jx, new_jy))
12002     return MP_NO_ACTION;
12003
12004   if (!player_can_move)
12005   {
12006     if (player->MovPos == 0)
12007     {
12008       player->is_moving = FALSE;
12009       player->is_digging = FALSE;
12010       player->is_collecting = FALSE;
12011       player->is_snapping = FALSE;
12012       player->is_pushing = FALSE;
12013     }
12014   }
12015
12016   if (!options.network && game.centered_player_nr == -1 &&
12017       !AllPlayersInSight(player, new_jx, new_jy))
12018     return MP_NO_ACTION;
12019
12020   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12021   if (can_move != MP_MOVING)
12022     return can_move;
12023
12024   /* check if DigField() has caused relocation of the player */
12025   if (player->jx != jx || player->jy != jy)
12026     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12027
12028   StorePlayer[jx][jy] = 0;
12029   player->last_jx = jx;
12030   player->last_jy = jy;
12031   player->jx = new_jx;
12032   player->jy = new_jy;
12033   StorePlayer[new_jx][new_jy] = player->element_nr;
12034
12035   if (player->move_delay_value_next != -1)
12036   {
12037     player->move_delay_value = player->move_delay_value_next;
12038     player->move_delay_value_next = -1;
12039   }
12040
12041   player->MovPos =
12042     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12043
12044   player->step_counter++;
12045
12046   PlayerVisit[jx][jy] = FrameCounter;
12047
12048   player->is_moving = TRUE;
12049
12050 #if 1
12051   /* should better be called in MovePlayer(), but this breaks some tapes */
12052   ScrollPlayer(player, SCROLL_INIT);
12053 #endif
12054
12055   return MP_MOVING;
12056 }
12057
12058 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12059 {
12060   int jx = player->jx, jy = player->jy;
12061   int old_jx = jx, old_jy = jy;
12062   int moved = MP_NO_ACTION;
12063
12064   if (!player->active)
12065     return FALSE;
12066
12067   if (!dx && !dy)
12068   {
12069     if (player->MovPos == 0)
12070     {
12071       player->is_moving = FALSE;
12072       player->is_digging = FALSE;
12073       player->is_collecting = FALSE;
12074       player->is_snapping = FALSE;
12075       player->is_pushing = FALSE;
12076     }
12077
12078     return FALSE;
12079   }
12080
12081   if (player->move_delay > 0)
12082     return FALSE;
12083
12084   player->move_delay = -1;              /* set to "uninitialized" value */
12085
12086   /* store if player is automatically moved to next field */
12087   player->is_auto_moving = (player->programmed_action != MV_NONE);
12088
12089   /* remove the last programmed player action */
12090   player->programmed_action = 0;
12091
12092   if (player->MovPos)
12093   {
12094     /* should only happen if pre-1.2 tape recordings are played */
12095     /* this is only for backward compatibility */
12096
12097     int original_move_delay_value = player->move_delay_value;
12098
12099 #if DEBUG
12100     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12101            tape.counter);
12102 #endif
12103
12104     /* scroll remaining steps with finest movement resolution */
12105     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12106
12107     while (player->MovPos)
12108     {
12109       ScrollPlayer(player, SCROLL_GO_ON);
12110       ScrollScreen(NULL, SCROLL_GO_ON);
12111
12112       AdvanceFrameAndPlayerCounters(player->index_nr);
12113
12114       DrawAllPlayers();
12115       BackToFront();
12116     }
12117
12118     player->move_delay_value = original_move_delay_value;
12119   }
12120
12121   player->is_active = FALSE;
12122
12123   if (player->last_move_dir & MV_HORIZONTAL)
12124   {
12125     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12126       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12127   }
12128   else
12129   {
12130     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12131       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12132   }
12133
12134   if (!moved && !player->is_active)
12135   {
12136     player->is_moving = FALSE;
12137     player->is_digging = FALSE;
12138     player->is_collecting = FALSE;
12139     player->is_snapping = FALSE;
12140     player->is_pushing = FALSE;
12141   }
12142
12143   jx = player->jx;
12144   jy = player->jy;
12145
12146   if (moved & MP_MOVING && !ScreenMovPos &&
12147       (player->index_nr == game.centered_player_nr ||
12148        game.centered_player_nr == -1))
12149   {
12150     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12151     int offset = game.scroll_delay_value;
12152
12153     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12154     {
12155       /* actual player has left the screen -- scroll in that direction */
12156       if (jx != old_jx)         /* player has moved horizontally */
12157         scroll_x += (jx - old_jx);
12158       else                      /* player has moved vertically */
12159         scroll_y += (jy - old_jy);
12160     }
12161     else
12162     {
12163       if (jx != old_jx)         /* player has moved horizontally */
12164       {
12165         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12166             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12167           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12168
12169         /* don't scroll over playfield boundaries */
12170         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12171           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12172
12173         /* don't scroll more than one field at a time */
12174         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12175
12176         /* don't scroll against the player's moving direction */
12177         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12178             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12179           scroll_x = old_scroll_x;
12180       }
12181       else                      /* player has moved vertically */
12182       {
12183         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12184             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12185           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12186
12187         /* don't scroll over playfield boundaries */
12188         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12189           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12190
12191         /* don't scroll more than one field at a time */
12192         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12193
12194         /* don't scroll against the player's moving direction */
12195         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12196             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12197           scroll_y = old_scroll_y;
12198       }
12199     }
12200
12201     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12202     {
12203       if (!options.network && game.centered_player_nr == -1 &&
12204           !AllPlayersInVisibleScreen())
12205       {
12206         scroll_x = old_scroll_x;
12207         scroll_y = old_scroll_y;
12208       }
12209       else
12210       {
12211         ScrollScreen(player, SCROLL_INIT);
12212         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12213       }
12214     }
12215   }
12216
12217   player->StepFrame = 0;
12218
12219   if (moved & MP_MOVING)
12220   {
12221     if (old_jx != jx && old_jy == jy)
12222       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12223     else if (old_jx == jx && old_jy != jy)
12224       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12225
12226     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12227
12228     player->last_move_dir = player->MovDir;
12229     player->is_moving = TRUE;
12230     player->is_snapping = FALSE;
12231     player->is_switching = FALSE;
12232     player->is_dropping = FALSE;
12233     player->is_dropping_pressed = FALSE;
12234     player->drop_pressed_delay = 0;
12235
12236 #if 0
12237     /* should better be called here than above, but this breaks some tapes */
12238     ScrollPlayer(player, SCROLL_INIT);
12239 #endif
12240   }
12241   else
12242   {
12243     CheckGravityMovementWhenNotMoving(player);
12244
12245     player->is_moving = FALSE;
12246
12247     /* at this point, the player is allowed to move, but cannot move right now
12248        (e.g. because of something blocking the way) -- ensure that the player
12249        is also allowed to move in the next frame (in old versions before 3.1.1,
12250        the player was forced to wait again for eight frames before next try) */
12251
12252     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12253       player->move_delay = 0;   /* allow direct movement in the next frame */
12254   }
12255
12256   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12257     player->move_delay = player->move_delay_value;
12258
12259   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12260   {
12261     TestIfPlayerTouchesBadThing(jx, jy);
12262     TestIfPlayerTouchesCustomElement(jx, jy);
12263   }
12264
12265   if (!player->active)
12266     RemovePlayer(player);
12267
12268   return moved;
12269 }
12270
12271 void ScrollPlayer(struct PlayerInfo *player, int mode)
12272 {
12273   int jx = player->jx, jy = player->jy;
12274   int last_jx = player->last_jx, last_jy = player->last_jy;
12275   int move_stepsize = TILEX / player->move_delay_value;
12276
12277   if (!player->active)
12278     return;
12279
12280   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12281     return;
12282
12283   if (mode == SCROLL_INIT)
12284   {
12285     player->actual_frame_counter = FrameCounter;
12286     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12287
12288     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12289         Feld[last_jx][last_jy] == EL_EMPTY)
12290     {
12291       int last_field_block_delay = 0;   /* start with no blocking at all */
12292       int block_delay_adjustment = player->block_delay_adjustment;
12293
12294       /* if player blocks last field, add delay for exactly one move */
12295       if (player->block_last_field)
12296       {
12297         last_field_block_delay += player->move_delay_value;
12298
12299         /* when blocking enabled, prevent moving up despite gravity */
12300         if (player->gravity && player->MovDir == MV_UP)
12301           block_delay_adjustment = -1;
12302       }
12303
12304       /* add block delay adjustment (also possible when not blocking) */
12305       last_field_block_delay += block_delay_adjustment;
12306
12307       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12308       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12309     }
12310
12311     if (player->MovPos != 0)    /* player has not yet reached destination */
12312       return;
12313   }
12314   else if (!FrameReached(&player->actual_frame_counter, 1))
12315     return;
12316
12317   if (player->MovPos != 0)
12318   {
12319     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12320     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12321
12322     /* before DrawPlayer() to draw correct player graphic for this case */
12323     if (player->MovPos == 0)
12324       CheckGravityMovement(player);
12325   }
12326
12327   if (player->MovPos == 0)      /* player reached destination field */
12328   {
12329     if (player->move_delay_reset_counter > 0)
12330     {
12331       player->move_delay_reset_counter--;
12332
12333       if (player->move_delay_reset_counter == 0)
12334       {
12335         /* continue with normal speed after quickly moving through gate */
12336         HALVE_PLAYER_SPEED(player);
12337
12338         /* be able to make the next move without delay */
12339         player->move_delay = 0;
12340       }
12341     }
12342
12343     player->last_jx = jx;
12344     player->last_jy = jy;
12345
12346     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12347         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12348         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12349         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12350         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12351         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12352         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12353         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12354     {
12355       DrawPlayer(player);       /* needed here only to cleanup last field */
12356       RemovePlayer(player);
12357
12358       if (local_player->friends_still_needed == 0 ||
12359           IS_SP_ELEMENT(Feld[jx][jy]))
12360         PlayerWins(player);
12361     }
12362
12363     /* this breaks one level: "machine", level 000 */
12364     {
12365       int move_direction = player->MovDir;
12366       int enter_side = MV_DIR_OPPOSITE(move_direction);
12367       int leave_side = move_direction;
12368       int old_jx = last_jx;
12369       int old_jy = last_jy;
12370       int old_element = Feld[old_jx][old_jy];
12371       int new_element = Feld[jx][jy];
12372
12373       if (IS_CUSTOM_ELEMENT(old_element))
12374         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12375                                    CE_LEFT_BY_PLAYER,
12376                                    player->index_bit, leave_side);
12377
12378       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12379                                           CE_PLAYER_LEAVES_X,
12380                                           player->index_bit, leave_side);
12381
12382       if (IS_CUSTOM_ELEMENT(new_element))
12383         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12384                                    player->index_bit, enter_side);
12385
12386       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12387                                           CE_PLAYER_ENTERS_X,
12388                                           player->index_bit, enter_side);
12389
12390       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12391                                         CE_MOVE_OF_X, move_direction);
12392     }
12393
12394     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12395     {
12396       TestIfPlayerTouchesBadThing(jx, jy);
12397       TestIfPlayerTouchesCustomElement(jx, jy);
12398
12399       /* needed because pushed element has not yet reached its destination,
12400          so it would trigger a change event at its previous field location */
12401       if (!player->is_pushing)
12402         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12403
12404       if (!player->active)
12405         RemovePlayer(player);
12406     }
12407
12408     if (!local_player->LevelSolved && level.use_step_counter)
12409     {
12410       int i;
12411
12412       TimePlayed++;
12413
12414       if (TimeLeft > 0)
12415       {
12416         TimeLeft--;
12417
12418         if (TimeLeft <= 10 && setup.time_limit)
12419           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12420
12421         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12422
12423         DisplayGameControlValues();
12424
12425         if (!TimeLeft && setup.time_limit)
12426           for (i = 0; i < MAX_PLAYERS; i++)
12427             KillPlayer(&stored_player[i]);
12428       }
12429       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12430       {
12431         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12432
12433         DisplayGameControlValues();
12434       }
12435     }
12436
12437     if (tape.single_step && tape.recording && !tape.pausing &&
12438         !player->programmed_action)
12439       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12440
12441     if (!player->programmed_action)
12442       CheckSaveEngineSnapshot(player);
12443   }
12444 }
12445
12446 void ScrollScreen(struct PlayerInfo *player, int mode)
12447 {
12448   static unsigned int screen_frame_counter = 0;
12449
12450   if (mode == SCROLL_INIT)
12451   {
12452     /* set scrolling step size according to actual player's moving speed */
12453     ScrollStepSize = TILEX / player->move_delay_value;
12454
12455     screen_frame_counter = FrameCounter;
12456     ScreenMovDir = player->MovDir;
12457     ScreenMovPos = player->MovPos;
12458     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12459     return;
12460   }
12461   else if (!FrameReached(&screen_frame_counter, 1))
12462     return;
12463
12464   if (ScreenMovPos)
12465   {
12466     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12467     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12468     redraw_mask |= REDRAW_FIELD;
12469   }
12470   else
12471     ScreenMovDir = MV_NONE;
12472 }
12473
12474 void TestIfPlayerTouchesCustomElement(int x, int y)
12475 {
12476   static int xy[4][2] =
12477   {
12478     { 0, -1 },
12479     { -1, 0 },
12480     { +1, 0 },
12481     { 0, +1 }
12482   };
12483   static int trigger_sides[4][2] =
12484   {
12485     /* center side       border side */
12486     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12487     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12488     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12489     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12490   };
12491   static int touch_dir[4] =
12492   {
12493     MV_LEFT | MV_RIGHT,
12494     MV_UP   | MV_DOWN,
12495     MV_UP   | MV_DOWN,
12496     MV_LEFT | MV_RIGHT
12497   };
12498   int center_element = Feld[x][y];      /* should always be non-moving! */
12499   int i;
12500
12501   for (i = 0; i < NUM_DIRECTIONS; i++)
12502   {
12503     int xx = x + xy[i][0];
12504     int yy = y + xy[i][1];
12505     int center_side = trigger_sides[i][0];
12506     int border_side = trigger_sides[i][1];
12507     int border_element;
12508
12509     if (!IN_LEV_FIELD(xx, yy))
12510       continue;
12511
12512     if (IS_PLAYER(x, y))                /* player found at center element */
12513     {
12514       struct PlayerInfo *player = PLAYERINFO(x, y);
12515
12516       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12517         border_element = Feld[xx][yy];          /* may be moving! */
12518       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12519         border_element = Feld[xx][yy];
12520       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12521         border_element = MovingOrBlocked2Element(xx, yy);
12522       else
12523         continue;               /* center and border element do not touch */
12524
12525       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12526                                  player->index_bit, border_side);
12527       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12528                                           CE_PLAYER_TOUCHES_X,
12529                                           player->index_bit, border_side);
12530
12531       {
12532         /* use player element that is initially defined in the level playfield,
12533            not the player element that corresponds to the runtime player number
12534            (example: a level that contains EL_PLAYER_3 as the only player would
12535            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12536         int player_element = PLAYERINFO(x, y)->initial_element;
12537
12538         CheckElementChangeBySide(xx, yy, border_element, player_element,
12539                                  CE_TOUCHING_X, border_side);
12540       }
12541     }
12542     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12543     {
12544       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12545
12546       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12547       {
12548         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12549           continue;             /* center and border element do not touch */
12550       }
12551
12552       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12553                                  player->index_bit, center_side);
12554       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12555                                           CE_PLAYER_TOUCHES_X,
12556                                           player->index_bit, center_side);
12557
12558       {
12559         /* use player element that is initially defined in the level playfield,
12560            not the player element that corresponds to the runtime player number
12561            (example: a level that contains EL_PLAYER_3 as the only player would
12562            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12563         int player_element = PLAYERINFO(xx, yy)->initial_element;
12564
12565         CheckElementChangeBySide(x, y, center_element, player_element,
12566                                  CE_TOUCHING_X, center_side);
12567       }
12568
12569       break;
12570     }
12571   }
12572 }
12573
12574 void TestIfElementTouchesCustomElement(int x, int y)
12575 {
12576   static int xy[4][2] =
12577   {
12578     { 0, -1 },
12579     { -1, 0 },
12580     { +1, 0 },
12581     { 0, +1 }
12582   };
12583   static int trigger_sides[4][2] =
12584   {
12585     /* center side      border side */
12586     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12587     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12588     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12589     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12590   };
12591   static int touch_dir[4] =
12592   {
12593     MV_LEFT | MV_RIGHT,
12594     MV_UP   | MV_DOWN,
12595     MV_UP   | MV_DOWN,
12596     MV_LEFT | MV_RIGHT
12597   };
12598   boolean change_center_element = FALSE;
12599   int center_element = Feld[x][y];      /* should always be non-moving! */
12600   int border_element_old[NUM_DIRECTIONS];
12601   int i;
12602
12603   for (i = 0; i < NUM_DIRECTIONS; i++)
12604   {
12605     int xx = x + xy[i][0];
12606     int yy = y + xy[i][1];
12607     int border_element;
12608
12609     border_element_old[i] = -1;
12610
12611     if (!IN_LEV_FIELD(xx, yy))
12612       continue;
12613
12614     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12615       border_element = Feld[xx][yy];    /* may be moving! */
12616     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12617       border_element = Feld[xx][yy];
12618     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12619       border_element = MovingOrBlocked2Element(xx, yy);
12620     else
12621       continue;                 /* center and border element do not touch */
12622
12623     border_element_old[i] = border_element;
12624   }
12625
12626   for (i = 0; i < NUM_DIRECTIONS; i++)
12627   {
12628     int xx = x + xy[i][0];
12629     int yy = y + xy[i][1];
12630     int center_side = trigger_sides[i][0];
12631     int border_element = border_element_old[i];
12632
12633     if (border_element == -1)
12634       continue;
12635
12636     /* check for change of border element */
12637     CheckElementChangeBySide(xx, yy, border_element, center_element,
12638                              CE_TOUCHING_X, center_side);
12639
12640     /* (center element cannot be player, so we dont have to check this here) */
12641   }
12642
12643   for (i = 0; i < NUM_DIRECTIONS; i++)
12644   {
12645     int xx = x + xy[i][0];
12646     int yy = y + xy[i][1];
12647     int border_side = trigger_sides[i][1];
12648     int border_element = border_element_old[i];
12649
12650     if (border_element == -1)
12651       continue;
12652
12653     /* check for change of center element (but change it only once) */
12654     if (!change_center_element)
12655       change_center_element =
12656         CheckElementChangeBySide(x, y, center_element, border_element,
12657                                  CE_TOUCHING_X, border_side);
12658
12659     if (IS_PLAYER(xx, yy))
12660     {
12661       /* use player element that is initially defined in the level playfield,
12662          not the player element that corresponds to the runtime player number
12663          (example: a level that contains EL_PLAYER_3 as the only player would
12664          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12665       int player_element = PLAYERINFO(xx, yy)->initial_element;
12666
12667       CheckElementChangeBySide(x, y, center_element, player_element,
12668                                CE_TOUCHING_X, border_side);
12669     }
12670   }
12671 }
12672
12673 void TestIfElementHitsCustomElement(int x, int y, int direction)
12674 {
12675   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12676   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12677   int hitx = x + dx, hity = y + dy;
12678   int hitting_element = Feld[x][y];
12679   int touched_element;
12680
12681   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12682     return;
12683
12684   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12685                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12686
12687   if (IN_LEV_FIELD(hitx, hity))
12688   {
12689     int opposite_direction = MV_DIR_OPPOSITE(direction);
12690     int hitting_side = direction;
12691     int touched_side = opposite_direction;
12692     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12693                           MovDir[hitx][hity] != direction ||
12694                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12695
12696     object_hit = TRUE;
12697
12698     if (object_hit)
12699     {
12700       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12701                                CE_HITTING_X, touched_side);
12702
12703       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12704                                CE_HIT_BY_X, hitting_side);
12705
12706       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12707                                CE_HIT_BY_SOMETHING, opposite_direction);
12708
12709       if (IS_PLAYER(hitx, hity))
12710       {
12711         /* use player element that is initially defined in the level playfield,
12712            not the player element that corresponds to the runtime player number
12713            (example: a level that contains EL_PLAYER_3 as the only player would
12714            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12715         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12716
12717         CheckElementChangeBySide(x, y, hitting_element, player_element,
12718                                  CE_HITTING_X, touched_side);
12719       }
12720     }
12721   }
12722
12723   /* "hitting something" is also true when hitting the playfield border */
12724   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12725                            CE_HITTING_SOMETHING, direction);
12726 }
12727
12728 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12729 {
12730   int i, kill_x = -1, kill_y = -1;
12731
12732   int bad_element = -1;
12733   static int test_xy[4][2] =
12734   {
12735     { 0, -1 },
12736     { -1, 0 },
12737     { +1, 0 },
12738     { 0, +1 }
12739   };
12740   static int test_dir[4] =
12741   {
12742     MV_UP,
12743     MV_LEFT,
12744     MV_RIGHT,
12745     MV_DOWN
12746   };
12747
12748   for (i = 0; i < NUM_DIRECTIONS; i++)
12749   {
12750     int test_x, test_y, test_move_dir, test_element;
12751
12752     test_x = good_x + test_xy[i][0];
12753     test_y = good_y + test_xy[i][1];
12754
12755     if (!IN_LEV_FIELD(test_x, test_y))
12756       continue;
12757
12758     test_move_dir =
12759       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12760
12761     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12762
12763     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12764        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12765     */
12766     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12767         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12768     {
12769       kill_x = test_x;
12770       kill_y = test_y;
12771       bad_element = test_element;
12772
12773       break;
12774     }
12775   }
12776
12777   if (kill_x != -1 || kill_y != -1)
12778   {
12779     if (IS_PLAYER(good_x, good_y))
12780     {
12781       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12782
12783       if (player->shield_deadly_time_left > 0 &&
12784           !IS_INDESTRUCTIBLE(bad_element))
12785         Bang(kill_x, kill_y);
12786       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12787         KillPlayer(player);
12788     }
12789     else
12790       Bang(good_x, good_y);
12791   }
12792 }
12793
12794 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12795 {
12796   int i, kill_x = -1, kill_y = -1;
12797   int bad_element = Feld[bad_x][bad_y];
12798   static int test_xy[4][2] =
12799   {
12800     { 0, -1 },
12801     { -1, 0 },
12802     { +1, 0 },
12803     { 0, +1 }
12804   };
12805   static int touch_dir[4] =
12806   {
12807     MV_LEFT | MV_RIGHT,
12808     MV_UP   | MV_DOWN,
12809     MV_UP   | MV_DOWN,
12810     MV_LEFT | MV_RIGHT
12811   };
12812   static int test_dir[4] =
12813   {
12814     MV_UP,
12815     MV_LEFT,
12816     MV_RIGHT,
12817     MV_DOWN
12818   };
12819
12820   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12821     return;
12822
12823   for (i = 0; i < NUM_DIRECTIONS; i++)
12824   {
12825     int test_x, test_y, test_move_dir, test_element;
12826
12827     test_x = bad_x + test_xy[i][0];
12828     test_y = bad_y + test_xy[i][1];
12829
12830     if (!IN_LEV_FIELD(test_x, test_y))
12831       continue;
12832
12833     test_move_dir =
12834       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12835
12836     test_element = Feld[test_x][test_y];
12837
12838     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12839        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12840     */
12841     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12842         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12843     {
12844       /* good thing is player or penguin that does not move away */
12845       if (IS_PLAYER(test_x, test_y))
12846       {
12847         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12848
12849         if (bad_element == EL_ROBOT && player->is_moving)
12850           continue;     /* robot does not kill player if he is moving */
12851
12852         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12853         {
12854           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12855             continue;           /* center and border element do not touch */
12856         }
12857
12858         kill_x = test_x;
12859         kill_y = test_y;
12860
12861         break;
12862       }
12863       else if (test_element == EL_PENGUIN)
12864       {
12865         kill_x = test_x;
12866         kill_y = test_y;
12867
12868         break;
12869       }
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 TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12891 {
12892   int bad_element = Feld[bad_x][bad_y];
12893   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12894   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12895   int test_x = bad_x + dx, test_y = bad_y + dy;
12896   int test_move_dir, test_element;
12897   int kill_x = -1, kill_y = -1;
12898
12899   if (!IN_LEV_FIELD(test_x, test_y))
12900     return;
12901
12902   test_move_dir =
12903     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12904
12905   test_element = Feld[test_x][test_y];
12906
12907   if (test_move_dir != bad_move_dir)
12908   {
12909     /* good thing can be player or penguin that does not move away */
12910     if (IS_PLAYER(test_x, test_y))
12911     {
12912       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12913
12914       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12915          player as being hit when he is moving towards the bad thing, because
12916          the "get hit by" condition would be lost after the player stops) */
12917       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12918         return;         /* player moves away from bad thing */
12919
12920       kill_x = test_x;
12921       kill_y = test_y;
12922     }
12923     else if (test_element == EL_PENGUIN)
12924     {
12925       kill_x = test_x;
12926       kill_y = test_y;
12927     }
12928   }
12929
12930   if (kill_x != -1 || kill_y != -1)
12931   {
12932     if (IS_PLAYER(kill_x, kill_y))
12933     {
12934       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12935
12936       if (player->shield_deadly_time_left > 0 &&
12937           !IS_INDESTRUCTIBLE(bad_element))
12938         Bang(bad_x, bad_y);
12939       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12940         KillPlayer(player);
12941     }
12942     else
12943       Bang(kill_x, kill_y);
12944   }
12945 }
12946
12947 void TestIfPlayerTouchesBadThing(int x, int y)
12948 {
12949   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12950 }
12951
12952 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12953 {
12954   TestIfGoodThingHitsBadThing(x, y, move_dir);
12955 }
12956
12957 void TestIfBadThingTouchesPlayer(int x, int y)
12958 {
12959   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12960 }
12961
12962 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12963 {
12964   TestIfBadThingHitsGoodThing(x, y, move_dir);
12965 }
12966
12967 void TestIfFriendTouchesBadThing(int x, int y)
12968 {
12969   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12970 }
12971
12972 void TestIfBadThingTouchesFriend(int x, int y)
12973 {
12974   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12975 }
12976
12977 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12978 {
12979   int i, kill_x = bad_x, kill_y = bad_y;
12980   static int xy[4][2] =
12981   {
12982     { 0, -1 },
12983     { -1, 0 },
12984     { +1, 0 },
12985     { 0, +1 }
12986   };
12987
12988   for (i = 0; i < NUM_DIRECTIONS; i++)
12989   {
12990     int x, y, element;
12991
12992     x = bad_x + xy[i][0];
12993     y = bad_y + xy[i][1];
12994     if (!IN_LEV_FIELD(x, y))
12995       continue;
12996
12997     element = Feld[x][y];
12998     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12999         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13000     {
13001       kill_x = x;
13002       kill_y = y;
13003       break;
13004     }
13005   }
13006
13007   if (kill_x != bad_x || kill_y != bad_y)
13008     Bang(bad_x, bad_y);
13009 }
13010
13011 void KillPlayer(struct PlayerInfo *player)
13012 {
13013   int jx = player->jx, jy = player->jy;
13014
13015   if (!player->active)
13016     return;
13017
13018 #if 0
13019   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13020          player->killed, player->active, player->reanimated);
13021 #endif
13022
13023   /* the following code was introduced to prevent an infinite loop when calling
13024      -> Bang()
13025      -> CheckTriggeredElementChangeExt()
13026      -> ExecuteCustomElementAction()
13027      -> KillPlayer()
13028      -> (infinitely repeating the above sequence of function calls)
13029      which occurs when killing the player while having a CE with the setting
13030      "kill player X when explosion of <player X>"; the solution using a new
13031      field "player->killed" was chosen for backwards compatibility, although
13032      clever use of the fields "player->active" etc. would probably also work */
13033 #if 1
13034   if (player->killed)
13035     return;
13036 #endif
13037
13038   player->killed = TRUE;
13039
13040   /* remove accessible field at the player's position */
13041   Feld[jx][jy] = EL_EMPTY;
13042
13043   /* deactivate shield (else Bang()/Explode() would not work right) */
13044   player->shield_normal_time_left = 0;
13045   player->shield_deadly_time_left = 0;
13046
13047 #if 0
13048   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13049          player->killed, player->active, player->reanimated);
13050 #endif
13051
13052   Bang(jx, jy);
13053
13054 #if 0
13055   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13056          player->killed, player->active, player->reanimated);
13057 #endif
13058
13059   if (player->reanimated)       /* killed player may have been reanimated */
13060     player->killed = player->reanimated = FALSE;
13061   else
13062     BuryPlayer(player);
13063 }
13064
13065 static void KillPlayerUnlessEnemyProtected(int x, int y)
13066 {
13067   if (!PLAYER_ENEMY_PROTECTED(x, y))
13068     KillPlayer(PLAYERINFO(x, y));
13069 }
13070
13071 static void KillPlayerUnlessExplosionProtected(int x, int y)
13072 {
13073   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13074     KillPlayer(PLAYERINFO(x, y));
13075 }
13076
13077 void BuryPlayer(struct PlayerInfo *player)
13078 {
13079   int jx = player->jx, jy = player->jy;
13080
13081   if (!player->active)
13082     return;
13083
13084   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13085   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13086
13087   player->GameOver = TRUE;
13088   RemovePlayer(player);
13089 }
13090
13091 void RemovePlayer(struct PlayerInfo *player)
13092 {
13093   int jx = player->jx, jy = player->jy;
13094   int i, found = FALSE;
13095
13096   player->present = FALSE;
13097   player->active = FALSE;
13098
13099   if (!ExplodeField[jx][jy])
13100     StorePlayer[jx][jy] = 0;
13101
13102   if (player->is_moving)
13103     TEST_DrawLevelField(player->last_jx, player->last_jy);
13104
13105   for (i = 0; i < MAX_PLAYERS; i++)
13106     if (stored_player[i].active)
13107       found = TRUE;
13108
13109   if (!found)
13110     AllPlayersGone = TRUE;
13111
13112   ExitX = ZX = jx;
13113   ExitY = ZY = jy;
13114 }
13115
13116 static void setFieldForSnapping(int x, int y, int element, int direction)
13117 {
13118   struct ElementInfo *ei = &element_info[element];
13119   int direction_bit = MV_DIR_TO_BIT(direction);
13120   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13121   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13122                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13123
13124   Feld[x][y] = EL_ELEMENT_SNAPPING;
13125   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13126
13127   ResetGfxAnimation(x, y);
13128
13129   GfxElement[x][y] = element;
13130   GfxAction[x][y] = action;
13131   GfxDir[x][y] = direction;
13132   GfxFrame[x][y] = -1;
13133 }
13134
13135 /*
13136   =============================================================================
13137   checkDiagonalPushing()
13138   -----------------------------------------------------------------------------
13139   check if diagonal input device direction results in pushing of object
13140   (by checking if the alternative direction is walkable, diggable, ...)
13141   =============================================================================
13142 */
13143
13144 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13145                                     int x, int y, int real_dx, int real_dy)
13146 {
13147   int jx, jy, dx, dy, xx, yy;
13148
13149   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13150     return TRUE;
13151
13152   /* diagonal direction: check alternative direction */
13153   jx = player->jx;
13154   jy = player->jy;
13155   dx = x - jx;
13156   dy = y - jy;
13157   xx = jx + (dx == 0 ? real_dx : 0);
13158   yy = jy + (dy == 0 ? real_dy : 0);
13159
13160   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13161 }
13162
13163 /*
13164   =============================================================================
13165   DigField()
13166   -----------------------------------------------------------------------------
13167   x, y:                 field next to player (non-diagonal) to try to dig to
13168   real_dx, real_dy:     direction as read from input device (can be diagonal)
13169   =============================================================================
13170 */
13171
13172 static int DigField(struct PlayerInfo *player,
13173                     int oldx, int oldy, int x, int y,
13174                     int real_dx, int real_dy, int mode)
13175 {
13176   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13177   boolean player_was_pushing = player->is_pushing;
13178   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13179   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13180   int jx = oldx, jy = oldy;
13181   int dx = x - jx, dy = y - jy;
13182   int nextx = x + dx, nexty = y + dy;
13183   int move_direction = (dx == -1 ? MV_LEFT  :
13184                         dx == +1 ? MV_RIGHT :
13185                         dy == -1 ? MV_UP    :
13186                         dy == +1 ? MV_DOWN  : MV_NONE);
13187   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13188   int dig_side = MV_DIR_OPPOSITE(move_direction);
13189   int old_element = Feld[jx][jy];
13190   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13191   int collect_count;
13192
13193   if (is_player)                /* function can also be called by EL_PENGUIN */
13194   {
13195     if (player->MovPos == 0)
13196     {
13197       player->is_digging = FALSE;
13198       player->is_collecting = FALSE;
13199     }
13200
13201     if (player->MovPos == 0)    /* last pushing move finished */
13202       player->is_pushing = FALSE;
13203
13204     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13205     {
13206       player->is_switching = FALSE;
13207       player->push_delay = -1;
13208
13209       return MP_NO_ACTION;
13210     }
13211   }
13212
13213   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13214     old_element = Back[jx][jy];
13215
13216   /* in case of element dropped at player position, check background */
13217   else if (Back[jx][jy] != EL_EMPTY &&
13218            game.engine_version >= VERSION_IDENT(2,2,0,0))
13219     old_element = Back[jx][jy];
13220
13221   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13222     return MP_NO_ACTION;        /* field has no opening in this direction */
13223
13224   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13225     return MP_NO_ACTION;        /* field has no opening in this direction */
13226
13227   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13228   {
13229     SplashAcid(x, y);
13230
13231     Feld[jx][jy] = player->artwork_element;
13232     InitMovingField(jx, jy, MV_DOWN);
13233     Store[jx][jy] = EL_ACID;
13234     ContinueMoving(jx, jy);
13235     BuryPlayer(player);
13236
13237     return MP_DONT_RUN_INTO;
13238   }
13239
13240   if (player_can_move && DONT_RUN_INTO(element))
13241   {
13242     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13243
13244     return MP_DONT_RUN_INTO;
13245   }
13246
13247   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13248     return MP_NO_ACTION;
13249
13250   collect_count = element_info[element].collect_count_initial;
13251
13252   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13253     return MP_NO_ACTION;
13254
13255   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13256     player_can_move = player_can_move_or_snap;
13257
13258   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13259       game.engine_version >= VERSION_IDENT(2,2,0,0))
13260   {
13261     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13262                                player->index_bit, dig_side);
13263     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13264                                         player->index_bit, dig_side);
13265
13266     if (element == EL_DC_LANDMINE)
13267       Bang(x, y);
13268
13269     if (Feld[x][y] != element)          /* field changed by snapping */
13270       return MP_ACTION;
13271
13272     return MP_NO_ACTION;
13273   }
13274
13275   if (player->gravity && is_player && !player->is_auto_moving &&
13276       canFallDown(player) && move_direction != MV_DOWN &&
13277       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13278     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13279
13280   if (player_can_move &&
13281       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13282   {
13283     int sound_element = SND_ELEMENT(element);
13284     int sound_action = ACTION_WALKING;
13285
13286     if (IS_RND_GATE(element))
13287     {
13288       if (!player->key[RND_GATE_NR(element)])
13289         return MP_NO_ACTION;
13290     }
13291     else if (IS_RND_GATE_GRAY(element))
13292     {
13293       if (!player->key[RND_GATE_GRAY_NR(element)])
13294         return MP_NO_ACTION;
13295     }
13296     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13297     {
13298       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13299         return MP_NO_ACTION;
13300     }
13301     else if (element == EL_EXIT_OPEN ||
13302              element == EL_EM_EXIT_OPEN ||
13303              element == EL_EM_EXIT_OPENING ||
13304              element == EL_STEEL_EXIT_OPEN ||
13305              element == EL_EM_STEEL_EXIT_OPEN ||
13306              element == EL_EM_STEEL_EXIT_OPENING ||
13307              element == EL_SP_EXIT_OPEN ||
13308              element == EL_SP_EXIT_OPENING)
13309     {
13310       sound_action = ACTION_PASSING;    /* player is passing exit */
13311     }
13312     else if (element == EL_EMPTY)
13313     {
13314       sound_action = ACTION_MOVING;             /* nothing to walk on */
13315     }
13316
13317     /* play sound from background or player, whatever is available */
13318     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13319       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13320     else
13321       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13322   }
13323   else if (player_can_move &&
13324            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13325   {
13326     if (!ACCESS_FROM(element, opposite_direction))
13327       return MP_NO_ACTION;      /* field not accessible from this direction */
13328
13329     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13330       return MP_NO_ACTION;
13331
13332     if (IS_EM_GATE(element))
13333     {
13334       if (!player->key[EM_GATE_NR(element)])
13335         return MP_NO_ACTION;
13336     }
13337     else if (IS_EM_GATE_GRAY(element))
13338     {
13339       if (!player->key[EM_GATE_GRAY_NR(element)])
13340         return MP_NO_ACTION;
13341     }
13342     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13343     {
13344       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13345         return MP_NO_ACTION;
13346     }
13347     else if (IS_EMC_GATE(element))
13348     {
13349       if (!player->key[EMC_GATE_NR(element)])
13350         return MP_NO_ACTION;
13351     }
13352     else if (IS_EMC_GATE_GRAY(element))
13353     {
13354       if (!player->key[EMC_GATE_GRAY_NR(element)])
13355         return MP_NO_ACTION;
13356     }
13357     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13358     {
13359       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13360         return MP_NO_ACTION;
13361     }
13362     else if (element == EL_DC_GATE_WHITE ||
13363              element == EL_DC_GATE_WHITE_GRAY ||
13364              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13365     {
13366       if (player->num_white_keys == 0)
13367         return MP_NO_ACTION;
13368
13369       player->num_white_keys--;
13370     }
13371     else if (IS_SP_PORT(element))
13372     {
13373       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13374           element == EL_SP_GRAVITY_PORT_RIGHT ||
13375           element == EL_SP_GRAVITY_PORT_UP ||
13376           element == EL_SP_GRAVITY_PORT_DOWN)
13377         player->gravity = !player->gravity;
13378       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13379                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13380                element == EL_SP_GRAVITY_ON_PORT_UP ||
13381                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13382         player->gravity = TRUE;
13383       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13384                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13385                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13386                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13387         player->gravity = FALSE;
13388     }
13389
13390     /* automatically move to the next field with double speed */
13391     player->programmed_action = move_direction;
13392
13393     if (player->move_delay_reset_counter == 0)
13394     {
13395       player->move_delay_reset_counter = 2;     /* two double speed steps */
13396
13397       DOUBLE_PLAYER_SPEED(player);
13398     }
13399
13400     PlayLevelSoundAction(x, y, ACTION_PASSING);
13401   }
13402   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13403   {
13404     RemoveField(x, y);
13405
13406     if (mode != DF_SNAP)
13407     {
13408       GfxElement[x][y] = GFX_ELEMENT(element);
13409       player->is_digging = TRUE;
13410     }
13411
13412     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13413
13414     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13415                                         player->index_bit, dig_side);
13416
13417     if (mode == DF_SNAP)
13418     {
13419       if (level.block_snap_field)
13420         setFieldForSnapping(x, y, element, move_direction);
13421       else
13422         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13423
13424       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13425                                           player->index_bit, dig_side);
13426     }
13427   }
13428   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13429   {
13430     RemoveField(x, y);
13431
13432     if (is_player && mode != DF_SNAP)
13433     {
13434       GfxElement[x][y] = element;
13435       player->is_collecting = TRUE;
13436     }
13437
13438     if (element == EL_SPEED_PILL)
13439     {
13440       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13441     }
13442     else if (element == EL_EXTRA_TIME && level.time > 0)
13443     {
13444       TimeLeft += level.extra_time;
13445
13446       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13447
13448       DisplayGameControlValues();
13449     }
13450     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13451     {
13452       player->shield_normal_time_left += level.shield_normal_time;
13453       if (element == EL_SHIELD_DEADLY)
13454         player->shield_deadly_time_left += level.shield_deadly_time;
13455     }
13456     else if (element == EL_DYNAMITE ||
13457              element == EL_EM_DYNAMITE ||
13458              element == EL_SP_DISK_RED)
13459     {
13460       if (player->inventory_size < MAX_INVENTORY_SIZE)
13461         player->inventory_element[player->inventory_size++] = element;
13462
13463       DrawGameDoorValues();
13464     }
13465     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13466     {
13467       player->dynabomb_count++;
13468       player->dynabombs_left++;
13469     }
13470     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13471     {
13472       player->dynabomb_size++;
13473     }
13474     else if (element == EL_DYNABOMB_INCREASE_POWER)
13475     {
13476       player->dynabomb_xl = TRUE;
13477     }
13478     else if (IS_KEY(element))
13479     {
13480       player->key[KEY_NR(element)] = TRUE;
13481
13482       DrawGameDoorValues();
13483     }
13484     else if (element == EL_DC_KEY_WHITE)
13485     {
13486       player->num_white_keys++;
13487
13488       /* display white keys? */
13489       /* DrawGameDoorValues(); */
13490     }
13491     else if (IS_ENVELOPE(element))
13492     {
13493       player->show_envelope = element;
13494     }
13495     else if (element == EL_EMC_LENSES)
13496     {
13497       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13498
13499       RedrawAllInvisibleElementsForLenses();
13500     }
13501     else if (element == EL_EMC_MAGNIFIER)
13502     {
13503       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13504
13505       RedrawAllInvisibleElementsForMagnifier();
13506     }
13507     else if (IS_DROPPABLE(element) ||
13508              IS_THROWABLE(element))     /* can be collected and dropped */
13509     {
13510       int i;
13511
13512       if (collect_count == 0)
13513         player->inventory_infinite_element = element;
13514       else
13515         for (i = 0; i < collect_count; i++)
13516           if (player->inventory_size < MAX_INVENTORY_SIZE)
13517             player->inventory_element[player->inventory_size++] = element;
13518
13519       DrawGameDoorValues();
13520     }
13521     else if (collect_count > 0)
13522     {
13523       local_player->gems_still_needed -= collect_count;
13524       if (local_player->gems_still_needed < 0)
13525         local_player->gems_still_needed = 0;
13526
13527       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13528
13529       DisplayGameControlValues();
13530     }
13531
13532     RaiseScoreElement(element);
13533     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13534
13535     if (is_player)
13536       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13537                                           player->index_bit, dig_side);
13538
13539     if (mode == DF_SNAP)
13540     {
13541       if (level.block_snap_field)
13542         setFieldForSnapping(x, y, element, move_direction);
13543       else
13544         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13545
13546       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13547                                           player->index_bit, dig_side);
13548     }
13549   }
13550   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13551   {
13552     if (mode == DF_SNAP && element != EL_BD_ROCK)
13553       return MP_NO_ACTION;
13554
13555     if (CAN_FALL(element) && dy)
13556       return MP_NO_ACTION;
13557
13558     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13559         !(element == EL_SPRING && level.use_spring_bug))
13560       return MP_NO_ACTION;
13561
13562     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13563         ((move_direction & MV_VERTICAL &&
13564           ((element_info[element].move_pattern & MV_LEFT &&
13565             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13566            (element_info[element].move_pattern & MV_RIGHT &&
13567             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13568          (move_direction & MV_HORIZONTAL &&
13569           ((element_info[element].move_pattern & MV_UP &&
13570             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13571            (element_info[element].move_pattern & MV_DOWN &&
13572             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13573       return MP_NO_ACTION;
13574
13575     /* do not push elements already moving away faster than player */
13576     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13577         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13578       return MP_NO_ACTION;
13579
13580     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13581     {
13582       if (player->push_delay_value == -1 || !player_was_pushing)
13583         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13584     }
13585     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13586     {
13587       if (player->push_delay_value == -1)
13588         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13589     }
13590     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13591     {
13592       if (!player->is_pushing)
13593         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13594     }
13595
13596     player->is_pushing = TRUE;
13597     player->is_active = TRUE;
13598
13599     if (!(IN_LEV_FIELD(nextx, nexty) &&
13600           (IS_FREE(nextx, nexty) ||
13601            (IS_SB_ELEMENT(element) &&
13602             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13603            (IS_CUSTOM_ELEMENT(element) &&
13604             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13605       return MP_NO_ACTION;
13606
13607     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13608       return MP_NO_ACTION;
13609
13610     if (player->push_delay == -1)       /* new pushing; restart delay */
13611       player->push_delay = 0;
13612
13613     if (player->push_delay < player->push_delay_value &&
13614         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13615         element != EL_SPRING && element != EL_BALLOON)
13616     {
13617       /* make sure that there is no move delay before next try to push */
13618       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13619         player->move_delay = 0;
13620
13621       return MP_NO_ACTION;
13622     }
13623
13624     if (IS_CUSTOM_ELEMENT(element) &&
13625         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13626     {
13627       if (!DigFieldByCE(nextx, nexty, element))
13628         return MP_NO_ACTION;
13629     }
13630
13631     if (IS_SB_ELEMENT(element))
13632     {
13633       if (element == EL_SOKOBAN_FIELD_FULL)
13634       {
13635         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13636         local_player->sokobanfields_still_needed++;
13637       }
13638
13639       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13640       {
13641         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13642         local_player->sokobanfields_still_needed--;
13643       }
13644
13645       Feld[x][y] = EL_SOKOBAN_OBJECT;
13646
13647       if (Back[x][y] == Back[nextx][nexty])
13648         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13649       else if (Back[x][y] != 0)
13650         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13651                                     ACTION_EMPTYING);
13652       else
13653         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13654                                     ACTION_FILLING);
13655
13656       if (local_player->sokobanfields_still_needed == 0 &&
13657           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13658       {
13659         PlayerWins(player);
13660
13661         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13662       }
13663     }
13664     else
13665       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13666
13667     InitMovingField(x, y, move_direction);
13668     GfxAction[x][y] = ACTION_PUSHING;
13669
13670     if (mode == DF_SNAP)
13671       ContinueMoving(x, y);
13672     else
13673       MovPos[x][y] = (dx != 0 ? dx : dy);
13674
13675     Pushed[x][y] = TRUE;
13676     Pushed[nextx][nexty] = TRUE;
13677
13678     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13679       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13680     else
13681       player->push_delay_value = -1;    /* get new value later */
13682
13683     /* check for element change _after_ element has been pushed */
13684     if (game.use_change_when_pushing_bug)
13685     {
13686       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13687                                  player->index_bit, dig_side);
13688       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13689                                           player->index_bit, dig_side);
13690     }
13691   }
13692   else if (IS_SWITCHABLE(element))
13693   {
13694     if (PLAYER_SWITCHING(player, x, y))
13695     {
13696       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13697                                           player->index_bit, dig_side);
13698
13699       return MP_ACTION;
13700     }
13701
13702     player->is_switching = TRUE;
13703     player->switch_x = x;
13704     player->switch_y = y;
13705
13706     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13707
13708     if (element == EL_ROBOT_WHEEL)
13709     {
13710       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13711       ZX = x;
13712       ZY = y;
13713
13714       game.robot_wheel_active = TRUE;
13715
13716       TEST_DrawLevelField(x, y);
13717     }
13718     else if (element == EL_SP_TERMINAL)
13719     {
13720       int xx, yy;
13721
13722       SCAN_PLAYFIELD(xx, yy)
13723       {
13724         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13725           Bang(xx, yy);
13726         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13727           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13728       }
13729     }
13730     else if (IS_BELT_SWITCH(element))
13731     {
13732       ToggleBeltSwitch(x, y);
13733     }
13734     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13735              element == EL_SWITCHGATE_SWITCH_DOWN ||
13736              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13737              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13738     {
13739       ToggleSwitchgateSwitch(x, y);
13740     }
13741     else if (element == EL_LIGHT_SWITCH ||
13742              element == EL_LIGHT_SWITCH_ACTIVE)
13743     {
13744       ToggleLightSwitch(x, y);
13745     }
13746     else if (element == EL_TIMEGATE_SWITCH ||
13747              element == EL_DC_TIMEGATE_SWITCH)
13748     {
13749       ActivateTimegateSwitch(x, y);
13750     }
13751     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13752              element == EL_BALLOON_SWITCH_RIGHT ||
13753              element == EL_BALLOON_SWITCH_UP    ||
13754              element == EL_BALLOON_SWITCH_DOWN  ||
13755              element == EL_BALLOON_SWITCH_NONE  ||
13756              element == EL_BALLOON_SWITCH_ANY)
13757     {
13758       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13759                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13760                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13761                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13762                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13763                              move_direction);
13764     }
13765     else if (element == EL_LAMP)
13766     {
13767       Feld[x][y] = EL_LAMP_ACTIVE;
13768       local_player->lights_still_needed--;
13769
13770       ResetGfxAnimation(x, y);
13771       TEST_DrawLevelField(x, y);
13772     }
13773     else if (element == EL_TIME_ORB_FULL)
13774     {
13775       Feld[x][y] = EL_TIME_ORB_EMPTY;
13776
13777       if (level.time > 0 || level.use_time_orb_bug)
13778       {
13779         TimeLeft += level.time_orb_time;
13780         game.no_time_limit = FALSE;
13781
13782         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13783
13784         DisplayGameControlValues();
13785       }
13786
13787       ResetGfxAnimation(x, y);
13788       TEST_DrawLevelField(x, y);
13789     }
13790     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13791              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13792     {
13793       int xx, yy;
13794
13795       game.ball_state = !game.ball_state;
13796
13797       SCAN_PLAYFIELD(xx, yy)
13798       {
13799         int e = Feld[xx][yy];
13800
13801         if (game.ball_state)
13802         {
13803           if (e == EL_EMC_MAGIC_BALL)
13804             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13805           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13806             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13807         }
13808         else
13809         {
13810           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13811             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13812           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13813             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13814         }
13815       }
13816     }
13817
13818     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13819                                         player->index_bit, dig_side);
13820
13821     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13822                                         player->index_bit, dig_side);
13823
13824     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13825                                         player->index_bit, dig_side);
13826
13827     return MP_ACTION;
13828   }
13829   else
13830   {
13831     if (!PLAYER_SWITCHING(player, x, y))
13832     {
13833       player->is_switching = TRUE;
13834       player->switch_x = x;
13835       player->switch_y = y;
13836
13837       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13838                                  player->index_bit, dig_side);
13839       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13840                                           player->index_bit, dig_side);
13841
13842       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13843                                  player->index_bit, dig_side);
13844       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13845                                           player->index_bit, dig_side);
13846     }
13847
13848     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13849                                player->index_bit, dig_side);
13850     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13851                                         player->index_bit, dig_side);
13852
13853     return MP_NO_ACTION;
13854   }
13855
13856   player->push_delay = -1;
13857
13858   if (is_player)                /* function can also be called by EL_PENGUIN */
13859   {
13860     if (Feld[x][y] != element)          /* really digged/collected something */
13861     {
13862       player->is_collecting = !player->is_digging;
13863       player->is_active = TRUE;
13864     }
13865   }
13866
13867   return MP_MOVING;
13868 }
13869
13870 static boolean DigFieldByCE(int x, int y, int digging_element)
13871 {
13872   int element = Feld[x][y];
13873
13874   if (!IS_FREE(x, y))
13875   {
13876     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13877                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13878                   ACTION_BREAKING);
13879
13880     /* no element can dig solid indestructible elements */
13881     if (IS_INDESTRUCTIBLE(element) &&
13882         !IS_DIGGABLE(element) &&
13883         !IS_COLLECTIBLE(element))
13884       return FALSE;
13885
13886     if (AmoebaNr[x][y] &&
13887         (element == EL_AMOEBA_FULL ||
13888          element == EL_BD_AMOEBA ||
13889          element == EL_AMOEBA_GROWING))
13890     {
13891       AmoebaCnt[AmoebaNr[x][y]]--;
13892       AmoebaCnt2[AmoebaNr[x][y]]--;
13893     }
13894
13895     if (IS_MOVING(x, y))
13896       RemoveMovingField(x, y);
13897     else
13898     {
13899       RemoveField(x, y);
13900       TEST_DrawLevelField(x, y);
13901     }
13902
13903     /* if digged element was about to explode, prevent the explosion */
13904     ExplodeField[x][y] = EX_TYPE_NONE;
13905
13906     PlayLevelSoundAction(x, y, action);
13907   }
13908
13909   Store[x][y] = EL_EMPTY;
13910
13911   /* this makes it possible to leave the removed element again */
13912   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13913     Store[x][y] = element;
13914
13915   return TRUE;
13916 }
13917
13918 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13919 {
13920   int jx = player->jx, jy = player->jy;
13921   int x = jx + dx, y = jy + dy;
13922   int snap_direction = (dx == -1 ? MV_LEFT  :
13923                         dx == +1 ? MV_RIGHT :
13924                         dy == -1 ? MV_UP    :
13925                         dy == +1 ? MV_DOWN  : MV_NONE);
13926   boolean can_continue_snapping = (level.continuous_snapping &&
13927                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13928
13929   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13930     return FALSE;
13931
13932   if (!player->active || !IN_LEV_FIELD(x, y))
13933     return FALSE;
13934
13935   if (dx && dy)
13936     return FALSE;
13937
13938   if (!dx && !dy)
13939   {
13940     if (player->MovPos == 0)
13941       player->is_pushing = FALSE;
13942
13943     player->is_snapping = FALSE;
13944
13945     if (player->MovPos == 0)
13946     {
13947       player->is_moving = FALSE;
13948       player->is_digging = FALSE;
13949       player->is_collecting = FALSE;
13950     }
13951
13952     return FALSE;
13953   }
13954
13955   /* prevent snapping with already pressed snap key when not allowed */
13956   if (player->is_snapping && !can_continue_snapping)
13957     return FALSE;
13958
13959   player->MovDir = snap_direction;
13960
13961   if (player->MovPos == 0)
13962   {
13963     player->is_moving = FALSE;
13964     player->is_digging = FALSE;
13965     player->is_collecting = FALSE;
13966   }
13967
13968   player->is_dropping = FALSE;
13969   player->is_dropping_pressed = FALSE;
13970   player->drop_pressed_delay = 0;
13971
13972   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13973     return FALSE;
13974
13975   player->is_snapping = TRUE;
13976   player->is_active = TRUE;
13977
13978   if (player->MovPos == 0)
13979   {
13980     player->is_moving = FALSE;
13981     player->is_digging = FALSE;
13982     player->is_collecting = FALSE;
13983   }
13984
13985   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13986     TEST_DrawLevelField(player->last_jx, player->last_jy);
13987
13988   TEST_DrawLevelField(x, y);
13989
13990   return TRUE;
13991 }
13992
13993 static boolean DropElement(struct PlayerInfo *player)
13994 {
13995   int old_element, new_element;
13996   int dropx = player->jx, dropy = player->jy;
13997   int drop_direction = player->MovDir;
13998   int drop_side = drop_direction;
13999   int drop_element = get_next_dropped_element(player);
14000
14001   player->is_dropping_pressed = TRUE;
14002
14003   /* do not drop an element on top of another element; when holding drop key
14004      pressed without moving, dropped element must move away before the next
14005      element can be dropped (this is especially important if the next element
14006      is dynamite, which can be placed on background for historical reasons) */
14007   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14008     return MP_ACTION;
14009
14010   if (IS_THROWABLE(drop_element))
14011   {
14012     dropx += GET_DX_FROM_DIR(drop_direction);
14013     dropy += GET_DY_FROM_DIR(drop_direction);
14014
14015     if (!IN_LEV_FIELD(dropx, dropy))
14016       return FALSE;
14017   }
14018
14019   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14020   new_element = drop_element;           /* default: no change when dropping */
14021
14022   /* check if player is active, not moving and ready to drop */
14023   if (!player->active || player->MovPos || player->drop_delay > 0)
14024     return FALSE;
14025
14026   /* check if player has anything that can be dropped */
14027   if (new_element == EL_UNDEFINED)
14028     return FALSE;
14029
14030   /* check if drop key was pressed long enough for EM style dynamite */
14031   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14032     return FALSE;
14033
14034   /* check if anything can be dropped at the current position */
14035   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14036     return FALSE;
14037
14038   /* collected custom elements can only be dropped on empty fields */
14039   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14040     return FALSE;
14041
14042   if (old_element != EL_EMPTY)
14043     Back[dropx][dropy] = old_element;   /* store old element on this field */
14044
14045   ResetGfxAnimation(dropx, dropy);
14046   ResetRandomAnimationValue(dropx, dropy);
14047
14048   if (player->inventory_size > 0 ||
14049       player->inventory_infinite_element != EL_UNDEFINED)
14050   {
14051     if (player->inventory_size > 0)
14052     {
14053       player->inventory_size--;
14054
14055       DrawGameDoorValues();
14056
14057       if (new_element == EL_DYNAMITE)
14058         new_element = EL_DYNAMITE_ACTIVE;
14059       else if (new_element == EL_EM_DYNAMITE)
14060         new_element = EL_EM_DYNAMITE_ACTIVE;
14061       else if (new_element == EL_SP_DISK_RED)
14062         new_element = EL_SP_DISK_RED_ACTIVE;
14063     }
14064
14065     Feld[dropx][dropy] = new_element;
14066
14067     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14068       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14069                           el2img(Feld[dropx][dropy]), 0);
14070
14071     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14072
14073     /* needed if previous element just changed to "empty" in the last frame */
14074     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14075
14076     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14077                                player->index_bit, drop_side);
14078     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14079                                         CE_PLAYER_DROPS_X,
14080                                         player->index_bit, drop_side);
14081
14082     TestIfElementTouchesCustomElement(dropx, dropy);
14083   }
14084   else          /* player is dropping a dyna bomb */
14085   {
14086     player->dynabombs_left--;
14087
14088     Feld[dropx][dropy] = new_element;
14089
14090     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14091       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14092                           el2img(Feld[dropx][dropy]), 0);
14093
14094     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14095   }
14096
14097   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14098     InitField_WithBug1(dropx, dropy, FALSE);
14099
14100   new_element = Feld[dropx][dropy];     /* element might have changed */
14101
14102   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14103       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14104   {
14105     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14106       MovDir[dropx][dropy] = drop_direction;
14107
14108     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14109
14110     /* do not cause impact style collision by dropping elements that can fall */
14111     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14112   }
14113
14114   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14115   player->is_dropping = TRUE;
14116
14117   player->drop_pressed_delay = 0;
14118   player->is_dropping_pressed = FALSE;
14119
14120   player->drop_x = dropx;
14121   player->drop_y = dropy;
14122
14123   return TRUE;
14124 }
14125
14126 /* ------------------------------------------------------------------------- */
14127 /* game sound playing functions                                              */
14128 /* ------------------------------------------------------------------------- */
14129
14130 static int *loop_sound_frame = NULL;
14131 static int *loop_sound_volume = NULL;
14132
14133 void InitPlayLevelSound()
14134 {
14135   int num_sounds = getSoundListSize();
14136
14137   checked_free(loop_sound_frame);
14138   checked_free(loop_sound_volume);
14139
14140   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14141   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14142 }
14143
14144 static void PlayLevelSound(int x, int y, int nr)
14145 {
14146   int sx = SCREENX(x), sy = SCREENY(y);
14147   int volume, stereo_position;
14148   int max_distance = 8;
14149   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14150
14151   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14152       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14153     return;
14154
14155   if (!IN_LEV_FIELD(x, y) ||
14156       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14157       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14158     return;
14159
14160   volume = SOUND_MAX_VOLUME;
14161
14162   if (!IN_SCR_FIELD(sx, sy))
14163   {
14164     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14165     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14166
14167     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14168   }
14169
14170   stereo_position = (SOUND_MAX_LEFT +
14171                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14172                      (SCR_FIELDX + 2 * max_distance));
14173
14174   if (IS_LOOP_SOUND(nr))
14175   {
14176     /* This assures that quieter loop sounds do not overwrite louder ones,
14177        while restarting sound volume comparison with each new game frame. */
14178
14179     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14180       return;
14181
14182     loop_sound_volume[nr] = volume;
14183     loop_sound_frame[nr] = FrameCounter;
14184   }
14185
14186   PlaySoundExt(nr, volume, stereo_position, type);
14187 }
14188
14189 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14190 {
14191   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14192                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14193                  y < LEVELY(BY1) ? LEVELY(BY1) :
14194                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14195                  sound_action);
14196 }
14197
14198 static void PlayLevelSoundAction(int x, int y, int action)
14199 {
14200   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14201 }
14202
14203 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14204 {
14205   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14206
14207   if (sound_effect != SND_UNDEFINED)
14208     PlayLevelSound(x, y, sound_effect);
14209 }
14210
14211 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14212                                               int action)
14213 {
14214   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14215
14216   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14217     PlayLevelSound(x, y, sound_effect);
14218 }
14219
14220 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14221 {
14222   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14223
14224   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14225     PlayLevelSound(x, y, sound_effect);
14226 }
14227
14228 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14229 {
14230   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14231
14232   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14233     StopSound(sound_effect);
14234 }
14235
14236 static void PlayLevelMusic()
14237 {
14238   if (levelset.music[level_nr] != MUS_UNDEFINED)
14239     PlayMusic(levelset.music[level_nr]);        /* from config file */
14240   else
14241     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14242 }
14243
14244 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14245 {
14246   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14247   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14248   int x = xx - 1 - offset;
14249   int y = yy - 1 - offset;
14250
14251   switch (sample)
14252   {
14253     case SAMPLE_blank:
14254       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14255       break;
14256
14257     case SAMPLE_roll:
14258       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14259       break;
14260
14261     case SAMPLE_stone:
14262       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14263       break;
14264
14265     case SAMPLE_nut:
14266       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14267       break;
14268
14269     case SAMPLE_crack:
14270       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14271       break;
14272
14273     case SAMPLE_bug:
14274       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14275       break;
14276
14277     case SAMPLE_tank:
14278       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14279       break;
14280
14281     case SAMPLE_android_clone:
14282       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14283       break;
14284
14285     case SAMPLE_android_move:
14286       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14287       break;
14288
14289     case SAMPLE_spring:
14290       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14291       break;
14292
14293     case SAMPLE_slurp:
14294       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14295       break;
14296
14297     case SAMPLE_eater:
14298       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14299       break;
14300
14301     case SAMPLE_eater_eat:
14302       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14303       break;
14304
14305     case SAMPLE_alien:
14306       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14307       break;
14308
14309     case SAMPLE_collect:
14310       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14311       break;
14312
14313     case SAMPLE_diamond:
14314       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14315       break;
14316
14317     case SAMPLE_squash:
14318       /* !!! CHECK THIS !!! */
14319 #if 1
14320       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14321 #else
14322       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14323 #endif
14324       break;
14325
14326     case SAMPLE_wonderfall:
14327       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14328       break;
14329
14330     case SAMPLE_drip:
14331       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14332       break;
14333
14334     case SAMPLE_push:
14335       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14336       break;
14337
14338     case SAMPLE_dirt:
14339       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14340       break;
14341
14342     case SAMPLE_acid:
14343       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14344       break;
14345
14346     case SAMPLE_ball:
14347       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14348       break;
14349
14350     case SAMPLE_grow:
14351       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14352       break;
14353
14354     case SAMPLE_wonder:
14355       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14356       break;
14357
14358     case SAMPLE_door:
14359       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14360       break;
14361
14362     case SAMPLE_exit_open:
14363       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14364       break;
14365
14366     case SAMPLE_exit_leave:
14367       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14368       break;
14369
14370     case SAMPLE_dynamite:
14371       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14372       break;
14373
14374     case SAMPLE_tick:
14375       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14376       break;
14377
14378     case SAMPLE_press:
14379       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14380       break;
14381
14382     case SAMPLE_wheel:
14383       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14384       break;
14385
14386     case SAMPLE_boom:
14387       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14388       break;
14389
14390     case SAMPLE_die:
14391       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14392       break;
14393
14394     case SAMPLE_time:
14395       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14396       break;
14397
14398     default:
14399       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14400       break;
14401   }
14402 }
14403
14404 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14405 {
14406   int element = map_element_SP_to_RND(element_sp);
14407   int action = map_action_SP_to_RND(action_sp);
14408   int offset = (setup.sp_show_border_elements ? 0 : 1);
14409   int x = xx - offset;
14410   int y = yy - offset;
14411
14412   PlayLevelSoundElementAction(x, y, element, action);
14413 }
14414
14415 void RaiseScore(int value)
14416 {
14417   local_player->score += value;
14418
14419   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14420
14421   DisplayGameControlValues();
14422 }
14423
14424 void RaiseScoreElement(int element)
14425 {
14426   switch (element)
14427   {
14428     case EL_EMERALD:
14429     case EL_BD_DIAMOND:
14430     case EL_EMERALD_YELLOW:
14431     case EL_EMERALD_RED:
14432     case EL_EMERALD_PURPLE:
14433     case EL_SP_INFOTRON:
14434       RaiseScore(level.score[SC_EMERALD]);
14435       break;
14436     case EL_DIAMOND:
14437       RaiseScore(level.score[SC_DIAMOND]);
14438       break;
14439     case EL_CRYSTAL:
14440       RaiseScore(level.score[SC_CRYSTAL]);
14441       break;
14442     case EL_PEARL:
14443       RaiseScore(level.score[SC_PEARL]);
14444       break;
14445     case EL_BUG:
14446     case EL_BD_BUTTERFLY:
14447     case EL_SP_ELECTRON:
14448       RaiseScore(level.score[SC_BUG]);
14449       break;
14450     case EL_SPACESHIP:
14451     case EL_BD_FIREFLY:
14452     case EL_SP_SNIKSNAK:
14453       RaiseScore(level.score[SC_SPACESHIP]);
14454       break;
14455     case EL_YAMYAM:
14456     case EL_DARK_YAMYAM:
14457       RaiseScore(level.score[SC_YAMYAM]);
14458       break;
14459     case EL_ROBOT:
14460       RaiseScore(level.score[SC_ROBOT]);
14461       break;
14462     case EL_PACMAN:
14463       RaiseScore(level.score[SC_PACMAN]);
14464       break;
14465     case EL_NUT:
14466       RaiseScore(level.score[SC_NUT]);
14467       break;
14468     case EL_DYNAMITE:
14469     case EL_EM_DYNAMITE:
14470     case EL_SP_DISK_RED:
14471     case EL_DYNABOMB_INCREASE_NUMBER:
14472     case EL_DYNABOMB_INCREASE_SIZE:
14473     case EL_DYNABOMB_INCREASE_POWER:
14474       RaiseScore(level.score[SC_DYNAMITE]);
14475       break;
14476     case EL_SHIELD_NORMAL:
14477     case EL_SHIELD_DEADLY:
14478       RaiseScore(level.score[SC_SHIELD]);
14479       break;
14480     case EL_EXTRA_TIME:
14481       RaiseScore(level.extra_time_score);
14482       break;
14483     case EL_KEY_1:
14484     case EL_KEY_2:
14485     case EL_KEY_3:
14486     case EL_KEY_4:
14487     case EL_EM_KEY_1:
14488     case EL_EM_KEY_2:
14489     case EL_EM_KEY_3:
14490     case EL_EM_KEY_4:
14491     case EL_EMC_KEY_5:
14492     case EL_EMC_KEY_6:
14493     case EL_EMC_KEY_7:
14494     case EL_EMC_KEY_8:
14495     case EL_DC_KEY_WHITE:
14496       RaiseScore(level.score[SC_KEY]);
14497       break;
14498     default:
14499       RaiseScore(element_info[element].collect_score);
14500       break;
14501   }
14502 }
14503
14504 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14505 {
14506   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14507   {
14508     /* closing door required in case of envelope style request dialogs */
14509     if (!skip_request)
14510       CloseDoor(DOOR_CLOSE_1);
14511
14512 #if defined(NETWORK_AVALIABLE)
14513     if (options.network)
14514       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14515     else
14516 #endif
14517     {
14518       if (quick_quit)
14519       {
14520         FadeSkipNextFadeIn();
14521
14522         game_status = GAME_MODE_MAIN;
14523
14524         DrawMainMenu();
14525       }
14526       else
14527       {
14528         game_status = GAME_MODE_MAIN;
14529
14530         DrawMainMenu();
14531       }
14532     }
14533   }
14534   else          /* continue playing the game */
14535   {
14536     if (tape.playing && tape.deactivate_display)
14537       TapeDeactivateDisplayOff(TRUE);
14538
14539     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14540
14541     if (tape.playing && tape.deactivate_display)
14542       TapeDeactivateDisplayOn();
14543   }
14544 }
14545
14546 void RequestQuitGame(boolean ask_if_really_quit)
14547 {
14548   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14549   boolean skip_request = AllPlayersGone || quick_quit;
14550
14551   RequestQuitGameExt(skip_request, quick_quit,
14552                      "Do you really want to quit the game?");
14553 }
14554
14555
14556 /* ------------------------------------------------------------------------- */
14557 /* random generator functions                                                */
14558 /* ------------------------------------------------------------------------- */
14559
14560 unsigned int InitEngineRandom_RND(int seed)
14561 {
14562   game.num_random_calls = 0;
14563
14564   return InitEngineRandom(seed);
14565 }
14566
14567 unsigned int RND(int max)
14568 {
14569   if (max > 0)
14570   {
14571     game.num_random_calls++;
14572
14573     return GetEngineRandom(max);
14574   }
14575
14576   return 0;
14577 }
14578
14579
14580 /* ------------------------------------------------------------------------- */
14581 /* game engine snapshot handling functions                                   */
14582 /* ------------------------------------------------------------------------- */
14583
14584 struct EngineSnapshotInfo
14585 {
14586   /* runtime values for custom element collect score */
14587   int collect_score[NUM_CUSTOM_ELEMENTS];
14588
14589   /* runtime values for group element choice position */
14590   int choice_pos[NUM_GROUP_ELEMENTS];
14591
14592   /* runtime values for belt position animations */
14593   int belt_graphic[4][NUM_BELT_PARTS];
14594   int belt_anim_mode[4][NUM_BELT_PARTS];
14595 };
14596
14597 static struct EngineSnapshotInfo engine_snapshot_rnd;
14598 static char *snapshot_level_identifier = NULL;
14599 static int snapshot_level_nr = -1;
14600
14601 static void SaveEngineSnapshotValues_RND()
14602 {
14603   static int belt_base_active_element[4] =
14604   {
14605     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14606     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14607     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14608     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14609   };
14610   int i, j;
14611
14612   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14613   {
14614     int element = EL_CUSTOM_START + i;
14615
14616     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14617   }
14618
14619   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14620   {
14621     int element = EL_GROUP_START + i;
14622
14623     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14624   }
14625
14626   for (i = 0; i < 4; i++)
14627   {
14628     for (j = 0; j < NUM_BELT_PARTS; j++)
14629     {
14630       int element = belt_base_active_element[i] + j;
14631       int graphic = el2img(element);
14632       int anim_mode = graphic_info[graphic].anim_mode;
14633
14634       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14635       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14636     }
14637   }
14638 }
14639
14640 static void LoadEngineSnapshotValues_RND()
14641 {
14642   unsigned int num_random_calls = game.num_random_calls;
14643   int i, j;
14644
14645   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14646   {
14647     int element = EL_CUSTOM_START + i;
14648
14649     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14650   }
14651
14652   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14653   {
14654     int element = EL_GROUP_START + i;
14655
14656     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14657   }
14658
14659   for (i = 0; i < 4; i++)
14660   {
14661     for (j = 0; j < NUM_BELT_PARTS; j++)
14662     {
14663       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14664       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14665
14666       graphic_info[graphic].anim_mode = anim_mode;
14667     }
14668   }
14669
14670   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14671   {
14672     InitRND(tape.random_seed);
14673     for (i = 0; i < num_random_calls; i++)
14674       RND(1);
14675   }
14676
14677   if (game.num_random_calls != num_random_calls)
14678   {
14679     Error(ERR_INFO, "number of random calls out of sync");
14680     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14681     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14682     Error(ERR_EXIT, "this should not happen -- please debug");
14683   }
14684 }
14685
14686 void FreeEngineSnapshotSingle()
14687 {
14688   FreeSnapshotSingle();
14689
14690   setString(&snapshot_level_identifier, NULL);
14691   snapshot_level_nr = -1;
14692 }
14693
14694 void FreeEngineSnapshotList()
14695 {
14696   FreeSnapshotList();
14697 }
14698
14699 ListNode *SaveEngineSnapshotBuffers()
14700 {
14701   ListNode *buffers = NULL;
14702
14703   /* copy some special values to a structure better suited for the snapshot */
14704
14705   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14706     SaveEngineSnapshotValues_RND();
14707   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14708     SaveEngineSnapshotValues_EM();
14709   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14710     SaveEngineSnapshotValues_SP(&buffers);
14711
14712   /* save values stored in special snapshot structure */
14713
14714   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14715     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14716   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14717     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14718   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14719     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14720
14721   /* save further RND engine values */
14722
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14726
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14731
14732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14737
14738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14741
14742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14743
14744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14745
14746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14748
14749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14767
14768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14770
14771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14774
14775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14777
14778   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14780   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14783
14784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14786
14787 #if 0
14788   ListNode *node = engine_snapshot_list_rnd;
14789   int num_bytes = 0;
14790
14791   while (node != NULL)
14792   {
14793     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14794
14795     node = node->next;
14796   }
14797
14798   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14799 #endif
14800
14801   return buffers;
14802 }
14803
14804 void SaveEngineSnapshotSingle()
14805 {
14806   ListNode *buffers = SaveEngineSnapshotBuffers();
14807
14808   /* finally save all snapshot buffers to single snapshot */
14809   SaveSnapshotSingle(buffers);
14810
14811   /* save level identification information */
14812   setString(&snapshot_level_identifier, leveldir_current->identifier);
14813   snapshot_level_nr = level_nr;
14814 }
14815
14816 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14817 {
14818   boolean save_snapshot =
14819     (initial_snapshot ||
14820      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14821      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14822       game.snapshot.changed_action));
14823
14824   game.snapshot.changed_action = FALSE;
14825
14826   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14827       tape.quick_resume ||
14828       !save_snapshot)
14829     return FALSE;
14830
14831   ListNode *buffers = SaveEngineSnapshotBuffers();
14832
14833   /* finally save all snapshot buffers to snapshot list */
14834   SaveSnapshotToList(buffers);
14835
14836   return TRUE;
14837 }
14838
14839 boolean SaveEngineSnapshotToList()
14840 {
14841   return SaveEngineSnapshotToListExt(FALSE);
14842 }
14843
14844 void SaveEngineSnapshotToListInitial()
14845 {
14846   FreeEngineSnapshotList();
14847
14848   SaveEngineSnapshotToListExt(TRUE);
14849 }
14850
14851 void LoadEngineSnapshotValues()
14852 {
14853   /* restore special values from snapshot structure */
14854
14855   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14856     LoadEngineSnapshotValues_RND();
14857   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14858     LoadEngineSnapshotValues_EM();
14859   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14860     LoadEngineSnapshotValues_SP();
14861 }
14862
14863 void LoadEngineSnapshotSingle()
14864 {
14865   LoadSnapshotSingle();
14866
14867   LoadEngineSnapshotValues();
14868 }
14869
14870 void LoadEngineSnapshot_Undo(int steps)
14871 {
14872   LoadSnapshotFromList_Older(steps);
14873
14874   LoadEngineSnapshotValues();
14875 }
14876
14877 void LoadEngineSnapshot_Redo(int steps)
14878 {
14879   LoadSnapshotFromList_Newer(steps);
14880
14881   LoadEngineSnapshotValues();
14882 }
14883
14884 boolean CheckEngineSnapshotSingle()
14885 {
14886   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14887           snapshot_level_nr == level_nr);
14888 }
14889
14890 boolean CheckEngineSnapshotList()
14891 {
14892   return CheckSnapshotList();
14893 }
14894
14895
14896 /* ---------- new game button stuff ---------------------------------------- */
14897
14898 static struct
14899 {
14900   int graphic;
14901   struct XY *pos;
14902   int gadget_id;
14903   char *infotext;
14904 } gamebutton_info[NUM_GAME_BUTTONS] =
14905 {
14906   {
14907     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14908     GAME_CTRL_ID_STOP,                  "stop game"
14909   },
14910   {
14911     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14912     GAME_CTRL_ID_PAUSE,                 "pause game"
14913   },
14914   {
14915     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14916     GAME_CTRL_ID_PLAY,                  "play game"
14917   },
14918   {
14919     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14920     GAME_CTRL_ID_UNDO,                  "undo step"
14921   },
14922   {
14923     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14924     GAME_CTRL_ID_REDO,                  "redo step"
14925   },
14926   {
14927     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14928     GAME_CTRL_ID_SAVE,                  "save game"
14929   },
14930   {
14931     IMG_GAME_BUTTON_GFX_PAUSE2,         &game.button.pause2,
14932     GAME_CTRL_ID_PAUSE2,                "pause game"
14933   },
14934   {
14935     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14936     GAME_CTRL_ID_LOAD,                  "load game"
14937   },
14938   {
14939     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14940     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14941   },
14942   {
14943     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14944     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14945   },
14946   {
14947     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14948     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14949   }
14950 };
14951
14952 void CreateGameButtons()
14953 {
14954   int i;
14955
14956   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14957   {
14958     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14959     struct XY *pos = gamebutton_info[i].pos;
14960     struct GadgetInfo *gi;
14961     int button_type;
14962     boolean checked;
14963     unsigned int event_mask;
14964     int base_x = (tape.show_game_buttons ? VX : DX);
14965     int base_y = (tape.show_game_buttons ? VY : DY);
14966     int gd_x   = gfx->src_x;
14967     int gd_y   = gfx->src_y;
14968     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14969     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14970     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14971     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14972     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14973     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14974     int id = i;
14975
14976     if (gfx->bitmap == NULL)
14977     {
14978       game_gadget[id] = NULL;
14979
14980       continue;
14981     }
14982
14983     if (id == GAME_CTRL_ID_STOP ||
14984         id == GAME_CTRL_ID_PLAY ||
14985         id == GAME_CTRL_ID_SAVE ||
14986         id == GAME_CTRL_ID_LOAD)
14987     {
14988       button_type = GD_TYPE_NORMAL_BUTTON;
14989       checked = FALSE;
14990       event_mask = GD_EVENT_RELEASED;
14991     }
14992     else if (id == GAME_CTRL_ID_UNDO ||
14993              id == GAME_CTRL_ID_REDO)
14994     {
14995       button_type = GD_TYPE_NORMAL_BUTTON;
14996       checked = FALSE;
14997       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14998     }
14999     else
15000     {
15001       button_type = GD_TYPE_CHECK_BUTTON;
15002       checked =
15003         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15004          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15005          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15006       event_mask = GD_EVENT_PRESSED;
15007     }
15008
15009     gi = CreateGadget(GDI_CUSTOM_ID, id,
15010                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15011                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15012                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15013                       GDI_WIDTH, gfx->width,
15014                       GDI_HEIGHT, gfx->height,
15015                       GDI_TYPE, button_type,
15016                       GDI_STATE, GD_BUTTON_UNPRESSED,
15017                       GDI_CHECKED, checked,
15018                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15019                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15020                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15021                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15022                       GDI_DIRECT_DRAW, FALSE,
15023                       GDI_EVENT_MASK, event_mask,
15024                       GDI_CALLBACK_ACTION, HandleGameButtons,
15025                       GDI_END);
15026
15027     if (gi == NULL)
15028       Error(ERR_EXIT, "cannot create gadget");
15029
15030     game_gadget[id] = gi;
15031   }
15032 }
15033
15034 void FreeGameButtons()
15035 {
15036   int i;
15037
15038   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15039     FreeGadget(game_gadget[i]);
15040 }
15041
15042 static void MapGameButtonsAtSamePosition(int id)
15043 {
15044   int i;
15045
15046   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15047     if (i != id &&
15048         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15049         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15050       MapGadget(game_gadget[i]);
15051 }
15052
15053 static void UnmapGameButtonsAtSamePosition(int id)
15054 {
15055   int i;
15056
15057   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15058     if (i != id &&
15059         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15060         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15061       UnmapGadget(game_gadget[i]);
15062 }
15063
15064 void MapUndoRedoButtons()
15065 {
15066   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15067   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15068
15069   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15070   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15071 }
15072
15073 void UnmapUndoRedoButtons()
15074 {
15075   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15076   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15077
15078   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15079   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15080 }
15081
15082 void MapGameButtons()
15083 {
15084   int i;
15085
15086   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15087     if (i != GAME_CTRL_ID_UNDO &&
15088         i != GAME_CTRL_ID_REDO)
15089       MapGadget(game_gadget[i]);
15090
15091   if (setup.show_snapshot_buttons)
15092   {
15093     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15094     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15095     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15096   }
15097   else
15098   {
15099     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15100     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15101     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15102   }
15103
15104   RedrawGameButtons();
15105 }
15106
15107 void UnmapGameButtons()
15108 {
15109   int i;
15110
15111   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15112     UnmapGadget(game_gadget[i]);
15113 }
15114
15115 void RedrawGameButtons()
15116 {
15117   int i;
15118
15119   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15120     RedrawGadget(game_gadget[i]);
15121
15122   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15123   redraw_mask &= ~REDRAW_ALL;
15124 }
15125
15126 void GameUndoRedoExt()
15127 {
15128   ClearPlayerAction();
15129
15130   tape.pausing = TRUE;
15131
15132   RedrawPlayfield();
15133   UpdateAndDisplayGameControlValues();
15134
15135   DrawCompleteVideoDisplay();
15136   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15137   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15138   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15139                     VIDEO_STATE_1STEP_OFF), 0);
15140
15141   BackToFront();
15142 }
15143
15144 void GameUndo(int steps)
15145 {
15146   if (!CheckEngineSnapshotList())
15147     return;
15148
15149   LoadEngineSnapshot_Undo(steps);
15150
15151   GameUndoRedoExt();
15152 }
15153
15154 void GameRedo(int steps)
15155 {
15156   if (!CheckEngineSnapshotList())
15157     return;
15158
15159   LoadEngineSnapshot_Redo(steps);
15160
15161   GameUndoRedoExt();
15162 }
15163
15164 static void HandleGameButtonsExt(int id, int button)
15165 {
15166   int steps = BUTTON_STEPSIZE(button);
15167   boolean handle_game_buttons =
15168     (game_status == GAME_MODE_PLAYING ||
15169      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15170
15171   if (!handle_game_buttons)
15172     return;
15173
15174   switch (id)
15175   {
15176     case GAME_CTRL_ID_STOP:
15177       if (game_status == GAME_MODE_MAIN)
15178         break;
15179
15180       if (tape.playing)
15181         TapeStop();
15182       else
15183         RequestQuitGame(TRUE);
15184
15185       break;
15186
15187     case GAME_CTRL_ID_PAUSE:
15188     case GAME_CTRL_ID_PAUSE2:
15189       if (options.network && game_status == GAME_MODE_PLAYING)
15190       {
15191 #if defined(NETWORK_AVALIABLE)
15192         if (tape.pausing)
15193           SendToServer_ContinuePlaying();
15194         else
15195           SendToServer_PausePlaying();
15196 #endif
15197       }
15198       else
15199         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15200       break;
15201
15202     case GAME_CTRL_ID_PLAY:
15203       if (game_status == GAME_MODE_MAIN)
15204       {
15205         StartGameActions(options.network, setup.autorecord, level.random_seed);
15206       }
15207       else if (tape.pausing)
15208       {
15209 #if defined(NETWORK_AVALIABLE)
15210         if (options.network)
15211           SendToServer_ContinuePlaying();
15212         else
15213 #endif
15214           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15215       }
15216       break;
15217
15218     case GAME_CTRL_ID_UNDO:
15219       GameUndo(steps);
15220       break;
15221
15222     case GAME_CTRL_ID_REDO:
15223       GameRedo(steps);
15224       break;
15225
15226     case GAME_CTRL_ID_SAVE:
15227       TapeQuickSave();
15228       break;
15229
15230     case GAME_CTRL_ID_LOAD:
15231       TapeQuickLoad();
15232       break;
15233
15234     case SOUND_CTRL_ID_MUSIC:
15235       if (setup.sound_music)
15236       { 
15237         setup.sound_music = FALSE;
15238
15239         FadeMusic();
15240       }
15241       else if (audio.music_available)
15242       { 
15243         setup.sound = setup.sound_music = TRUE;
15244
15245         SetAudioMode(setup.sound);
15246
15247         PlayLevelMusic();
15248       }
15249       break;
15250
15251     case SOUND_CTRL_ID_LOOPS:
15252       if (setup.sound_loops)
15253         setup.sound_loops = FALSE;
15254       else if (audio.loops_available)
15255       {
15256         setup.sound = setup.sound_loops = TRUE;
15257
15258         SetAudioMode(setup.sound);
15259       }
15260       break;
15261
15262     case SOUND_CTRL_ID_SIMPLE:
15263       if (setup.sound_simple)
15264         setup.sound_simple = FALSE;
15265       else if (audio.sound_available)
15266       {
15267         setup.sound = setup.sound_simple = TRUE;
15268
15269         SetAudioMode(setup.sound);
15270       }
15271       break;
15272
15273     default:
15274       break;
15275   }
15276 }
15277
15278 static void HandleGameButtons(struct GadgetInfo *gi)
15279 {
15280   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15281 }
15282
15283 void HandleSoundButtonKeys(Key key)
15284 {
15285
15286   if (key == setup.shortcut.sound_simple)
15287     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15288   else if (key == setup.shortcut.sound_loops)
15289     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15290   else if (key == setup.shortcut.sound_music)
15291     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15292 }