removed shortly visible video display when starting game from level editor
[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 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(void);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1118
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1120
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1124
1125 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1126 {                                                                       \
1127   if (recursion_loop_detected)                                          \
1128     return (rc);                                                        \
1129                                                                         \
1130   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1131   {                                                                     \
1132     recursion_loop_detected = TRUE;                                     \
1133     recursion_loop_element = (e);                                       \
1134   }                                                                     \
1135                                                                         \
1136   recursion_loop_depth++;                                               \
1137 }
1138
1139 #define RECURSION_LOOP_DETECTION_END()                                  \
1140 {                                                                       \
1141   recursion_loop_depth--;                                               \
1142 }
1143
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1147
1148 static int map_player_action[MAX_PLAYERS];
1149
1150
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after  */
1153 /* a specified time, eventually calling a function when changing             */
1154 /* ------------------------------------------------------------------------- */
1155
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1159
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1163
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1167
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1170
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1173
1174 struct ChangingElementInfo
1175 {
1176   int element;
1177   int target_element;
1178   int change_delay;
1179   void (*pre_change_function)(int x, int y);
1180   void (*change_function)(int x, int y);
1181   void (*post_change_function)(int x, int y);
1182 };
1183
1184 static struct ChangingElementInfo change_delay_list[] =
1185 {
1186   {
1187     EL_NUT_BREAKING,
1188     EL_EMERALD,
1189     6,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_PEARL_BREAKING,
1196     EL_EMPTY,
1197     8,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EXIT_OPENING,
1204     EL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EXIT_CLOSING,
1212     EL_EXIT_CLOSED,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_STEEL_EXIT_OPENING,
1220     EL_STEEL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_STEEL_EXIT_CLOSING,
1228     EL_STEEL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EM_EXIT_OPENING,
1236     EL_EM_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_EM_EXIT_CLOSING,
1244     EL_EMPTY,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260     EL_STEELWALL,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_OPENING,
1268     EL_SP_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SP_EXIT_CLOSING,
1276     EL_SP_EXIT_CLOSED,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_OPENING,
1284     EL_SWITCHGATE_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SWITCHGATE_CLOSING,
1292     EL_SWITCHGATE_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_OPENING,
1300     EL_TIMEGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_TIMEGATE_CLOSING,
1308     EL_TIMEGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314
1315   {
1316     EL_ACID_SPLASH_LEFT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_ACID_SPLASH_RIGHT,
1325     EL_EMPTY,
1326     8,
1327     NULL,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE,
1333     EL_SP_BUGGY_BASE_ACTIVATING,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVATING,
1341     EL_SP_BUGGY_BASE_ACTIVE,
1342     0,
1343     InitBuggyBase,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE_ACTIVE,
1349     EL_SP_BUGGY_BASE,
1350     0,
1351     InitBuggyBase,
1352     WarnBuggyBase,
1353     NULL
1354   },
1355   {
1356     EL_TRAP,
1357     EL_TRAP_ACTIVE,
1358     0,
1359     InitTrap,
1360     NULL,
1361     ActivateTrap
1362   },
1363   {
1364     EL_TRAP_ACTIVE,
1365     EL_TRAP,
1366     31,
1367     NULL,
1368     ChangeActiveTrap,
1369     NULL
1370   },
1371   {
1372     EL_ROBOT_WHEEL_ACTIVE,
1373     EL_ROBOT_WHEEL,
1374     0,
1375     InitRobotWheel,
1376     RunRobotWheel,
1377     StopRobotWheel
1378   },
1379   {
1380     EL_TIMEGATE_SWITCH_ACTIVE,
1381     EL_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389     EL_DC_TIMEGATE_SWITCH,
1390     0,
1391     InitTimegateWheel,
1392     RunTimegateWheel,
1393     NULL
1394   },
1395   {
1396     EL_EMC_MAGIC_BALL_ACTIVE,
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     0,
1399     InitMagicBallDelay,
1400     NULL,
1401     ActivateMagicBall
1402   },
1403   {
1404     EL_EMC_SPRING_BUMPER_ACTIVE,
1405     EL_EMC_SPRING_BUMPER,
1406     8,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_SHRINKING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL
1418   },
1419   {
1420     EL_DIAGONAL_GROWING,
1421     EL_UNDEFINED,
1422     0,
1423     NULL,
1424     NULL,
1425     NULL,
1426   },
1427
1428   {
1429     EL_UNDEFINED,
1430     EL_UNDEFINED,
1431     -1,
1432     NULL,
1433     NULL,
1434     NULL
1435   }
1436 };
1437
1438 struct
1439 {
1440   int element;
1441   int push_delay_fixed, push_delay_random;
1442 }
1443 push_delay_list[] =
1444 {
1445   { EL_SPRING,                  0, 0 },
1446   { EL_BALLOON,                 0, 0 },
1447
1448   { EL_SOKOBAN_OBJECT,          2, 0 },
1449   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1450   { EL_SATELLITE,               2, 0 },
1451   { EL_SP_DISK_YELLOW,          2, 0 },
1452
1453   { EL_UNDEFINED,               0, 0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int move_stepsize;
1460 }
1461 move_stepsize_list[] =
1462 {
1463   { EL_AMOEBA_DROP,             2 },
1464   { EL_AMOEBA_DROPPING,         2 },
1465   { EL_QUICKSAND_FILLING,       1 },
1466   { EL_QUICKSAND_EMPTYING,      1 },
1467   { EL_QUICKSAND_FAST_FILLING,  2 },
1468   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469   { EL_MAGIC_WALL_FILLING,      2 },
1470   { EL_MAGIC_WALL_EMPTYING,     2 },
1471   { EL_BD_MAGIC_WALL_FILLING,   2 },
1472   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1473   { EL_DC_MAGIC_WALL_FILLING,   2 },
1474   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1475
1476   { EL_UNDEFINED,               0 },
1477 };
1478
1479 struct
1480 {
1481   int element;
1482   int count;
1483 }
1484 collect_count_list[] =
1485 {
1486   { EL_EMERALD,                 1 },
1487   { EL_BD_DIAMOND,              1 },
1488   { EL_EMERALD_YELLOW,          1 },
1489   { EL_EMERALD_RED,             1 },
1490   { EL_EMERALD_PURPLE,          1 },
1491   { EL_DIAMOND,                 3 },
1492   { EL_SP_INFOTRON,             1 },
1493   { EL_PEARL,                   5 },
1494   { EL_CRYSTAL,                 8 },
1495
1496   { EL_UNDEFINED,               0 },
1497 };
1498
1499 struct
1500 {
1501   int element;
1502   int direction;
1503 }
1504 access_direction_list[] =
1505 {
1506   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1508   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1509   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1512   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1513   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1514   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1515   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1516   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1517
1518   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1519   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1520   { EL_SP_PORT_UP,                                                   MV_DOWN },
1521   { EL_SP_PORT_DOWN,                                         MV_UP           },
1522   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1523   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1524   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1526   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1527   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1529   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1530   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1531   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1532   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1533   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1534   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1535   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1536   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1537
1538   { EL_UNDEFINED,                       MV_NONE                              }
1539 };
1540
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1542
1543 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1546                                  IS_JUST_CHANGING(x, y))
1547
1548 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1549
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1555
1556 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1557                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1558                                      (y) += playfield_scan_delta_y)     \
1559                                 for ((x) = playfield_scan_start_x;      \
1560                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1561                                      (x) += playfield_scan_delta_x)
1562
1563 #ifdef DEBUG
1564 void DEBUG_SetMaximumDynamite()
1565 {
1566   int i;
1567
1568   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570       local_player->inventory_element[local_player->inventory_size++] =
1571         EL_DYNAMITE;
1572 }
1573 #endif
1574
1575 static void InitPlayfieldScanModeVars()
1576 {
1577   if (game.use_reverse_scan_direction)
1578   {
1579     playfield_scan_start_x = lev_fieldx - 1;
1580     playfield_scan_start_y = lev_fieldy - 1;
1581
1582     playfield_scan_delta_x = -1;
1583     playfield_scan_delta_y = -1;
1584   }
1585   else
1586   {
1587     playfield_scan_start_x = 0;
1588     playfield_scan_start_y = 0;
1589
1590     playfield_scan_delta_x = 1;
1591     playfield_scan_delta_y = 1;
1592   }
1593 }
1594
1595 static void InitPlayfieldScanMode(int mode)
1596 {
1597   game.use_reverse_scan_direction =
1598     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1599
1600   InitPlayfieldScanModeVars();
1601 }
1602
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1604 {
1605   move_stepsize =
1606     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1607
1608   /* make sure that stepsize value is always a power of 2 */
1609   move_stepsize = (1 << log_2(move_stepsize));
1610
1611   return TILEX / move_stepsize;
1612 }
1613
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1615                                boolean init_game)
1616 {
1617   int player_nr = player->index_nr;
1618   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1620
1621   /* do no immediately change move delay -- the player might just be moving */
1622   player->move_delay_value_next = move_delay;
1623
1624   /* information if player can move must be set separately */
1625   player->cannot_move = cannot_move;
1626
1627   if (init_game)
1628   {
1629     player->move_delay       = game.initial_move_delay[player_nr];
1630     player->move_delay_value = game.initial_move_delay_value[player_nr];
1631
1632     player->move_delay_value_next = -1;
1633
1634     player->move_delay_reset_counter = 0;
1635   }
1636 }
1637
1638 void GetPlayerConfig()
1639 {
1640   GameFrameDelay = setup.game_frame_delay;
1641
1642   if (!audio.sound_available)
1643     setup.sound_simple = FALSE;
1644
1645   if (!audio.loops_available)
1646     setup.sound_loops = FALSE;
1647
1648   if (!audio.music_available)
1649     setup.sound_music = FALSE;
1650
1651   if (!video.fullscreen_available)
1652     setup.fullscreen = FALSE;
1653
1654   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1655
1656   SetAudioMode(setup.sound);
1657 }
1658
1659 int GetElementFromGroupElement(int element)
1660 {
1661   if (IS_GROUP_ELEMENT(element))
1662   {
1663     struct ElementGroupInfo *group = element_info[element].group;
1664     int last_anim_random_frame = gfx.anim_random_frame;
1665     int element_pos;
1666
1667     if (group->choice_mode == ANIM_RANDOM)
1668       gfx.anim_random_frame = RND(group->num_elements_resolved);
1669
1670     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671                                     group->choice_mode, 0,
1672                                     group->choice_pos);
1673
1674     if (group->choice_mode == ANIM_RANDOM)
1675       gfx.anim_random_frame = last_anim_random_frame;
1676
1677     group->choice_pos++;
1678
1679     element = group->element_resolved[element_pos];
1680   }
1681
1682   return element;
1683 }
1684
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1686 {
1687   if (element == EL_SP_MURPHY)
1688   {
1689     if (init_game)
1690     {
1691       if (stored_player[0].present)
1692       {
1693         Feld[x][y] = EL_SP_MURPHY_CLONE;
1694
1695         return;
1696       }
1697       else
1698       {
1699         stored_player[0].initial_element = element;
1700         stored_player[0].use_murphy = TRUE;
1701
1702         if (!level.use_artwork_element[0])
1703           stored_player[0].artwork_element = EL_SP_MURPHY;
1704       }
1705
1706       Feld[x][y] = EL_PLAYER_1;
1707     }
1708   }
1709
1710   if (init_game)
1711   {
1712     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713     int jx = player->jx, jy = player->jy;
1714
1715     player->present = TRUE;
1716
1717     player->block_last_field = (element == EL_SP_MURPHY ?
1718                                 level.sp_block_last_field :
1719                                 level.block_last_field);
1720
1721     /* ---------- initialize player's last field block delay --------------- */
1722
1723     /* always start with reliable default value (no adjustment needed) */
1724     player->block_delay_adjustment = 0;
1725
1726     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727     if (player->block_last_field && element == EL_SP_MURPHY)
1728       player->block_delay_adjustment = 1;
1729
1730     /* special case 2: in game engines before 3.1.1, blocking was different */
1731     if (game.use_block_last_field_bug)
1732       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1733
1734     if (!options.network || player->connected)
1735     {
1736       player->active = TRUE;
1737
1738       /* remove potentially duplicate players */
1739       if (StorePlayer[jx][jy] == Feld[x][y])
1740         StorePlayer[jx][jy] = 0;
1741
1742       StorePlayer[x][y] = Feld[x][y];
1743
1744 #if DEBUG_INIT_PLAYER
1745       if (options.debug)
1746       {
1747         printf("- player element %d activated", player->element_nr);
1748         printf(" (local player is %d and currently %s)\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753 #endif
1754
1755     Feld[x][y] = EL_EMPTY;
1756
1757     player->jx = player->last_jx = x;
1758     player->jy = player->last_jy = y;
1759   }
1760
1761   if (!init_game)
1762   {
1763     int player_nr = GET_PLAYER_NR(element);
1764     struct PlayerInfo *player = &stored_player[player_nr];
1765
1766     if (player->active && player->killed)
1767       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1768   }
1769 }
1770
1771 static void InitField(int x, int y, boolean init_game)
1772 {
1773   int element = Feld[x][y];
1774
1775   switch (element)
1776   {
1777     case EL_SP_MURPHY:
1778     case EL_PLAYER_1:
1779     case EL_PLAYER_2:
1780     case EL_PLAYER_3:
1781     case EL_PLAYER_4:
1782       InitPlayerField(x, y, element, init_game);
1783       break;
1784
1785     case EL_SOKOBAN_FIELD_PLAYER:
1786       element = Feld[x][y] = EL_PLAYER_1;
1787       InitField(x, y, init_game);
1788
1789       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790       InitField(x, y, init_game);
1791       break;
1792
1793     case EL_SOKOBAN_FIELD_EMPTY:
1794       local_player->sokobanfields_still_needed++;
1795       break;
1796
1797     case EL_STONEBLOCK:
1798       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1808       break;
1809
1810     case EL_BUG:
1811     case EL_BUG_RIGHT:
1812     case EL_BUG_UP:
1813     case EL_BUG_LEFT:
1814     case EL_BUG_DOWN:
1815     case EL_SPACESHIP:
1816     case EL_SPACESHIP_RIGHT:
1817     case EL_SPACESHIP_UP:
1818     case EL_SPACESHIP_LEFT:
1819     case EL_SPACESHIP_DOWN:
1820     case EL_BD_BUTTERFLY:
1821     case EL_BD_BUTTERFLY_RIGHT:
1822     case EL_BD_BUTTERFLY_UP:
1823     case EL_BD_BUTTERFLY_LEFT:
1824     case EL_BD_BUTTERFLY_DOWN:
1825     case EL_BD_FIREFLY:
1826     case EL_BD_FIREFLY_RIGHT:
1827     case EL_BD_FIREFLY_UP:
1828     case EL_BD_FIREFLY_LEFT:
1829     case EL_BD_FIREFLY_DOWN:
1830     case EL_PACMAN_RIGHT:
1831     case EL_PACMAN_UP:
1832     case EL_PACMAN_LEFT:
1833     case EL_PACMAN_DOWN:
1834     case EL_YAMYAM:
1835     case EL_YAMYAM_LEFT:
1836     case EL_YAMYAM_RIGHT:
1837     case EL_YAMYAM_UP:
1838     case EL_YAMYAM_DOWN:
1839     case EL_DARK_YAMYAM:
1840     case EL_ROBOT:
1841     case EL_PACMAN:
1842     case EL_SP_SNIKSNAK:
1843     case EL_SP_ELECTRON:
1844     case EL_MOLE:
1845     case EL_MOLE_LEFT:
1846     case EL_MOLE_RIGHT:
1847     case EL_MOLE_UP:
1848     case EL_MOLE_DOWN:
1849       InitMovDir(x, y);
1850       break;
1851
1852     case EL_AMOEBA_FULL:
1853     case EL_BD_AMOEBA:
1854       InitAmoebaNr(x, y);
1855       break;
1856
1857     case EL_AMOEBA_DROP:
1858       if (y == lev_fieldy - 1)
1859       {
1860         Feld[x][y] = EL_AMOEBA_GROWING;
1861         Store[x][y] = EL_AMOEBA_WET;
1862       }
1863       break;
1864
1865     case EL_DYNAMITE_ACTIVE:
1866     case EL_SP_DISK_RED_ACTIVE:
1867     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871       MovDelay[x][y] = 96;
1872       break;
1873
1874     case EL_EM_DYNAMITE_ACTIVE:
1875       MovDelay[x][y] = 32;
1876       break;
1877
1878     case EL_LAMP:
1879       local_player->lights_still_needed++;
1880       break;
1881
1882     case EL_PENGUIN:
1883       local_player->friends_still_needed++;
1884       break;
1885
1886     case EL_PIG:
1887     case EL_DRAGON:
1888       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1889       break;
1890
1891     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1903       if (init_game)
1904       {
1905         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1908
1909         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1910         {
1911           game.belt_dir[belt_nr] = belt_dir;
1912           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1913         }
1914         else    /* more than one switch -- set it like the first switch */
1915         {
1916           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1917         }
1918       }
1919       break;
1920
1921     case EL_LIGHT_SWITCH_ACTIVE:
1922       if (init_game)
1923         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1924       break;
1925
1926     case EL_INVISIBLE_STEELWALL:
1927     case EL_INVISIBLE_WALL:
1928     case EL_INVISIBLE_SAND:
1929       if (game.light_time_left > 0 ||
1930           game.lenses_time_left > 0)
1931         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1932       break;
1933
1934     case EL_EMC_MAGIC_BALL:
1935       if (game.ball_state)
1936         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1937       break;
1938
1939     case EL_EMC_MAGIC_BALL_SWITCH:
1940       if (game.ball_state)
1941         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1942       break;
1943
1944     case EL_TRIGGER_PLAYER:
1945     case EL_TRIGGER_ELEMENT:
1946     case EL_TRIGGER_CE_VALUE:
1947     case EL_TRIGGER_CE_SCORE:
1948     case EL_SELF:
1949     case EL_ANY_ELEMENT:
1950     case EL_CURRENT_CE_VALUE:
1951     case EL_CURRENT_CE_SCORE:
1952     case EL_PREV_CE_1:
1953     case EL_PREV_CE_2:
1954     case EL_PREV_CE_3:
1955     case EL_PREV_CE_4:
1956     case EL_PREV_CE_5:
1957     case EL_PREV_CE_6:
1958     case EL_PREV_CE_7:
1959     case EL_PREV_CE_8:
1960     case EL_NEXT_CE_1:
1961     case EL_NEXT_CE_2:
1962     case EL_NEXT_CE_3:
1963     case EL_NEXT_CE_4:
1964     case EL_NEXT_CE_5:
1965     case EL_NEXT_CE_6:
1966     case EL_NEXT_CE_7:
1967     case EL_NEXT_CE_8:
1968       /* reference elements should not be used on the playfield */
1969       Feld[x][y] = EL_EMPTY;
1970       break;
1971
1972     default:
1973       if (IS_CUSTOM_ELEMENT(element))
1974       {
1975         if (CAN_MOVE(element))
1976           InitMovDir(x, y);
1977
1978         if (!element_info[element].use_last_ce_value || init_game)
1979           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1980       }
1981       else if (IS_GROUP_ELEMENT(element))
1982       {
1983         Feld[x][y] = GetElementFromGroupElement(element);
1984
1985         InitField(x, y, init_game);
1986       }
1987
1988       break;
1989   }
1990
1991   if (!init_game)
1992     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1993 }
1994
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1996 {
1997   InitField(x, y, init_game);
1998
1999   /* not needed to call InitMovDir() -- already done by InitField()! */
2000   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001       CAN_MOVE(Feld[x][y]))
2002     InitMovDir(x, y);
2003 }
2004
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2006 {
2007   int old_element = Feld[x][y];
2008
2009   InitField(x, y, init_game);
2010
2011   /* not needed to call InitMovDir() -- already done by InitField()! */
2012   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013       CAN_MOVE(old_element) &&
2014       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2015     InitMovDir(x, y);
2016
2017   /* this case is in fact a combination of not less than three bugs:
2018      first, it calls InitMovDir() for elements that can move, although this is
2019      already done by InitField(); then, it checks the element that was at this
2020      field _before_ the call to InitField() (which can change it); lastly, it
2021      was not called for "mole with direction" elements, which were treated as
2022      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2023   */
2024 }
2025
2026 static int get_key_element_from_nr(int key_nr)
2027 {
2028   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030                           EL_EM_KEY_1 : EL_KEY_1);
2031
2032   return key_base_element + key_nr;
2033 }
2034
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2036 {
2037   return (player->inventory_size > 0 ?
2038           player->inventory_element[player->inventory_size - 1] :
2039           player->inventory_infinite_element != EL_UNDEFINED ?
2040           player->inventory_infinite_element :
2041           player->dynabombs_left > 0 ?
2042           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2043           EL_UNDEFINED);
2044 }
2045
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2047 {
2048   /* pos >= 0: get element from bottom of the stack;
2049      pos <  0: get element from top of the stack */
2050
2051   if (pos < 0)
2052   {
2053     int min_inventory_size = -pos;
2054     int inventory_pos = player->inventory_size - min_inventory_size;
2055     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2056
2057     return (player->inventory_size >= min_inventory_size ?
2058             player->inventory_element[inventory_pos] :
2059             player->inventory_infinite_element != EL_UNDEFINED ?
2060             player->inventory_infinite_element :
2061             player->dynabombs_left >= min_dynabombs_left ?
2062             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2063             EL_UNDEFINED);
2064   }
2065   else
2066   {
2067     int min_dynabombs_left = pos + 1;
2068     int min_inventory_size = pos + 1 - player->dynabombs_left;
2069     int inventory_pos = pos - player->dynabombs_left;
2070
2071     return (player->inventory_infinite_element != EL_UNDEFINED ?
2072             player->inventory_infinite_element :
2073             player->dynabombs_left >= min_dynabombs_left ?
2074             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075             player->inventory_size >= min_inventory_size ?
2076             player->inventory_element[inventory_pos] :
2077             EL_UNDEFINED);
2078   }
2079 }
2080
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2082 {
2083   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2085   int compare_result;
2086
2087   if (gpo1->sort_priority != gpo2->sort_priority)
2088     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2089   else
2090     compare_result = gpo1->nr - gpo2->nr;
2091
2092   return compare_result;
2093 }
2094
2095 int getPlayerInventorySize(int player_nr)
2096 {
2097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098     return level.native_em_level->ply[player_nr]->dynamite;
2099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100     return level.native_sp_level->game_sp->red_disk_count;
2101   else
2102     return stored_player[player_nr].inventory_size;
2103 }
2104
2105 void InitGameControlValues()
2106 {
2107   int i;
2108
2109   for (i = 0; game_panel_controls[i].nr != -1; i++)
2110   {
2111     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113     struct TextPosInfo *pos = gpc->pos;
2114     int nr = gpc->nr;
2115     int type = gpc->type;
2116
2117     if (nr != i)
2118     {
2119       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120       Error(ERR_EXIT, "this should not happen -- please debug");
2121     }
2122
2123     /* force update of game controls after initialization */
2124     gpc->value = gpc->last_value = -1;
2125     gpc->frame = gpc->last_frame = -1;
2126     gpc->gfx_frame = -1;
2127
2128     /* determine panel value width for later calculation of alignment */
2129     if (type == TYPE_INTEGER || type == TYPE_STRING)
2130     {
2131       pos->width = pos->size * getFontWidth(pos->font);
2132       pos->height = getFontHeight(pos->font);
2133     }
2134     else if (type == TYPE_ELEMENT)
2135     {
2136       pos->width = pos->size;
2137       pos->height = pos->size;
2138     }
2139
2140     /* fill structure for game panel draw order */
2141     gpo->nr = gpc->nr;
2142     gpo->sort_priority = pos->sort_priority;
2143   }
2144
2145   /* sort game panel controls according to sort_priority and control number */
2146   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2148 }
2149
2150 void UpdatePlayfieldElementCount()
2151 {
2152   boolean use_element_count = FALSE;
2153   int i, j, x, y;
2154
2155   /* first check if it is needed at all to calculate playfield element count */
2156   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158       use_element_count = TRUE;
2159
2160   if (!use_element_count)
2161     return;
2162
2163   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164     element_info[i].element_count = 0;
2165
2166   SCAN_PLAYFIELD(x, y)
2167   {
2168     element_info[Feld[x][y]].element_count++;
2169   }
2170
2171   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173       if (IS_IN_GROUP(j, i))
2174         element_info[EL_GROUP_START + i].element_count +=
2175           element_info[j].element_count;
2176 }
2177
2178 void UpdateGameControlValues()
2179 {
2180   int i, k;
2181   int time = (local_player->LevelSolved ?
2182               local_player->LevelSolved_CountingTime :
2183               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184               level.native_em_level->lev->time :
2185               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186               level.native_sp_level->game_sp->time_played :
2187               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188               game_mm.energy_left :
2189               game.no_time_limit ? TimePlayed : TimeLeft);
2190   int score = (local_player->LevelSolved ?
2191                local_player->LevelSolved_CountingScore :
2192                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193                level.native_em_level->lev->score :
2194                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195                level.native_sp_level->game_sp->score :
2196                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2197                game_mm.score :
2198                local_player->score);
2199   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200               level.native_em_level->lev->required :
2201               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202               level.native_sp_level->game_sp->infotrons_still_needed :
2203               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204               game_mm.kettles_still_needed :
2205               local_player->gems_still_needed);
2206   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207                      level.native_em_level->lev->required > 0 :
2208                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211                      game_mm.kettles_still_needed > 0 ||
2212                      game_mm.lights_still_needed > 0 :
2213                      local_player->gems_still_needed > 0 ||
2214                      local_player->sokobanfields_still_needed > 0 ||
2215                      local_player->lights_still_needed > 0);
2216   int health = (local_player->LevelSolved ?
2217                 local_player->LevelSolved_CountingHealth :
2218                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219                 MM_HEALTH(game_mm.laser_overload_value) :
2220                 local_player->health);
2221
2222   UpdatePlayfieldElementCount();
2223
2224   /* update game panel control values */
2225
2226   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2228
2229   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230   for (i = 0; i < MAX_NUM_KEYS; i++)
2231     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2234
2235   if (game.centered_player_nr == -1)
2236   {
2237     for (i = 0; i < MAX_PLAYERS; i++)
2238     {
2239       /* only one player in Supaplex game engine */
2240       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2241         break;
2242
2243       for (k = 0; k < MAX_NUM_KEYS; k++)
2244       {
2245         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246         {
2247           if (level.native_em_level->ply[i]->keys & (1 << k))
2248             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249               get_key_element_from_nr(k);
2250         }
2251         else if (stored_player[i].key[k])
2252           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253             get_key_element_from_nr(k);
2254       }
2255
2256       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257         getPlayerInventorySize(i);
2258
2259       if (stored_player[i].num_white_keys > 0)
2260         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2261           EL_DC_KEY_WHITE;
2262
2263       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264         stored_player[i].num_white_keys;
2265     }
2266   }
2267   else
2268   {
2269     int player_nr = game.centered_player_nr;
2270
2271     for (k = 0; k < MAX_NUM_KEYS; k++)
2272     {
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2274       {
2275         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277             get_key_element_from_nr(k);
2278       }
2279       else if (stored_player[player_nr].key[k])
2280         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281           get_key_element_from_nr(k);
2282     }
2283
2284     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285       getPlayerInventorySize(player_nr);
2286
2287     if (stored_player[player_nr].num_white_keys > 0)
2288       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2289
2290     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291       stored_player[player_nr].num_white_keys;
2292   }
2293
2294   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2295   {
2296     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297       get_inventory_element_from_pos(local_player, i);
2298     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, -i - 1);
2300   }
2301
2302   game_panel_controls[GAME_PANEL_SCORE].value = score;
2303   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2304
2305   game_panel_controls[GAME_PANEL_TIME].value = time;
2306
2307   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2310
2311   if (level.time == 0)
2312     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2313   else
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2315
2316   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2318
2319   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2320
2321   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2323      EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325     local_player->shield_normal_time_left;
2326   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2328      EL_EMPTY);
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330     local_player->shield_deadly_time_left;
2331
2332   game_panel_controls[GAME_PANEL_EXIT].value =
2333     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2334
2335   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339      EL_EMC_MAGIC_BALL_SWITCH);
2340
2341   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344     game.light_time_left;
2345
2346   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349     game.timegate_time_left;
2350
2351   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2353
2354   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357     game.lenses_time_left;
2358
2359   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362     game.magnify_time_left;
2363
2364   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2366      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2368      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2369      EL_BALLOON_SWITCH_NONE);
2370
2371   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372     local_player->dynabomb_count;
2373   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374     local_player->dynabomb_size;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2377
2378   game_panel_controls[GAME_PANEL_PENGUINS].value =
2379     local_player->friends_still_needed;
2380
2381   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382     local_player->sokobanfields_still_needed;
2383   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384     local_player->sokobanfields_still_needed;
2385
2386   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2388
2389   for (i = 0; i < NUM_BELTS; i++)
2390   {
2391     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2396   }
2397
2398   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401     game.magic_wall_time_left;
2402
2403   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404     local_player->gravity;
2405
2406   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2408
2409   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412        game.panel.element[i].id : EL_UNDEFINED);
2413
2414   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417        element_info[game.panel.element_count[i].id].element_count : 0);
2418
2419   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422        element_info[game.panel.ce_score[i].id].collect_score : 0);
2423
2424   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427        element_info[game.panel.ce_score_element[i].id].collect_score :
2428        EL_UNDEFINED);
2429
2430   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2433
2434   /* update game panel control frames */
2435
2436   for (i = 0; game_panel_controls[i].nr != -1; i++)
2437   {
2438     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2439
2440     if (gpc->type == TYPE_ELEMENT)
2441     {
2442       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2443       {
2444         int last_anim_random_frame = gfx.anim_random_frame;
2445         int element = gpc->value;
2446         int graphic = el2panelimg(element);
2447
2448         if (gpc->value != gpc->last_value)
2449         {
2450           gpc->gfx_frame = 0;
2451           gpc->gfx_random = INIT_GFX_RANDOM();
2452         }
2453         else
2454         {
2455           gpc->gfx_frame++;
2456
2457           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459             gpc->gfx_random = INIT_GFX_RANDOM();
2460         }
2461
2462         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463           gfx.anim_random_frame = gpc->gfx_random;
2464
2465         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466           gpc->gfx_frame = element_info[element].collect_score;
2467
2468         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2469                                               gpc->gfx_frame);
2470
2471         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472           gfx.anim_random_frame = last_anim_random_frame;
2473       }
2474     }
2475     else if (gpc->type == TYPE_GRAPHIC)
2476     {
2477       if (gpc->graphic != IMG_UNDEFINED)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int graphic = gpc->graphic;
2481
2482         if (gpc->value != gpc->last_value)
2483         {
2484           gpc->gfx_frame = 0;
2485           gpc->gfx_random = INIT_GFX_RANDOM();
2486         }
2487         else
2488         {
2489           gpc->gfx_frame++;
2490
2491           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493             gpc->gfx_random = INIT_GFX_RANDOM();
2494         }
2495
2496         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497           gfx.anim_random_frame = gpc->gfx_random;
2498
2499         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2500
2501         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502           gfx.anim_random_frame = last_anim_random_frame;
2503       }
2504     }
2505   }
2506 }
2507
2508 void DisplayGameControlValues()
2509 {
2510   boolean redraw_panel = FALSE;
2511   int i;
2512
2513   for (i = 0; game_panel_controls[i].nr != -1; i++)
2514   {
2515     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2516
2517     if (PANEL_DEACTIVATED(gpc->pos))
2518       continue;
2519
2520     if (gpc->value == gpc->last_value &&
2521         gpc->frame == gpc->last_frame)
2522       continue;
2523
2524     redraw_panel = TRUE;
2525   }
2526
2527   if (!redraw_panel)
2528     return;
2529
2530   /* copy default game door content to main double buffer */
2531
2532   /* !!! CHECK AGAIN !!! */
2533   SetPanelBackground();
2534   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2536
2537   /* redraw game control buttons */
2538   RedrawGameButtons();
2539
2540   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2541
2542   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2543   {
2544     int nr = game_panel_order[i].nr;
2545     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546     struct TextPosInfo *pos = gpc->pos;
2547     int type = gpc->type;
2548     int value = gpc->value;
2549     int frame = gpc->frame;
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558     gpc->last_value = value;
2559     gpc->last_frame = frame;
2560
2561     if (type == TYPE_INTEGER)
2562     {
2563       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564           nr == GAME_PANEL_TIME)
2565       {
2566         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567
2568         if (use_dynamic_size)           /* use dynamic number of digits */
2569         {
2570           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572           int size2 = size1 + 1;
2573           int font1 = pos->font;
2574           int font2 = pos->font_alt;
2575
2576           size = (value < value_change ? size1 : size2);
2577           font = (value < value_change ? font1 : font2);
2578         }
2579       }
2580
2581       /* correct text size if "digits" is zero or less */
2582       if (size <= 0)
2583         size = strlen(int2str(value, size));
2584
2585       /* dynamically correct text alignment */
2586       pos->width = size * getFontWidth(font);
2587
2588       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589                   int2str(value, size), font, mask_mode);
2590     }
2591     else if (type == TYPE_ELEMENT)
2592     {
2593       int element, graphic;
2594       Bitmap *src_bitmap;
2595       int src_x, src_y;
2596       int width, height;
2597       int dst_x = PANEL_XPOS(pos);
2598       int dst_y = PANEL_YPOS(pos);
2599
2600       if (value != EL_UNDEFINED && value != EL_EMPTY)
2601       {
2602         element = value;
2603         graphic = el2panelimg(value);
2604
2605         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2606
2607         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2608           size = TILESIZE;
2609
2610         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2611                               &src_x, &src_y);
2612
2613         width  = graphic_info[graphic].width  * size / TILESIZE;
2614         height = graphic_info[graphic].height * size / TILESIZE;
2615
2616         if (draw_masked)
2617           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2618                            dst_x, dst_y);
2619         else
2620           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2621                      dst_x, dst_y);
2622       }
2623     }
2624     else if (type == TYPE_GRAPHIC)
2625     {
2626       int graphic        = gpc->graphic;
2627       int graphic_active = gpc->graphic_active;
2628       Bitmap *src_bitmap;
2629       int src_x, src_y;
2630       int width, height;
2631       int dst_x = PANEL_XPOS(pos);
2632       int dst_y = PANEL_YPOS(pos);
2633       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2635
2636       if (graphic != IMG_UNDEFINED && !skip)
2637       {
2638         if (pos->style == STYLE_REVERSE)
2639           value = 100 - value;
2640
2641         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2642
2643         if (pos->direction & MV_HORIZONTAL)
2644         {
2645           width  = graphic_info[graphic_active].width * value / 100;
2646           height = graphic_info[graphic_active].height;
2647
2648           if (pos->direction == MV_LEFT)
2649           {
2650             src_x += graphic_info[graphic_active].width - width;
2651             dst_x += graphic_info[graphic_active].width - width;
2652           }
2653         }
2654         else
2655         {
2656           width  = graphic_info[graphic_active].width;
2657           height = graphic_info[graphic_active].height * value / 100;
2658
2659           if (pos->direction == MV_UP)
2660           {
2661             src_y += graphic_info[graphic_active].height - height;
2662             dst_y += graphic_info[graphic_active].height - height;
2663           }
2664         }
2665
2666         if (draw_masked)
2667           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2668                            dst_x, dst_y);
2669         else
2670           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2671                      dst_x, dst_y);
2672
2673         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2674
2675         if (pos->direction & MV_HORIZONTAL)
2676         {
2677           if (pos->direction == MV_RIGHT)
2678           {
2679             src_x += width;
2680             dst_x += width;
2681           }
2682           else
2683           {
2684             dst_x = PANEL_XPOS(pos);
2685           }
2686
2687           width = graphic_info[graphic].width - width;
2688         }
2689         else
2690         {
2691           if (pos->direction == MV_DOWN)
2692           {
2693             src_y += height;
2694             dst_y += height;
2695           }
2696           else
2697           {
2698             dst_y = PANEL_YPOS(pos);
2699           }
2700
2701           height = graphic_info[graphic].height - height;
2702         }
2703
2704         if (draw_masked)
2705           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2706                            dst_x, dst_y);
2707         else
2708           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2709                      dst_x, dst_y);
2710       }
2711     }
2712     else if (type == TYPE_STRING)
2713     {
2714       boolean active = (value != 0);
2715       char *state_normal = "off";
2716       char *state_active = "on";
2717       char *state = (active ? state_active : state_normal);
2718       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2720                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2721                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2722
2723       if (nr == GAME_PANEL_GRAVITY_STATE)
2724       {
2725         int font1 = pos->font;          /* (used for normal state) */
2726         int font2 = pos->font_alt;      /* (used for active state) */
2727
2728         font = (active ? font2 : font1);
2729       }
2730
2731       if (s != NULL)
2732       {
2733         char *s_cut;
2734
2735         if (size <= 0)
2736         {
2737           /* don't truncate output if "chars" is zero or less */
2738           size = strlen(s);
2739
2740           /* dynamically correct text alignment */
2741           pos->width = size * getFontWidth(font);
2742         }
2743
2744         s_cut = getStringCopyN(s, size);
2745
2746         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747                     s_cut, font, mask_mode);
2748
2749         free(s_cut);
2750       }
2751     }
2752
2753     redraw_mask |= REDRAW_DOOR_1;
2754   }
2755
2756   SetGameStatus(GAME_MODE_PLAYING);
2757 }
2758
2759 void UpdateAndDisplayGameControlValues()
2760 {
2761   if (tape.deactivate_display)
2762     return;
2763
2764   UpdateGameControlValues();
2765   DisplayGameControlValues();
2766 }
2767
2768 void UpdateGameDoorValues()
2769 {
2770   UpdateGameControlValues();
2771 }
2772
2773 void DrawGameDoorValues()
2774 {
2775   DisplayGameControlValues();
2776 }
2777
2778
2779 /*
2780   =============================================================================
2781   InitGameEngine()
2782   -----------------------------------------------------------------------------
2783   initialize game engine due to level / tape version number
2784   =============================================================================
2785 */
2786
2787 static void InitGameEngine()
2788 {
2789   int i, j, k, l, x, y;
2790
2791   /* set game engine from tape file when re-playing, else from level file */
2792   game.engine_version = (tape.playing ? tape.engine_version :
2793                          level.game_version);
2794
2795   /* set single or multi-player game mode (needed for re-playing tapes) */
2796   game.team_mode = setup.team_mode;
2797
2798   if (tape.playing)
2799   {
2800     int num_players = 0;
2801
2802     for (i = 0; i < MAX_PLAYERS; i++)
2803       if (tape.player_participates[i])
2804         num_players++;
2805
2806     /* multi-player tapes contain input data for more than one player */
2807     game.team_mode = (num_players > 1);
2808   }
2809
2810   /* ---------------------------------------------------------------------- */
2811   /* set flags for bugs and changes according to active game engine version */
2812   /* ---------------------------------------------------------------------- */
2813
2814   /*
2815     Summary of bugfix/change:
2816     Fixed handling for custom elements that change when pushed by the player.
2817
2818     Fixed/changed in version:
2819     3.1.0
2820
2821     Description:
2822     Before 3.1.0, custom elements that "change when pushing" changed directly
2823     after the player started pushing them (until then handled in "DigField()").
2824     Since 3.1.0, these custom elements are not changed until the "pushing"
2825     move of the element is finished (now handled in "ContinueMoving()").
2826
2827     Affected levels/tapes:
2828     The first condition is generally needed for all levels/tapes before version
2829     3.1.0, which might use the old behaviour before it was changed; known tapes
2830     that are affected are some tapes from the level set "Walpurgis Gardens" by
2831     Jamie Cullen.
2832     The second condition is an exception from the above case and is needed for
2833     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834     above (including some development versions of 3.1.0), but before it was
2835     known that this change would break tapes like the above and was fixed in
2836     3.1.1, so that the changed behaviour was active although the engine version
2837     while recording maybe was before 3.1.0. There is at least one tape that is
2838     affected by this exception, which is the tape for the one-level set "Bug
2839     Machine" by Juergen Bonhagen.
2840   */
2841
2842   game.use_change_when_pushing_bug =
2843     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2844      !(tape.playing &&
2845        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2847
2848   /*
2849     Summary of bugfix/change:
2850     Fixed handling for blocking the field the player leaves when moving.
2851
2852     Fixed/changed in version:
2853     3.1.1
2854
2855     Description:
2856     Before 3.1.1, when "block last field when moving" was enabled, the field
2857     the player is leaving when moving was blocked for the time of the move,
2858     and was directly unblocked afterwards. This resulted in the last field
2859     being blocked for exactly one less than the number of frames of one player
2860     move. Additionally, even when blocking was disabled, the last field was
2861     blocked for exactly one frame.
2862     Since 3.1.1, due to changes in player movement handling, the last field
2863     is not blocked at all when blocking is disabled. When blocking is enabled,
2864     the last field is blocked for exactly the number of frames of one player
2865     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866     last field is blocked for exactly one more than the number of frames of
2867     one player move.
2868
2869     Affected levels/tapes:
2870     (!!! yet to be determined -- probably many !!!)
2871   */
2872
2873   game.use_block_last_field_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,1,0));
2875
2876   game_em.use_single_button =
2877     (game.engine_version > VERSION_IDENT(4,0,0,2));
2878
2879   game_em.use_snap_key_bug =
2880     (game.engine_version < VERSION_IDENT(4,0,1,0));
2881
2882   /* ---------------------------------------------------------------------- */
2883
2884   /* set maximal allowed number of custom element changes per game frame */
2885   game.max_num_changes_per_frame = 1;
2886
2887   /* default scan direction: scan playfield from top/left to bottom/right */
2888   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2889
2890   /* dynamically adjust element properties according to game engine version */
2891   InitElementPropertiesEngine(game.engine_version);
2892
2893 #if 0
2894   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895   printf("          tape version == %06d [%s] [file: %06d]\n",
2896          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2897          tape.file_version);
2898   printf("       => game.engine_version == %06d\n", game.engine_version);
2899 #endif
2900
2901   /* ---------- initialize player's initial move delay --------------------- */
2902
2903   /* dynamically adjust player properties according to level information */
2904   for (i = 0; i < MAX_PLAYERS; i++)
2905     game.initial_move_delay_value[i] =
2906       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2907
2908   /* dynamically adjust player properties according to game engine version */
2909   for (i = 0; i < MAX_PLAYERS; i++)
2910     game.initial_move_delay[i] =
2911       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912        game.initial_move_delay_value[i] : 0);
2913
2914   /* ---------- initialize player's initial push delay --------------------- */
2915
2916   /* dynamically adjust player properties according to game engine version */
2917   game.initial_push_delay_value =
2918     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2919
2920   /* ---------- initialize changing elements ------------------------------- */
2921
2922   /* initialize changing elements information */
2923   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2924   {
2925     struct ElementInfo *ei = &element_info[i];
2926
2927     /* this pointer might have been changed in the level editor */
2928     ei->change = &ei->change_page[0];
2929
2930     if (!IS_CUSTOM_ELEMENT(i))
2931     {
2932       ei->change->target_element = EL_EMPTY_SPACE;
2933       ei->change->delay_fixed = 0;
2934       ei->change->delay_random = 0;
2935       ei->change->delay_frames = 1;
2936     }
2937
2938     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2939     {
2940       ei->has_change_event[j] = FALSE;
2941
2942       ei->event_page_nr[j] = 0;
2943       ei->event_page[j] = &ei->change_page[0];
2944     }
2945   }
2946
2947   /* add changing elements from pre-defined list */
2948   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2949   {
2950     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951     struct ElementInfo *ei = &element_info[ch_delay->element];
2952
2953     ei->change->target_element       = ch_delay->target_element;
2954     ei->change->delay_fixed          = ch_delay->change_delay;
2955
2956     ei->change->pre_change_function  = ch_delay->pre_change_function;
2957     ei->change->change_function      = ch_delay->change_function;
2958     ei->change->post_change_function = ch_delay->post_change_function;
2959
2960     ei->change->can_change = TRUE;
2961     ei->change->can_change_or_has_action = TRUE;
2962
2963     ei->has_change_event[CE_DELAY] = TRUE;
2964
2965     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2967   }
2968
2969   /* ---------- initialize internal run-time variables --------------------- */
2970
2971   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2972   {
2973     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2974
2975     for (j = 0; j < ei->num_change_pages; j++)
2976     {
2977       ei->change_page[j].can_change_or_has_action =
2978         (ei->change_page[j].can_change |
2979          ei->change_page[j].has_action);
2980     }
2981   }
2982
2983   /* add change events from custom element configuration */
2984   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2985   {
2986     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2987
2988     for (j = 0; j < ei->num_change_pages; j++)
2989     {
2990       if (!ei->change_page[j].can_change_or_has_action)
2991         continue;
2992
2993       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2994       {
2995         /* only add event page for the first page found with this event */
2996         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2997         {
2998           ei->has_change_event[k] = TRUE;
2999
3000           ei->event_page_nr[k] = j;
3001           ei->event_page[k] = &ei->change_page[j];
3002         }
3003       }
3004     }
3005   }
3006
3007   /* ---------- initialize reference elements in change conditions --------- */
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     int element = EL_CUSTOM_START + i;
3012     struct ElementInfo *ei = &element_info[element];
3013
3014     for (j = 0; j < ei->num_change_pages; j++)
3015     {
3016       int trigger_element = ei->change_page[j].initial_trigger_element;
3017
3018       if (trigger_element >= EL_PREV_CE_8 &&
3019           trigger_element <= EL_NEXT_CE_8)
3020         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3021
3022       ei->change_page[j].trigger_element = trigger_element;
3023     }
3024   }
3025
3026   /* ---------- initialize run-time trigger player and element ------------- */
3027
3028   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3029   {
3030     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3031
3032     for (j = 0; j < ei->num_change_pages; j++)
3033     {
3034       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038       ei->change_page[j].actual_trigger_ce_value = 0;
3039       ei->change_page[j].actual_trigger_ce_score = 0;
3040     }
3041   }
3042
3043   /* ---------- initialize trigger events ---------------------------------- */
3044
3045   /* initialize trigger events information */
3046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048       trigger_events[i][j] = FALSE;
3049
3050   /* add trigger events from element change event properties */
3051   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3052   {
3053     struct ElementInfo *ei = &element_info[i];
3054
3055     for (j = 0; j < ei->num_change_pages; j++)
3056     {
3057       if (!ei->change_page[j].can_change_or_has_action)
3058         continue;
3059
3060       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3061       {
3062         int trigger_element = ei->change_page[j].trigger_element;
3063
3064         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3065         {
3066           if (ei->change_page[j].has_event[k])
3067           {
3068             if (IS_GROUP_ELEMENT(trigger_element))
3069             {
3070               struct ElementGroupInfo *group =
3071                 element_info[trigger_element].group;
3072
3073               for (l = 0; l < group->num_elements_resolved; l++)
3074                 trigger_events[group->element_resolved[l]][k] = TRUE;
3075             }
3076             else if (trigger_element == EL_ANY_ELEMENT)
3077               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078                 trigger_events[l][k] = TRUE;
3079             else
3080               trigger_events[trigger_element][k] = TRUE;
3081           }
3082         }
3083       }
3084     }
3085   }
3086
3087   /* ---------- initialize push delay -------------------------------------- */
3088
3089   /* initialize push delay values to default */
3090   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3091   {
3092     if (!IS_CUSTOM_ELEMENT(i))
3093     {
3094       /* set default push delay values (corrected since version 3.0.7-1) */
3095       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3096       {
3097         element_info[i].push_delay_fixed = 2;
3098         element_info[i].push_delay_random = 8;
3099       }
3100       else
3101       {
3102         element_info[i].push_delay_fixed = 8;
3103         element_info[i].push_delay_random = 8;
3104       }
3105     }
3106   }
3107
3108   /* set push delay value for certain elements from pre-defined list */
3109   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3110   {
3111     int e = push_delay_list[i].element;
3112
3113     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3114     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3115   }
3116
3117   /* set push delay value for Supaplex elements for newer engine versions */
3118   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3119   {
3120     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3121     {
3122       if (IS_SP_ELEMENT(i))
3123       {
3124         /* set SP push delay to just enough to push under a falling zonk */
3125         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3126
3127         element_info[i].push_delay_fixed  = delay;
3128         element_info[i].push_delay_random = 0;
3129       }
3130     }
3131   }
3132
3133   /* ---------- initialize move stepsize ----------------------------------- */
3134
3135   /* initialize move stepsize values to default */
3136   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137     if (!IS_CUSTOM_ELEMENT(i))
3138       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3139
3140   /* set move stepsize value for certain elements from pre-defined list */
3141   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3142   {
3143     int e = move_stepsize_list[i].element;
3144
3145     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3146   }
3147
3148   /* ---------- initialize collect score ----------------------------------- */
3149
3150   /* initialize collect score values for custom elements from initial value */
3151   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     if (IS_CUSTOM_ELEMENT(i))
3153       element_info[i].collect_score = element_info[i].collect_score_initial;
3154
3155   /* ---------- initialize collect count ----------------------------------- */
3156
3157   /* initialize collect count values for non-custom elements */
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     if (!IS_CUSTOM_ELEMENT(i))
3160       element_info[i].collect_count_initial = 0;
3161
3162   /* add collect count values for all elements from pre-defined list */
3163   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164     element_info[collect_count_list[i].element].collect_count_initial =
3165       collect_count_list[i].count;
3166
3167   /* ---------- initialize access direction -------------------------------- */
3168
3169   /* initialize access direction values to default (access from every side) */
3170   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171     if (!IS_CUSTOM_ELEMENT(i))
3172       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3173
3174   /* set access direction value for certain elements from pre-defined list */
3175   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176     element_info[access_direction_list[i].element].access_direction =
3177       access_direction_list[i].direction;
3178
3179   /* ---------- initialize explosion content ------------------------------- */
3180   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3181   {
3182     if (IS_CUSTOM_ELEMENT(i))
3183       continue;
3184
3185     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3186     {
3187       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3188
3189       element_info[i].content.e[x][y] =
3190         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192          i == EL_PLAYER_3 ? EL_EMERALD :
3193          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194          i == EL_MOLE ? EL_EMERALD_RED :
3195          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200          i == EL_WALL_EMERALD ? EL_EMERALD :
3201          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206          i == EL_WALL_PEARL ? EL_PEARL :
3207          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3208          EL_EMPTY);
3209     }
3210   }
3211
3212   /* ---------- initialize recursion detection ------------------------------ */
3213   recursion_loop_depth = 0;
3214   recursion_loop_detected = FALSE;
3215   recursion_loop_element = EL_UNDEFINED;
3216
3217   /* ---------- initialize graphics engine ---------------------------------- */
3218   game.scroll_delay_value =
3219     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3221   game.scroll_delay_value =
3222     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3223
3224   /* ---------- initialize game engine snapshots ---------------------------- */
3225   for (i = 0; i < MAX_PLAYERS; i++)
3226     game.snapshot.last_action[i] = 0;
3227   game.snapshot.changed_action = FALSE;
3228   game.snapshot.collected_item = FALSE;
3229   game.snapshot.mode =
3230     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231      SNAPSHOT_MODE_EVERY_STEP :
3232      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233      SNAPSHOT_MODE_EVERY_MOVE :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236   game.snapshot.save_snapshot = FALSE;
3237
3238   /* ---------- initialize level time for Supaplex engine ------------------- */
3239   /* Supaplex levels with time limit currently unsupported -- should be added */
3240   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3241     level.time = 0;
3242 }
3243
3244 int get_num_special_action(int element, int action_first, int action_last)
3245 {
3246   int num_special_action = 0;
3247   int i, j;
3248
3249   for (i = action_first; i <= action_last; i++)
3250   {
3251     boolean found = FALSE;
3252
3253     for (j = 0; j < NUM_DIRECTIONS; j++)
3254       if (el_act_dir2img(element, i, j) !=
3255           el_act_dir2img(element, ACTION_DEFAULT, j))
3256         found = TRUE;
3257
3258     if (found)
3259       num_special_action++;
3260     else
3261       break;
3262   }
3263
3264   return num_special_action;
3265 }
3266
3267
3268 /*
3269   =============================================================================
3270   InitGame()
3271   -----------------------------------------------------------------------------
3272   initialize and start new game
3273   =============================================================================
3274 */
3275
3276 void InitGame()
3277 {
3278   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280   int fade_mask = REDRAW_FIELD;
3281
3282   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3283   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3284   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3285   int initial_move_dir = MV_DOWN;
3286   int i, j, x, y;
3287
3288   // required here to update video display before fading (FIX THIS)
3289   DrawMaskedBorder(REDRAW_DOOR_2);
3290
3291   if (!game.restart_level)
3292     CloseDoor(DOOR_CLOSE_1);
3293
3294   SetGameStatus(GAME_MODE_PLAYING);
3295
3296   if (level_editor_test_game)
3297     FadeSkipNextFadeIn();
3298   else
3299     FadeSetEnterScreen();
3300
3301   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302     fade_mask = REDRAW_ALL;
3303
3304   FadeLevelSoundsAndMusic();
3305
3306   ExpireSoundLoops(TRUE);
3307
3308   if (!level_editor_test_game)
3309     FadeOut(fade_mask);
3310
3311   /* needed if different viewport properties defined for playing */
3312   ChangeViewportPropertiesIfNeeded();
3313
3314   ClearField();
3315
3316   DrawCompleteVideoDisplay();
3317
3318   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3319
3320   InitGameEngine();
3321   InitGameControlValues();
3322
3323   /* don't play tapes over network */
3324   network_playing = (options.network && !tape.playing);
3325
3326   for (i = 0; i < MAX_PLAYERS; i++)
3327   {
3328     struct PlayerInfo *player = &stored_player[i];
3329
3330     player->index_nr = i;
3331     player->index_bit = (1 << i);
3332     player->element_nr = EL_PLAYER_1 + i;
3333
3334     player->present = FALSE;
3335     player->active = FALSE;
3336     player->mapped = FALSE;
3337
3338     player->killed = FALSE;
3339     player->reanimated = FALSE;
3340
3341     player->action = 0;
3342     player->effective_action = 0;
3343     player->programmed_action = 0;
3344
3345     player->mouse_action.lx = 0;
3346     player->mouse_action.ly = 0;
3347     player->mouse_action.button = 0;
3348
3349     player->effective_mouse_action.lx = 0;
3350     player->effective_mouse_action.ly = 0;
3351     player->effective_mouse_action.button = 0;
3352
3353     player->score = 0;
3354     player->score_final = 0;
3355
3356     player->health = MAX_HEALTH;
3357     player->health_final = MAX_HEALTH;
3358
3359     player->gems_still_needed = level.gems_needed;
3360     player->sokobanfields_still_needed = 0;
3361     player->lights_still_needed = 0;
3362     player->friends_still_needed = 0;
3363
3364     for (j = 0; j < MAX_NUM_KEYS; j++)
3365       player->key[j] = FALSE;
3366
3367     player->num_white_keys = 0;
3368
3369     player->dynabomb_count = 0;
3370     player->dynabomb_size = 1;
3371     player->dynabombs_left = 0;
3372     player->dynabomb_xl = FALSE;
3373
3374     player->MovDir = initial_move_dir;
3375     player->MovPos = 0;
3376     player->GfxPos = 0;
3377     player->GfxDir = initial_move_dir;
3378     player->GfxAction = ACTION_DEFAULT;
3379     player->Frame = 0;
3380     player->StepFrame = 0;
3381
3382     player->initial_element = player->element_nr;
3383     player->artwork_element =
3384       (level.use_artwork_element[i] ? level.artwork_element[i] :
3385        player->element_nr);
3386     player->use_murphy = FALSE;
3387
3388     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3389     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3390
3391     player->gravity = level.initial_player_gravity[i];
3392
3393     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3394
3395     player->actual_frame_counter = 0;
3396
3397     player->step_counter = 0;
3398
3399     player->last_move_dir = initial_move_dir;
3400
3401     player->is_active = FALSE;
3402
3403     player->is_waiting = FALSE;
3404     player->is_moving = FALSE;
3405     player->is_auto_moving = FALSE;
3406     player->is_digging = FALSE;
3407     player->is_snapping = FALSE;
3408     player->is_collecting = FALSE;
3409     player->is_pushing = FALSE;
3410     player->is_switching = FALSE;
3411     player->is_dropping = FALSE;
3412     player->is_dropping_pressed = FALSE;
3413
3414     player->is_bored = FALSE;
3415     player->is_sleeping = FALSE;
3416
3417     player->was_waiting = TRUE;
3418     player->was_moving = FALSE;
3419     player->was_snapping = FALSE;
3420     player->was_dropping = FALSE;
3421
3422     player->force_dropping = FALSE;
3423
3424     player->frame_counter_bored = -1;
3425     player->frame_counter_sleeping = -1;
3426
3427     player->anim_delay_counter = 0;
3428     player->post_delay_counter = 0;
3429
3430     player->dir_waiting = initial_move_dir;
3431     player->action_waiting = ACTION_DEFAULT;
3432     player->last_action_waiting = ACTION_DEFAULT;
3433     player->special_action_bored = ACTION_DEFAULT;
3434     player->special_action_sleeping = ACTION_DEFAULT;
3435
3436     player->switch_x = -1;
3437     player->switch_y = -1;
3438
3439     player->drop_x = -1;
3440     player->drop_y = -1;
3441
3442     player->show_envelope = 0;
3443
3444     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3445
3446     player->push_delay       = -1;      /* initialized when pushing starts */
3447     player->push_delay_value = game.initial_push_delay_value;
3448
3449     player->drop_delay = 0;
3450     player->drop_pressed_delay = 0;
3451
3452     player->last_jx = -1;
3453     player->last_jy = -1;
3454     player->jx = -1;
3455     player->jy = -1;
3456
3457     player->shield_normal_time_left = 0;
3458     player->shield_deadly_time_left = 0;
3459
3460     player->inventory_infinite_element = EL_UNDEFINED;
3461     player->inventory_size = 0;
3462
3463     if (level.use_initial_inventory[i])
3464     {
3465       for (j = 0; j < level.initial_inventory_size[i]; j++)
3466       {
3467         int element = level.initial_inventory_content[i][j];
3468         int collect_count = element_info[element].collect_count_initial;
3469         int k;
3470
3471         if (!IS_CUSTOM_ELEMENT(element))
3472           collect_count = 1;
3473
3474         if (collect_count == 0)
3475           player->inventory_infinite_element = element;
3476         else
3477           for (k = 0; k < collect_count; k++)
3478             if (player->inventory_size < MAX_INVENTORY_SIZE)
3479               player->inventory_element[player->inventory_size++] = element;
3480       }
3481     }
3482
3483     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3484     SnapField(player, 0, 0);
3485
3486     player->LevelSolved = FALSE;
3487     player->GameOver = FALSE;
3488
3489     player->LevelSolved_GameWon = FALSE;
3490     player->LevelSolved_GameEnd = FALSE;
3491     player->LevelSolved_PanelOff = FALSE;
3492     player->LevelSolved_SaveTape = FALSE;
3493     player->LevelSolved_SaveScore = FALSE;
3494
3495     player->LevelSolved_CountingTime = 0;
3496     player->LevelSolved_CountingScore = 0;
3497     player->LevelSolved_CountingHealth = 0;
3498
3499     map_player_action[i] = i;
3500   }
3501
3502   network_player_action_received = FALSE;
3503
3504 #if defined(NETWORK_AVALIABLE)
3505   /* initial null action */
3506   if (network_playing)
3507     SendToServer_MovePlayer(MV_NONE);
3508 #endif
3509
3510   ZX = ZY = -1;
3511   ExitX = ExitY = -1;
3512
3513   FrameCounter = 0;
3514   TimeFrames = 0;
3515   TimePlayed = 0;
3516   TimeLeft = level.time;
3517   TapeTime = 0;
3518
3519   ScreenMovDir = MV_NONE;
3520   ScreenMovPos = 0;
3521   ScreenGfxPos = 0;
3522
3523   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3524
3525   AllPlayersGone = FALSE;
3526
3527   game.no_time_limit = (level.time == 0);
3528
3529   game.yamyam_content_nr = 0;
3530   game.robot_wheel_active = FALSE;
3531   game.magic_wall_active = FALSE;
3532   game.magic_wall_time_left = 0;
3533   game.light_time_left = 0;
3534   game.timegate_time_left = 0;
3535   game.switchgate_pos = 0;
3536   game.wind_direction = level.wind_direction_initial;
3537
3538   game.lenses_time_left = 0;
3539   game.magnify_time_left = 0;
3540
3541   game.ball_state = level.ball_state_initial;
3542   game.ball_content_nr = 0;
3543
3544   game.envelope_active = FALSE;
3545
3546   /* set focus to local player for network games, else to all players */
3547   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3548   game.centered_player_nr_next = game.centered_player_nr;
3549   game.set_centered_player = FALSE;
3550
3551   if (network_playing && tape.recording)
3552   {
3553     /* store client dependent player focus when recording network games */
3554     tape.centered_player_nr_next = game.centered_player_nr_next;
3555     tape.set_centered_player = TRUE;
3556   }
3557
3558   for (i = 0; i < NUM_BELTS; i++)
3559   {
3560     game.belt_dir[i] = MV_NONE;
3561     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3562   }
3563
3564   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3565     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3566
3567 #if DEBUG_INIT_PLAYER
3568   if (options.debug)
3569   {
3570     printf("Player status at level initialization:\n");
3571   }
3572 #endif
3573
3574   SCAN_PLAYFIELD(x, y)
3575   {
3576     Feld[x][y] = level.field[x][y];
3577     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3578     ChangeDelay[x][y] = 0;
3579     ChangePage[x][y] = -1;
3580     CustomValue[x][y] = 0;              /* initialized in InitField() */
3581     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3582     AmoebaNr[x][y] = 0;
3583     WasJustMoving[x][y] = 0;
3584     WasJustFalling[x][y] = 0;
3585     CheckCollision[x][y] = 0;
3586     CheckImpact[x][y] = 0;
3587     Stop[x][y] = FALSE;
3588     Pushed[x][y] = FALSE;
3589
3590     ChangeCount[x][y] = 0;
3591     ChangeEvent[x][y] = -1;
3592
3593     ExplodePhase[x][y] = 0;
3594     ExplodeDelay[x][y] = 0;
3595     ExplodeField[x][y] = EX_TYPE_NONE;
3596
3597     RunnerVisit[x][y] = 0;
3598     PlayerVisit[x][y] = 0;
3599
3600     GfxFrame[x][y] = 0;
3601     GfxRandom[x][y] = INIT_GFX_RANDOM();
3602     GfxElement[x][y] = EL_UNDEFINED;
3603     GfxAction[x][y] = ACTION_DEFAULT;
3604     GfxDir[x][y] = MV_NONE;
3605     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3606   }
3607
3608   SCAN_PLAYFIELD(x, y)
3609   {
3610     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3611       emulate_bd = FALSE;
3612     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3613       emulate_sb = FALSE;
3614     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3615       emulate_sp = FALSE;
3616
3617     InitField(x, y, TRUE);
3618
3619     ResetGfxAnimation(x, y);
3620   }
3621
3622   InitBeltMovement();
3623
3624   for (i = 0; i < MAX_PLAYERS; i++)
3625   {
3626     struct PlayerInfo *player = &stored_player[i];
3627
3628     /* set number of special actions for bored and sleeping animation */
3629     player->num_special_action_bored =
3630       get_num_special_action(player->artwork_element,
3631                              ACTION_BORING_1, ACTION_BORING_LAST);
3632     player->num_special_action_sleeping =
3633       get_num_special_action(player->artwork_element,
3634                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3635   }
3636
3637   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3638                     emulate_sb ? EMU_SOKOBAN :
3639                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3640
3641   /* initialize type of slippery elements */
3642   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3643   {
3644     if (!IS_CUSTOM_ELEMENT(i))
3645     {
3646       /* default: elements slip down either to the left or right randomly */
3647       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3648
3649       /* SP style elements prefer to slip down on the left side */
3650       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3651         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3652
3653       /* BD style elements prefer to slip down on the left side */
3654       if (game.emulation == EMU_BOULDERDASH)
3655         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3656     }
3657   }
3658
3659   /* initialize explosion and ignition delay */
3660   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3661   {
3662     if (!IS_CUSTOM_ELEMENT(i))
3663     {
3664       int num_phase = 8;
3665       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3666                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3667                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3668       int last_phase = (num_phase + 1) * delay;
3669       int half_phase = (num_phase / 2) * delay;
3670
3671       element_info[i].explosion_delay = last_phase - 1;
3672       element_info[i].ignition_delay = half_phase;
3673
3674       if (i == EL_BLACK_ORB)
3675         element_info[i].ignition_delay = 1;
3676     }
3677   }
3678
3679   /* correct non-moving belts to start moving left */
3680   for (i = 0; i < NUM_BELTS; i++)
3681     if (game.belt_dir[i] == MV_NONE)
3682       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3683
3684 #if USE_NEW_PLAYER_ASSIGNMENTS
3685   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3686   /* choose default local player */
3687   local_player = &stored_player[0];
3688
3689   for (i = 0; i < MAX_PLAYERS; i++)
3690     stored_player[i].connected = FALSE;
3691
3692   local_player->connected = TRUE;
3693   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3694
3695   if (tape.playing)
3696   {
3697     for (i = 0; i < MAX_PLAYERS; i++)
3698       stored_player[i].connected = tape.player_participates[i];
3699   }
3700   else if (game.team_mode && !options.network)
3701   {
3702     /* try to guess locally connected team mode players (needed for correct
3703        assignment of player figures from level to locally playing players) */
3704
3705     for (i = 0; i < MAX_PLAYERS; i++)
3706       if (setup.input[i].use_joystick ||
3707           setup.input[i].key.left != KSYM_UNDEFINED)
3708         stored_player[i].connected = TRUE;
3709   }
3710
3711 #if DEBUG_INIT_PLAYER
3712   if (options.debug)
3713   {
3714     printf("Player status after level initialization:\n");
3715
3716     for (i = 0; i < MAX_PLAYERS; i++)
3717     {
3718       struct PlayerInfo *player = &stored_player[i];
3719
3720       printf("- player %d: present == %d, connected == %d, active == %d",
3721              i + 1,
3722              player->present,
3723              player->connected,
3724              player->active);
3725
3726       if (local_player == player)
3727         printf(" (local player)");
3728
3729       printf("\n");
3730     }
3731   }
3732 #endif
3733
3734 #if DEBUG_INIT_PLAYER
3735   if (options.debug)
3736     printf("Reassigning players ...\n");
3737 #endif
3738
3739   /* check if any connected player was not found in playfield */
3740   for (i = 0; i < MAX_PLAYERS; i++)
3741   {
3742     struct PlayerInfo *player = &stored_player[i];
3743
3744     if (player->connected && !player->present)
3745     {
3746       struct PlayerInfo *field_player = NULL;
3747
3748 #if DEBUG_INIT_PLAYER
3749       if (options.debug)
3750         printf("- looking for field player for player %d ...\n", i + 1);
3751 #endif
3752
3753       /* assign first free player found that is present in the playfield */
3754
3755       /* first try: look for unmapped playfield player that is not connected */
3756       for (j = 0; j < MAX_PLAYERS; j++)
3757         if (field_player == NULL &&
3758             stored_player[j].present &&
3759             !stored_player[j].mapped &&
3760             !stored_player[j].connected)
3761           field_player = &stored_player[j];
3762
3763       /* second try: look for *any* unmapped playfield player */
3764       for (j = 0; j < MAX_PLAYERS; j++)
3765         if (field_player == NULL &&
3766             stored_player[j].present &&
3767             !stored_player[j].mapped)
3768           field_player = &stored_player[j];
3769
3770       if (field_player != NULL)
3771       {
3772         int jx = field_player->jx, jy = field_player->jy;
3773
3774 #if DEBUG_INIT_PLAYER
3775         if (options.debug)
3776           printf("- found player %d\n", field_player->index_nr + 1);
3777 #endif
3778
3779         player->present = FALSE;
3780         player->active = FALSE;
3781
3782         field_player->present = TRUE;
3783         field_player->active = TRUE;
3784
3785         /*
3786         player->initial_element = field_player->initial_element;
3787         player->artwork_element = field_player->artwork_element;
3788
3789         player->block_last_field       = field_player->block_last_field;
3790         player->block_delay_adjustment = field_player->block_delay_adjustment;
3791         */
3792
3793         StorePlayer[jx][jy] = field_player->element_nr;
3794
3795         field_player->jx = field_player->last_jx = jx;
3796         field_player->jy = field_player->last_jy = jy;
3797
3798         if (local_player == player)
3799           local_player = field_player;
3800
3801         map_player_action[field_player->index_nr] = i;
3802
3803         field_player->mapped = TRUE;
3804
3805 #if DEBUG_INIT_PLAYER
3806         if (options.debug)
3807           printf("- map_player_action[%d] == %d\n",
3808                  field_player->index_nr + 1, i + 1);
3809 #endif
3810       }
3811     }
3812
3813     if (player->connected && player->present)
3814       player->mapped = TRUE;
3815   }
3816
3817 #if DEBUG_INIT_PLAYER
3818   if (options.debug)
3819   {
3820     printf("Player status after player assignment (first stage):\n");
3821
3822     for (i = 0; i < MAX_PLAYERS; i++)
3823     {
3824       struct PlayerInfo *player = &stored_player[i];
3825
3826       printf("- player %d: present == %d, connected == %d, active == %d",
3827              i + 1,
3828              player->present,
3829              player->connected,
3830              player->active);
3831
3832       if (local_player == player)
3833         printf(" (local player)");
3834
3835       printf("\n");
3836     }
3837   }
3838 #endif
3839
3840 #else
3841
3842   /* check if any connected player was not found in playfield */
3843   for (i = 0; i < MAX_PLAYERS; i++)
3844   {
3845     struct PlayerInfo *player = &stored_player[i];
3846
3847     if (player->connected && !player->present)
3848     {
3849       for (j = 0; j < MAX_PLAYERS; j++)
3850       {
3851         struct PlayerInfo *field_player = &stored_player[j];
3852         int jx = field_player->jx, jy = field_player->jy;
3853
3854         /* assign first free player found that is present in the playfield */
3855         if (field_player->present && !field_player->connected)
3856         {
3857           player->present = TRUE;
3858           player->active = TRUE;
3859
3860           field_player->present = FALSE;
3861           field_player->active = FALSE;
3862
3863           player->initial_element = field_player->initial_element;
3864           player->artwork_element = field_player->artwork_element;
3865
3866           player->block_last_field       = field_player->block_last_field;
3867           player->block_delay_adjustment = field_player->block_delay_adjustment;
3868
3869           StorePlayer[jx][jy] = player->element_nr;
3870
3871           player->jx = player->last_jx = jx;
3872           player->jy = player->last_jy = jy;
3873
3874           break;
3875         }
3876       }
3877     }
3878   }
3879 #endif
3880
3881 #if 0
3882   printf("::: local_player->present == %d\n", local_player->present);
3883 #endif
3884
3885   if (tape.playing)
3886   {
3887     /* when playing a tape, eliminate all players who do not participate */
3888
3889 #if USE_NEW_PLAYER_ASSIGNMENTS
3890
3891     if (!game.team_mode)
3892     {
3893       for (i = 0; i < MAX_PLAYERS; i++)
3894       {
3895         if (stored_player[i].active &&
3896             !tape.player_participates[map_player_action[i]])
3897         {
3898           struct PlayerInfo *player = &stored_player[i];
3899           int jx = player->jx, jy = player->jy;
3900
3901 #if DEBUG_INIT_PLAYER
3902           if (options.debug)
3903             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3904 #endif
3905
3906           player->active = FALSE;
3907           StorePlayer[jx][jy] = 0;
3908           Feld[jx][jy] = EL_EMPTY;
3909         }
3910       }
3911     }
3912
3913 #else
3914
3915     for (i = 0; i < MAX_PLAYERS; i++)
3916     {
3917       if (stored_player[i].active &&
3918           !tape.player_participates[i])
3919       {
3920         struct PlayerInfo *player = &stored_player[i];
3921         int jx = player->jx, jy = player->jy;
3922
3923         player->active = FALSE;
3924         StorePlayer[jx][jy] = 0;
3925         Feld[jx][jy] = EL_EMPTY;
3926       }
3927     }
3928 #endif
3929   }
3930   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3931   {
3932     /* when in single player mode, eliminate all but the first active player */
3933
3934     for (i = 0; i < MAX_PLAYERS; i++)
3935     {
3936       if (stored_player[i].active)
3937       {
3938         for (j = i + 1; j < MAX_PLAYERS; j++)
3939         {
3940           if (stored_player[j].active)
3941           {
3942             struct PlayerInfo *player = &stored_player[j];
3943             int jx = player->jx, jy = player->jy;
3944
3945             player->active = FALSE;
3946             player->present = FALSE;
3947
3948             StorePlayer[jx][jy] = 0;
3949             Feld[jx][jy] = EL_EMPTY;
3950           }
3951         }
3952       }
3953     }
3954   }
3955
3956   /* when recording the game, store which players take part in the game */
3957   if (tape.recording)
3958   {
3959 #if USE_NEW_PLAYER_ASSIGNMENTS
3960     for (i = 0; i < MAX_PLAYERS; i++)
3961       if (stored_player[i].connected)
3962         tape.player_participates[i] = TRUE;
3963 #else
3964     for (i = 0; i < MAX_PLAYERS; i++)
3965       if (stored_player[i].active)
3966         tape.player_participates[i] = TRUE;
3967 #endif
3968   }
3969
3970 #if DEBUG_INIT_PLAYER
3971   if (options.debug)
3972   {
3973     printf("Player status after player assignment (final stage):\n");
3974
3975     for (i = 0; i < MAX_PLAYERS; i++)
3976     {
3977       struct PlayerInfo *player = &stored_player[i];
3978
3979       printf("- player %d: present == %d, connected == %d, active == %d",
3980              i + 1,
3981              player->present,
3982              player->connected,
3983              player->active);
3984
3985       if (local_player == player)
3986         printf(" (local player)");
3987
3988       printf("\n");
3989     }
3990   }
3991 #endif
3992
3993   if (BorderElement == EL_EMPTY)
3994   {
3995     SBX_Left = 0;
3996     SBX_Right = lev_fieldx - SCR_FIELDX;
3997     SBY_Upper = 0;
3998     SBY_Lower = lev_fieldy - SCR_FIELDY;
3999   }
4000   else
4001   {
4002     SBX_Left = -1;
4003     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4004     SBY_Upper = -1;
4005     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4006   }
4007
4008   if (full_lev_fieldx <= SCR_FIELDX)
4009     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4010   if (full_lev_fieldy <= SCR_FIELDY)
4011     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4012
4013   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4014     SBX_Left--;
4015   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4016     SBY_Upper--;
4017
4018   /* if local player not found, look for custom element that might create
4019      the player (make some assumptions about the right custom element) */
4020   if (!local_player->present)
4021   {
4022     int start_x = 0, start_y = 0;
4023     int found_rating = 0;
4024     int found_element = EL_UNDEFINED;
4025     int player_nr = local_player->index_nr;
4026
4027     SCAN_PLAYFIELD(x, y)
4028     {
4029       int element = Feld[x][y];
4030       int content;
4031       int xx, yy;
4032       boolean is_player;
4033
4034       if (level.use_start_element[player_nr] &&
4035           level.start_element[player_nr] == element &&
4036           found_rating < 4)
4037       {
4038         start_x = x;
4039         start_y = y;
4040
4041         found_rating = 4;
4042         found_element = element;
4043       }
4044
4045       if (!IS_CUSTOM_ELEMENT(element))
4046         continue;
4047
4048       if (CAN_CHANGE(element))
4049       {
4050         for (i = 0; i < element_info[element].num_change_pages; i++)
4051         {
4052           /* check for player created from custom element as single target */
4053           content = element_info[element].change_page[i].target_element;
4054           is_player = ELEM_IS_PLAYER(content);
4055
4056           if (is_player && (found_rating < 3 ||
4057                             (found_rating == 3 && element < found_element)))
4058           {
4059             start_x = x;
4060             start_y = y;
4061
4062             found_rating = 3;
4063             found_element = element;
4064           }
4065         }
4066       }
4067
4068       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4069       {
4070         /* check for player created from custom element as explosion content */
4071         content = element_info[element].content.e[xx][yy];
4072         is_player = ELEM_IS_PLAYER(content);
4073
4074         if (is_player && (found_rating < 2 ||
4075                           (found_rating == 2 && element < found_element)))
4076         {
4077           start_x = x + xx - 1;
4078           start_y = y + yy - 1;
4079
4080           found_rating = 2;
4081           found_element = element;
4082         }
4083
4084         if (!CAN_CHANGE(element))
4085           continue;
4086
4087         for (i = 0; i < element_info[element].num_change_pages; i++)
4088         {
4089           /* check for player created from custom element as extended target */
4090           content =
4091             element_info[element].change_page[i].target_content.e[xx][yy];
4092
4093           is_player = ELEM_IS_PLAYER(content);
4094
4095           if (is_player && (found_rating < 1 ||
4096                             (found_rating == 1 && element < found_element)))
4097           {
4098             start_x = x + xx - 1;
4099             start_y = y + yy - 1;
4100
4101             found_rating = 1;
4102             found_element = element;
4103           }
4104         }
4105       }
4106     }
4107
4108     scroll_x = SCROLL_POSITION_X(start_x);
4109     scroll_y = SCROLL_POSITION_Y(start_y);
4110   }
4111   else
4112   {
4113     scroll_x = SCROLL_POSITION_X(local_player->jx);
4114     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4115   }
4116
4117   /* !!! FIX THIS (START) !!! */
4118   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4119   {
4120     InitGameEngine_EM();
4121   }
4122   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4123   {
4124     InitGameEngine_SP();
4125   }
4126   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4127   {
4128     InitGameEngine_MM();
4129   }
4130   else
4131   {
4132     DrawLevel(REDRAW_FIELD);
4133     DrawAllPlayers();
4134
4135     /* after drawing the level, correct some elements */
4136     if (game.timegate_time_left == 0)
4137       CloseAllOpenTimegates();
4138   }
4139
4140   /* blit playfield from scroll buffer to normal back buffer for fading in */
4141   BlitScreenToBitmap(backbuffer);
4142   /* !!! FIX THIS (END) !!! */
4143
4144   DrawMaskedBorder(fade_mask);
4145
4146   FadeIn(fade_mask);
4147
4148 #if 1
4149   // full screen redraw is required at this point in the following cases:
4150   // - special editor door undrawn when game was started from level editor
4151   // - drawing area (playfield) was changed and has to be removed completely
4152   redraw_mask = REDRAW_ALL;
4153   BackToFront();
4154 #endif
4155
4156   if (!game.restart_level)
4157   {
4158     /* copy default game door content to main double buffer */
4159
4160     /* !!! CHECK AGAIN !!! */
4161     SetPanelBackground();
4162     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4163     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4164   }
4165
4166   SetPanelBackground();
4167   SetDrawBackgroundMask(REDRAW_DOOR_1);
4168
4169   UpdateAndDisplayGameControlValues();
4170
4171   if (!game.restart_level)
4172   {
4173     UnmapGameButtons();
4174     UnmapTapeButtons();
4175
4176     FreeGameButtons();
4177     CreateGameButtons();
4178
4179     MapGameButtons();
4180     MapTapeButtons();
4181
4182     /* copy actual game door content to door double buffer for OpenDoor() */
4183     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4184
4185     OpenDoor(DOOR_OPEN_ALL);
4186
4187     PlaySound(SND_GAME_STARTING);
4188
4189     if (setup.sound_music)
4190       PlayLevelMusic();
4191
4192     KeyboardAutoRepeatOffUnlessAutoplay();
4193
4194 #if DEBUG_INIT_PLAYER
4195     if (options.debug)
4196     {
4197       printf("Player status (final):\n");
4198
4199       for (i = 0; i < MAX_PLAYERS; i++)
4200       {
4201         struct PlayerInfo *player = &stored_player[i];
4202
4203         printf("- player %d: present == %d, connected == %d, active == %d",
4204                i + 1,
4205                player->present,
4206                player->connected,
4207                player->active);
4208
4209         if (local_player == player)
4210           printf(" (local player)");
4211
4212         printf("\n");
4213       }
4214     }
4215 #endif
4216   }
4217
4218   UnmapAllGadgets();
4219
4220   MapGameButtons();
4221   MapTapeButtons();
4222
4223   if (!game.restart_level && !tape.playing)
4224   {
4225     LevelStats_incPlayed(level_nr);
4226
4227     SaveLevelSetup_SeriesInfo();
4228   }
4229
4230   game.restart_level = FALSE;
4231   game.restart_game_message = NULL;
4232
4233   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4234     InitGameActions_MM();
4235
4236   SaveEngineSnapshotToListInitial();
4237 }
4238
4239 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4240                         int actual_player_x, int actual_player_y)
4241 {
4242   /* this is used for non-R'n'D game engines to update certain engine values */
4243
4244   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4245   {
4246     actual_player_x = correctLevelPosX_EM(actual_player_x);
4247     actual_player_y = correctLevelPosY_EM(actual_player_y);
4248   }
4249
4250   /* needed to determine if sounds are played within the visible screen area */
4251   scroll_x = actual_scroll_x;
4252   scroll_y = actual_scroll_y;
4253
4254   /* needed to get player position for "follow finger" playing input method */
4255   local_player->jx = actual_player_x;
4256   local_player->jy = actual_player_y;
4257 }
4258
4259 void InitMovDir(int x, int y)
4260 {
4261   int i, element = Feld[x][y];
4262   static int xy[4][2] =
4263   {
4264     {  0, +1 },
4265     { +1,  0 },
4266     {  0, -1 },
4267     { -1,  0 }
4268   };
4269   static int direction[3][4] =
4270   {
4271     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4272     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4273     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4274   };
4275
4276   switch (element)
4277   {
4278     case EL_BUG_RIGHT:
4279     case EL_BUG_UP:
4280     case EL_BUG_LEFT:
4281     case EL_BUG_DOWN:
4282       Feld[x][y] = EL_BUG;
4283       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4284       break;
4285
4286     case EL_SPACESHIP_RIGHT:
4287     case EL_SPACESHIP_UP:
4288     case EL_SPACESHIP_LEFT:
4289     case EL_SPACESHIP_DOWN:
4290       Feld[x][y] = EL_SPACESHIP;
4291       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4292       break;
4293
4294     case EL_BD_BUTTERFLY_RIGHT:
4295     case EL_BD_BUTTERFLY_UP:
4296     case EL_BD_BUTTERFLY_LEFT:
4297     case EL_BD_BUTTERFLY_DOWN:
4298       Feld[x][y] = EL_BD_BUTTERFLY;
4299       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4300       break;
4301
4302     case EL_BD_FIREFLY_RIGHT:
4303     case EL_BD_FIREFLY_UP:
4304     case EL_BD_FIREFLY_LEFT:
4305     case EL_BD_FIREFLY_DOWN:
4306       Feld[x][y] = EL_BD_FIREFLY;
4307       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4308       break;
4309
4310     case EL_PACMAN_RIGHT:
4311     case EL_PACMAN_UP:
4312     case EL_PACMAN_LEFT:
4313     case EL_PACMAN_DOWN:
4314       Feld[x][y] = EL_PACMAN;
4315       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4316       break;
4317
4318     case EL_YAMYAM_LEFT:
4319     case EL_YAMYAM_RIGHT:
4320     case EL_YAMYAM_UP:
4321     case EL_YAMYAM_DOWN:
4322       Feld[x][y] = EL_YAMYAM;
4323       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4324       break;
4325
4326     case EL_SP_SNIKSNAK:
4327       MovDir[x][y] = MV_UP;
4328       break;
4329
4330     case EL_SP_ELECTRON:
4331       MovDir[x][y] = MV_LEFT;
4332       break;
4333
4334     case EL_MOLE_LEFT:
4335     case EL_MOLE_RIGHT:
4336     case EL_MOLE_UP:
4337     case EL_MOLE_DOWN:
4338       Feld[x][y] = EL_MOLE;
4339       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4340       break;
4341
4342     default:
4343       if (IS_CUSTOM_ELEMENT(element))
4344       {
4345         struct ElementInfo *ei = &element_info[element];
4346         int move_direction_initial = ei->move_direction_initial;
4347         int move_pattern = ei->move_pattern;
4348
4349         if (move_direction_initial == MV_START_PREVIOUS)
4350         {
4351           if (MovDir[x][y] != MV_NONE)
4352             return;
4353
4354           move_direction_initial = MV_START_AUTOMATIC;
4355         }
4356
4357         if (move_direction_initial == MV_START_RANDOM)
4358           MovDir[x][y] = 1 << RND(4);
4359         else if (move_direction_initial & MV_ANY_DIRECTION)
4360           MovDir[x][y] = move_direction_initial;
4361         else if (move_pattern == MV_ALL_DIRECTIONS ||
4362                  move_pattern == MV_TURNING_LEFT ||
4363                  move_pattern == MV_TURNING_RIGHT ||
4364                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4365                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4366                  move_pattern == MV_TURNING_RANDOM)
4367           MovDir[x][y] = 1 << RND(4);
4368         else if (move_pattern == MV_HORIZONTAL)
4369           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4370         else if (move_pattern == MV_VERTICAL)
4371           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4372         else if (move_pattern & MV_ANY_DIRECTION)
4373           MovDir[x][y] = element_info[element].move_pattern;
4374         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4375                  move_pattern == MV_ALONG_RIGHT_SIDE)
4376         {
4377           /* use random direction as default start direction */
4378           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4379             MovDir[x][y] = 1 << RND(4);
4380
4381           for (i = 0; i < NUM_DIRECTIONS; i++)
4382           {
4383             int x1 = x + xy[i][0];
4384             int y1 = y + xy[i][1];
4385
4386             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4387             {
4388               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4389                 MovDir[x][y] = direction[0][i];
4390               else
4391                 MovDir[x][y] = direction[1][i];
4392
4393               break;
4394             }
4395           }
4396         }                
4397       }
4398       else
4399       {
4400         MovDir[x][y] = 1 << RND(4);
4401
4402         if (element != EL_BUG &&
4403             element != EL_SPACESHIP &&
4404             element != EL_BD_BUTTERFLY &&
4405             element != EL_BD_FIREFLY)
4406           break;
4407
4408         for (i = 0; i < NUM_DIRECTIONS; i++)
4409         {
4410           int x1 = x + xy[i][0];
4411           int y1 = y + xy[i][1];
4412
4413           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4414           {
4415             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4416             {
4417               MovDir[x][y] = direction[0][i];
4418               break;
4419             }
4420             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4421                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4422             {
4423               MovDir[x][y] = direction[1][i];
4424               break;
4425             }
4426           }
4427         }
4428       }
4429       break;
4430   }
4431
4432   GfxDir[x][y] = MovDir[x][y];
4433 }
4434
4435 void InitAmoebaNr(int x, int y)
4436 {
4437   int i;
4438   int group_nr = AmoebeNachbarNr(x, y);
4439
4440   if (group_nr == 0)
4441   {
4442     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4443     {
4444       if (AmoebaCnt[i] == 0)
4445       {
4446         group_nr = i;
4447         break;
4448       }
4449     }
4450   }
4451
4452   AmoebaNr[x][y] = group_nr;
4453   AmoebaCnt[group_nr]++;
4454   AmoebaCnt2[group_nr]++;
4455 }
4456
4457 static void PlayerWins(struct PlayerInfo *player)
4458 {
4459   player->LevelSolved = TRUE;
4460   player->GameOver = TRUE;
4461
4462   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4463                          level.native_em_level->lev->score :
4464                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4465                          game_mm.score :
4466                          player->score);
4467   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4468                           MM_HEALTH(game_mm.laser_overload_value) :
4469                           player->health);
4470
4471   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4472                                       TimeLeft);
4473   player->LevelSolved_CountingScore = player->score_final;
4474   player->LevelSolved_CountingHealth = player->health_final;
4475 }
4476
4477 void GameWon()
4478 {
4479   static int time_count_steps;
4480   static int time, time_final;
4481   static int score, score_final;
4482   static int health, health_final;
4483   static int game_over_delay_1 = 0;
4484   static int game_over_delay_2 = 0;
4485   static int game_over_delay_3 = 0;
4486   int game_over_delay_value_1 = 50;
4487   int game_over_delay_value_2 = 25;
4488   int game_over_delay_value_3 = 50;
4489
4490   if (!local_player->LevelSolved_GameWon)
4491   {
4492     int i;
4493
4494     /* do not start end game actions before the player stops moving (to exit) */
4495     if (local_player->MovPos)
4496       return;
4497
4498     local_player->LevelSolved_GameWon = TRUE;
4499     local_player->LevelSolved_SaveTape = tape.recording;
4500     local_player->LevelSolved_SaveScore = !tape.playing;
4501
4502     if (!tape.playing)
4503     {
4504       LevelStats_incSolved(level_nr);
4505
4506       SaveLevelSetup_SeriesInfo();
4507     }
4508
4509     if (tape.auto_play)         /* tape might already be stopped here */
4510       tape.auto_play_level_solved = TRUE;
4511
4512     TapeStop();
4513
4514     game_over_delay_1 = 0;
4515     game_over_delay_2 = 0;
4516     game_over_delay_3 = game_over_delay_value_3;
4517
4518     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4519     score = score_final = local_player->score_final;
4520     health = health_final = local_player->health_final;
4521
4522     if (level.score[SC_TIME_BONUS] > 0)
4523     {
4524       if (TimeLeft > 0)
4525       {
4526         time_final = 0;
4527         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4528       }
4529       else if (game.no_time_limit && TimePlayed < 999)
4530       {
4531         time_final = 999;
4532         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4533       }
4534
4535       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4536
4537       game_over_delay_1 = game_over_delay_value_1;
4538
4539       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4540       {
4541         health_final = 0;
4542         score_final += health * level.score[SC_TIME_BONUS];
4543
4544         game_over_delay_2 = game_over_delay_value_2;
4545       }
4546
4547       local_player->score_final = score_final;
4548       local_player->health_final = health_final;
4549     }
4550
4551     if (level_editor_test_game)
4552     {
4553       time = time_final;
4554       score = score_final;
4555
4556       local_player->LevelSolved_CountingTime = time;
4557       local_player->LevelSolved_CountingScore = score;
4558
4559       game_panel_controls[GAME_PANEL_TIME].value = time;
4560       game_panel_controls[GAME_PANEL_SCORE].value = score;
4561
4562       DisplayGameControlValues();
4563     }
4564
4565     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4566     {
4567       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4568       {
4569         /* close exit door after last player */
4570         if ((AllPlayersGone &&
4571              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4572               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4573               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4574             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4575             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4576         {
4577           int element = Feld[ExitX][ExitY];
4578
4579           Feld[ExitX][ExitY] =
4580             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4581              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4582              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4583              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4584              EL_EM_STEEL_EXIT_CLOSING);
4585
4586           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4587         }
4588
4589         /* player disappears */
4590         DrawLevelField(ExitX, ExitY);
4591       }
4592
4593       for (i = 0; i < MAX_PLAYERS; i++)
4594       {
4595         struct PlayerInfo *player = &stored_player[i];
4596
4597         if (player->present)
4598         {
4599           RemovePlayer(player);
4600
4601           /* player disappears */
4602           DrawLevelField(player->jx, player->jy);
4603         }
4604       }
4605     }
4606
4607     PlaySound(SND_GAME_WINNING);
4608   }
4609
4610   if (game_over_delay_1 > 0)
4611   {
4612     game_over_delay_1--;
4613
4614     return;
4615   }
4616
4617   if (time != time_final)
4618   {
4619     int time_to_go = ABS(time_final - time);
4620     int time_count_dir = (time < time_final ? +1 : -1);
4621
4622     if (time_to_go < time_count_steps)
4623       time_count_steps = 1;
4624
4625     time  += time_count_steps * time_count_dir;
4626     score += time_count_steps * level.score[SC_TIME_BONUS];
4627
4628     local_player->LevelSolved_CountingTime = time;
4629     local_player->LevelSolved_CountingScore = score;
4630
4631     game_panel_controls[GAME_PANEL_TIME].value = time;
4632     game_panel_controls[GAME_PANEL_SCORE].value = score;
4633
4634     DisplayGameControlValues();
4635
4636     if (time == time_final)
4637       StopSound(SND_GAME_LEVELTIME_BONUS);
4638     else if (setup.sound_loops)
4639       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4640     else
4641       PlaySound(SND_GAME_LEVELTIME_BONUS);
4642
4643     return;
4644   }
4645
4646   if (game_over_delay_2 > 0)
4647   {
4648     game_over_delay_2--;
4649
4650     return;
4651   }
4652
4653   if (health != health_final)
4654   {
4655     int health_count_dir = (health < health_final ? +1 : -1);
4656
4657     health += health_count_dir;
4658     score  += level.score[SC_TIME_BONUS];
4659
4660     local_player->LevelSolved_CountingHealth = health;
4661     local_player->LevelSolved_CountingScore = score;
4662
4663     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4664     game_panel_controls[GAME_PANEL_SCORE].value = score;
4665
4666     DisplayGameControlValues();
4667
4668     if (health == health_final)
4669       StopSound(SND_GAME_LEVELTIME_BONUS);
4670     else if (setup.sound_loops)
4671       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4672     else
4673       PlaySound(SND_GAME_LEVELTIME_BONUS);
4674
4675     return;
4676   }
4677
4678   local_player->LevelSolved_PanelOff = TRUE;
4679
4680   if (game_over_delay_3 > 0)
4681   {
4682     game_over_delay_3--;
4683
4684     return;
4685   }
4686
4687   GameEnd();
4688 }
4689
4690 void GameEnd()
4691 {
4692   int hi_pos;
4693   boolean raise_level = FALSE;
4694
4695   local_player->LevelSolved_GameEnd = TRUE;
4696
4697   if (local_player->LevelSolved_SaveTape)
4698   {
4699     /* make sure that request dialog to save tape does not open door again */
4700     if (!global.use_envelope_request)
4701       CloseDoor(DOOR_CLOSE_1);
4702
4703     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4704   }
4705
4706   /* if no tape is to be saved, close both doors simultaneously */
4707   CloseDoor(DOOR_CLOSE_ALL);
4708
4709   if (level_editor_test_game)
4710   {
4711     SetGameStatus(GAME_MODE_MAIN);
4712
4713     DrawMainMenu();
4714
4715     return;
4716   }
4717
4718   if (!local_player->LevelSolved_SaveScore)
4719   {
4720     SetGameStatus(GAME_MODE_MAIN);
4721
4722     DrawMainMenu();
4723
4724     return;
4725   }
4726
4727   if (level_nr == leveldir_current->handicap_level)
4728   {
4729     leveldir_current->handicap_level++;
4730
4731     SaveLevelSetup_SeriesInfo();
4732   }
4733
4734   if (setup.increment_levels &&
4735       level_nr < leveldir_current->last_level)
4736     raise_level = TRUE;                 /* advance to next level */
4737
4738   if ((hi_pos = NewHiScore()) >= 0) 
4739   {
4740     SetGameStatus(GAME_MODE_SCORES);
4741
4742     DrawHallOfFame(hi_pos);
4743
4744     if (raise_level)
4745     {
4746       level_nr++;
4747       TapeErase();
4748     }
4749   }
4750   else
4751   {
4752     SetGameStatus(GAME_MODE_MAIN);
4753
4754     if (raise_level)
4755     {
4756       level_nr++;
4757       TapeErase();
4758     }
4759
4760     DrawMainMenu();
4761   }
4762 }
4763
4764 int NewHiScore()
4765 {
4766   int k, l;
4767   int position = -1;
4768   boolean one_score_entry_per_name = !program.many_scores_per_name;
4769
4770   LoadScore(level_nr);
4771
4772   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4773       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4774     return -1;
4775
4776   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4777   {
4778     if (local_player->score_final > highscore[k].Score)
4779     {
4780       /* player has made it to the hall of fame */
4781
4782       if (k < MAX_SCORE_ENTRIES - 1)
4783       {
4784         int m = MAX_SCORE_ENTRIES - 1;
4785
4786         if (one_score_entry_per_name)
4787         {
4788           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4789             if (strEqual(setup.player_name, highscore[l].Name))
4790               m = l;
4791
4792           if (m == k)   /* player's new highscore overwrites his old one */
4793             goto put_into_list;
4794         }
4795
4796         for (l = m; l > k; l--)
4797         {
4798           strcpy(highscore[l].Name, highscore[l - 1].Name);
4799           highscore[l].Score = highscore[l - 1].Score;
4800         }
4801       }
4802
4803       put_into_list:
4804
4805       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4806       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4807       highscore[k].Score = local_player->score_final; 
4808       position = k;
4809
4810       break;
4811     }
4812     else if (one_score_entry_per_name &&
4813              !strncmp(setup.player_name, highscore[k].Name,
4814                       MAX_PLAYER_NAME_LEN))
4815       break;    /* player already there with a higher score */
4816   }
4817
4818   if (position >= 0) 
4819     SaveScore(level_nr);
4820
4821   return position;
4822 }
4823
4824 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4825 {
4826   int element = Feld[x][y];
4827   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4828   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4829   int horiz_move = (dx != 0);
4830   int sign = (horiz_move ? dx : dy);
4831   int step = sign * element_info[element].move_stepsize;
4832
4833   /* special values for move stepsize for spring and things on conveyor belt */
4834   if (horiz_move)
4835   {
4836     if (CAN_FALL(element) &&
4837         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4838       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4839     else if (element == EL_SPRING)
4840       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4841   }
4842
4843   return step;
4844 }
4845
4846 inline static int getElementMoveStepsize(int x, int y)
4847 {
4848   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4849 }
4850
4851 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4852 {
4853   if (player->GfxAction != action || player->GfxDir != dir)
4854   {
4855     player->GfxAction = action;
4856     player->GfxDir = dir;
4857     player->Frame = 0;
4858     player->StepFrame = 0;
4859   }
4860 }
4861
4862 static void ResetGfxFrame(int x, int y)
4863 {
4864   // profiling showed that "autotest" spends 10~20% of its time in this function
4865   if (DrawingDeactivatedField())
4866     return;
4867
4868   int element = Feld[x][y];
4869   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4870
4871   if (graphic_info[graphic].anim_global_sync)
4872     GfxFrame[x][y] = FrameCounter;
4873   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4874     GfxFrame[x][y] = CustomValue[x][y];
4875   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4876     GfxFrame[x][y] = element_info[element].collect_score;
4877   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4878     GfxFrame[x][y] = ChangeDelay[x][y];
4879 }
4880
4881 static void ResetGfxAnimation(int x, int y)
4882 {
4883   GfxAction[x][y] = ACTION_DEFAULT;
4884   GfxDir[x][y] = MovDir[x][y];
4885   GfxFrame[x][y] = 0;
4886
4887   ResetGfxFrame(x, y);
4888 }
4889
4890 static void ResetRandomAnimationValue(int x, int y)
4891 {
4892   GfxRandom[x][y] = INIT_GFX_RANDOM();
4893 }
4894
4895 void InitMovingField(int x, int y, int direction)
4896 {
4897   int element = Feld[x][y];
4898   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4899   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4900   int newx = x + dx;
4901   int newy = y + dy;
4902   boolean is_moving_before, is_moving_after;
4903
4904   /* check if element was/is moving or being moved before/after mode change */
4905   is_moving_before = (WasJustMoving[x][y] != 0);
4906   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4907
4908   /* reset animation only for moving elements which change direction of moving
4909      or which just started or stopped moving
4910      (else CEs with property "can move" / "not moving" are reset each frame) */
4911   if (is_moving_before != is_moving_after ||
4912       direction != MovDir[x][y])
4913     ResetGfxAnimation(x, y);
4914
4915   MovDir[x][y] = direction;
4916   GfxDir[x][y] = direction;
4917
4918   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4919                      direction == MV_DOWN && CAN_FALL(element) ?
4920                      ACTION_FALLING : ACTION_MOVING);
4921
4922   /* this is needed for CEs with property "can move" / "not moving" */
4923
4924   if (is_moving_after)
4925   {
4926     if (Feld[newx][newy] == EL_EMPTY)
4927       Feld[newx][newy] = EL_BLOCKED;
4928
4929     MovDir[newx][newy] = MovDir[x][y];
4930
4931     CustomValue[newx][newy] = CustomValue[x][y];
4932
4933     GfxFrame[newx][newy] = GfxFrame[x][y];
4934     GfxRandom[newx][newy] = GfxRandom[x][y];
4935     GfxAction[newx][newy] = GfxAction[x][y];
4936     GfxDir[newx][newy] = GfxDir[x][y];
4937   }
4938 }
4939
4940 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4941 {
4942   int direction = MovDir[x][y];
4943   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4944   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4945
4946   *goes_to_x = newx;
4947   *goes_to_y = newy;
4948 }
4949
4950 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4951 {
4952   int oldx = x, oldy = y;
4953   int direction = MovDir[x][y];
4954
4955   if (direction == MV_LEFT)
4956     oldx++;
4957   else if (direction == MV_RIGHT)
4958     oldx--;
4959   else if (direction == MV_UP)
4960     oldy++;
4961   else if (direction == MV_DOWN)
4962     oldy--;
4963
4964   *comes_from_x = oldx;
4965   *comes_from_y = oldy;
4966 }
4967
4968 int MovingOrBlocked2Element(int x, int y)
4969 {
4970   int element = Feld[x][y];
4971
4972   if (element == EL_BLOCKED)
4973   {
4974     int oldx, oldy;
4975
4976     Blocked2Moving(x, y, &oldx, &oldy);
4977     return Feld[oldx][oldy];
4978   }
4979   else
4980     return element;
4981 }
4982
4983 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4984 {
4985   /* like MovingOrBlocked2Element(), but if element is moving
4986      and (x,y) is the field the moving element is just leaving,
4987      return EL_BLOCKED instead of the element value */
4988   int element = Feld[x][y];
4989
4990   if (IS_MOVING(x, y))
4991   {
4992     if (element == EL_BLOCKED)
4993     {
4994       int oldx, oldy;
4995
4996       Blocked2Moving(x, y, &oldx, &oldy);
4997       return Feld[oldx][oldy];
4998     }
4999     else
5000       return EL_BLOCKED;
5001   }
5002   else
5003     return element;
5004 }
5005
5006 static void RemoveField(int x, int y)
5007 {
5008   Feld[x][y] = EL_EMPTY;
5009
5010   MovPos[x][y] = 0;
5011   MovDir[x][y] = 0;
5012   MovDelay[x][y] = 0;
5013
5014   CustomValue[x][y] = 0;
5015
5016   AmoebaNr[x][y] = 0;
5017   ChangeDelay[x][y] = 0;
5018   ChangePage[x][y] = -1;
5019   Pushed[x][y] = FALSE;
5020
5021   GfxElement[x][y] = EL_UNDEFINED;
5022   GfxAction[x][y] = ACTION_DEFAULT;
5023   GfxDir[x][y] = MV_NONE;
5024 }
5025
5026 void RemoveMovingField(int x, int y)
5027 {
5028   int oldx = x, oldy = y, newx = x, newy = y;
5029   int element = Feld[x][y];
5030   int next_element = EL_UNDEFINED;
5031
5032   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5033     return;
5034
5035   if (IS_MOVING(x, y))
5036   {
5037     Moving2Blocked(x, y, &newx, &newy);
5038
5039     if (Feld[newx][newy] != EL_BLOCKED)
5040     {
5041       /* element is moving, but target field is not free (blocked), but
5042          already occupied by something different (example: acid pool);
5043          in this case, only remove the moving field, but not the target */
5044
5045       RemoveField(oldx, oldy);
5046
5047       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5048
5049       TEST_DrawLevelField(oldx, oldy);
5050
5051       return;
5052     }
5053   }
5054   else if (element == EL_BLOCKED)
5055   {
5056     Blocked2Moving(x, y, &oldx, &oldy);
5057     if (!IS_MOVING(oldx, oldy))
5058       return;
5059   }
5060
5061   if (element == EL_BLOCKED &&
5062       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5063        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5064        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5065        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5066        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5067        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5068     next_element = get_next_element(Feld[oldx][oldy]);
5069
5070   RemoveField(oldx, oldy);
5071   RemoveField(newx, newy);
5072
5073   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5074
5075   if (next_element != EL_UNDEFINED)
5076     Feld[oldx][oldy] = next_element;
5077
5078   TEST_DrawLevelField(oldx, oldy);
5079   TEST_DrawLevelField(newx, newy);
5080 }
5081
5082 void DrawDynamite(int x, int y)
5083 {
5084   int sx = SCREENX(x), sy = SCREENY(y);
5085   int graphic = el2img(Feld[x][y]);
5086   int frame;
5087
5088   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5089     return;
5090
5091   if (IS_WALKABLE_INSIDE(Back[x][y]))
5092     return;
5093
5094   if (Back[x][y])
5095     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5096   else if (Store[x][y])
5097     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5098
5099   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5100
5101   if (Back[x][y] || Store[x][y])
5102     DrawGraphicThruMask(sx, sy, graphic, frame);
5103   else
5104     DrawGraphic(sx, sy, graphic, frame);
5105 }
5106
5107 void CheckDynamite(int x, int y)
5108 {
5109   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5110   {
5111     MovDelay[x][y]--;
5112
5113     if (MovDelay[x][y] != 0)
5114     {
5115       DrawDynamite(x, y);
5116       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5117
5118       return;
5119     }
5120   }
5121
5122   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5123
5124   Bang(x, y);
5125 }
5126
5127 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5128 {
5129   boolean num_checked_players = 0;
5130   int i;
5131
5132   for (i = 0; i < MAX_PLAYERS; i++)
5133   {
5134     if (stored_player[i].active)
5135     {
5136       int sx = stored_player[i].jx;
5137       int sy = stored_player[i].jy;
5138
5139       if (num_checked_players == 0)
5140       {
5141         *sx1 = *sx2 = sx;
5142         *sy1 = *sy2 = sy;
5143       }
5144       else
5145       {
5146         *sx1 = MIN(*sx1, sx);
5147         *sy1 = MIN(*sy1, sy);
5148         *sx2 = MAX(*sx2, sx);
5149         *sy2 = MAX(*sy2, sy);
5150       }
5151
5152       num_checked_players++;
5153     }
5154   }
5155 }
5156
5157 static boolean checkIfAllPlayersFitToScreen_RND()
5158 {
5159   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5160
5161   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5162
5163   return (sx2 - sx1 < SCR_FIELDX &&
5164           sy2 - sy1 < SCR_FIELDY);
5165 }
5166
5167 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5168 {
5169   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5170
5171   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5172
5173   *sx = (sx1 + sx2) / 2;
5174   *sy = (sy1 + sy2) / 2;
5175 }
5176
5177 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5178                         boolean center_screen, boolean quick_relocation)
5179 {
5180   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5181   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5182   boolean no_delay = (tape.warp_forward);
5183   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5184   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5185   int new_scroll_x, new_scroll_y;
5186
5187   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5188   {
5189     /* case 1: quick relocation inside visible screen (without scrolling) */
5190
5191     RedrawPlayfield();
5192
5193     return;
5194   }
5195
5196   if (!level.shifted_relocation || center_screen)
5197   {
5198     /* relocation _with_ centering of screen */
5199
5200     new_scroll_x = SCROLL_POSITION_X(x);
5201     new_scroll_y = SCROLL_POSITION_Y(y);
5202   }
5203   else
5204   {
5205     /* relocation _without_ centering of screen */
5206
5207     int center_scroll_x = SCROLL_POSITION_X(old_x);
5208     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5209     int offset_x = x + (scroll_x - center_scroll_x);
5210     int offset_y = y + (scroll_y - center_scroll_y);
5211
5212     /* for new screen position, apply previous offset to center position */
5213     new_scroll_x = SCROLL_POSITION_X(offset_x);
5214     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5215   }
5216
5217   if (quick_relocation)
5218   {
5219     /* case 2: quick relocation (redraw without visible scrolling) */
5220
5221     scroll_x = new_scroll_x;
5222     scroll_y = new_scroll_y;
5223
5224     RedrawPlayfield();
5225
5226     return;
5227   }
5228
5229   /* case 3: visible relocation (with scrolling to new position) */
5230
5231   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5232
5233   SetVideoFrameDelay(wait_delay_value);
5234
5235   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5236   {
5237     int dx = 0, dy = 0;
5238     int fx = FX, fy = FY;
5239
5240     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5241     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5242
5243     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5244       break;
5245
5246     scroll_x -= dx;
5247     scroll_y -= dy;
5248
5249     fx += dx * TILEX / 2;
5250     fy += dy * TILEY / 2;
5251
5252     ScrollLevel(dx, dy);
5253     DrawAllPlayers();
5254
5255     /* scroll in two steps of half tile size to make things smoother */
5256     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5257
5258     /* scroll second step to align at full tile size */
5259     BlitScreenToBitmap(window);
5260   }
5261
5262   DrawAllPlayers();
5263   BackToFront();
5264
5265   SetVideoFrameDelay(frame_delay_value_old);
5266 }
5267
5268 void RelocatePlayer(int jx, int jy, int el_player_raw)
5269 {
5270   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5271   int player_nr = GET_PLAYER_NR(el_player);
5272   struct PlayerInfo *player = &stored_player[player_nr];
5273   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5274   boolean no_delay = (tape.warp_forward);
5275   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5276   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5277   int old_jx = player->jx;
5278   int old_jy = player->jy;
5279   int old_element = Feld[old_jx][old_jy];
5280   int element = Feld[jx][jy];
5281   boolean player_relocated = (old_jx != jx || old_jy != jy);
5282
5283   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5284   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5285   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5286   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5287   int leave_side_horiz = move_dir_horiz;
5288   int leave_side_vert  = move_dir_vert;
5289   int enter_side = enter_side_horiz | enter_side_vert;
5290   int leave_side = leave_side_horiz | leave_side_vert;
5291
5292   if (player->GameOver)         /* do not reanimate dead player */
5293     return;
5294
5295   if (!player_relocated)        /* no need to relocate the player */
5296     return;
5297
5298   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5299   {
5300     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5301     DrawLevelField(jx, jy);
5302   }
5303
5304   if (player->present)
5305   {
5306     while (player->MovPos)
5307     {
5308       ScrollPlayer(player, SCROLL_GO_ON);
5309       ScrollScreen(NULL, SCROLL_GO_ON);
5310
5311       AdvanceFrameAndPlayerCounters(player->index_nr);
5312
5313       DrawPlayer(player);
5314
5315       BackToFront_WithFrameDelay(wait_delay_value);
5316     }
5317
5318     DrawPlayer(player);         /* needed here only to cleanup last field */
5319     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5320
5321     player->is_moving = FALSE;
5322   }
5323
5324   if (IS_CUSTOM_ELEMENT(old_element))
5325     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5326                                CE_LEFT_BY_PLAYER,
5327                                player->index_bit, leave_side);
5328
5329   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5330                                       CE_PLAYER_LEAVES_X,
5331                                       player->index_bit, leave_side);
5332
5333   Feld[jx][jy] = el_player;
5334   InitPlayerField(jx, jy, el_player, TRUE);
5335
5336   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5337      possible that the relocation target field did not contain a player element,
5338      but a walkable element, to which the new player was relocated -- in this
5339      case, restore that (already initialized!) element on the player field */
5340   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5341   {
5342     Feld[jx][jy] = element;     /* restore previously existing element */
5343   }
5344
5345   /* only visually relocate centered player */
5346   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5347                      FALSE, level.instant_relocation);
5348
5349   TestIfPlayerTouchesBadThing(jx, jy);
5350   TestIfPlayerTouchesCustomElement(jx, jy);
5351
5352   if (IS_CUSTOM_ELEMENT(element))
5353     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5354                                player->index_bit, enter_side);
5355
5356   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5357                                       player->index_bit, enter_side);
5358
5359   if (player->is_switching)
5360   {
5361     /* ensure that relocation while still switching an element does not cause
5362        a new element to be treated as also switched directly after relocation
5363        (this is important for teleporter switches that teleport the player to
5364        a place where another teleporter switch is in the same direction, which
5365        would then incorrectly be treated as immediately switched before the
5366        direction key that caused the switch was released) */
5367
5368     player->switch_x += jx - old_jx;
5369     player->switch_y += jy - old_jy;
5370   }
5371 }
5372
5373 void Explode(int ex, int ey, int phase, int mode)
5374 {
5375   int x, y;
5376   int last_phase;
5377   int border_element;
5378
5379   /* !!! eliminate this variable !!! */
5380   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5381
5382   if (game.explosions_delayed)
5383   {
5384     ExplodeField[ex][ey] = mode;
5385     return;
5386   }
5387
5388   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5389   {
5390     int center_element = Feld[ex][ey];
5391     int artwork_element, explosion_element;     /* set these values later */
5392
5393     /* remove things displayed in background while burning dynamite */
5394     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5395       Back[ex][ey] = 0;
5396
5397     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5398     {
5399       /* put moving element to center field (and let it explode there) */
5400       center_element = MovingOrBlocked2Element(ex, ey);
5401       RemoveMovingField(ex, ey);
5402       Feld[ex][ey] = center_element;
5403     }
5404
5405     /* now "center_element" is finally determined -- set related values now */
5406     artwork_element = center_element;           /* for custom player artwork */
5407     explosion_element = center_element;         /* for custom player artwork */
5408
5409     if (IS_PLAYER(ex, ey))
5410     {
5411       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5412
5413       artwork_element = stored_player[player_nr].artwork_element;
5414
5415       if (level.use_explosion_element[player_nr])
5416       {
5417         explosion_element = level.explosion_element[player_nr];
5418         artwork_element = explosion_element;
5419       }
5420     }
5421
5422     if (mode == EX_TYPE_NORMAL ||
5423         mode == EX_TYPE_CENTER ||
5424         mode == EX_TYPE_CROSS)
5425       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5426
5427     last_phase = element_info[explosion_element].explosion_delay + 1;
5428
5429     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5430     {
5431       int xx = x - ex + 1;
5432       int yy = y - ey + 1;
5433       int element;
5434
5435       if (!IN_LEV_FIELD(x, y) ||
5436           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5437           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5438         continue;
5439
5440       element = Feld[x][y];
5441
5442       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5443       {
5444         element = MovingOrBlocked2Element(x, y);
5445
5446         if (!IS_EXPLOSION_PROOF(element))
5447           RemoveMovingField(x, y);
5448       }
5449
5450       /* indestructible elements can only explode in center (but not flames) */
5451       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5452                                            mode == EX_TYPE_BORDER)) ||
5453           element == EL_FLAMES)
5454         continue;
5455
5456       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5457          behaviour, for example when touching a yamyam that explodes to rocks
5458          with active deadly shield, a rock is created under the player !!! */
5459       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5460 #if 0
5461       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5462           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5463            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5464 #else
5465       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5466 #endif
5467       {
5468         if (IS_ACTIVE_BOMB(element))
5469         {
5470           /* re-activate things under the bomb like gate or penguin */
5471           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5472           Back[x][y] = 0;
5473         }
5474
5475         continue;
5476       }
5477
5478       /* save walkable background elements while explosion on same tile */
5479       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5480           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5481         Back[x][y] = element;
5482
5483       /* ignite explodable elements reached by other explosion */
5484       if (element == EL_EXPLOSION)
5485         element = Store2[x][y];
5486
5487       if (AmoebaNr[x][y] &&
5488           (element == EL_AMOEBA_FULL ||
5489            element == EL_BD_AMOEBA ||
5490            element == EL_AMOEBA_GROWING))
5491       {
5492         AmoebaCnt[AmoebaNr[x][y]]--;
5493         AmoebaCnt2[AmoebaNr[x][y]]--;
5494       }
5495
5496       RemoveField(x, y);
5497
5498       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5499       {
5500         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5501
5502         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5503
5504         if (PLAYERINFO(ex, ey)->use_murphy)
5505           Store[x][y] = EL_EMPTY;
5506       }
5507
5508       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5509          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5510       else if (ELEM_IS_PLAYER(center_element))
5511         Store[x][y] = EL_EMPTY;
5512       else if (center_element == EL_YAMYAM)
5513         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5514       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5515         Store[x][y] = element_info[center_element].content.e[xx][yy];
5516 #if 1
5517       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5518          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5519          otherwise) -- FIX THIS !!! */
5520       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5521         Store[x][y] = element_info[element].content.e[1][1];
5522 #else
5523       else if (!CAN_EXPLODE(element))
5524         Store[x][y] = element_info[element].content.e[1][1];
5525 #endif
5526       else
5527         Store[x][y] = EL_EMPTY;
5528
5529       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5530           center_element == EL_AMOEBA_TO_DIAMOND)
5531         Store2[x][y] = element;
5532
5533       Feld[x][y] = EL_EXPLOSION;
5534       GfxElement[x][y] = artwork_element;
5535
5536       ExplodePhase[x][y] = 1;
5537       ExplodeDelay[x][y] = last_phase;
5538
5539       Stop[x][y] = TRUE;
5540     }
5541
5542     if (center_element == EL_YAMYAM)
5543       game.yamyam_content_nr =
5544         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5545
5546     return;
5547   }
5548
5549   if (Stop[ex][ey])
5550     return;
5551
5552   x = ex;
5553   y = ey;
5554
5555   if (phase == 1)
5556     GfxFrame[x][y] = 0;         /* restart explosion animation */
5557
5558   last_phase = ExplodeDelay[x][y];
5559
5560   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5561
5562   /* this can happen if the player leaves an explosion just in time */
5563   if (GfxElement[x][y] == EL_UNDEFINED)
5564     GfxElement[x][y] = EL_EMPTY;
5565
5566   border_element = Store2[x][y];
5567   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5568     border_element = StorePlayer[x][y];
5569
5570   if (phase == element_info[border_element].ignition_delay ||
5571       phase == last_phase)
5572   {
5573     boolean border_explosion = FALSE;
5574
5575     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5576         !PLAYER_EXPLOSION_PROTECTED(x, y))
5577     {
5578       KillPlayerUnlessExplosionProtected(x, y);
5579       border_explosion = TRUE;
5580     }
5581     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5582     {
5583       Feld[x][y] = Store2[x][y];
5584       Store2[x][y] = 0;
5585       Bang(x, y);
5586       border_explosion = TRUE;
5587     }
5588     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5589     {
5590       AmoebeUmwandeln(x, y);
5591       Store2[x][y] = 0;
5592       border_explosion = TRUE;
5593     }
5594
5595     /* if an element just explodes due to another explosion (chain-reaction),
5596        do not immediately end the new explosion when it was the last frame of
5597        the explosion (as it would be done in the following "if"-statement!) */
5598     if (border_explosion && phase == last_phase)
5599       return;
5600   }
5601
5602   if (phase == last_phase)
5603   {
5604     int element;
5605
5606     element = Feld[x][y] = Store[x][y];
5607     Store[x][y] = Store2[x][y] = 0;
5608     GfxElement[x][y] = EL_UNDEFINED;
5609
5610     /* player can escape from explosions and might therefore be still alive */
5611     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5612         element <= EL_PLAYER_IS_EXPLODING_4)
5613     {
5614       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5615       int explosion_element = EL_PLAYER_1 + player_nr;
5616       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5617       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5618
5619       if (level.use_explosion_element[player_nr])
5620         explosion_element = level.explosion_element[player_nr];
5621
5622       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5623                     element_info[explosion_element].content.e[xx][yy]);
5624     }
5625
5626     /* restore probably existing indestructible background element */
5627     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5628       element = Feld[x][y] = Back[x][y];
5629     Back[x][y] = 0;
5630
5631     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5632     GfxDir[x][y] = MV_NONE;
5633     ChangeDelay[x][y] = 0;
5634     ChangePage[x][y] = -1;
5635
5636     CustomValue[x][y] = 0;
5637
5638     InitField_WithBug2(x, y, FALSE);
5639
5640     TEST_DrawLevelField(x, y);
5641
5642     TestIfElementTouchesCustomElement(x, y);
5643
5644     if (GFX_CRUMBLED(element))
5645       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5646
5647     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5648       StorePlayer[x][y] = 0;
5649
5650     if (ELEM_IS_PLAYER(element))
5651       RelocatePlayer(x, y, element);
5652   }
5653   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5654   {
5655     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5656     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5657
5658     if (phase == delay)
5659       TEST_DrawLevelFieldCrumbled(x, y);
5660
5661     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5662     {
5663       DrawLevelElement(x, y, Back[x][y]);
5664       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5665     }
5666     else if (IS_WALKABLE_UNDER(Back[x][y]))
5667     {
5668       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5669       DrawLevelElementThruMask(x, y, Back[x][y]);
5670     }
5671     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5672       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5673   }
5674 }
5675
5676 void DynaExplode(int ex, int ey)
5677 {
5678   int i, j;
5679   int dynabomb_element = Feld[ex][ey];
5680   int dynabomb_size = 1;
5681   boolean dynabomb_xl = FALSE;
5682   struct PlayerInfo *player;
5683   static int xy[4][2] =
5684   {
5685     { 0, -1 },
5686     { -1, 0 },
5687     { +1, 0 },
5688     { 0, +1 }
5689   };
5690
5691   if (IS_ACTIVE_BOMB(dynabomb_element))
5692   {
5693     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5694     dynabomb_size = player->dynabomb_size;
5695     dynabomb_xl = player->dynabomb_xl;
5696     player->dynabombs_left++;
5697   }
5698
5699   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5700
5701   for (i = 0; i < NUM_DIRECTIONS; i++)
5702   {
5703     for (j = 1; j <= dynabomb_size; j++)
5704     {
5705       int x = ex + j * xy[i][0];
5706       int y = ey + j * xy[i][1];
5707       int element;
5708
5709       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5710         break;
5711
5712       element = Feld[x][y];
5713
5714       /* do not restart explosions of fields with active bombs */
5715       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5716         continue;
5717
5718       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5719
5720       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5721           !IS_DIGGABLE(element) && !dynabomb_xl)
5722         break;
5723     }
5724   }
5725 }
5726
5727 void Bang(int x, int y)
5728 {
5729   int element = MovingOrBlocked2Element(x, y);
5730   int explosion_type = EX_TYPE_NORMAL;
5731
5732   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5733   {
5734     struct PlayerInfo *player = PLAYERINFO(x, y);
5735
5736     element = Feld[x][y] = player->initial_element;
5737
5738     if (level.use_explosion_element[player->index_nr])
5739     {
5740       int explosion_element = level.explosion_element[player->index_nr];
5741
5742       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5743         explosion_type = EX_TYPE_CROSS;
5744       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5745         explosion_type = EX_TYPE_CENTER;
5746     }
5747   }
5748
5749   switch (element)
5750   {
5751     case EL_BUG:
5752     case EL_SPACESHIP:
5753     case EL_BD_BUTTERFLY:
5754     case EL_BD_FIREFLY:
5755     case EL_YAMYAM:
5756     case EL_DARK_YAMYAM:
5757     case EL_ROBOT:
5758     case EL_PACMAN:
5759     case EL_MOLE:
5760       RaiseScoreElement(element);
5761       break;
5762
5763     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5764     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5765     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5766     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5767     case EL_DYNABOMB_INCREASE_NUMBER:
5768     case EL_DYNABOMB_INCREASE_SIZE:
5769     case EL_DYNABOMB_INCREASE_POWER:
5770       explosion_type = EX_TYPE_DYNA;
5771       break;
5772
5773     case EL_DC_LANDMINE:
5774       explosion_type = EX_TYPE_CENTER;
5775       break;
5776
5777     case EL_PENGUIN:
5778     case EL_LAMP:
5779     case EL_LAMP_ACTIVE:
5780     case EL_AMOEBA_TO_DIAMOND:
5781       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5782         explosion_type = EX_TYPE_CENTER;
5783       break;
5784
5785     default:
5786       if (element_info[element].explosion_type == EXPLODES_CROSS)
5787         explosion_type = EX_TYPE_CROSS;
5788       else if (element_info[element].explosion_type == EXPLODES_1X1)
5789         explosion_type = EX_TYPE_CENTER;
5790       break;
5791   }
5792
5793   if (explosion_type == EX_TYPE_DYNA)
5794     DynaExplode(x, y);
5795   else
5796     Explode(x, y, EX_PHASE_START, explosion_type);
5797
5798   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5799 }
5800
5801 void SplashAcid(int x, int y)
5802 {
5803   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5804       (!IN_LEV_FIELD(x - 1, y - 2) ||
5805        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5806     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5807
5808   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5809       (!IN_LEV_FIELD(x + 1, y - 2) ||
5810        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5811     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5812
5813   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5814 }
5815
5816 static void InitBeltMovement()
5817 {
5818   static int belt_base_element[4] =
5819   {
5820     EL_CONVEYOR_BELT_1_LEFT,
5821     EL_CONVEYOR_BELT_2_LEFT,
5822     EL_CONVEYOR_BELT_3_LEFT,
5823     EL_CONVEYOR_BELT_4_LEFT
5824   };
5825   static int belt_base_active_element[4] =
5826   {
5827     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5828     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5829     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5830     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5831   };
5832
5833   int x, y, i, j;
5834
5835   /* set frame order for belt animation graphic according to belt direction */
5836   for (i = 0; i < NUM_BELTS; i++)
5837   {
5838     int belt_nr = i;
5839
5840     for (j = 0; j < NUM_BELT_PARTS; j++)
5841     {
5842       int element = belt_base_active_element[belt_nr] + j;
5843       int graphic_1 = el2img(element);
5844       int graphic_2 = el2panelimg(element);
5845
5846       if (game.belt_dir[i] == MV_LEFT)
5847       {
5848         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5849         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5850       }
5851       else
5852       {
5853         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5854         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5855       }
5856     }
5857   }
5858
5859   SCAN_PLAYFIELD(x, y)
5860   {
5861     int element = Feld[x][y];
5862
5863     for (i = 0; i < NUM_BELTS; i++)
5864     {
5865       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5866       {
5867         int e_belt_nr = getBeltNrFromBeltElement(element);
5868         int belt_nr = i;
5869
5870         if (e_belt_nr == belt_nr)
5871         {
5872           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5873
5874           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5875         }
5876       }
5877     }
5878   }
5879 }
5880
5881 static void ToggleBeltSwitch(int x, int y)
5882 {
5883   static int belt_base_element[4] =
5884   {
5885     EL_CONVEYOR_BELT_1_LEFT,
5886     EL_CONVEYOR_BELT_2_LEFT,
5887     EL_CONVEYOR_BELT_3_LEFT,
5888     EL_CONVEYOR_BELT_4_LEFT
5889   };
5890   static int belt_base_active_element[4] =
5891   {
5892     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5893     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5894     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5895     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5896   };
5897   static int belt_base_switch_element[4] =
5898   {
5899     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5900     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5901     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5902     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5903   };
5904   static int belt_move_dir[4] =
5905   {
5906     MV_LEFT,
5907     MV_NONE,
5908     MV_RIGHT,
5909     MV_NONE,
5910   };
5911
5912   int element = Feld[x][y];
5913   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5914   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5915   int belt_dir = belt_move_dir[belt_dir_nr];
5916   int xx, yy, i;
5917
5918   if (!IS_BELT_SWITCH(element))
5919     return;
5920
5921   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5922   game.belt_dir[belt_nr] = belt_dir;
5923
5924   if (belt_dir_nr == 3)
5925     belt_dir_nr = 1;
5926
5927   /* set frame order for belt animation graphic according to belt direction */
5928   for (i = 0; i < NUM_BELT_PARTS; i++)
5929   {
5930     int element = belt_base_active_element[belt_nr] + i;
5931     int graphic_1 = el2img(element);
5932     int graphic_2 = el2panelimg(element);
5933
5934     if (belt_dir == MV_LEFT)
5935     {
5936       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5937       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5938     }
5939     else
5940     {
5941       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5942       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5943     }
5944   }
5945
5946   SCAN_PLAYFIELD(xx, yy)
5947   {
5948     int element = Feld[xx][yy];
5949
5950     if (IS_BELT_SWITCH(element))
5951     {
5952       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5953
5954       if (e_belt_nr == belt_nr)
5955       {
5956         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5957         TEST_DrawLevelField(xx, yy);
5958       }
5959     }
5960     else if (IS_BELT(element) && belt_dir != MV_NONE)
5961     {
5962       int e_belt_nr = getBeltNrFromBeltElement(element);
5963
5964       if (e_belt_nr == belt_nr)
5965       {
5966         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5967
5968         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5969         TEST_DrawLevelField(xx, yy);
5970       }
5971     }
5972     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5973     {
5974       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5975
5976       if (e_belt_nr == belt_nr)
5977       {
5978         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5979
5980         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5981         TEST_DrawLevelField(xx, yy);
5982       }
5983     }
5984   }
5985 }
5986
5987 static void ToggleSwitchgateSwitch(int x, int y)
5988 {
5989   int xx, yy;
5990
5991   game.switchgate_pos = !game.switchgate_pos;
5992
5993   SCAN_PLAYFIELD(xx, yy)
5994   {
5995     int element = Feld[xx][yy];
5996
5997     if (element == EL_SWITCHGATE_SWITCH_UP)
5998     {
5999       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6000       TEST_DrawLevelField(xx, yy);
6001     }
6002     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6003     {
6004       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6005       TEST_DrawLevelField(xx, yy);
6006     }
6007     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6008     {
6009       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6010       TEST_DrawLevelField(xx, yy);
6011     }
6012     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6013     {
6014       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6015       TEST_DrawLevelField(xx, yy);
6016     }
6017     else if (element == EL_SWITCHGATE_OPEN ||
6018              element == EL_SWITCHGATE_OPENING)
6019     {
6020       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6021
6022       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6023     }
6024     else if (element == EL_SWITCHGATE_CLOSED ||
6025              element == EL_SWITCHGATE_CLOSING)
6026     {
6027       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6028
6029       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6030     }
6031   }
6032 }
6033
6034 static int getInvisibleActiveFromInvisibleElement(int element)
6035 {
6036   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6037           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6038           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6039           element);
6040 }
6041
6042 static int getInvisibleFromInvisibleActiveElement(int element)
6043 {
6044   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6045           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6046           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6047           element);
6048 }
6049
6050 static void RedrawAllLightSwitchesAndInvisibleElements()
6051 {
6052   int x, y;
6053
6054   SCAN_PLAYFIELD(x, y)
6055   {
6056     int element = Feld[x][y];
6057
6058     if (element == EL_LIGHT_SWITCH &&
6059         game.light_time_left > 0)
6060     {
6061       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6062       TEST_DrawLevelField(x, y);
6063     }
6064     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6065              game.light_time_left == 0)
6066     {
6067       Feld[x][y] = EL_LIGHT_SWITCH;
6068       TEST_DrawLevelField(x, y);
6069     }
6070     else if (element == EL_EMC_DRIPPER &&
6071              game.light_time_left > 0)
6072     {
6073       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6074       TEST_DrawLevelField(x, y);
6075     }
6076     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6077              game.light_time_left == 0)
6078     {
6079       Feld[x][y] = EL_EMC_DRIPPER;
6080       TEST_DrawLevelField(x, y);
6081     }
6082     else if (element == EL_INVISIBLE_STEELWALL ||
6083              element == EL_INVISIBLE_WALL ||
6084              element == EL_INVISIBLE_SAND)
6085     {
6086       if (game.light_time_left > 0)
6087         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6088
6089       TEST_DrawLevelField(x, y);
6090
6091       /* uncrumble neighbour fields, if needed */
6092       if (element == EL_INVISIBLE_SAND)
6093         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6094     }
6095     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6096              element == EL_INVISIBLE_WALL_ACTIVE ||
6097              element == EL_INVISIBLE_SAND_ACTIVE)
6098     {
6099       if (game.light_time_left == 0)
6100         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6101
6102       TEST_DrawLevelField(x, y);
6103
6104       /* re-crumble neighbour fields, if needed */
6105       if (element == EL_INVISIBLE_SAND)
6106         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6107     }
6108   }
6109 }
6110
6111 static void RedrawAllInvisibleElementsForLenses()
6112 {
6113   int x, y;
6114
6115   SCAN_PLAYFIELD(x, y)
6116   {
6117     int element = Feld[x][y];
6118
6119     if (element == EL_EMC_DRIPPER &&
6120         game.lenses_time_left > 0)
6121     {
6122       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6123       TEST_DrawLevelField(x, y);
6124     }
6125     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6126              game.lenses_time_left == 0)
6127     {
6128       Feld[x][y] = EL_EMC_DRIPPER;
6129       TEST_DrawLevelField(x, y);
6130     }
6131     else if (element == EL_INVISIBLE_STEELWALL ||
6132              element == EL_INVISIBLE_WALL ||
6133              element == EL_INVISIBLE_SAND)
6134     {
6135       if (game.lenses_time_left > 0)
6136         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6137
6138       TEST_DrawLevelField(x, y);
6139
6140       /* uncrumble neighbour fields, if needed */
6141       if (element == EL_INVISIBLE_SAND)
6142         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6143     }
6144     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6145              element == EL_INVISIBLE_WALL_ACTIVE ||
6146              element == EL_INVISIBLE_SAND_ACTIVE)
6147     {
6148       if (game.lenses_time_left == 0)
6149         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6150
6151       TEST_DrawLevelField(x, y);
6152
6153       /* re-crumble neighbour fields, if needed */
6154       if (element == EL_INVISIBLE_SAND)
6155         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6156     }
6157   }
6158 }
6159
6160 static void RedrawAllInvisibleElementsForMagnifier()
6161 {
6162   int x, y;
6163
6164   SCAN_PLAYFIELD(x, y)
6165   {
6166     int element = Feld[x][y];
6167
6168     if (element == EL_EMC_FAKE_GRASS &&
6169         game.magnify_time_left > 0)
6170     {
6171       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6172       TEST_DrawLevelField(x, y);
6173     }
6174     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6175              game.magnify_time_left == 0)
6176     {
6177       Feld[x][y] = EL_EMC_FAKE_GRASS;
6178       TEST_DrawLevelField(x, y);
6179     }
6180     else if (IS_GATE_GRAY(element) &&
6181              game.magnify_time_left > 0)
6182     {
6183       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6184                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6185                     IS_EM_GATE_GRAY(element) ?
6186                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6187                     IS_EMC_GATE_GRAY(element) ?
6188                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6189                     IS_DC_GATE_GRAY(element) ?
6190                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6191                     element);
6192       TEST_DrawLevelField(x, y);
6193     }
6194     else if (IS_GATE_GRAY_ACTIVE(element) &&
6195              game.magnify_time_left == 0)
6196     {
6197       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6198                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6199                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6200                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6201                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6202                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6203                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6204                     EL_DC_GATE_WHITE_GRAY :
6205                     element);
6206       TEST_DrawLevelField(x, y);
6207     }
6208   }
6209 }
6210
6211 static void ToggleLightSwitch(int x, int y)
6212 {
6213   int element = Feld[x][y];
6214
6215   game.light_time_left =
6216     (element == EL_LIGHT_SWITCH ?
6217      level.time_light * FRAMES_PER_SECOND : 0);
6218
6219   RedrawAllLightSwitchesAndInvisibleElements();
6220 }
6221
6222 static void ActivateTimegateSwitch(int x, int y)
6223 {
6224   int xx, yy;
6225
6226   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6227
6228   SCAN_PLAYFIELD(xx, yy)
6229   {
6230     int element = Feld[xx][yy];
6231
6232     if (element == EL_TIMEGATE_CLOSED ||
6233         element == EL_TIMEGATE_CLOSING)
6234     {
6235       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6236       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6237     }
6238
6239     /*
6240     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6241     {
6242       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6243       TEST_DrawLevelField(xx, yy);
6244     }
6245     */
6246
6247   }
6248
6249   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6250                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6251 }
6252
6253 void Impact(int x, int y)
6254 {
6255   boolean last_line = (y == lev_fieldy - 1);
6256   boolean object_hit = FALSE;
6257   boolean impact = (last_line || object_hit);
6258   int element = Feld[x][y];
6259   int smashed = EL_STEELWALL;
6260
6261   if (!last_line)       /* check if element below was hit */
6262   {
6263     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6264       return;
6265
6266     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6267                                          MovDir[x][y + 1] != MV_DOWN ||
6268                                          MovPos[x][y + 1] <= TILEY / 2));
6269
6270     /* do not smash moving elements that left the smashed field in time */
6271     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6272         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6273       object_hit = FALSE;
6274
6275 #if USE_QUICKSAND_IMPACT_BUGFIX
6276     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6277     {
6278       RemoveMovingField(x, y + 1);
6279       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6280       Feld[x][y + 2] = EL_ROCK;
6281       TEST_DrawLevelField(x, y + 2);
6282
6283       object_hit = TRUE;
6284     }
6285
6286     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6287     {
6288       RemoveMovingField(x, y + 1);
6289       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6290       Feld[x][y + 2] = EL_ROCK;
6291       TEST_DrawLevelField(x, y + 2);
6292
6293       object_hit = TRUE;
6294     }
6295 #endif
6296
6297     if (object_hit)
6298       smashed = MovingOrBlocked2Element(x, y + 1);
6299
6300     impact = (last_line || object_hit);
6301   }
6302
6303   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6304   {
6305     SplashAcid(x, y + 1);
6306     return;
6307   }
6308
6309   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6310   /* only reset graphic animation if graphic really changes after impact */
6311   if (impact &&
6312       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6313   {
6314     ResetGfxAnimation(x, y);
6315     TEST_DrawLevelField(x, y);
6316   }
6317
6318   if (impact && CAN_EXPLODE_IMPACT(element))
6319   {
6320     Bang(x, y);
6321     return;
6322   }
6323   else if (impact && element == EL_PEARL &&
6324            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6325   {
6326     ResetGfxAnimation(x, y);
6327
6328     Feld[x][y] = EL_PEARL_BREAKING;
6329     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6330     return;
6331   }
6332   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6333   {
6334     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6335
6336     return;
6337   }
6338
6339   if (impact && element == EL_AMOEBA_DROP)
6340   {
6341     if (object_hit && IS_PLAYER(x, y + 1))
6342       KillPlayerUnlessEnemyProtected(x, y + 1);
6343     else if (object_hit && smashed == EL_PENGUIN)
6344       Bang(x, y + 1);
6345     else
6346     {
6347       Feld[x][y] = EL_AMOEBA_GROWING;
6348       Store[x][y] = EL_AMOEBA_WET;
6349
6350       ResetRandomAnimationValue(x, y);
6351     }
6352     return;
6353   }
6354
6355   if (object_hit)               /* check which object was hit */
6356   {
6357     if ((CAN_PASS_MAGIC_WALL(element) && 
6358          (smashed == EL_MAGIC_WALL ||
6359           smashed == EL_BD_MAGIC_WALL)) ||
6360         (CAN_PASS_DC_MAGIC_WALL(element) &&
6361          smashed == EL_DC_MAGIC_WALL))
6362     {
6363       int xx, yy;
6364       int activated_magic_wall =
6365         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6366          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6367          EL_DC_MAGIC_WALL_ACTIVE);
6368
6369       /* activate magic wall / mill */
6370       SCAN_PLAYFIELD(xx, yy)
6371       {
6372         if (Feld[xx][yy] == smashed)
6373           Feld[xx][yy] = activated_magic_wall;
6374       }
6375
6376       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6377       game.magic_wall_active = TRUE;
6378
6379       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6380                             SND_MAGIC_WALL_ACTIVATING :
6381                             smashed == EL_BD_MAGIC_WALL ?
6382                             SND_BD_MAGIC_WALL_ACTIVATING :
6383                             SND_DC_MAGIC_WALL_ACTIVATING));
6384     }
6385
6386     if (IS_PLAYER(x, y + 1))
6387     {
6388       if (CAN_SMASH_PLAYER(element))
6389       {
6390         KillPlayerUnlessEnemyProtected(x, y + 1);
6391         return;
6392       }
6393     }
6394     else if (smashed == EL_PENGUIN)
6395     {
6396       if (CAN_SMASH_PLAYER(element))
6397       {
6398         Bang(x, y + 1);
6399         return;
6400       }
6401     }
6402     else if (element == EL_BD_DIAMOND)
6403     {
6404       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6405       {
6406         Bang(x, y + 1);
6407         return;
6408       }
6409     }
6410     else if (((element == EL_SP_INFOTRON ||
6411                element == EL_SP_ZONK) &&
6412               (smashed == EL_SP_SNIKSNAK ||
6413                smashed == EL_SP_ELECTRON ||
6414                smashed == EL_SP_DISK_ORANGE)) ||
6415              (element == EL_SP_INFOTRON &&
6416               smashed == EL_SP_DISK_YELLOW))
6417     {
6418       Bang(x, y + 1);
6419       return;
6420     }
6421     else if (CAN_SMASH_EVERYTHING(element))
6422     {
6423       if (IS_CLASSIC_ENEMY(smashed) ||
6424           CAN_EXPLODE_SMASHED(smashed))
6425       {
6426         Bang(x, y + 1);
6427         return;
6428       }
6429       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6430       {
6431         if (smashed == EL_LAMP ||
6432             smashed == EL_LAMP_ACTIVE)
6433         {
6434           Bang(x, y + 1);
6435           return;
6436         }
6437         else if (smashed == EL_NUT)
6438         {
6439           Feld[x][y + 1] = EL_NUT_BREAKING;
6440           PlayLevelSound(x, y, SND_NUT_BREAKING);
6441           RaiseScoreElement(EL_NUT);
6442           return;
6443         }
6444         else if (smashed == EL_PEARL)
6445         {
6446           ResetGfxAnimation(x, y);
6447
6448           Feld[x][y + 1] = EL_PEARL_BREAKING;
6449           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6450           return;
6451         }
6452         else if (smashed == EL_DIAMOND)
6453         {
6454           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6455           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6456           return;
6457         }
6458         else if (IS_BELT_SWITCH(smashed))
6459         {
6460           ToggleBeltSwitch(x, y + 1);
6461         }
6462         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6463                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6464                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6465                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6466         {
6467           ToggleSwitchgateSwitch(x, y + 1);
6468         }
6469         else if (smashed == EL_LIGHT_SWITCH ||
6470                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6471         {
6472           ToggleLightSwitch(x, y + 1);
6473         }
6474         else
6475         {
6476           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6477
6478           CheckElementChangeBySide(x, y + 1, smashed, element,
6479                                    CE_SWITCHED, CH_SIDE_TOP);
6480           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6481                                             CH_SIDE_TOP);
6482         }
6483       }
6484       else
6485       {
6486         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6487       }
6488     }
6489   }
6490
6491   /* play sound of magic wall / mill */
6492   if (!last_line &&
6493       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6494        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6495        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6496   {
6497     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6498       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6499     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6500       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6501     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6502       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6503
6504     return;
6505   }
6506
6507   /* play sound of object that hits the ground */
6508   if (last_line || object_hit)
6509     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6510 }
6511
6512 inline static void TurnRoundExt(int x, int y)
6513 {
6514   static struct
6515   {
6516     int dx, dy;
6517   } move_xy[] =
6518   {
6519     {  0,  0 },
6520     { -1,  0 },
6521     { +1,  0 },
6522     {  0,  0 },
6523     {  0, -1 },
6524     {  0,  0 }, { 0, 0 }, { 0, 0 },
6525     {  0, +1 }
6526   };
6527   static struct
6528   {
6529     int left, right, back;
6530   } turn[] =
6531   {
6532     { 0,        0,              0        },
6533     { MV_DOWN,  MV_UP,          MV_RIGHT },
6534     { MV_UP,    MV_DOWN,        MV_LEFT  },
6535     { 0,        0,              0        },
6536     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6537     { 0,        0,              0        },
6538     { 0,        0,              0        },
6539     { 0,        0,              0        },
6540     { MV_RIGHT, MV_LEFT,        MV_UP    }
6541   };
6542
6543   int element = Feld[x][y];
6544   int move_pattern = element_info[element].move_pattern;
6545
6546   int old_move_dir = MovDir[x][y];
6547   int left_dir  = turn[old_move_dir].left;
6548   int right_dir = turn[old_move_dir].right;
6549   int back_dir  = turn[old_move_dir].back;
6550
6551   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6552   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6553   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6554   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6555
6556   int left_x  = x + left_dx,  left_y  = y + left_dy;
6557   int right_x = x + right_dx, right_y = y + right_dy;
6558   int move_x  = x + move_dx,  move_y  = y + move_dy;
6559
6560   int xx, yy;
6561
6562   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6563   {
6564     TestIfBadThingTouchesOtherBadThing(x, y);
6565
6566     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6567       MovDir[x][y] = right_dir;
6568     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6569       MovDir[x][y] = left_dir;
6570
6571     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6572       MovDelay[x][y] = 9;
6573     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6574       MovDelay[x][y] = 1;
6575   }
6576   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6577   {
6578     TestIfBadThingTouchesOtherBadThing(x, y);
6579
6580     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6581       MovDir[x][y] = left_dir;
6582     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6583       MovDir[x][y] = right_dir;
6584
6585     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6586       MovDelay[x][y] = 9;
6587     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6588       MovDelay[x][y] = 1;
6589   }
6590   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6591   {
6592     TestIfBadThingTouchesOtherBadThing(x, y);
6593
6594     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6595       MovDir[x][y] = left_dir;
6596     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6597       MovDir[x][y] = right_dir;
6598
6599     if (MovDir[x][y] != old_move_dir)
6600       MovDelay[x][y] = 9;
6601   }
6602   else if (element == EL_YAMYAM)
6603   {
6604     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6605     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6606
6607     if (can_turn_left && can_turn_right)
6608       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6609     else if (can_turn_left)
6610       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6611     else if (can_turn_right)
6612       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6613     else
6614       MovDir[x][y] = back_dir;
6615
6616     MovDelay[x][y] = 16 + 16 * RND(3);
6617   }
6618   else if (element == EL_DARK_YAMYAM)
6619   {
6620     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6621                                                          left_x, left_y);
6622     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6623                                                          right_x, right_y);
6624
6625     if (can_turn_left && can_turn_right)
6626       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6627     else if (can_turn_left)
6628       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6629     else if (can_turn_right)
6630       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6631     else
6632       MovDir[x][y] = back_dir;
6633
6634     MovDelay[x][y] = 16 + 16 * RND(3);
6635   }
6636   else if (element == EL_PACMAN)
6637   {
6638     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6639     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6640
6641     if (can_turn_left && can_turn_right)
6642       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6643     else if (can_turn_left)
6644       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6645     else if (can_turn_right)
6646       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6647     else
6648       MovDir[x][y] = back_dir;
6649
6650     MovDelay[x][y] = 6 + RND(40);
6651   }
6652   else if (element == EL_PIG)
6653   {
6654     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6655     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6656     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6657     boolean should_turn_left, should_turn_right, should_move_on;
6658     int rnd_value = 24;
6659     int rnd = RND(rnd_value);
6660
6661     should_turn_left = (can_turn_left &&
6662                         (!can_move_on ||
6663                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6664                                                    y + back_dy + left_dy)));
6665     should_turn_right = (can_turn_right &&
6666                          (!can_move_on ||
6667                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6668                                                     y + back_dy + right_dy)));
6669     should_move_on = (can_move_on &&
6670                       (!can_turn_left ||
6671                        !can_turn_right ||
6672                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6673                                                  y + move_dy + left_dy) ||
6674                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6675                                                  y + move_dy + right_dy)));
6676
6677     if (should_turn_left || should_turn_right || should_move_on)
6678     {
6679       if (should_turn_left && should_turn_right && should_move_on)
6680         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6681                         rnd < 2 * rnd_value / 3 ? right_dir :
6682                         old_move_dir);
6683       else if (should_turn_left && should_turn_right)
6684         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6685       else if (should_turn_left && should_move_on)
6686         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6687       else if (should_turn_right && should_move_on)
6688         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6689       else if (should_turn_left)
6690         MovDir[x][y] = left_dir;
6691       else if (should_turn_right)
6692         MovDir[x][y] = right_dir;
6693       else if (should_move_on)
6694         MovDir[x][y] = old_move_dir;
6695     }
6696     else if (can_move_on && rnd > rnd_value / 8)
6697       MovDir[x][y] = old_move_dir;
6698     else if (can_turn_left && can_turn_right)
6699       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6700     else if (can_turn_left && rnd > rnd_value / 8)
6701       MovDir[x][y] = left_dir;
6702     else if (can_turn_right && rnd > rnd_value/8)
6703       MovDir[x][y] = right_dir;
6704     else
6705       MovDir[x][y] = back_dir;
6706
6707     xx = x + move_xy[MovDir[x][y]].dx;
6708     yy = y + move_xy[MovDir[x][y]].dy;
6709
6710     if (!IN_LEV_FIELD(xx, yy) ||
6711         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6712       MovDir[x][y] = old_move_dir;
6713
6714     MovDelay[x][y] = 0;
6715   }
6716   else if (element == EL_DRAGON)
6717   {
6718     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6719     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6720     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6721     int rnd_value = 24;
6722     int rnd = RND(rnd_value);
6723
6724     if (can_move_on && rnd > rnd_value / 8)
6725       MovDir[x][y] = old_move_dir;
6726     else if (can_turn_left && can_turn_right)
6727       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6728     else if (can_turn_left && rnd > rnd_value / 8)
6729       MovDir[x][y] = left_dir;
6730     else if (can_turn_right && rnd > rnd_value / 8)
6731       MovDir[x][y] = right_dir;
6732     else
6733       MovDir[x][y] = back_dir;
6734
6735     xx = x + move_xy[MovDir[x][y]].dx;
6736     yy = y + move_xy[MovDir[x][y]].dy;
6737
6738     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6739       MovDir[x][y] = old_move_dir;
6740
6741     MovDelay[x][y] = 0;
6742   }
6743   else if (element == EL_MOLE)
6744   {
6745     boolean can_move_on =
6746       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6747                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6748                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6749     if (!can_move_on)
6750     {
6751       boolean can_turn_left =
6752         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6753                               IS_AMOEBOID(Feld[left_x][left_y])));
6754
6755       boolean can_turn_right =
6756         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6757                               IS_AMOEBOID(Feld[right_x][right_y])));
6758
6759       if (can_turn_left && can_turn_right)
6760         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6761       else if (can_turn_left)
6762         MovDir[x][y] = left_dir;
6763       else
6764         MovDir[x][y] = right_dir;
6765     }
6766
6767     if (MovDir[x][y] != old_move_dir)
6768       MovDelay[x][y] = 9;
6769   }
6770   else if (element == EL_BALLOON)
6771   {
6772     MovDir[x][y] = game.wind_direction;
6773     MovDelay[x][y] = 0;
6774   }
6775   else if (element == EL_SPRING)
6776   {
6777     if (MovDir[x][y] & MV_HORIZONTAL)
6778     {
6779       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6780           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6781       {
6782         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6783         ResetGfxAnimation(move_x, move_y);
6784         TEST_DrawLevelField(move_x, move_y);
6785
6786         MovDir[x][y] = back_dir;
6787       }
6788       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6789                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6790         MovDir[x][y] = MV_NONE;
6791     }
6792
6793     MovDelay[x][y] = 0;
6794   }
6795   else if (element == EL_ROBOT ||
6796            element == EL_SATELLITE ||
6797            element == EL_PENGUIN ||
6798            element == EL_EMC_ANDROID)
6799   {
6800     int attr_x = -1, attr_y = -1;
6801
6802     if (AllPlayersGone)
6803     {
6804       attr_x = ExitX;
6805       attr_y = ExitY;
6806     }
6807     else
6808     {
6809       int i;
6810
6811       for (i = 0; i < MAX_PLAYERS; i++)
6812       {
6813         struct PlayerInfo *player = &stored_player[i];
6814         int jx = player->jx, jy = player->jy;
6815
6816         if (!player->active)
6817           continue;
6818
6819         if (attr_x == -1 ||
6820             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6821         {
6822           attr_x = jx;
6823           attr_y = jy;
6824         }
6825       }
6826     }
6827
6828     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6829         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6830          game.engine_version < VERSION_IDENT(3,1,0,0)))
6831     {
6832       attr_x = ZX;
6833       attr_y = ZY;
6834     }
6835
6836     if (element == EL_PENGUIN)
6837     {
6838       int i;
6839       static int xy[4][2] =
6840       {
6841         { 0, -1 },
6842         { -1, 0 },
6843         { +1, 0 },
6844         { 0, +1 }
6845       };
6846
6847       for (i = 0; i < NUM_DIRECTIONS; i++)
6848       {
6849         int ex = x + xy[i][0];
6850         int ey = y + xy[i][1];
6851
6852         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6853                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6854                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6855                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6856         {
6857           attr_x = ex;
6858           attr_y = ey;
6859           break;
6860         }
6861       }
6862     }
6863
6864     MovDir[x][y] = MV_NONE;
6865     if (attr_x < x)
6866       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6867     else if (attr_x > x)
6868       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6869     if (attr_y < y)
6870       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6871     else if (attr_y > y)
6872       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6873
6874     if (element == EL_ROBOT)
6875     {
6876       int newx, newy;
6877
6878       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6879         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6880       Moving2Blocked(x, y, &newx, &newy);
6881
6882       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6883         MovDelay[x][y] = 8 + 8 * !RND(3);
6884       else
6885         MovDelay[x][y] = 16;
6886     }
6887     else if (element == EL_PENGUIN)
6888     {
6889       int newx, newy;
6890
6891       MovDelay[x][y] = 1;
6892
6893       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6894       {
6895         boolean first_horiz = RND(2);
6896         int new_move_dir = MovDir[x][y];
6897
6898         MovDir[x][y] =
6899           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6900         Moving2Blocked(x, y, &newx, &newy);
6901
6902         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6903           return;
6904
6905         MovDir[x][y] =
6906           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6907         Moving2Blocked(x, y, &newx, &newy);
6908
6909         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6910           return;
6911
6912         MovDir[x][y] = old_move_dir;
6913         return;
6914       }
6915     }
6916     else if (element == EL_SATELLITE)
6917     {
6918       int newx, newy;
6919
6920       MovDelay[x][y] = 1;
6921
6922       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6923       {
6924         boolean first_horiz = RND(2);
6925         int new_move_dir = MovDir[x][y];
6926
6927         MovDir[x][y] =
6928           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6929         Moving2Blocked(x, y, &newx, &newy);
6930
6931         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6932           return;
6933
6934         MovDir[x][y] =
6935           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6936         Moving2Blocked(x, y, &newx, &newy);
6937
6938         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6939           return;
6940
6941         MovDir[x][y] = old_move_dir;
6942         return;
6943       }
6944     }
6945     else if (element == EL_EMC_ANDROID)
6946     {
6947       static int check_pos[16] =
6948       {
6949         -1,             /*  0 => (invalid)          */
6950         7,              /*  1 => MV_LEFT            */
6951         3,              /*  2 => MV_RIGHT           */
6952         -1,             /*  3 => (invalid)          */
6953         1,              /*  4 =>            MV_UP   */
6954         0,              /*  5 => MV_LEFT  | MV_UP   */
6955         2,              /*  6 => MV_RIGHT | MV_UP   */
6956         -1,             /*  7 => (invalid)          */
6957         5,              /*  8 =>            MV_DOWN */
6958         6,              /*  9 => MV_LEFT  | MV_DOWN */
6959         4,              /* 10 => MV_RIGHT | MV_DOWN */
6960         -1,             /* 11 => (invalid)          */
6961         -1,             /* 12 => (invalid)          */
6962         -1,             /* 13 => (invalid)          */
6963         -1,             /* 14 => (invalid)          */
6964         -1,             /* 15 => (invalid)          */
6965       };
6966       static struct
6967       {
6968         int dx, dy;
6969         int dir;
6970       } check_xy[8] =
6971       {
6972         { -1, -1,       MV_LEFT  | MV_UP   },
6973         {  0, -1,                  MV_UP   },
6974         { +1, -1,       MV_RIGHT | MV_UP   },
6975         { +1,  0,       MV_RIGHT           },
6976         { +1, +1,       MV_RIGHT | MV_DOWN },
6977         {  0, +1,                  MV_DOWN },
6978         { -1, +1,       MV_LEFT  | MV_DOWN },
6979         { -1,  0,       MV_LEFT            },
6980       };
6981       int start_pos, check_order;
6982       boolean can_clone = FALSE;
6983       int i;
6984
6985       /* check if there is any free field around current position */
6986       for (i = 0; i < 8; i++)
6987       {
6988         int newx = x + check_xy[i].dx;
6989         int newy = y + check_xy[i].dy;
6990
6991         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6992         {
6993           can_clone = TRUE;
6994
6995           break;
6996         }
6997       }
6998
6999       if (can_clone)            /* randomly find an element to clone */
7000       {
7001         can_clone = FALSE;
7002
7003         start_pos = check_pos[RND(8)];
7004         check_order = (RND(2) ? -1 : +1);
7005
7006         for (i = 0; i < 8; i++)
7007         {
7008           int pos_raw = start_pos + i * check_order;
7009           int pos = (pos_raw + 8) % 8;
7010           int newx = x + check_xy[pos].dx;
7011           int newy = y + check_xy[pos].dy;
7012
7013           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7014           {
7015             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7016             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7017
7018             Store[x][y] = Feld[newx][newy];
7019
7020             can_clone = TRUE;
7021
7022             break;
7023           }
7024         }
7025       }
7026
7027       if (can_clone)            /* randomly find a direction to move */
7028       {
7029         can_clone = FALSE;
7030
7031         start_pos = check_pos[RND(8)];
7032         check_order = (RND(2) ? -1 : +1);
7033
7034         for (i = 0; i < 8; i++)
7035         {
7036           int pos_raw = start_pos + i * check_order;
7037           int pos = (pos_raw + 8) % 8;
7038           int newx = x + check_xy[pos].dx;
7039           int newy = y + check_xy[pos].dy;
7040           int new_move_dir = check_xy[pos].dir;
7041
7042           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7043           {
7044             MovDir[x][y] = new_move_dir;
7045             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7046
7047             can_clone = TRUE;
7048
7049             break;
7050           }
7051         }
7052       }
7053
7054       if (can_clone)            /* cloning and moving successful */
7055         return;
7056
7057       /* cannot clone -- try to move towards player */
7058
7059       start_pos = check_pos[MovDir[x][y] & 0x0f];
7060       check_order = (RND(2) ? -1 : +1);
7061
7062       for (i = 0; i < 3; i++)
7063       {
7064         /* first check start_pos, then previous/next or (next/previous) pos */
7065         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7066         int pos = (pos_raw + 8) % 8;
7067         int newx = x + check_xy[pos].dx;
7068         int newy = y + check_xy[pos].dy;
7069         int new_move_dir = check_xy[pos].dir;
7070
7071         if (IS_PLAYER(newx, newy))
7072           break;
7073
7074         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7075         {
7076           MovDir[x][y] = new_move_dir;
7077           MovDelay[x][y] = level.android_move_time * 8 + 1;
7078
7079           break;
7080         }
7081       }
7082     }
7083   }
7084   else if (move_pattern == MV_TURNING_LEFT ||
7085            move_pattern == MV_TURNING_RIGHT ||
7086            move_pattern == MV_TURNING_LEFT_RIGHT ||
7087            move_pattern == MV_TURNING_RIGHT_LEFT ||
7088            move_pattern == MV_TURNING_RANDOM ||
7089            move_pattern == MV_ALL_DIRECTIONS)
7090   {
7091     boolean can_turn_left =
7092       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7093     boolean can_turn_right =
7094       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7095
7096     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7097       return;
7098
7099     if (move_pattern == MV_TURNING_LEFT)
7100       MovDir[x][y] = left_dir;
7101     else if (move_pattern == MV_TURNING_RIGHT)
7102       MovDir[x][y] = right_dir;
7103     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7104       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7105     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7106       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7107     else if (move_pattern == MV_TURNING_RANDOM)
7108       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7109                       can_turn_right && !can_turn_left ? right_dir :
7110                       RND(2) ? left_dir : right_dir);
7111     else if (can_turn_left && can_turn_right)
7112       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7113     else if (can_turn_left)
7114       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7115     else if (can_turn_right)
7116       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7117     else
7118       MovDir[x][y] = back_dir;
7119
7120     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7121   }
7122   else if (move_pattern == MV_HORIZONTAL ||
7123            move_pattern == MV_VERTICAL)
7124   {
7125     if (move_pattern & old_move_dir)
7126       MovDir[x][y] = back_dir;
7127     else if (move_pattern == MV_HORIZONTAL)
7128       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7129     else if (move_pattern == MV_VERTICAL)
7130       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7131
7132     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7133   }
7134   else if (move_pattern & MV_ANY_DIRECTION)
7135   {
7136     MovDir[x][y] = move_pattern;
7137     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7138   }
7139   else if (move_pattern & MV_WIND_DIRECTION)
7140   {
7141     MovDir[x][y] = game.wind_direction;
7142     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7143   }
7144   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7145   {
7146     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7147       MovDir[x][y] = left_dir;
7148     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7149       MovDir[x][y] = right_dir;
7150
7151     if (MovDir[x][y] != old_move_dir)
7152       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7153   }
7154   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7155   {
7156     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7157       MovDir[x][y] = right_dir;
7158     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7159       MovDir[x][y] = left_dir;
7160
7161     if (MovDir[x][y] != old_move_dir)
7162       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7163   }
7164   else if (move_pattern == MV_TOWARDS_PLAYER ||
7165            move_pattern == MV_AWAY_FROM_PLAYER)
7166   {
7167     int attr_x = -1, attr_y = -1;
7168     int newx, newy;
7169     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7170
7171     if (AllPlayersGone)
7172     {
7173       attr_x = ExitX;
7174       attr_y = ExitY;
7175     }
7176     else
7177     {
7178       int i;
7179
7180       for (i = 0; i < MAX_PLAYERS; i++)
7181       {
7182         struct PlayerInfo *player = &stored_player[i];
7183         int jx = player->jx, jy = player->jy;
7184
7185         if (!player->active)
7186           continue;
7187
7188         if (attr_x == -1 ||
7189             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7190         {
7191           attr_x = jx;
7192           attr_y = jy;
7193         }
7194       }
7195     }
7196
7197     MovDir[x][y] = MV_NONE;
7198     if (attr_x < x)
7199       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7200     else if (attr_x > x)
7201       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7202     if (attr_y < y)
7203       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7204     else if (attr_y > y)
7205       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7206
7207     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7208
7209     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7210     {
7211       boolean first_horiz = RND(2);
7212       int new_move_dir = MovDir[x][y];
7213
7214       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7215       {
7216         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7217         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7218
7219         return;
7220       }
7221
7222       MovDir[x][y] =
7223         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7224       Moving2Blocked(x, y, &newx, &newy);
7225
7226       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7227         return;
7228
7229       MovDir[x][y] =
7230         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7231       Moving2Blocked(x, y, &newx, &newy);
7232
7233       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7234         return;
7235
7236       MovDir[x][y] = old_move_dir;
7237     }
7238   }
7239   else if (move_pattern == MV_WHEN_PUSHED ||
7240            move_pattern == MV_WHEN_DROPPED)
7241   {
7242     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7243       MovDir[x][y] = MV_NONE;
7244
7245     MovDelay[x][y] = 0;
7246   }
7247   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7248   {
7249     static int test_xy[7][2] =
7250     {
7251       { 0, -1 },
7252       { -1, 0 },
7253       { +1, 0 },
7254       { 0, +1 },
7255       { 0, -1 },
7256       { -1, 0 },
7257       { +1, 0 },
7258     };
7259     static int test_dir[7] =
7260     {
7261       MV_UP,
7262       MV_LEFT,
7263       MV_RIGHT,
7264       MV_DOWN,
7265       MV_UP,
7266       MV_LEFT,
7267       MV_RIGHT,
7268     };
7269     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7270     int move_preference = -1000000;     /* start with very low preference */
7271     int new_move_dir = MV_NONE;
7272     int start_test = RND(4);
7273     int i;
7274
7275     for (i = 0; i < NUM_DIRECTIONS; i++)
7276     {
7277       int move_dir = test_dir[start_test + i];
7278       int move_dir_preference;
7279
7280       xx = x + test_xy[start_test + i][0];
7281       yy = y + test_xy[start_test + i][1];
7282
7283       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7284           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7285       {
7286         new_move_dir = move_dir;
7287
7288         break;
7289       }
7290
7291       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7292         continue;
7293
7294       move_dir_preference = -1 * RunnerVisit[xx][yy];
7295       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7296         move_dir_preference = PlayerVisit[xx][yy];
7297
7298       if (move_dir_preference > move_preference)
7299       {
7300         /* prefer field that has not been visited for the longest time */
7301         move_preference = move_dir_preference;
7302         new_move_dir = move_dir;
7303       }
7304       else if (move_dir_preference == move_preference &&
7305                move_dir == old_move_dir)
7306       {
7307         /* prefer last direction when all directions are preferred equally */
7308         move_preference = move_dir_preference;
7309         new_move_dir = move_dir;
7310       }
7311     }
7312
7313     MovDir[x][y] = new_move_dir;
7314     if (old_move_dir != new_move_dir)
7315       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7316   }
7317 }
7318
7319 static void TurnRound(int x, int y)
7320 {
7321   int direction = MovDir[x][y];
7322
7323   TurnRoundExt(x, y);
7324
7325   GfxDir[x][y] = MovDir[x][y];
7326
7327   if (direction != MovDir[x][y])
7328     GfxFrame[x][y] = 0;
7329
7330   if (MovDelay[x][y])
7331     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7332
7333   ResetGfxFrame(x, y);
7334 }
7335
7336 static boolean JustBeingPushed(int x, int y)
7337 {
7338   int i;
7339
7340   for (i = 0; i < MAX_PLAYERS; i++)
7341   {
7342     struct PlayerInfo *player = &stored_player[i];
7343
7344     if (player->active && player->is_pushing && player->MovPos)
7345     {
7346       int next_jx = player->jx + (player->jx - player->last_jx);
7347       int next_jy = player->jy + (player->jy - player->last_jy);
7348
7349       if (x == next_jx && y == next_jy)
7350         return TRUE;
7351     }
7352   }
7353
7354   return FALSE;
7355 }
7356
7357 void StartMoving(int x, int y)
7358 {
7359   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7360   int element = Feld[x][y];
7361
7362   if (Stop[x][y])
7363     return;
7364
7365   if (MovDelay[x][y] == 0)
7366     GfxAction[x][y] = ACTION_DEFAULT;
7367
7368   if (CAN_FALL(element) && y < lev_fieldy - 1)
7369   {
7370     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7371         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7372       if (JustBeingPushed(x, y))
7373         return;
7374
7375     if (element == EL_QUICKSAND_FULL)
7376     {
7377       if (IS_FREE(x, y + 1))
7378       {
7379         InitMovingField(x, y, MV_DOWN);
7380         started_moving = TRUE;
7381
7382         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7383 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7384         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7385           Store[x][y] = EL_ROCK;
7386 #else
7387         Store[x][y] = EL_ROCK;
7388 #endif
7389
7390         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7391       }
7392       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7393       {
7394         if (!MovDelay[x][y])
7395         {
7396           MovDelay[x][y] = TILEY + 1;
7397
7398           ResetGfxAnimation(x, y);
7399           ResetGfxAnimation(x, y + 1);
7400         }
7401
7402         if (MovDelay[x][y])
7403         {
7404           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7405           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7406
7407           MovDelay[x][y]--;
7408           if (MovDelay[x][y])
7409             return;
7410         }
7411
7412         Feld[x][y] = EL_QUICKSAND_EMPTY;
7413         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7414         Store[x][y + 1] = Store[x][y];
7415         Store[x][y] = 0;
7416
7417         PlayLevelSoundAction(x, y, ACTION_FILLING);
7418       }
7419       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7420       {
7421         if (!MovDelay[x][y])
7422         {
7423           MovDelay[x][y] = TILEY + 1;
7424
7425           ResetGfxAnimation(x, y);
7426           ResetGfxAnimation(x, y + 1);
7427         }
7428
7429         if (MovDelay[x][y])
7430         {
7431           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7432           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7433
7434           MovDelay[x][y]--;
7435           if (MovDelay[x][y])
7436             return;
7437         }
7438
7439         Feld[x][y] = EL_QUICKSAND_EMPTY;
7440         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7441         Store[x][y + 1] = Store[x][y];
7442         Store[x][y] = 0;
7443
7444         PlayLevelSoundAction(x, y, ACTION_FILLING);
7445       }
7446     }
7447     else if (element == EL_QUICKSAND_FAST_FULL)
7448     {
7449       if (IS_FREE(x, y + 1))
7450       {
7451         InitMovingField(x, y, MV_DOWN);
7452         started_moving = TRUE;
7453
7454         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7455 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7456         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7457           Store[x][y] = EL_ROCK;
7458 #else
7459         Store[x][y] = EL_ROCK;
7460 #endif
7461
7462         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7463       }
7464       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7465       {
7466         if (!MovDelay[x][y])
7467         {
7468           MovDelay[x][y] = TILEY + 1;
7469
7470           ResetGfxAnimation(x, y);
7471           ResetGfxAnimation(x, y + 1);
7472         }
7473
7474         if (MovDelay[x][y])
7475         {
7476           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7477           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7478
7479           MovDelay[x][y]--;
7480           if (MovDelay[x][y])
7481             return;
7482         }
7483
7484         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7485         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7486         Store[x][y + 1] = Store[x][y];
7487         Store[x][y] = 0;
7488
7489         PlayLevelSoundAction(x, y, ACTION_FILLING);
7490       }
7491       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7492       {
7493         if (!MovDelay[x][y])
7494         {
7495           MovDelay[x][y] = TILEY + 1;
7496
7497           ResetGfxAnimation(x, y);
7498           ResetGfxAnimation(x, y + 1);
7499         }
7500
7501         if (MovDelay[x][y])
7502         {
7503           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7504           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7505
7506           MovDelay[x][y]--;
7507           if (MovDelay[x][y])
7508             return;
7509         }
7510
7511         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7512         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7513         Store[x][y + 1] = Store[x][y];
7514         Store[x][y] = 0;
7515
7516         PlayLevelSoundAction(x, y, ACTION_FILLING);
7517       }
7518     }
7519     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7520              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7521     {
7522       InitMovingField(x, y, MV_DOWN);
7523       started_moving = TRUE;
7524
7525       Feld[x][y] = EL_QUICKSAND_FILLING;
7526       Store[x][y] = element;
7527
7528       PlayLevelSoundAction(x, y, ACTION_FILLING);
7529     }
7530     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7531              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7532     {
7533       InitMovingField(x, y, MV_DOWN);
7534       started_moving = TRUE;
7535
7536       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7537       Store[x][y] = element;
7538
7539       PlayLevelSoundAction(x, y, ACTION_FILLING);
7540     }
7541     else if (element == EL_MAGIC_WALL_FULL)
7542     {
7543       if (IS_FREE(x, y + 1))
7544       {
7545         InitMovingField(x, y, MV_DOWN);
7546         started_moving = TRUE;
7547
7548         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7549         Store[x][y] = EL_CHANGED(Store[x][y]);
7550       }
7551       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7552       {
7553         if (!MovDelay[x][y])
7554           MovDelay[x][y] = TILEY / 4 + 1;
7555
7556         if (MovDelay[x][y])
7557         {
7558           MovDelay[x][y]--;
7559           if (MovDelay[x][y])
7560             return;
7561         }
7562
7563         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7564         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7565         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7566         Store[x][y] = 0;
7567       }
7568     }
7569     else if (element == EL_BD_MAGIC_WALL_FULL)
7570     {
7571       if (IS_FREE(x, y + 1))
7572       {
7573         InitMovingField(x, y, MV_DOWN);
7574         started_moving = TRUE;
7575
7576         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7577         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7578       }
7579       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7580       {
7581         if (!MovDelay[x][y])
7582           MovDelay[x][y] = TILEY / 4 + 1;
7583
7584         if (MovDelay[x][y])
7585         {
7586           MovDelay[x][y]--;
7587           if (MovDelay[x][y])
7588             return;
7589         }
7590
7591         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7592         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7593         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7594         Store[x][y] = 0;
7595       }
7596     }
7597     else if (element == EL_DC_MAGIC_WALL_FULL)
7598     {
7599       if (IS_FREE(x, y + 1))
7600       {
7601         InitMovingField(x, y, MV_DOWN);
7602         started_moving = TRUE;
7603
7604         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7605         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7606       }
7607       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7608       {
7609         if (!MovDelay[x][y])
7610           MovDelay[x][y] = TILEY / 4 + 1;
7611
7612         if (MovDelay[x][y])
7613         {
7614           MovDelay[x][y]--;
7615           if (MovDelay[x][y])
7616             return;
7617         }
7618
7619         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7620         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7621         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7622         Store[x][y] = 0;
7623       }
7624     }
7625     else if ((CAN_PASS_MAGIC_WALL(element) &&
7626               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7627                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7628              (CAN_PASS_DC_MAGIC_WALL(element) &&
7629               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7630
7631     {
7632       InitMovingField(x, y, MV_DOWN);
7633       started_moving = TRUE;
7634
7635       Feld[x][y] =
7636         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7637          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7638          EL_DC_MAGIC_WALL_FILLING);
7639       Store[x][y] = element;
7640     }
7641     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7642     {
7643       SplashAcid(x, y + 1);
7644
7645       InitMovingField(x, y, MV_DOWN);
7646       started_moving = TRUE;
7647
7648       Store[x][y] = EL_ACID;
7649     }
7650     else if (
7651              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7652               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7653              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7654               CAN_FALL(element) && WasJustFalling[x][y] &&
7655               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7656
7657              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7658               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7659               (Feld[x][y + 1] == EL_BLOCKED)))
7660     {
7661       /* this is needed for a special case not covered by calling "Impact()"
7662          from "ContinueMoving()": if an element moves to a tile directly below
7663          another element which was just falling on that tile (which was empty
7664          in the previous frame), the falling element above would just stop
7665          instead of smashing the element below (in previous version, the above
7666          element was just checked for "moving" instead of "falling", resulting
7667          in incorrect smashes caused by horizontal movement of the above
7668          element; also, the case of the player being the element to smash was
7669          simply not covered here... :-/ ) */
7670
7671       CheckCollision[x][y] = 0;
7672       CheckImpact[x][y] = 0;
7673
7674       Impact(x, y);
7675     }
7676     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7677     {
7678       if (MovDir[x][y] == MV_NONE)
7679       {
7680         InitMovingField(x, y, MV_DOWN);
7681         started_moving = TRUE;
7682       }
7683     }
7684     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7685     {
7686       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7687         MovDir[x][y] = MV_DOWN;
7688
7689       InitMovingField(x, y, MV_DOWN);
7690       started_moving = TRUE;
7691     }
7692     else if (element == EL_AMOEBA_DROP)
7693     {
7694       Feld[x][y] = EL_AMOEBA_GROWING;
7695       Store[x][y] = EL_AMOEBA_WET;
7696     }
7697     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7698               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7699              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7700              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7701     {
7702       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7703                                 (IS_FREE(x - 1, y + 1) ||
7704                                  Feld[x - 1][y + 1] == EL_ACID));
7705       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7706                                 (IS_FREE(x + 1, y + 1) ||
7707                                  Feld[x + 1][y + 1] == EL_ACID));
7708       boolean can_fall_any  = (can_fall_left || can_fall_right);
7709       boolean can_fall_both = (can_fall_left && can_fall_right);
7710       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7711
7712       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7713       {
7714         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7715           can_fall_right = FALSE;
7716         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7717           can_fall_left = FALSE;
7718         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7719           can_fall_right = FALSE;
7720         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7721           can_fall_left = FALSE;
7722
7723         can_fall_any  = (can_fall_left || can_fall_right);
7724         can_fall_both = FALSE;
7725       }
7726
7727       if (can_fall_both)
7728       {
7729         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7730           can_fall_right = FALSE;       /* slip down on left side */
7731         else
7732           can_fall_left = !(can_fall_right = RND(2));
7733
7734         can_fall_both = FALSE;
7735       }
7736
7737       if (can_fall_any)
7738       {
7739         /* if not determined otherwise, prefer left side for slipping down */
7740         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7741         started_moving = TRUE;
7742       }
7743     }
7744     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7745     {
7746       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7747       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7748       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7749       int belt_dir = game.belt_dir[belt_nr];
7750
7751       if ((belt_dir == MV_LEFT  && left_is_free) ||
7752           (belt_dir == MV_RIGHT && right_is_free))
7753       {
7754         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7755
7756         InitMovingField(x, y, belt_dir);
7757         started_moving = TRUE;
7758
7759         Pushed[x][y] = TRUE;
7760         Pushed[nextx][y] = TRUE;
7761
7762         GfxAction[x][y] = ACTION_DEFAULT;
7763       }
7764       else
7765       {
7766         MovDir[x][y] = 0;       /* if element was moving, stop it */
7767       }
7768     }
7769   }
7770
7771   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7772   if (CAN_MOVE(element) && !started_moving)
7773   {
7774     int move_pattern = element_info[element].move_pattern;
7775     int newx, newy;
7776
7777     Moving2Blocked(x, y, &newx, &newy);
7778
7779     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7780       return;
7781
7782     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7783         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7784     {
7785       WasJustMoving[x][y] = 0;
7786       CheckCollision[x][y] = 0;
7787
7788       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7789
7790       if (Feld[x][y] != element)        /* element has changed */
7791         return;
7792     }
7793
7794     if (!MovDelay[x][y])        /* start new movement phase */
7795     {
7796       /* all objects that can change their move direction after each step
7797          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7798
7799       if (element != EL_YAMYAM &&
7800           element != EL_DARK_YAMYAM &&
7801           element != EL_PACMAN &&
7802           !(move_pattern & MV_ANY_DIRECTION) &&
7803           move_pattern != MV_TURNING_LEFT &&
7804           move_pattern != MV_TURNING_RIGHT &&
7805           move_pattern != MV_TURNING_LEFT_RIGHT &&
7806           move_pattern != MV_TURNING_RIGHT_LEFT &&
7807           move_pattern != MV_TURNING_RANDOM)
7808       {
7809         TurnRound(x, y);
7810
7811         if (MovDelay[x][y] && (element == EL_BUG ||
7812                                element == EL_SPACESHIP ||
7813                                element == EL_SP_SNIKSNAK ||
7814                                element == EL_SP_ELECTRON ||
7815                                element == EL_MOLE))
7816           TEST_DrawLevelField(x, y);
7817       }
7818     }
7819
7820     if (MovDelay[x][y])         /* wait some time before next movement */
7821     {
7822       MovDelay[x][y]--;
7823
7824       if (element == EL_ROBOT ||
7825           element == EL_YAMYAM ||
7826           element == EL_DARK_YAMYAM)
7827       {
7828         DrawLevelElementAnimationIfNeeded(x, y, element);
7829         PlayLevelSoundAction(x, y, ACTION_WAITING);
7830       }
7831       else if (element == EL_SP_ELECTRON)
7832         DrawLevelElementAnimationIfNeeded(x, y, element);
7833       else if (element == EL_DRAGON)
7834       {
7835         int i;
7836         int dir = MovDir[x][y];
7837         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7838         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7839         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7840                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7841                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7842                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7843         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7844
7845         GfxAction[x][y] = ACTION_ATTACKING;
7846
7847         if (IS_PLAYER(x, y))
7848           DrawPlayerField(x, y);
7849         else
7850           TEST_DrawLevelField(x, y);
7851
7852         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7853
7854         for (i = 1; i <= 3; i++)
7855         {
7856           int xx = x + i * dx;
7857           int yy = y + i * dy;
7858           int sx = SCREENX(xx);
7859           int sy = SCREENY(yy);
7860           int flame_graphic = graphic + (i - 1);
7861
7862           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7863             break;
7864
7865           if (MovDelay[x][y])
7866           {
7867             int flamed = MovingOrBlocked2Element(xx, yy);
7868
7869             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7870               Bang(xx, yy);
7871             else
7872               RemoveMovingField(xx, yy);
7873
7874             ChangeDelay[xx][yy] = 0;
7875
7876             Feld[xx][yy] = EL_FLAMES;
7877
7878             if (IN_SCR_FIELD(sx, sy))
7879             {
7880               TEST_DrawLevelFieldCrumbled(xx, yy);
7881               DrawGraphic(sx, sy, flame_graphic, frame);
7882             }
7883           }
7884           else
7885           {
7886             if (Feld[xx][yy] == EL_FLAMES)
7887               Feld[xx][yy] = EL_EMPTY;
7888             TEST_DrawLevelField(xx, yy);
7889           }
7890         }
7891       }
7892
7893       if (MovDelay[x][y])       /* element still has to wait some time */
7894       {
7895         PlayLevelSoundAction(x, y, ACTION_WAITING);
7896
7897         return;
7898       }
7899     }
7900
7901     /* now make next step */
7902
7903     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7904
7905     if (DONT_COLLIDE_WITH(element) &&
7906         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7907         !PLAYER_ENEMY_PROTECTED(newx, newy))
7908     {
7909       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7910
7911       return;
7912     }
7913
7914     else if (CAN_MOVE_INTO_ACID(element) &&
7915              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7916              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7917              (MovDir[x][y] == MV_DOWN ||
7918               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7919     {
7920       SplashAcid(newx, newy);
7921       Store[x][y] = EL_ACID;
7922     }
7923     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7924     {
7925       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7926           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7927           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7928           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7929       {
7930         RemoveField(x, y);
7931         TEST_DrawLevelField(x, y);
7932
7933         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7934         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7935           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7936
7937         local_player->friends_still_needed--;
7938         if (!local_player->friends_still_needed &&
7939             !local_player->GameOver && AllPlayersGone)
7940           PlayerWins(local_player);
7941
7942         return;
7943       }
7944       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7945       {
7946         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7947           TEST_DrawLevelField(newx, newy);
7948         else
7949           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7950       }
7951       else if (!IS_FREE(newx, newy))
7952       {
7953         GfxAction[x][y] = ACTION_WAITING;
7954
7955         if (IS_PLAYER(x, y))
7956           DrawPlayerField(x, y);
7957         else
7958           TEST_DrawLevelField(x, y);
7959
7960         return;
7961       }
7962     }
7963     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7964     {
7965       if (IS_FOOD_PIG(Feld[newx][newy]))
7966       {
7967         if (IS_MOVING(newx, newy))
7968           RemoveMovingField(newx, newy);
7969         else
7970         {
7971           Feld[newx][newy] = EL_EMPTY;
7972           TEST_DrawLevelField(newx, newy);
7973         }
7974
7975         PlayLevelSound(x, y, SND_PIG_DIGGING);
7976       }
7977       else if (!IS_FREE(newx, newy))
7978       {
7979         if (IS_PLAYER(x, y))
7980           DrawPlayerField(x, y);
7981         else
7982           TEST_DrawLevelField(x, y);
7983
7984         return;
7985       }
7986     }
7987     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7988     {
7989       if (Store[x][y] != EL_EMPTY)
7990       {
7991         boolean can_clone = FALSE;
7992         int xx, yy;
7993
7994         /* check if element to clone is still there */
7995         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7996         {
7997           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7998           {
7999             can_clone = TRUE;
8000
8001             break;
8002           }
8003         }
8004
8005         /* cannot clone or target field not free anymore -- do not clone */
8006         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8007           Store[x][y] = EL_EMPTY;
8008       }
8009
8010       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8011       {
8012         if (IS_MV_DIAGONAL(MovDir[x][y]))
8013         {
8014           int diagonal_move_dir = MovDir[x][y];
8015           int stored = Store[x][y];
8016           int change_delay = 8;
8017           int graphic;
8018
8019           /* android is moving diagonally */
8020
8021           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8022
8023           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8024           GfxElement[x][y] = EL_EMC_ANDROID;
8025           GfxAction[x][y] = ACTION_SHRINKING;
8026           GfxDir[x][y] = diagonal_move_dir;
8027           ChangeDelay[x][y] = change_delay;
8028
8029           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8030                                    GfxDir[x][y]);
8031
8032           DrawLevelGraphicAnimation(x, y, graphic);
8033           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8034
8035           if (Feld[newx][newy] == EL_ACID)
8036           {
8037             SplashAcid(newx, newy);
8038
8039             return;
8040           }
8041
8042           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8043
8044           Store[newx][newy] = EL_EMC_ANDROID;
8045           GfxElement[newx][newy] = EL_EMC_ANDROID;
8046           GfxAction[newx][newy] = ACTION_GROWING;
8047           GfxDir[newx][newy] = diagonal_move_dir;
8048           ChangeDelay[newx][newy] = change_delay;
8049
8050           graphic = el_act_dir2img(GfxElement[newx][newy],
8051                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8052
8053           DrawLevelGraphicAnimation(newx, newy, graphic);
8054           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8055
8056           return;
8057         }
8058         else
8059         {
8060           Feld[newx][newy] = EL_EMPTY;
8061           TEST_DrawLevelField(newx, newy);
8062
8063           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8064         }
8065       }
8066       else if (!IS_FREE(newx, newy))
8067       {
8068         return;
8069       }
8070     }
8071     else if (IS_CUSTOM_ELEMENT(element) &&
8072              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8073     {
8074       if (!DigFieldByCE(newx, newy, element))
8075         return;
8076
8077       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8078       {
8079         RunnerVisit[x][y] = FrameCounter;
8080         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8081       }
8082     }
8083     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8084     {
8085       if (!IS_FREE(newx, newy))
8086       {
8087         if (IS_PLAYER(x, y))
8088           DrawPlayerField(x, y);
8089         else
8090           TEST_DrawLevelField(x, y);
8091
8092         return;
8093       }
8094       else
8095       {
8096         boolean wanna_flame = !RND(10);
8097         int dx = newx - x, dy = newy - y;
8098         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8099         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8100         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8101                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8102         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8103                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8104
8105         if ((wanna_flame ||
8106              IS_CLASSIC_ENEMY(element1) ||
8107              IS_CLASSIC_ENEMY(element2)) &&
8108             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8109             element1 != EL_FLAMES && element2 != EL_FLAMES)
8110         {
8111           ResetGfxAnimation(x, y);
8112           GfxAction[x][y] = ACTION_ATTACKING;
8113
8114           if (IS_PLAYER(x, y))
8115             DrawPlayerField(x, y);
8116           else
8117             TEST_DrawLevelField(x, y);
8118
8119           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8120
8121           MovDelay[x][y] = 50;
8122
8123           Feld[newx][newy] = EL_FLAMES;
8124           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8125             Feld[newx1][newy1] = EL_FLAMES;
8126           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8127             Feld[newx2][newy2] = EL_FLAMES;
8128
8129           return;
8130         }
8131       }
8132     }
8133     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8134              Feld[newx][newy] == EL_DIAMOND)
8135     {
8136       if (IS_MOVING(newx, newy))
8137         RemoveMovingField(newx, newy);
8138       else
8139       {
8140         Feld[newx][newy] = EL_EMPTY;
8141         TEST_DrawLevelField(newx, newy);
8142       }
8143
8144       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8145     }
8146     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8147              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8148     {
8149       if (AmoebaNr[newx][newy])
8150       {
8151         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8152         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8153             Feld[newx][newy] == EL_BD_AMOEBA)
8154           AmoebaCnt[AmoebaNr[newx][newy]]--;
8155       }
8156
8157       if (IS_MOVING(newx, newy))
8158       {
8159         RemoveMovingField(newx, newy);
8160       }
8161       else
8162       {
8163         Feld[newx][newy] = EL_EMPTY;
8164         TEST_DrawLevelField(newx, newy);
8165       }
8166
8167       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8168     }
8169     else if ((element == EL_PACMAN || element == EL_MOLE)
8170              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8171     {
8172       if (AmoebaNr[newx][newy])
8173       {
8174         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8175         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8176             Feld[newx][newy] == EL_BD_AMOEBA)
8177           AmoebaCnt[AmoebaNr[newx][newy]]--;
8178       }
8179
8180       if (element == EL_MOLE)
8181       {
8182         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8183         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8184
8185         ResetGfxAnimation(x, y);
8186         GfxAction[x][y] = ACTION_DIGGING;
8187         TEST_DrawLevelField(x, y);
8188
8189         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8190
8191         return;                         /* wait for shrinking amoeba */
8192       }
8193       else      /* element == EL_PACMAN */
8194       {
8195         Feld[newx][newy] = EL_EMPTY;
8196         TEST_DrawLevelField(newx, newy);
8197         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8198       }
8199     }
8200     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8201              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8202               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8203     {
8204       /* wait for shrinking amoeba to completely disappear */
8205       return;
8206     }
8207     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8208     {
8209       /* object was running against a wall */
8210
8211       TurnRound(x, y);
8212
8213       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8214         DrawLevelElementAnimation(x, y, element);
8215
8216       if (DONT_TOUCH(element))
8217         TestIfBadThingTouchesPlayer(x, y);
8218
8219       return;
8220     }
8221
8222     InitMovingField(x, y, MovDir[x][y]);
8223
8224     PlayLevelSoundAction(x, y, ACTION_MOVING);
8225   }
8226
8227   if (MovDir[x][y])
8228     ContinueMoving(x, y);
8229 }
8230
8231 void ContinueMoving(int x, int y)
8232 {
8233   int element = Feld[x][y];
8234   struct ElementInfo *ei = &element_info[element];
8235   int direction = MovDir[x][y];
8236   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8237   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8238   int newx = x + dx, newy = y + dy;
8239   int stored = Store[x][y];
8240   int stored_new = Store[newx][newy];
8241   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8242   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8243   boolean last_line = (newy == lev_fieldy - 1);
8244
8245   MovPos[x][y] += getElementMoveStepsize(x, y);
8246
8247   if (pushed_by_player) /* special case: moving object pushed by player */
8248     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8249
8250   if (ABS(MovPos[x][y]) < TILEX)
8251   {
8252     TEST_DrawLevelField(x, y);
8253
8254     return;     /* element is still moving */
8255   }
8256
8257   /* element reached destination field */
8258
8259   Feld[x][y] = EL_EMPTY;
8260   Feld[newx][newy] = element;
8261   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8262
8263   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8264   {
8265     element = Feld[newx][newy] = EL_ACID;
8266   }
8267   else if (element == EL_MOLE)
8268   {
8269     Feld[x][y] = EL_SAND;
8270
8271     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8272   }
8273   else if (element == EL_QUICKSAND_FILLING)
8274   {
8275     element = Feld[newx][newy] = get_next_element(element);
8276     Store[newx][newy] = Store[x][y];
8277   }
8278   else if (element == EL_QUICKSAND_EMPTYING)
8279   {
8280     Feld[x][y] = get_next_element(element);
8281     element = Feld[newx][newy] = Store[x][y];
8282   }
8283   else if (element == EL_QUICKSAND_FAST_FILLING)
8284   {
8285     element = Feld[newx][newy] = get_next_element(element);
8286     Store[newx][newy] = Store[x][y];
8287   }
8288   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8289   {
8290     Feld[x][y] = get_next_element(element);
8291     element = Feld[newx][newy] = Store[x][y];
8292   }
8293   else if (element == EL_MAGIC_WALL_FILLING)
8294   {
8295     element = Feld[newx][newy] = get_next_element(element);
8296     if (!game.magic_wall_active)
8297       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8298     Store[newx][newy] = Store[x][y];
8299   }
8300   else if (element == EL_MAGIC_WALL_EMPTYING)
8301   {
8302     Feld[x][y] = get_next_element(element);
8303     if (!game.magic_wall_active)
8304       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8305     element = Feld[newx][newy] = Store[x][y];
8306
8307     InitField(newx, newy, FALSE);
8308   }
8309   else if (element == EL_BD_MAGIC_WALL_FILLING)
8310   {
8311     element = Feld[newx][newy] = get_next_element(element);
8312     if (!game.magic_wall_active)
8313       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8314     Store[newx][newy] = Store[x][y];
8315   }
8316   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8317   {
8318     Feld[x][y] = get_next_element(element);
8319     if (!game.magic_wall_active)
8320       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8321     element = Feld[newx][newy] = Store[x][y];
8322
8323     InitField(newx, newy, FALSE);
8324   }
8325   else if (element == EL_DC_MAGIC_WALL_FILLING)
8326   {
8327     element = Feld[newx][newy] = get_next_element(element);
8328     if (!game.magic_wall_active)
8329       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8330     Store[newx][newy] = Store[x][y];
8331   }
8332   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8333   {
8334     Feld[x][y] = get_next_element(element);
8335     if (!game.magic_wall_active)
8336       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8337     element = Feld[newx][newy] = Store[x][y];
8338
8339     InitField(newx, newy, FALSE);
8340   }
8341   else if (element == EL_AMOEBA_DROPPING)
8342   {
8343     Feld[x][y] = get_next_element(element);
8344     element = Feld[newx][newy] = Store[x][y];
8345   }
8346   else if (element == EL_SOKOBAN_OBJECT)
8347   {
8348     if (Back[x][y])
8349       Feld[x][y] = Back[x][y];
8350
8351     if (Back[newx][newy])
8352       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8353
8354     Back[x][y] = Back[newx][newy] = 0;
8355   }
8356
8357   Store[x][y] = EL_EMPTY;
8358   MovPos[x][y] = 0;
8359   MovDir[x][y] = 0;
8360   MovDelay[x][y] = 0;
8361
8362   MovDelay[newx][newy] = 0;
8363
8364   if (CAN_CHANGE_OR_HAS_ACTION(element))
8365   {
8366     /* copy element change control values to new field */
8367     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8368     ChangePage[newx][newy]  = ChangePage[x][y];
8369     ChangeCount[newx][newy] = ChangeCount[x][y];
8370     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8371   }
8372
8373   CustomValue[newx][newy] = CustomValue[x][y];
8374
8375   ChangeDelay[x][y] = 0;
8376   ChangePage[x][y] = -1;
8377   ChangeCount[x][y] = 0;
8378   ChangeEvent[x][y] = -1;
8379
8380   CustomValue[x][y] = 0;
8381
8382   /* copy animation control values to new field */
8383   GfxFrame[newx][newy]  = GfxFrame[x][y];
8384   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8385   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8386   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8387
8388   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8389
8390   /* some elements can leave other elements behind after moving */
8391   if (ei->move_leave_element != EL_EMPTY &&
8392       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8393       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8394   {
8395     int move_leave_element = ei->move_leave_element;
8396
8397     /* this makes it possible to leave the removed element again */
8398     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8399       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8400
8401     Feld[x][y] = move_leave_element;
8402
8403     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8404       MovDir[x][y] = direction;
8405
8406     InitField(x, y, FALSE);
8407
8408     if (GFX_CRUMBLED(Feld[x][y]))
8409       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8410
8411     if (ELEM_IS_PLAYER(move_leave_element))
8412       RelocatePlayer(x, y, move_leave_element);
8413   }
8414
8415   /* do this after checking for left-behind element */
8416   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8417
8418   if (!CAN_MOVE(element) ||
8419       (CAN_FALL(element) && direction == MV_DOWN &&
8420        (element == EL_SPRING ||
8421         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8422         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8423     GfxDir[x][y] = MovDir[newx][newy] = 0;
8424
8425   TEST_DrawLevelField(x, y);
8426   TEST_DrawLevelField(newx, newy);
8427
8428   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8429
8430   /* prevent pushed element from moving on in pushed direction */
8431   if (pushed_by_player && CAN_MOVE(element) &&
8432       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8433       !(element_info[element].move_pattern & direction))
8434     TurnRound(newx, newy);
8435
8436   /* prevent elements on conveyor belt from moving on in last direction */
8437   if (pushed_by_conveyor && CAN_FALL(element) &&
8438       direction & MV_HORIZONTAL)
8439     MovDir[newx][newy] = 0;
8440
8441   if (!pushed_by_player)
8442   {
8443     int nextx = newx + dx, nexty = newy + dy;
8444     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8445
8446     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8447
8448     if (CAN_FALL(element) && direction == MV_DOWN)
8449       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8450
8451     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8452       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8453
8454     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8455       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8456   }
8457
8458   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8459   {
8460     TestIfBadThingTouchesPlayer(newx, newy);
8461     TestIfBadThingTouchesFriend(newx, newy);
8462
8463     if (!IS_CUSTOM_ELEMENT(element))
8464       TestIfBadThingTouchesOtherBadThing(newx, newy);
8465   }
8466   else if (element == EL_PENGUIN)
8467     TestIfFriendTouchesBadThing(newx, newy);
8468
8469   if (DONT_GET_HIT_BY(element))
8470   {
8471     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8472   }
8473
8474   /* give the player one last chance (one more frame) to move away */
8475   if (CAN_FALL(element) && direction == MV_DOWN &&
8476       (last_line || (!IS_FREE(x, newy + 1) &&
8477                      (!IS_PLAYER(x, newy + 1) ||
8478                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8479     Impact(x, newy);
8480
8481   if (pushed_by_player && !game.use_change_when_pushing_bug)
8482   {
8483     int push_side = MV_DIR_OPPOSITE(direction);
8484     struct PlayerInfo *player = PLAYERINFO(x, y);
8485
8486     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8487                                player->index_bit, push_side);
8488     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8489                                         player->index_bit, push_side);
8490   }
8491
8492   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8493     MovDelay[newx][newy] = 1;
8494
8495   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8496
8497   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8498   TestIfElementHitsCustomElement(newx, newy, direction);
8499   TestIfPlayerTouchesCustomElement(newx, newy);
8500   TestIfElementTouchesCustomElement(newx, newy);
8501
8502   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8503       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8504     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8505                              MV_DIR_OPPOSITE(direction));
8506 }
8507
8508 int AmoebeNachbarNr(int ax, int ay)
8509 {
8510   int i;
8511   int element = Feld[ax][ay];
8512   int group_nr = 0;
8513   static int xy[4][2] =
8514   {
8515     { 0, -1 },
8516     { -1, 0 },
8517     { +1, 0 },
8518     { 0, +1 }
8519   };
8520
8521   for (i = 0; i < NUM_DIRECTIONS; i++)
8522   {
8523     int x = ax + xy[i][0];
8524     int y = ay + xy[i][1];
8525
8526     if (!IN_LEV_FIELD(x, y))
8527       continue;
8528
8529     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8530       group_nr = AmoebaNr[x][y];
8531   }
8532
8533   return group_nr;
8534 }
8535
8536 void AmoebenVereinigen(int ax, int ay)
8537 {
8538   int i, x, y, xx, yy;
8539   int new_group_nr = AmoebaNr[ax][ay];
8540   static int xy[4][2] =
8541   {
8542     { 0, -1 },
8543     { -1, 0 },
8544     { +1, 0 },
8545     { 0, +1 }
8546   };
8547
8548   if (new_group_nr == 0)
8549     return;
8550
8551   for (i = 0; i < NUM_DIRECTIONS; i++)
8552   {
8553     x = ax + xy[i][0];
8554     y = ay + xy[i][1];
8555
8556     if (!IN_LEV_FIELD(x, y))
8557       continue;
8558
8559     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8560          Feld[x][y] == EL_BD_AMOEBA ||
8561          Feld[x][y] == EL_AMOEBA_DEAD) &&
8562         AmoebaNr[x][y] != new_group_nr)
8563     {
8564       int old_group_nr = AmoebaNr[x][y];
8565
8566       if (old_group_nr == 0)
8567         return;
8568
8569       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8570       AmoebaCnt[old_group_nr] = 0;
8571       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8572       AmoebaCnt2[old_group_nr] = 0;
8573
8574       SCAN_PLAYFIELD(xx, yy)
8575       {
8576         if (AmoebaNr[xx][yy] == old_group_nr)
8577           AmoebaNr[xx][yy] = new_group_nr;
8578       }
8579     }
8580   }
8581 }
8582
8583 void AmoebeUmwandeln(int ax, int ay)
8584 {
8585   int i, x, y;
8586
8587   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8588   {
8589     int group_nr = AmoebaNr[ax][ay];
8590
8591 #ifdef DEBUG
8592     if (group_nr == 0)
8593     {
8594       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8595       printf("AmoebeUmwandeln(): This should never happen!\n");
8596       return;
8597     }
8598 #endif
8599
8600     SCAN_PLAYFIELD(x, y)
8601     {
8602       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8603       {
8604         AmoebaNr[x][y] = 0;
8605         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8606       }
8607     }
8608
8609     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8610                             SND_AMOEBA_TURNING_TO_GEM :
8611                             SND_AMOEBA_TURNING_TO_ROCK));
8612     Bang(ax, ay);
8613   }
8614   else
8615   {
8616     static int xy[4][2] =
8617     {
8618       { 0, -1 },
8619       { -1, 0 },
8620       { +1, 0 },
8621       { 0, +1 }
8622     };
8623
8624     for (i = 0; i < NUM_DIRECTIONS; i++)
8625     {
8626       x = ax + xy[i][0];
8627       y = ay + xy[i][1];
8628
8629       if (!IN_LEV_FIELD(x, y))
8630         continue;
8631
8632       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8633       {
8634         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8635                               SND_AMOEBA_TURNING_TO_GEM :
8636                               SND_AMOEBA_TURNING_TO_ROCK));
8637         Bang(x, y);
8638       }
8639     }
8640   }
8641 }
8642
8643 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8644 {
8645   int x, y;
8646   int group_nr = AmoebaNr[ax][ay];
8647   boolean done = FALSE;
8648
8649 #ifdef DEBUG
8650   if (group_nr == 0)
8651   {
8652     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8653     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8654     return;
8655   }
8656 #endif
8657
8658   SCAN_PLAYFIELD(x, y)
8659   {
8660     if (AmoebaNr[x][y] == group_nr &&
8661         (Feld[x][y] == EL_AMOEBA_DEAD ||
8662          Feld[x][y] == EL_BD_AMOEBA ||
8663          Feld[x][y] == EL_AMOEBA_GROWING))
8664     {
8665       AmoebaNr[x][y] = 0;
8666       Feld[x][y] = new_element;
8667       InitField(x, y, FALSE);
8668       TEST_DrawLevelField(x, y);
8669       done = TRUE;
8670     }
8671   }
8672
8673   if (done)
8674     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8675                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8676                             SND_BD_AMOEBA_TURNING_TO_GEM));
8677 }
8678
8679 void AmoebeWaechst(int x, int y)
8680 {
8681   static unsigned int sound_delay = 0;
8682   static unsigned int sound_delay_value = 0;
8683
8684   if (!MovDelay[x][y])          /* start new growing cycle */
8685   {
8686     MovDelay[x][y] = 7;
8687
8688     if (DelayReached(&sound_delay, sound_delay_value))
8689     {
8690       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8691       sound_delay_value = 30;
8692     }
8693   }
8694
8695   if (MovDelay[x][y])           /* wait some time before growing bigger */
8696   {
8697     MovDelay[x][y]--;
8698     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8699     {
8700       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8701                                            6 - MovDelay[x][y]);
8702
8703       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8704     }
8705
8706     if (!MovDelay[x][y])
8707     {
8708       Feld[x][y] = Store[x][y];
8709       Store[x][y] = 0;
8710       TEST_DrawLevelField(x, y);
8711     }
8712   }
8713 }
8714
8715 void AmoebaDisappearing(int x, int y)
8716 {
8717   static unsigned int sound_delay = 0;
8718   static unsigned int sound_delay_value = 0;
8719
8720   if (!MovDelay[x][y])          /* start new shrinking cycle */
8721   {
8722     MovDelay[x][y] = 7;
8723
8724     if (DelayReached(&sound_delay, sound_delay_value))
8725       sound_delay_value = 30;
8726   }
8727
8728   if (MovDelay[x][y])           /* wait some time before shrinking */
8729   {
8730     MovDelay[x][y]--;
8731     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8732     {
8733       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8734                                            6 - MovDelay[x][y]);
8735
8736       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8737     }
8738
8739     if (!MovDelay[x][y])
8740     {
8741       Feld[x][y] = EL_EMPTY;
8742       TEST_DrawLevelField(x, y);
8743
8744       /* don't let mole enter this field in this cycle;
8745          (give priority to objects falling to this field from above) */
8746       Stop[x][y] = TRUE;
8747     }
8748   }
8749 }
8750
8751 void AmoebeAbleger(int ax, int ay)
8752 {
8753   int i;
8754   int element = Feld[ax][ay];
8755   int graphic = el2img(element);
8756   int newax = ax, neway = ay;
8757   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8758   static int xy[4][2] =
8759   {
8760     { 0, -1 },
8761     { -1, 0 },
8762     { +1, 0 },
8763     { 0, +1 }
8764   };
8765
8766   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8767   {
8768     Feld[ax][ay] = EL_AMOEBA_DEAD;
8769     TEST_DrawLevelField(ax, ay);
8770     return;
8771   }
8772
8773   if (IS_ANIMATED(graphic))
8774     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8775
8776   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8777     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8778
8779   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8780   {
8781     MovDelay[ax][ay]--;
8782     if (MovDelay[ax][ay])
8783       return;
8784   }
8785
8786   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8787   {
8788     int start = RND(4);
8789     int x = ax + xy[start][0];
8790     int y = ay + xy[start][1];
8791
8792     if (!IN_LEV_FIELD(x, y))
8793       return;
8794
8795     if (IS_FREE(x, y) ||
8796         CAN_GROW_INTO(Feld[x][y]) ||
8797         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8798         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8799     {
8800       newax = x;
8801       neway = y;
8802     }
8803
8804     if (newax == ax && neway == ay)
8805       return;
8806   }
8807   else                          /* normal or "filled" (BD style) amoeba */
8808   {
8809     int start = RND(4);
8810     boolean waiting_for_player = FALSE;
8811
8812     for (i = 0; i < NUM_DIRECTIONS; i++)
8813     {
8814       int j = (start + i) % 4;
8815       int x = ax + xy[j][0];
8816       int y = ay + xy[j][1];
8817
8818       if (!IN_LEV_FIELD(x, y))
8819         continue;
8820
8821       if (IS_FREE(x, y) ||
8822           CAN_GROW_INTO(Feld[x][y]) ||
8823           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8824           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8825       {
8826         newax = x;
8827         neway = y;
8828         break;
8829       }
8830       else if (IS_PLAYER(x, y))
8831         waiting_for_player = TRUE;
8832     }
8833
8834     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8835     {
8836       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8837       {
8838         Feld[ax][ay] = EL_AMOEBA_DEAD;
8839         TEST_DrawLevelField(ax, ay);
8840         AmoebaCnt[AmoebaNr[ax][ay]]--;
8841
8842         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8843         {
8844           if (element == EL_AMOEBA_FULL)
8845             AmoebeUmwandeln(ax, ay);
8846           else if (element == EL_BD_AMOEBA)
8847             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8848         }
8849       }
8850       return;
8851     }
8852     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8853     {
8854       /* amoeba gets larger by growing in some direction */
8855
8856       int new_group_nr = AmoebaNr[ax][ay];
8857
8858 #ifdef DEBUG
8859   if (new_group_nr == 0)
8860   {
8861     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8862     printf("AmoebeAbleger(): This should never happen!\n");
8863     return;
8864   }
8865 #endif
8866
8867       AmoebaNr[newax][neway] = new_group_nr;
8868       AmoebaCnt[new_group_nr]++;
8869       AmoebaCnt2[new_group_nr]++;
8870
8871       /* if amoeba touches other amoeba(s) after growing, unify them */
8872       AmoebenVereinigen(newax, neway);
8873
8874       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8875       {
8876         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8877         return;
8878       }
8879     }
8880   }
8881
8882   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8883       (neway == lev_fieldy - 1 && newax != ax))
8884   {
8885     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8886     Store[newax][neway] = element;
8887   }
8888   else if (neway == ay || element == EL_EMC_DRIPPER)
8889   {
8890     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8891
8892     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8893   }
8894   else
8895   {
8896     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8897     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8898     Store[ax][ay] = EL_AMOEBA_DROP;
8899     ContinueMoving(ax, ay);
8900     return;
8901   }
8902
8903   TEST_DrawLevelField(newax, neway);
8904 }
8905
8906 void Life(int ax, int ay)
8907 {
8908   int x1, y1, x2, y2;
8909   int life_time = 40;
8910   int element = Feld[ax][ay];
8911   int graphic = el2img(element);
8912   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8913                          level.biomaze);
8914   boolean changed = FALSE;
8915
8916   if (IS_ANIMATED(graphic))
8917     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8918
8919   if (Stop[ax][ay])
8920     return;
8921
8922   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8923     MovDelay[ax][ay] = life_time;
8924
8925   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8926   {
8927     MovDelay[ax][ay]--;
8928     if (MovDelay[ax][ay])
8929       return;
8930   }
8931
8932   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8933   {
8934     int xx = ax+x1, yy = ay+y1;
8935     int nachbarn = 0;
8936
8937     if (!IN_LEV_FIELD(xx, yy))
8938       continue;
8939
8940     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8941     {
8942       int x = xx+x2, y = yy+y2;
8943
8944       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8945         continue;
8946
8947       if (((Feld[x][y] == element ||
8948             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8949            !Stop[x][y]) ||
8950           (IS_FREE(x, y) && Stop[x][y]))
8951         nachbarn++;
8952     }
8953
8954     if (xx == ax && yy == ay)           /* field in the middle */
8955     {
8956       if (nachbarn < life_parameter[0] ||
8957           nachbarn > life_parameter[1])
8958       {
8959         Feld[xx][yy] = EL_EMPTY;
8960         if (!Stop[xx][yy])
8961           TEST_DrawLevelField(xx, yy);
8962         Stop[xx][yy] = TRUE;
8963         changed = TRUE;
8964       }
8965     }
8966     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8967     {                                   /* free border field */
8968       if (nachbarn >= life_parameter[2] &&
8969           nachbarn <= life_parameter[3])
8970       {
8971         Feld[xx][yy] = element;
8972         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8973         if (!Stop[xx][yy])
8974           TEST_DrawLevelField(xx, yy);
8975         Stop[xx][yy] = TRUE;
8976         changed = TRUE;
8977       }
8978     }
8979   }
8980
8981   if (changed)
8982     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8983                    SND_GAME_OF_LIFE_GROWING);
8984 }
8985
8986 static void InitRobotWheel(int x, int y)
8987 {
8988   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8989 }
8990
8991 static void RunRobotWheel(int x, int y)
8992 {
8993   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8994 }
8995
8996 static void StopRobotWheel(int x, int y)
8997 {
8998   if (ZX == x && ZY == y)
8999   {
9000     ZX = ZY = -1;
9001
9002     game.robot_wheel_active = FALSE;
9003   }
9004 }
9005
9006 static void InitTimegateWheel(int x, int y)
9007 {
9008   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9009 }
9010
9011 static void RunTimegateWheel(int x, int y)
9012 {
9013   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9014 }
9015
9016 static void InitMagicBallDelay(int x, int y)
9017 {
9018   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9019 }
9020
9021 static void ActivateMagicBall(int bx, int by)
9022 {
9023   int x, y;
9024
9025   if (level.ball_random)
9026   {
9027     int pos_border = RND(8);    /* select one of the eight border elements */
9028     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9029     int xx = pos_content % 3;
9030     int yy = pos_content / 3;
9031
9032     x = bx - 1 + xx;
9033     y = by - 1 + yy;
9034
9035     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9036       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9037   }
9038   else
9039   {
9040     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9041     {
9042       int xx = x - bx + 1;
9043       int yy = y - by + 1;
9044
9045       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9046         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9047     }
9048   }
9049
9050   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9051 }
9052
9053 void CheckExit(int x, int y)
9054 {
9055   if (local_player->gems_still_needed > 0 ||
9056       local_player->sokobanfields_still_needed > 0 ||
9057       local_player->lights_still_needed > 0)
9058   {
9059     int element = Feld[x][y];
9060     int graphic = el2img(element);
9061
9062     if (IS_ANIMATED(graphic))
9063       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9064
9065     return;
9066   }
9067
9068   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9069     return;
9070
9071   Feld[x][y] = EL_EXIT_OPENING;
9072
9073   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9074 }
9075
9076 void CheckExitEM(int x, int y)
9077 {
9078   if (local_player->gems_still_needed > 0 ||
9079       local_player->sokobanfields_still_needed > 0 ||
9080       local_player->lights_still_needed > 0)
9081   {
9082     int element = Feld[x][y];
9083     int graphic = el2img(element);
9084
9085     if (IS_ANIMATED(graphic))
9086       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9087
9088     return;
9089   }
9090
9091   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9092     return;
9093
9094   Feld[x][y] = EL_EM_EXIT_OPENING;
9095
9096   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9097 }
9098
9099 void CheckExitSteel(int x, int y)
9100 {
9101   if (local_player->gems_still_needed > 0 ||
9102       local_player->sokobanfields_still_needed > 0 ||
9103       local_player->lights_still_needed > 0)
9104   {
9105     int element = Feld[x][y];
9106     int graphic = el2img(element);
9107
9108     if (IS_ANIMATED(graphic))
9109       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9110
9111     return;
9112   }
9113
9114   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9115     return;
9116
9117   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9118
9119   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9120 }
9121
9122 void CheckExitSteelEM(int x, int y)
9123 {
9124   if (local_player->gems_still_needed > 0 ||
9125       local_player->sokobanfields_still_needed > 0 ||
9126       local_player->lights_still_needed > 0)
9127   {
9128     int element = Feld[x][y];
9129     int graphic = el2img(element);
9130
9131     if (IS_ANIMATED(graphic))
9132       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9133
9134     return;
9135   }
9136
9137   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9138     return;
9139
9140   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9141
9142   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9143 }
9144
9145 void CheckExitSP(int x, int y)
9146 {
9147   if (local_player->gems_still_needed > 0)
9148   {
9149     int element = Feld[x][y];
9150     int graphic = el2img(element);
9151
9152     if (IS_ANIMATED(graphic))
9153       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9154
9155     return;
9156   }
9157
9158   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9159     return;
9160
9161   Feld[x][y] = EL_SP_EXIT_OPENING;
9162
9163   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9164 }
9165
9166 static void CloseAllOpenTimegates()
9167 {
9168   int x, y;
9169
9170   SCAN_PLAYFIELD(x, y)
9171   {
9172     int element = Feld[x][y];
9173
9174     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9175     {
9176       Feld[x][y] = EL_TIMEGATE_CLOSING;
9177
9178       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9179     }
9180   }
9181 }
9182
9183 void DrawTwinkleOnField(int x, int y)
9184 {
9185   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9186     return;
9187
9188   if (Feld[x][y] == EL_BD_DIAMOND)
9189     return;
9190
9191   if (MovDelay[x][y] == 0)      /* next animation frame */
9192     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9193
9194   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9195   {
9196     MovDelay[x][y]--;
9197
9198     DrawLevelElementAnimation(x, y, Feld[x][y]);
9199
9200     if (MovDelay[x][y] != 0)
9201     {
9202       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9203                                            10 - MovDelay[x][y]);
9204
9205       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9206     }
9207   }
9208 }
9209
9210 void MauerWaechst(int x, int y)
9211 {
9212   int delay = 6;
9213
9214   if (!MovDelay[x][y])          /* next animation frame */
9215     MovDelay[x][y] = 3 * delay;
9216
9217   if (MovDelay[x][y])           /* wait some time before next frame */
9218   {
9219     MovDelay[x][y]--;
9220
9221     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9222     {
9223       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9224       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9225
9226       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9227     }
9228
9229     if (!MovDelay[x][y])
9230     {
9231       if (MovDir[x][y] == MV_LEFT)
9232       {
9233         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9234           TEST_DrawLevelField(x - 1, y);
9235       }
9236       else if (MovDir[x][y] == MV_RIGHT)
9237       {
9238         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9239           TEST_DrawLevelField(x + 1, y);
9240       }
9241       else if (MovDir[x][y] == MV_UP)
9242       {
9243         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9244           TEST_DrawLevelField(x, y - 1);
9245       }
9246       else
9247       {
9248         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9249           TEST_DrawLevelField(x, y + 1);
9250       }
9251
9252       Feld[x][y] = Store[x][y];
9253       Store[x][y] = 0;
9254       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9255       TEST_DrawLevelField(x, y);
9256     }
9257   }
9258 }
9259
9260 void MauerAbleger(int ax, int ay)
9261 {
9262   int element = Feld[ax][ay];
9263   int graphic = el2img(element);
9264   boolean oben_frei = FALSE, unten_frei = FALSE;
9265   boolean links_frei = FALSE, rechts_frei = FALSE;
9266   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9267   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9268   boolean new_wall = FALSE;
9269
9270   if (IS_ANIMATED(graphic))
9271     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9272
9273   if (!MovDelay[ax][ay])        /* start building new wall */
9274     MovDelay[ax][ay] = 6;
9275
9276   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9277   {
9278     MovDelay[ax][ay]--;
9279     if (MovDelay[ax][ay])
9280       return;
9281   }
9282
9283   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9284     oben_frei = TRUE;
9285   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9286     unten_frei = TRUE;
9287   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9288     links_frei = TRUE;
9289   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9290     rechts_frei = TRUE;
9291
9292   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9293       element == EL_EXPANDABLE_WALL_ANY)
9294   {
9295     if (oben_frei)
9296     {
9297       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9298       Store[ax][ay-1] = element;
9299       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9300       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9301         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9302                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9303       new_wall = TRUE;
9304     }
9305     if (unten_frei)
9306     {
9307       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9308       Store[ax][ay+1] = element;
9309       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9310       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9311         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9312                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9313       new_wall = TRUE;
9314     }
9315   }
9316
9317   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9318       element == EL_EXPANDABLE_WALL_ANY ||
9319       element == EL_EXPANDABLE_WALL ||
9320       element == EL_BD_EXPANDABLE_WALL)
9321   {
9322     if (links_frei)
9323     {
9324       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9325       Store[ax-1][ay] = element;
9326       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9327       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9328         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9329                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9330       new_wall = TRUE;
9331     }
9332
9333     if (rechts_frei)
9334     {
9335       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9336       Store[ax+1][ay] = element;
9337       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9338       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9339         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9340                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9341       new_wall = TRUE;
9342     }
9343   }
9344
9345   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9346     TEST_DrawLevelField(ax, ay);
9347
9348   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9349     oben_massiv = TRUE;
9350   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9351     unten_massiv = TRUE;
9352   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9353     links_massiv = TRUE;
9354   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9355     rechts_massiv = TRUE;
9356
9357   if (((oben_massiv && unten_massiv) ||
9358        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9359        element == EL_EXPANDABLE_WALL) &&
9360       ((links_massiv && rechts_massiv) ||
9361        element == EL_EXPANDABLE_WALL_VERTICAL))
9362     Feld[ax][ay] = EL_WALL;
9363
9364   if (new_wall)
9365     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9366 }
9367
9368 void MauerAblegerStahl(int ax, int ay)
9369 {
9370   int element = Feld[ax][ay];
9371   int graphic = el2img(element);
9372   boolean oben_frei = FALSE, unten_frei = FALSE;
9373   boolean links_frei = FALSE, rechts_frei = FALSE;
9374   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9375   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9376   boolean new_wall = FALSE;
9377
9378   if (IS_ANIMATED(graphic))
9379     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9380
9381   if (!MovDelay[ax][ay])        /* start building new wall */
9382     MovDelay[ax][ay] = 6;
9383
9384   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9385   {
9386     MovDelay[ax][ay]--;
9387     if (MovDelay[ax][ay])
9388       return;
9389   }
9390
9391   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9392     oben_frei = TRUE;
9393   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9394     unten_frei = TRUE;
9395   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9396     links_frei = TRUE;
9397   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9398     rechts_frei = TRUE;
9399
9400   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9401       element == EL_EXPANDABLE_STEELWALL_ANY)
9402   {
9403     if (oben_frei)
9404     {
9405       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9406       Store[ax][ay-1] = element;
9407       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9408       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9409         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9410                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9411       new_wall = TRUE;
9412     }
9413     if (unten_frei)
9414     {
9415       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9416       Store[ax][ay+1] = element;
9417       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9418       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9419         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9420                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9421       new_wall = TRUE;
9422     }
9423   }
9424
9425   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9426       element == EL_EXPANDABLE_STEELWALL_ANY)
9427   {
9428     if (links_frei)
9429     {
9430       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9431       Store[ax-1][ay] = element;
9432       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9433       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9434         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9435                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9436       new_wall = TRUE;
9437     }
9438
9439     if (rechts_frei)
9440     {
9441       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9442       Store[ax+1][ay] = element;
9443       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9444       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9445         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9446                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9447       new_wall = TRUE;
9448     }
9449   }
9450
9451   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9452     oben_massiv = TRUE;
9453   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9454     unten_massiv = TRUE;
9455   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9456     links_massiv = TRUE;
9457   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9458     rechts_massiv = TRUE;
9459
9460   if (((oben_massiv && unten_massiv) ||
9461        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9462       ((links_massiv && rechts_massiv) ||
9463        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9464     Feld[ax][ay] = EL_STEELWALL;
9465
9466   if (new_wall)
9467     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9468 }
9469
9470 void CheckForDragon(int x, int y)
9471 {
9472   int i, j;
9473   boolean dragon_found = FALSE;
9474   static int xy[4][2] =
9475   {
9476     { 0, -1 },
9477     { -1, 0 },
9478     { +1, 0 },
9479     { 0, +1 }
9480   };
9481
9482   for (i = 0; i < NUM_DIRECTIONS; i++)
9483   {
9484     for (j = 0; j < 4; j++)
9485     {
9486       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9487
9488       if (IN_LEV_FIELD(xx, yy) &&
9489           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9490       {
9491         if (Feld[xx][yy] == EL_DRAGON)
9492           dragon_found = TRUE;
9493       }
9494       else
9495         break;
9496     }
9497   }
9498
9499   if (!dragon_found)
9500   {
9501     for (i = 0; i < NUM_DIRECTIONS; i++)
9502     {
9503       for (j = 0; j < 3; j++)
9504       {
9505         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9506   
9507         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9508         {
9509           Feld[xx][yy] = EL_EMPTY;
9510           TEST_DrawLevelField(xx, yy);
9511         }
9512         else
9513           break;
9514       }
9515     }
9516   }
9517 }
9518
9519 static void InitBuggyBase(int x, int y)
9520 {
9521   int element = Feld[x][y];
9522   int activating_delay = FRAMES_PER_SECOND / 4;
9523
9524   ChangeDelay[x][y] =
9525     (element == EL_SP_BUGGY_BASE ?
9526      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9527      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9528      activating_delay :
9529      element == EL_SP_BUGGY_BASE_ACTIVE ?
9530      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9531 }
9532
9533 static void WarnBuggyBase(int x, int y)
9534 {
9535   int i;
9536   static int xy[4][2] =
9537   {
9538     { 0, -1 },
9539     { -1, 0 },
9540     { +1, 0 },
9541     { 0, +1 }
9542   };
9543
9544   for (i = 0; i < NUM_DIRECTIONS; i++)
9545   {
9546     int xx = x + xy[i][0];
9547     int yy = y + xy[i][1];
9548
9549     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9550     {
9551       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9552
9553       break;
9554     }
9555   }
9556 }
9557
9558 static void InitTrap(int x, int y)
9559 {
9560   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9561 }
9562
9563 static void ActivateTrap(int x, int y)
9564 {
9565   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9566 }
9567
9568 static void ChangeActiveTrap(int x, int y)
9569 {
9570   int graphic = IMG_TRAP_ACTIVE;
9571
9572   /* if new animation frame was drawn, correct crumbled sand border */
9573   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9574     TEST_DrawLevelFieldCrumbled(x, y);
9575 }
9576
9577 static int getSpecialActionElement(int element, int number, int base_element)
9578 {
9579   return (element != EL_EMPTY ? element :
9580           number != -1 ? base_element + number - 1 :
9581           EL_EMPTY);
9582 }
9583
9584 static int getModifiedActionNumber(int value_old, int operator, int operand,
9585                                    int value_min, int value_max)
9586 {
9587   int value_new = (operator == CA_MODE_SET      ? operand :
9588                    operator == CA_MODE_ADD      ? value_old + operand :
9589                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9590                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9591                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9592                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9593                    value_old);
9594
9595   return (value_new < value_min ? value_min :
9596           value_new > value_max ? value_max :
9597           value_new);
9598 }
9599
9600 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9601 {
9602   struct ElementInfo *ei = &element_info[element];
9603   struct ElementChangeInfo *change = &ei->change_page[page];
9604   int target_element = change->target_element;
9605   int action_type = change->action_type;
9606   int action_mode = change->action_mode;
9607   int action_arg = change->action_arg;
9608   int action_element = change->action_element;
9609   int i;
9610
9611   if (!change->has_action)
9612     return;
9613
9614   /* ---------- determine action paramater values -------------------------- */
9615
9616   int level_time_value =
9617     (level.time > 0 ? TimeLeft :
9618      TimePlayed);
9619
9620   int action_arg_element_raw =
9621     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9622      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9623      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9624      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9625      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9626      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9627      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9628      EL_EMPTY);
9629   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9630
9631   int action_arg_direction =
9632     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9633      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9634      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9635      change->actual_trigger_side :
9636      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9637      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9638      MV_NONE);
9639
9640   int action_arg_number_min =
9641     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9642      CA_ARG_MIN);
9643
9644   int action_arg_number_max =
9645     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9646      action_type == CA_SET_LEVEL_GEMS ? 999 :
9647      action_type == CA_SET_LEVEL_TIME ? 9999 :
9648      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9649      action_type == CA_SET_CE_VALUE ? 9999 :
9650      action_type == CA_SET_CE_SCORE ? 9999 :
9651      CA_ARG_MAX);
9652
9653   int action_arg_number_reset =
9654     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9655      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9656      action_type == CA_SET_LEVEL_TIME ? level.time :
9657      action_type == CA_SET_LEVEL_SCORE ? 0 :
9658      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9659      action_type == CA_SET_CE_SCORE ? 0 :
9660      0);
9661
9662   int action_arg_number =
9663     (action_arg <= CA_ARG_MAX ? action_arg :
9664      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9665      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9666      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9667      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9668      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9669      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9670      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9671      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9672      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9673      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9674      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9675      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9676      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9677      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9678      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9679      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9680      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9681      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9682      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9683      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9684      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9685      -1);
9686
9687   int action_arg_number_old =
9688     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9689      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9690      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9691      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9692      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9693      0);
9694
9695   int action_arg_number_new =
9696     getModifiedActionNumber(action_arg_number_old,
9697                             action_mode, action_arg_number,
9698                             action_arg_number_min, action_arg_number_max);
9699
9700   int trigger_player_bits =
9701     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9702      change->actual_trigger_player_bits : change->trigger_player);
9703
9704   int action_arg_player_bits =
9705     (action_arg >= CA_ARG_PLAYER_1 &&
9706      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9707      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9708      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9709      PLAYER_BITS_ANY);
9710
9711   /* ---------- execute action  -------------------------------------------- */
9712
9713   switch (action_type)
9714   {
9715     case CA_NO_ACTION:
9716     {
9717       return;
9718     }
9719
9720     /* ---------- level actions  ------------------------------------------- */
9721
9722     case CA_RESTART_LEVEL:
9723     {
9724       game.restart_level = TRUE;
9725
9726       break;
9727     }
9728
9729     case CA_SHOW_ENVELOPE:
9730     {
9731       int element = getSpecialActionElement(action_arg_element,
9732                                             action_arg_number, EL_ENVELOPE_1);
9733
9734       if (IS_ENVELOPE(element))
9735         local_player->show_envelope = element;
9736
9737       break;
9738     }
9739
9740     case CA_SET_LEVEL_TIME:
9741     {
9742       if (level.time > 0)       /* only modify limited time value */
9743       {
9744         TimeLeft = action_arg_number_new;
9745
9746         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9747
9748         DisplayGameControlValues();
9749
9750         if (!TimeLeft && setup.time_limit)
9751           for (i = 0; i < MAX_PLAYERS; i++)
9752             KillPlayer(&stored_player[i]);
9753       }
9754
9755       break;
9756     }
9757
9758     case CA_SET_LEVEL_SCORE:
9759     {
9760       local_player->score = action_arg_number_new;
9761
9762       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9763
9764       DisplayGameControlValues();
9765
9766       break;
9767     }
9768
9769     case CA_SET_LEVEL_GEMS:
9770     {
9771       local_player->gems_still_needed = action_arg_number_new;
9772
9773       game.snapshot.collected_item = TRUE;
9774
9775       game_panel_controls[GAME_PANEL_GEMS].value =
9776         local_player->gems_still_needed;
9777
9778       DisplayGameControlValues();
9779
9780       break;
9781     }
9782
9783     case CA_SET_LEVEL_WIND:
9784     {
9785       game.wind_direction = action_arg_direction;
9786
9787       break;
9788     }
9789
9790     case CA_SET_LEVEL_RANDOM_SEED:
9791     {
9792       /* ensure that setting a new random seed while playing is predictable */
9793       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9794
9795       break;
9796     }
9797
9798     /* ---------- player actions  ------------------------------------------ */
9799
9800     case CA_MOVE_PLAYER:
9801     {
9802       /* automatically move to the next field in specified direction */
9803       for (i = 0; i < MAX_PLAYERS; i++)
9804         if (trigger_player_bits & (1 << i))
9805           stored_player[i].programmed_action = action_arg_direction;
9806
9807       break;
9808     }
9809
9810     case CA_EXIT_PLAYER:
9811     {
9812       for (i = 0; i < MAX_PLAYERS; i++)
9813         if (action_arg_player_bits & (1 << i))
9814           PlayerWins(&stored_player[i]);
9815
9816       break;
9817     }
9818
9819     case CA_KILL_PLAYER:
9820     {
9821       for (i = 0; i < MAX_PLAYERS; i++)
9822         if (action_arg_player_bits & (1 << i))
9823           KillPlayer(&stored_player[i]);
9824
9825       break;
9826     }
9827
9828     case CA_SET_PLAYER_KEYS:
9829     {
9830       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9831       int element = getSpecialActionElement(action_arg_element,
9832                                             action_arg_number, EL_KEY_1);
9833
9834       if (IS_KEY(element))
9835       {
9836         for (i = 0; i < MAX_PLAYERS; i++)
9837         {
9838           if (trigger_player_bits & (1 << i))
9839           {
9840             stored_player[i].key[KEY_NR(element)] = key_state;
9841
9842             DrawGameDoorValues();
9843           }
9844         }
9845       }
9846
9847       break;
9848     }
9849
9850     case CA_SET_PLAYER_SPEED:
9851     {
9852       for (i = 0; i < MAX_PLAYERS; i++)
9853       {
9854         if (trigger_player_bits & (1 << i))
9855         {
9856           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9857
9858           if (action_arg == CA_ARG_SPEED_FASTER &&
9859               stored_player[i].cannot_move)
9860           {
9861             action_arg_number = STEPSIZE_VERY_SLOW;
9862           }
9863           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9864                    action_arg == CA_ARG_SPEED_FASTER)
9865           {
9866             action_arg_number = 2;
9867             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9868                            CA_MODE_MULTIPLY);
9869           }
9870           else if (action_arg == CA_ARG_NUMBER_RESET)
9871           {
9872             action_arg_number = level.initial_player_stepsize[i];
9873           }
9874
9875           move_stepsize =
9876             getModifiedActionNumber(move_stepsize,
9877                                     action_mode,
9878                                     action_arg_number,
9879                                     action_arg_number_min,
9880                                     action_arg_number_max);
9881
9882           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9883         }
9884       }
9885
9886       break;
9887     }
9888
9889     case CA_SET_PLAYER_SHIELD:
9890     {
9891       for (i = 0; i < MAX_PLAYERS; i++)
9892       {
9893         if (trigger_player_bits & (1 << i))
9894         {
9895           if (action_arg == CA_ARG_SHIELD_OFF)
9896           {
9897             stored_player[i].shield_normal_time_left = 0;
9898             stored_player[i].shield_deadly_time_left = 0;
9899           }
9900           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9901           {
9902             stored_player[i].shield_normal_time_left = 999999;
9903           }
9904           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9905           {
9906             stored_player[i].shield_normal_time_left = 999999;
9907             stored_player[i].shield_deadly_time_left = 999999;
9908           }
9909         }
9910       }
9911
9912       break;
9913     }
9914
9915     case CA_SET_PLAYER_GRAVITY:
9916     {
9917       for (i = 0; i < MAX_PLAYERS; i++)
9918       {
9919         if (trigger_player_bits & (1 << i))
9920         {
9921           stored_player[i].gravity =
9922             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9923              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9924              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9925              stored_player[i].gravity);
9926         }
9927       }
9928
9929       break;
9930     }
9931
9932     case CA_SET_PLAYER_ARTWORK:
9933     {
9934       for (i = 0; i < MAX_PLAYERS; i++)
9935       {
9936         if (trigger_player_bits & (1 << i))
9937         {
9938           int artwork_element = action_arg_element;
9939
9940           if (action_arg == CA_ARG_ELEMENT_RESET)
9941             artwork_element =
9942               (level.use_artwork_element[i] ? level.artwork_element[i] :
9943                stored_player[i].element_nr);
9944
9945           if (stored_player[i].artwork_element != artwork_element)
9946             stored_player[i].Frame = 0;
9947
9948           stored_player[i].artwork_element = artwork_element;
9949
9950           SetPlayerWaiting(&stored_player[i], FALSE);
9951
9952           /* set number of special actions for bored and sleeping animation */
9953           stored_player[i].num_special_action_bored =
9954             get_num_special_action(artwork_element,
9955                                    ACTION_BORING_1, ACTION_BORING_LAST);
9956           stored_player[i].num_special_action_sleeping =
9957             get_num_special_action(artwork_element,
9958                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9959         }
9960       }
9961
9962       break;
9963     }
9964
9965     case CA_SET_PLAYER_INVENTORY:
9966     {
9967       for (i = 0; i < MAX_PLAYERS; i++)
9968       {
9969         struct PlayerInfo *player = &stored_player[i];
9970         int j, k;
9971
9972         if (trigger_player_bits & (1 << i))
9973         {
9974           int inventory_element = action_arg_element;
9975
9976           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9977               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9978               action_arg == CA_ARG_ELEMENT_ACTION)
9979           {
9980             int element = inventory_element;
9981             int collect_count = element_info[element].collect_count_initial;
9982
9983             if (!IS_CUSTOM_ELEMENT(element))
9984               collect_count = 1;
9985
9986             if (collect_count == 0)
9987               player->inventory_infinite_element = element;
9988             else
9989               for (k = 0; k < collect_count; k++)
9990                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9991                   player->inventory_element[player->inventory_size++] =
9992                     element;
9993           }
9994           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9995                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9996                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9997           {
9998             if (player->inventory_infinite_element != EL_UNDEFINED &&
9999                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10000                                      action_arg_element_raw))
10001               player->inventory_infinite_element = EL_UNDEFINED;
10002
10003             for (k = 0, j = 0; j < player->inventory_size; j++)
10004             {
10005               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10006                                         action_arg_element_raw))
10007                 player->inventory_element[k++] = player->inventory_element[j];
10008             }
10009
10010             player->inventory_size = k;
10011           }
10012           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10013           {
10014             if (player->inventory_size > 0)
10015             {
10016               for (j = 0; j < player->inventory_size - 1; j++)
10017                 player->inventory_element[j] = player->inventory_element[j + 1];
10018
10019               player->inventory_size--;
10020             }
10021           }
10022           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10023           {
10024             if (player->inventory_size > 0)
10025               player->inventory_size--;
10026           }
10027           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10028           {
10029             player->inventory_infinite_element = EL_UNDEFINED;
10030             player->inventory_size = 0;
10031           }
10032           else if (action_arg == CA_ARG_INVENTORY_RESET)
10033           {
10034             player->inventory_infinite_element = EL_UNDEFINED;
10035             player->inventory_size = 0;
10036
10037             if (level.use_initial_inventory[i])
10038             {
10039               for (j = 0; j < level.initial_inventory_size[i]; j++)
10040               {
10041                 int element = level.initial_inventory_content[i][j];
10042                 int collect_count = element_info[element].collect_count_initial;
10043
10044                 if (!IS_CUSTOM_ELEMENT(element))
10045                   collect_count = 1;
10046
10047                 if (collect_count == 0)
10048                   player->inventory_infinite_element = element;
10049                 else
10050                   for (k = 0; k < collect_count; k++)
10051                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10052                       player->inventory_element[player->inventory_size++] =
10053                         element;
10054               }
10055             }
10056           }
10057         }
10058       }
10059
10060       break;
10061     }
10062
10063     /* ---------- CE actions  ---------------------------------------------- */
10064
10065     case CA_SET_CE_VALUE:
10066     {
10067       int last_ce_value = CustomValue[x][y];
10068
10069       CustomValue[x][y] = action_arg_number_new;
10070
10071       if (CustomValue[x][y] != last_ce_value)
10072       {
10073         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10074         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10075
10076         if (CustomValue[x][y] == 0)
10077         {
10078           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10079           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10080         }
10081       }
10082
10083       break;
10084     }
10085
10086     case CA_SET_CE_SCORE:
10087     {
10088       int last_ce_score = ei->collect_score;
10089
10090       ei->collect_score = action_arg_number_new;
10091
10092       if (ei->collect_score != last_ce_score)
10093       {
10094         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10095         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10096
10097         if (ei->collect_score == 0)
10098         {
10099           int xx, yy;
10100
10101           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10102           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10103
10104           /*
10105             This is a very special case that seems to be a mixture between
10106             CheckElementChange() and CheckTriggeredElementChange(): while
10107             the first one only affects single elements that are triggered
10108             directly, the second one affects multiple elements in the playfield
10109             that are triggered indirectly by another element. This is a third
10110             case: Changing the CE score always affects multiple identical CEs,
10111             so every affected CE must be checked, not only the single CE for
10112             which the CE score was changed in the first place (as every instance
10113             of that CE shares the same CE score, and therefore also can change)!
10114           */
10115           SCAN_PLAYFIELD(xx, yy)
10116           {
10117             if (Feld[xx][yy] == element)
10118               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10119                                  CE_SCORE_GETS_ZERO);
10120           }
10121         }
10122       }
10123
10124       break;
10125     }
10126
10127     case CA_SET_CE_ARTWORK:
10128     {
10129       int artwork_element = action_arg_element;
10130       boolean reset_frame = FALSE;
10131       int xx, yy;
10132
10133       if (action_arg == CA_ARG_ELEMENT_RESET)
10134         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10135                            element);
10136
10137       if (ei->gfx_element != artwork_element)
10138         reset_frame = TRUE;
10139
10140       ei->gfx_element = artwork_element;
10141
10142       SCAN_PLAYFIELD(xx, yy)
10143       {
10144         if (Feld[xx][yy] == element)
10145         {
10146           if (reset_frame)
10147           {
10148             ResetGfxAnimation(xx, yy);
10149             ResetRandomAnimationValue(xx, yy);
10150           }
10151
10152           TEST_DrawLevelField(xx, yy);
10153         }
10154       }
10155
10156       break;
10157     }
10158
10159     /* ---------- engine actions  ------------------------------------------ */
10160
10161     case CA_SET_ENGINE_SCAN_MODE:
10162     {
10163       InitPlayfieldScanMode(action_arg);
10164
10165       break;
10166     }
10167
10168     default:
10169       break;
10170   }
10171 }
10172
10173 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10174 {
10175   int old_element = Feld[x][y];
10176   int new_element = GetElementFromGroupElement(element);
10177   int previous_move_direction = MovDir[x][y];
10178   int last_ce_value = CustomValue[x][y];
10179   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10180   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10181   boolean add_player_onto_element = (new_element_is_player &&
10182                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10183                                      IS_WALKABLE(old_element));
10184
10185   if (!add_player_onto_element)
10186   {
10187     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10188       RemoveMovingField(x, y);
10189     else
10190       RemoveField(x, y);
10191
10192     Feld[x][y] = new_element;
10193
10194     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10195       MovDir[x][y] = previous_move_direction;
10196
10197     if (element_info[new_element].use_last_ce_value)
10198       CustomValue[x][y] = last_ce_value;
10199
10200     InitField_WithBug1(x, y, FALSE);
10201
10202     new_element = Feld[x][y];   /* element may have changed */
10203
10204     ResetGfxAnimation(x, y);
10205     ResetRandomAnimationValue(x, y);
10206
10207     TEST_DrawLevelField(x, y);
10208
10209     if (GFX_CRUMBLED(new_element))
10210       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10211   }
10212
10213   /* check if element under the player changes from accessible to unaccessible
10214      (needed for special case of dropping element which then changes) */
10215   /* (must be checked after creating new element for walkable group elements) */
10216   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10217       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10218   {
10219     Bang(x, y);
10220
10221     return;
10222   }
10223
10224   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10225   if (new_element_is_player)
10226     RelocatePlayer(x, y, new_element);
10227
10228   if (is_change)
10229     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10230
10231   TestIfBadThingTouchesPlayer(x, y);
10232   TestIfPlayerTouchesCustomElement(x, y);
10233   TestIfElementTouchesCustomElement(x, y);
10234 }
10235
10236 static void CreateField(int x, int y, int element)
10237 {
10238   CreateFieldExt(x, y, element, FALSE);
10239 }
10240
10241 static void CreateElementFromChange(int x, int y, int element)
10242 {
10243   element = GET_VALID_RUNTIME_ELEMENT(element);
10244
10245   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10246   {
10247     int old_element = Feld[x][y];
10248
10249     /* prevent changed element from moving in same engine frame
10250        unless both old and new element can either fall or move */
10251     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10252         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10253       Stop[x][y] = TRUE;
10254   }
10255
10256   CreateFieldExt(x, y, element, TRUE);
10257 }
10258
10259 static boolean ChangeElement(int x, int y, int element, int page)
10260 {
10261   struct ElementInfo *ei = &element_info[element];
10262   struct ElementChangeInfo *change = &ei->change_page[page];
10263   int ce_value = CustomValue[x][y];
10264   int ce_score = ei->collect_score;
10265   int target_element;
10266   int old_element = Feld[x][y];
10267
10268   /* always use default change event to prevent running into a loop */
10269   if (ChangeEvent[x][y] == -1)
10270     ChangeEvent[x][y] = CE_DELAY;
10271
10272   if (ChangeEvent[x][y] == CE_DELAY)
10273   {
10274     /* reset actual trigger element, trigger player and action element */
10275     change->actual_trigger_element = EL_EMPTY;
10276     change->actual_trigger_player = EL_EMPTY;
10277     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10278     change->actual_trigger_side = CH_SIDE_NONE;
10279     change->actual_trigger_ce_value = 0;
10280     change->actual_trigger_ce_score = 0;
10281   }
10282
10283   /* do not change elements more than a specified maximum number of changes */
10284   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10285     return FALSE;
10286
10287   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10288
10289   if (change->explode)
10290   {
10291     Bang(x, y);
10292
10293     return TRUE;
10294   }
10295
10296   if (change->use_target_content)
10297   {
10298     boolean complete_replace = TRUE;
10299     boolean can_replace[3][3];
10300     int xx, yy;
10301
10302     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10303     {
10304       boolean is_empty;
10305       boolean is_walkable;
10306       boolean is_diggable;
10307       boolean is_collectible;
10308       boolean is_removable;
10309       boolean is_destructible;
10310       int ex = x + xx - 1;
10311       int ey = y + yy - 1;
10312       int content_element = change->target_content.e[xx][yy];
10313       int e;
10314
10315       can_replace[xx][yy] = TRUE;
10316
10317       if (ex == x && ey == y)   /* do not check changing element itself */
10318         continue;
10319
10320       if (content_element == EL_EMPTY_SPACE)
10321       {
10322         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10323
10324         continue;
10325       }
10326
10327       if (!IN_LEV_FIELD(ex, ey))
10328       {
10329         can_replace[xx][yy] = FALSE;
10330         complete_replace = FALSE;
10331
10332         continue;
10333       }
10334
10335       e = Feld[ex][ey];
10336
10337       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10338         e = MovingOrBlocked2Element(ex, ey);
10339
10340       is_empty = (IS_FREE(ex, ey) ||
10341                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10342
10343       is_walkable     = (is_empty || IS_WALKABLE(e));
10344       is_diggable     = (is_empty || IS_DIGGABLE(e));
10345       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10346       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10347       is_removable    = (is_diggable || is_collectible);
10348
10349       can_replace[xx][yy] =
10350         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10351           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10352           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10353           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10354           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10355           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10356          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10357
10358       if (!can_replace[xx][yy])
10359         complete_replace = FALSE;
10360     }
10361
10362     if (!change->only_if_complete || complete_replace)
10363     {
10364       boolean something_has_changed = FALSE;
10365
10366       if (change->only_if_complete && change->use_random_replace &&
10367           RND(100) < change->random_percentage)
10368         return FALSE;
10369
10370       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10371       {
10372         int ex = x + xx - 1;
10373         int ey = y + yy - 1;
10374         int content_element;
10375
10376         if (can_replace[xx][yy] && (!change->use_random_replace ||
10377                                     RND(100) < change->random_percentage))
10378         {
10379           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10380             RemoveMovingField(ex, ey);
10381
10382           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10383
10384           content_element = change->target_content.e[xx][yy];
10385           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10386                                               ce_value, ce_score);
10387
10388           CreateElementFromChange(ex, ey, target_element);
10389
10390           something_has_changed = TRUE;
10391
10392           /* for symmetry reasons, freeze newly created border elements */
10393           if (ex != x || ey != y)
10394             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10395         }
10396       }
10397
10398       if (something_has_changed)
10399       {
10400         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10401         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10402       }
10403     }
10404   }
10405   else
10406   {
10407     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10408                                         ce_value, ce_score);
10409
10410     if (element == EL_DIAGONAL_GROWING ||
10411         element == EL_DIAGONAL_SHRINKING)
10412     {
10413       target_element = Store[x][y];
10414
10415       Store[x][y] = EL_EMPTY;
10416     }
10417
10418     CreateElementFromChange(x, y, target_element);
10419
10420     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10421     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10422   }
10423
10424   /* this uses direct change before indirect change */
10425   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10426
10427   return TRUE;
10428 }
10429
10430 static void HandleElementChange(int x, int y, int page)
10431 {
10432   int element = MovingOrBlocked2Element(x, y);
10433   struct ElementInfo *ei = &element_info[element];
10434   struct ElementChangeInfo *change = &ei->change_page[page];
10435   boolean handle_action_before_change = FALSE;
10436
10437 #ifdef DEBUG
10438   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10439       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10440   {
10441     printf("\n\n");
10442     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10443            x, y, element, element_info[element].token_name);
10444     printf("HandleElementChange(): This should never happen!\n");
10445     printf("\n\n");
10446   }
10447 #endif
10448
10449   /* this can happen with classic bombs on walkable, changing elements */
10450   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10451   {
10452     return;
10453   }
10454
10455   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10456   {
10457     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10458
10459     if (change->can_change)
10460     {
10461       /* !!! not clear why graphic animation should be reset at all here !!! */
10462       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10463       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10464
10465       /*
10466         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10467
10468         When using an animation frame delay of 1 (this only happens with
10469         "sp_zonk.moving.left/right" in the classic graphics), the default
10470         (non-moving) animation shows wrong animation frames (while the
10471         moving animation, like "sp_zonk.moving.left/right", is correct,
10472         so this graphical bug never shows up with the classic graphics).
10473         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10474         be drawn instead of the correct frames 0,1,2,3. This is caused by
10475         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10476         an element change: First when the change delay ("ChangeDelay[][]")
10477         counter has reached zero after decrementing, then a second time in
10478         the next frame (after "GfxFrame[][]" was already incremented) when
10479         "ChangeDelay[][]" is reset to the initial delay value again.
10480
10481         This causes frame 0 to be drawn twice, while the last frame won't
10482         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10483
10484         As some animations may already be cleverly designed around this bug
10485         (at least the "Snake Bite" snake tail animation does this), it cannot
10486         simply be fixed here without breaking such existing animations.
10487         Unfortunately, it cannot easily be detected if a graphics set was
10488         designed "before" or "after" the bug was fixed. As a workaround,
10489         a new graphics set option "game.graphics_engine_version" was added
10490         to be able to specify the game's major release version for which the
10491         graphics set was designed, which can then be used to decide if the
10492         bugfix should be used (version 4 and above) or not (version 3 or
10493         below, or if no version was specified at all, as with old sets).
10494
10495         (The wrong/fixed animation frames can be tested with the test level set
10496         "test_gfxframe" and level "000", which contains a specially prepared
10497         custom element at level position (x/y) == (11/9) which uses the zonk
10498         animation mentioned above. Using "game.graphics_engine_version: 4"
10499         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10500         This can also be seen from the debug output for this test element.)
10501       */
10502
10503       /* when a custom element is about to change (for example by change delay),
10504          do not reset graphic animation when the custom element is moving */
10505       if (game.graphics_engine_version < 4 &&
10506           !IS_MOVING(x, y))
10507       {
10508         ResetGfxAnimation(x, y);
10509         ResetRandomAnimationValue(x, y);
10510       }
10511
10512       if (change->pre_change_function)
10513         change->pre_change_function(x, y);
10514     }
10515   }
10516
10517   ChangeDelay[x][y]--;
10518
10519   if (ChangeDelay[x][y] != 0)           /* continue element change */
10520   {
10521     if (change->can_change)
10522     {
10523       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10524
10525       if (IS_ANIMATED(graphic))
10526         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10527
10528       if (change->change_function)
10529         change->change_function(x, y);
10530     }
10531   }
10532   else                                  /* finish element change */
10533   {
10534     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10535     {
10536       page = ChangePage[x][y];
10537       ChangePage[x][y] = -1;
10538
10539       change = &ei->change_page[page];
10540     }
10541
10542     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10543     {
10544       ChangeDelay[x][y] = 1;            /* try change after next move step */
10545       ChangePage[x][y] = page;          /* remember page to use for change */
10546
10547       return;
10548     }
10549
10550     /* special case: set new level random seed before changing element */
10551     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10552       handle_action_before_change = TRUE;
10553
10554     if (change->has_action && handle_action_before_change)
10555       ExecuteCustomElementAction(x, y, element, page);
10556
10557     if (change->can_change)
10558     {
10559       if (ChangeElement(x, y, element, page))
10560       {
10561         if (change->post_change_function)
10562           change->post_change_function(x, y);
10563       }
10564     }
10565
10566     if (change->has_action && !handle_action_before_change)
10567       ExecuteCustomElementAction(x, y, element, page);
10568   }
10569 }
10570
10571 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10572                                               int trigger_element,
10573                                               int trigger_event,
10574                                               int trigger_player,
10575                                               int trigger_side,
10576                                               int trigger_page)
10577 {
10578   boolean change_done_any = FALSE;
10579   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10580   int i;
10581
10582   if (!(trigger_events[trigger_element][trigger_event]))
10583     return FALSE;
10584
10585   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10586
10587   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10588   {
10589     int element = EL_CUSTOM_START + i;
10590     boolean change_done = FALSE;
10591     int p;
10592
10593     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10594         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10595       continue;
10596
10597     for (p = 0; p < element_info[element].num_change_pages; p++)
10598     {
10599       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10600
10601       if (change->can_change_or_has_action &&
10602           change->has_event[trigger_event] &&
10603           change->trigger_side & trigger_side &&
10604           change->trigger_player & trigger_player &&
10605           change->trigger_page & trigger_page_bits &&
10606           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10607       {
10608         change->actual_trigger_element = trigger_element;
10609         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10610         change->actual_trigger_player_bits = trigger_player;
10611         change->actual_trigger_side = trigger_side;
10612         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10613         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10614
10615         if ((change->can_change && !change_done) || change->has_action)
10616         {
10617           int x, y;
10618
10619           SCAN_PLAYFIELD(x, y)
10620           {
10621             if (Feld[x][y] == element)
10622             {
10623               if (change->can_change && !change_done)
10624               {
10625                 /* if element already changed in this frame, not only prevent
10626                    another element change (checked in ChangeElement()), but
10627                    also prevent additional element actions for this element */
10628
10629                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10630                     !level.use_action_after_change_bug)
10631                   continue;
10632
10633                 ChangeDelay[x][y] = 1;
10634                 ChangeEvent[x][y] = trigger_event;
10635
10636                 HandleElementChange(x, y, p);
10637               }
10638               else if (change->has_action)
10639               {
10640                 /* if element already changed in this frame, not only prevent
10641                    another element change (checked in ChangeElement()), but
10642                    also prevent additional element actions for this element */
10643
10644                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10645                     !level.use_action_after_change_bug)
10646                   continue;
10647
10648                 ExecuteCustomElementAction(x, y, element, p);
10649                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10650               }
10651             }
10652           }
10653
10654           if (change->can_change)
10655           {
10656             change_done = TRUE;
10657             change_done_any = TRUE;
10658           }
10659         }
10660       }
10661     }
10662   }
10663
10664   RECURSION_LOOP_DETECTION_END();
10665
10666   return change_done_any;
10667 }
10668
10669 static boolean CheckElementChangeExt(int x, int y,
10670                                      int element,
10671                                      int trigger_element,
10672                                      int trigger_event,
10673                                      int trigger_player,
10674                                      int trigger_side)
10675 {
10676   boolean change_done = FALSE;
10677   int p;
10678
10679   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10680       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10681     return FALSE;
10682
10683   if (Feld[x][y] == EL_BLOCKED)
10684   {
10685     Blocked2Moving(x, y, &x, &y);
10686     element = Feld[x][y];
10687   }
10688
10689   /* check if element has already changed or is about to change after moving */
10690   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10691        Feld[x][y] != element) ||
10692
10693       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10694        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10695         ChangePage[x][y] != -1)))
10696     return FALSE;
10697
10698   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10699
10700   for (p = 0; p < element_info[element].num_change_pages; p++)
10701   {
10702     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10703
10704     /* check trigger element for all events where the element that is checked
10705        for changing interacts with a directly adjacent element -- this is
10706        different to element changes that affect other elements to change on the
10707        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10708     boolean check_trigger_element =
10709       (trigger_event == CE_TOUCHING_X ||
10710        trigger_event == CE_HITTING_X ||
10711        trigger_event == CE_HIT_BY_X ||
10712        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10713
10714     if (change->can_change_or_has_action &&
10715         change->has_event[trigger_event] &&
10716         change->trigger_side & trigger_side &&
10717         change->trigger_player & trigger_player &&
10718         (!check_trigger_element ||
10719          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10720     {
10721       change->actual_trigger_element = trigger_element;
10722       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10723       change->actual_trigger_player_bits = trigger_player;
10724       change->actual_trigger_side = trigger_side;
10725       change->actual_trigger_ce_value = CustomValue[x][y];
10726       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10727
10728       /* special case: trigger element not at (x,y) position for some events */
10729       if (check_trigger_element)
10730       {
10731         static struct
10732         {
10733           int dx, dy;
10734         } move_xy[] =
10735           {
10736             {  0,  0 },
10737             { -1,  0 },
10738             { +1,  0 },
10739             {  0,  0 },
10740             {  0, -1 },
10741             {  0,  0 }, { 0, 0 }, { 0, 0 },
10742             {  0, +1 }
10743           };
10744
10745         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10746         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10747
10748         change->actual_trigger_ce_value = CustomValue[xx][yy];
10749         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10750       }
10751
10752       if (change->can_change && !change_done)
10753       {
10754         ChangeDelay[x][y] = 1;
10755         ChangeEvent[x][y] = trigger_event;
10756
10757         HandleElementChange(x, y, p);
10758
10759         change_done = TRUE;
10760       }
10761       else if (change->has_action)
10762       {
10763         ExecuteCustomElementAction(x, y, element, p);
10764         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10765       }
10766     }
10767   }
10768
10769   RECURSION_LOOP_DETECTION_END();
10770
10771   return change_done;
10772 }
10773
10774 static void PlayPlayerSound(struct PlayerInfo *player)
10775 {
10776   int jx = player->jx, jy = player->jy;
10777   int sound_element = player->artwork_element;
10778   int last_action = player->last_action_waiting;
10779   int action = player->action_waiting;
10780
10781   if (player->is_waiting)
10782   {
10783     if (action != last_action)
10784       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10785     else
10786       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10787   }
10788   else
10789   {
10790     if (action != last_action)
10791       StopSound(element_info[sound_element].sound[last_action]);
10792
10793     if (last_action == ACTION_SLEEPING)
10794       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10795   }
10796 }
10797
10798 static void PlayAllPlayersSound()
10799 {
10800   int i;
10801
10802   for (i = 0; i < MAX_PLAYERS; i++)
10803     if (stored_player[i].active)
10804       PlayPlayerSound(&stored_player[i]);
10805 }
10806
10807 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10808 {
10809   boolean last_waiting = player->is_waiting;
10810   int move_dir = player->MovDir;
10811
10812   player->dir_waiting = move_dir;
10813   player->last_action_waiting = player->action_waiting;
10814
10815   if (is_waiting)
10816   {
10817     if (!last_waiting)          /* not waiting -> waiting */
10818     {
10819       player->is_waiting = TRUE;
10820
10821       player->frame_counter_bored =
10822         FrameCounter +
10823         game.player_boring_delay_fixed +
10824         GetSimpleRandom(game.player_boring_delay_random);
10825       player->frame_counter_sleeping =
10826         FrameCounter +
10827         game.player_sleeping_delay_fixed +
10828         GetSimpleRandom(game.player_sleeping_delay_random);
10829
10830       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10831     }
10832
10833     if (game.player_sleeping_delay_fixed +
10834         game.player_sleeping_delay_random > 0 &&
10835         player->anim_delay_counter == 0 &&
10836         player->post_delay_counter == 0 &&
10837         FrameCounter >= player->frame_counter_sleeping)
10838       player->is_sleeping = TRUE;
10839     else if (game.player_boring_delay_fixed +
10840              game.player_boring_delay_random > 0 &&
10841              FrameCounter >= player->frame_counter_bored)
10842       player->is_bored = TRUE;
10843
10844     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10845                               player->is_bored ? ACTION_BORING :
10846                               ACTION_WAITING);
10847
10848     if (player->is_sleeping && player->use_murphy)
10849     {
10850       /* special case for sleeping Murphy when leaning against non-free tile */
10851
10852       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10853           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10854            !IS_MOVING(player->jx - 1, player->jy)))
10855         move_dir = MV_LEFT;
10856       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10857                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10858                 !IS_MOVING(player->jx + 1, player->jy)))
10859         move_dir = MV_RIGHT;
10860       else
10861         player->is_sleeping = FALSE;
10862
10863       player->dir_waiting = move_dir;
10864     }
10865
10866     if (player->is_sleeping)
10867     {
10868       if (player->num_special_action_sleeping > 0)
10869       {
10870         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10871         {
10872           int last_special_action = player->special_action_sleeping;
10873           int num_special_action = player->num_special_action_sleeping;
10874           int special_action =
10875             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10876              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10877              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10878              last_special_action + 1 : ACTION_SLEEPING);
10879           int special_graphic =
10880             el_act_dir2img(player->artwork_element, special_action, move_dir);
10881
10882           player->anim_delay_counter =
10883             graphic_info[special_graphic].anim_delay_fixed +
10884             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10885           player->post_delay_counter =
10886             graphic_info[special_graphic].post_delay_fixed +
10887             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10888
10889           player->special_action_sleeping = special_action;
10890         }
10891
10892         if (player->anim_delay_counter > 0)
10893         {
10894           player->action_waiting = player->special_action_sleeping;
10895           player->anim_delay_counter--;
10896         }
10897         else if (player->post_delay_counter > 0)
10898         {
10899           player->post_delay_counter--;
10900         }
10901       }
10902     }
10903     else if (player->is_bored)
10904     {
10905       if (player->num_special_action_bored > 0)
10906       {
10907         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10908         {
10909           int special_action =
10910             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10911           int special_graphic =
10912             el_act_dir2img(player->artwork_element, special_action, move_dir);
10913
10914           player->anim_delay_counter =
10915             graphic_info[special_graphic].anim_delay_fixed +
10916             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10917           player->post_delay_counter =
10918             graphic_info[special_graphic].post_delay_fixed +
10919             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10920
10921           player->special_action_bored = special_action;
10922         }
10923
10924         if (player->anim_delay_counter > 0)
10925         {
10926           player->action_waiting = player->special_action_bored;
10927           player->anim_delay_counter--;
10928         }
10929         else if (player->post_delay_counter > 0)
10930         {
10931           player->post_delay_counter--;
10932         }
10933       }
10934     }
10935   }
10936   else if (last_waiting)        /* waiting -> not waiting */
10937   {
10938     player->is_waiting = FALSE;
10939     player->is_bored = FALSE;
10940     player->is_sleeping = FALSE;
10941
10942     player->frame_counter_bored = -1;
10943     player->frame_counter_sleeping = -1;
10944
10945     player->anim_delay_counter = 0;
10946     player->post_delay_counter = 0;
10947
10948     player->dir_waiting = player->MovDir;
10949     player->action_waiting = ACTION_DEFAULT;
10950
10951     player->special_action_bored = ACTION_DEFAULT;
10952     player->special_action_sleeping = ACTION_DEFAULT;
10953   }
10954 }
10955
10956 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10957 {
10958   if ((!player->is_moving  && player->was_moving) ||
10959       (player->MovPos == 0 && player->was_moving) ||
10960       (player->is_snapping && !player->was_snapping) ||
10961       (player->is_dropping && !player->was_dropping))
10962   {
10963     if (!CheckSaveEngineSnapshotToList())
10964       return;
10965
10966     player->was_moving = FALSE;
10967     player->was_snapping = TRUE;
10968     player->was_dropping = TRUE;
10969   }
10970   else
10971   {
10972     if (player->is_moving)
10973       player->was_moving = TRUE;
10974
10975     if (!player->is_snapping)
10976       player->was_snapping = FALSE;
10977
10978     if (!player->is_dropping)
10979       player->was_dropping = FALSE;
10980   }
10981 }
10982
10983 static void CheckSingleStepMode(struct PlayerInfo *player)
10984 {
10985   if (tape.single_step && tape.recording && !tape.pausing)
10986   {
10987     /* as it is called "single step mode", just return to pause mode when the
10988        player stopped moving after one tile (or never starts moving at all) */
10989     if (!player->is_moving &&
10990         !player->is_pushing &&
10991         !player->is_dropping_pressed)
10992     {
10993       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10994       SnapField(player, 0, 0);                  /* stop snapping */
10995     }
10996   }
10997
10998   CheckSaveEngineSnapshot(player);
10999 }
11000
11001 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11002 {
11003   int left      = player_action & JOY_LEFT;
11004   int right     = player_action & JOY_RIGHT;
11005   int up        = player_action & JOY_UP;
11006   int down      = player_action & JOY_DOWN;
11007   int button1   = player_action & JOY_BUTTON_1;
11008   int button2   = player_action & JOY_BUTTON_2;
11009   int dx        = (left ? -1 : right ? 1 : 0);
11010   int dy        = (up   ? -1 : down  ? 1 : 0);
11011
11012   if (!player->active || tape.pausing)
11013     return 0;
11014
11015   if (player_action)
11016   {
11017     if (button1)
11018       SnapField(player, dx, dy);
11019     else
11020     {
11021       if (button2)
11022         DropElement(player);
11023
11024       MovePlayer(player, dx, dy);
11025     }
11026
11027     CheckSingleStepMode(player);
11028
11029     SetPlayerWaiting(player, FALSE);
11030
11031     return player_action;
11032   }
11033   else
11034   {
11035     /* no actions for this player (no input at player's configured device) */
11036
11037     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11038     SnapField(player, 0, 0);
11039     CheckGravityMovementWhenNotMoving(player);
11040
11041     if (player->MovPos == 0)
11042       SetPlayerWaiting(player, TRUE);
11043
11044     if (player->MovPos == 0)    /* needed for tape.playing */
11045       player->is_moving = FALSE;
11046
11047     player->is_dropping = FALSE;
11048     player->is_dropping_pressed = FALSE;
11049     player->drop_pressed_delay = 0;
11050
11051     CheckSingleStepMode(player);
11052
11053     return 0;
11054   }
11055 }
11056
11057 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11058                                          byte *tape_action)
11059 {
11060   if (!tape.use_mouse)
11061     return;
11062
11063   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11064   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11065   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11066 }
11067
11068 static void SetTapeActionFromMouseAction(byte *tape_action,
11069                                          struct MouseActionInfo *mouse_action)
11070 {
11071   if (!tape.use_mouse)
11072     return;
11073
11074   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11075   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11076   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11077 }
11078
11079 static void CheckLevelTime()
11080 {
11081   int i;
11082
11083   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11084   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11085   {
11086     if (level.native_em_level->lev->home == 0)  /* all players at home */
11087     {
11088       PlayerWins(local_player);
11089
11090       AllPlayersGone = TRUE;
11091
11092       level.native_em_level->lev->home = -1;
11093     }
11094
11095     if (level.native_em_level->ply[0]->alive == 0 &&
11096         level.native_em_level->ply[1]->alive == 0 &&
11097         level.native_em_level->ply[2]->alive == 0 &&
11098         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11099       AllPlayersGone = TRUE;
11100   }
11101   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11102   {
11103     if (game_sp.LevelSolved &&
11104         !game_sp.GameOver)                              /* game won */
11105     {
11106       PlayerWins(local_player);
11107
11108       game_sp.GameOver = TRUE;
11109
11110       AllPlayersGone = TRUE;
11111     }
11112
11113     if (game_sp.GameOver)                               /* game lost */
11114       AllPlayersGone = TRUE;
11115   }
11116   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11117   {
11118     if (game_mm.level_solved &&
11119         !game_mm.game_over)                             /* game won */
11120     {
11121       PlayerWins(local_player);
11122
11123       game_mm.game_over = TRUE;
11124
11125       AllPlayersGone = TRUE;
11126     }
11127
11128     if (game_mm.game_over)                              /* game lost */
11129       AllPlayersGone = TRUE;
11130   }
11131
11132   if (TimeFrames >= FRAMES_PER_SECOND)
11133   {
11134     TimeFrames = 0;
11135     TapeTime++;
11136
11137     for (i = 0; i < MAX_PLAYERS; i++)
11138     {
11139       struct PlayerInfo *player = &stored_player[i];
11140
11141       if (SHIELD_ON(player))
11142       {
11143         player->shield_normal_time_left--;
11144
11145         if (player->shield_deadly_time_left > 0)
11146           player->shield_deadly_time_left--;
11147       }
11148     }
11149
11150     if (!local_player->LevelSolved && !level.use_step_counter)
11151     {
11152       TimePlayed++;
11153
11154       if (TimeLeft > 0)
11155       {
11156         TimeLeft--;
11157
11158         if (TimeLeft <= 10 && setup.time_limit)
11159           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11160
11161         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11162            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11163
11164         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11165
11166         if (!TimeLeft && setup.time_limit)
11167         {
11168           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11169             level.native_em_level->lev->killed_out_of_time = TRUE;
11170           else
11171             for (i = 0; i < MAX_PLAYERS; i++)
11172               KillPlayer(&stored_player[i]);
11173         }
11174       }
11175       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11176       {
11177         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11178       }
11179
11180       level.native_em_level->lev->time =
11181         (game.no_time_limit ? TimePlayed : TimeLeft);
11182     }
11183
11184     if (tape.recording || tape.playing)
11185       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11186   }
11187
11188   if (tape.recording || tape.playing)
11189     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11190
11191   UpdateAndDisplayGameControlValues();
11192 }
11193
11194 void AdvanceFrameAndPlayerCounters(int player_nr)
11195 {
11196   int i;
11197
11198   /* advance frame counters (global frame counter and time frame counter) */
11199   FrameCounter++;
11200   TimeFrames++;
11201
11202   /* advance player counters (counters for move delay, move animation etc.) */
11203   for (i = 0; i < MAX_PLAYERS; i++)
11204   {
11205     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11206     int move_delay_value = stored_player[i].move_delay_value;
11207     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11208
11209     if (!advance_player_counters)       /* not all players may be affected */
11210       continue;
11211
11212     if (move_frames == 0)       /* less than one move per game frame */
11213     {
11214       int stepsize = TILEX / move_delay_value;
11215       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11216       int count = (stored_player[i].is_moving ?
11217                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11218
11219       if (count % delay == 0)
11220         move_frames = 1;
11221     }
11222
11223     stored_player[i].Frame += move_frames;
11224
11225     if (stored_player[i].MovPos != 0)
11226       stored_player[i].StepFrame += move_frames;
11227
11228     if (stored_player[i].move_delay > 0)
11229       stored_player[i].move_delay--;
11230
11231     /* due to bugs in previous versions, counter must count up, not down */
11232     if (stored_player[i].push_delay != -1)
11233       stored_player[i].push_delay++;
11234
11235     if (stored_player[i].drop_delay > 0)
11236       stored_player[i].drop_delay--;
11237
11238     if (stored_player[i].is_dropping_pressed)
11239       stored_player[i].drop_pressed_delay++;
11240   }
11241 }
11242
11243 void StartGameActions(boolean init_network_game, boolean record_tape,
11244                       int random_seed)
11245 {
11246   unsigned int new_random_seed = InitRND(random_seed);
11247
11248   if (record_tape)
11249     TapeStartRecording(new_random_seed);
11250
11251 #if defined(NETWORK_AVALIABLE)
11252   if (init_network_game)
11253   {
11254     SendToServer_StartPlaying();
11255
11256     return;
11257   }
11258 #endif
11259
11260   InitGame();
11261 }
11262
11263 void GameActionsExt()
11264 {
11265 #if 0
11266   static unsigned int game_frame_delay = 0;
11267 #endif
11268   unsigned int game_frame_delay_value;
11269   byte *recorded_player_action;
11270   byte summarized_player_action = 0;
11271   byte tape_action[MAX_PLAYERS];
11272   int i;
11273
11274   /* detect endless loops, caused by custom element programming */
11275   if (recursion_loop_detected && recursion_loop_depth == 0)
11276   {
11277     char *message = getStringCat3("Internal Error! Element ",
11278                                   EL_NAME(recursion_loop_element),
11279                                   " caused endless loop! Quit the game?");
11280
11281     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11282           EL_NAME(recursion_loop_element));
11283
11284     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11285
11286     recursion_loop_detected = FALSE;    /* if game should be continued */
11287
11288     free(message);
11289
11290     return;
11291   }
11292
11293   if (game.restart_level)
11294     StartGameActions(options.network, setup.autorecord, level.random_seed);
11295
11296   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11297   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11298   {
11299     if (level.native_em_level->lev->home == 0)  /* all players at home */
11300     {
11301       PlayerWins(local_player);
11302
11303       AllPlayersGone = TRUE;
11304
11305       level.native_em_level->lev->home = -1;
11306     }
11307
11308     if (level.native_em_level->ply[0]->alive == 0 &&
11309         level.native_em_level->ply[1]->alive == 0 &&
11310         level.native_em_level->ply[2]->alive == 0 &&
11311         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11312       AllPlayersGone = TRUE;
11313   }
11314   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11315   {
11316     if (game_sp.LevelSolved &&
11317         !game_sp.GameOver)                              /* game won */
11318     {
11319       PlayerWins(local_player);
11320
11321       game_sp.GameOver = TRUE;
11322
11323       AllPlayersGone = TRUE;
11324     }
11325
11326     if (game_sp.GameOver)                               /* game lost */
11327       AllPlayersGone = TRUE;
11328   }
11329   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11330   {
11331     if (game_mm.level_solved &&
11332         !game_mm.game_over)                             /* game won */
11333     {
11334       PlayerWins(local_player);
11335
11336       game_mm.game_over = TRUE;
11337
11338       AllPlayersGone = TRUE;
11339     }
11340
11341     if (game_mm.game_over)                              /* game lost */
11342       AllPlayersGone = TRUE;
11343   }
11344
11345   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11346     GameWon();
11347
11348   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11349     TapeStop();
11350
11351   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11352     return;
11353
11354   game_frame_delay_value =
11355     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11356
11357   if (tape.playing && tape.warp_forward && !tape.pausing)
11358     game_frame_delay_value = 0;
11359
11360   SetVideoFrameDelay(game_frame_delay_value);
11361
11362 #if 0
11363 #if 0
11364   /* ---------- main game synchronization point ---------- */
11365
11366   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11367
11368   printf("::: skip == %d\n", skip);
11369
11370 #else
11371   /* ---------- main game synchronization point ---------- */
11372
11373   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11374 #endif
11375 #endif
11376
11377   if (network_playing && !network_player_action_received)
11378   {
11379     /* try to get network player actions in time */
11380
11381 #if defined(NETWORK_AVALIABLE)
11382     /* last chance to get network player actions without main loop delay */
11383     HandleNetworking();
11384 #endif
11385
11386     /* game was quit by network peer */
11387     if (game_status != GAME_MODE_PLAYING)
11388       return;
11389
11390     if (!network_player_action_received)
11391       return;           /* failed to get network player actions in time */
11392
11393     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11394   }
11395
11396   if (tape.pausing)
11397     return;
11398
11399   /* at this point we know that we really continue executing the game */
11400
11401   network_player_action_received = FALSE;
11402
11403   /* when playing tape, read previously recorded player input from tape data */
11404   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11405
11406   local_player->effective_mouse_action = local_player->mouse_action;
11407
11408   if (recorded_player_action != NULL)
11409     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11410                                  recorded_player_action);
11411
11412   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11413   if (tape.pausing)
11414     return;
11415
11416   if (tape.set_centered_player)
11417   {
11418     game.centered_player_nr_next = tape.centered_player_nr_next;
11419     game.set_centered_player = TRUE;
11420   }
11421
11422   for (i = 0; i < MAX_PLAYERS; i++)
11423   {
11424     summarized_player_action |= stored_player[i].action;
11425
11426     if (!network_playing && (game.team_mode || tape.playing))
11427       stored_player[i].effective_action = stored_player[i].action;
11428   }
11429
11430 #if defined(NETWORK_AVALIABLE)
11431   if (network_playing)
11432     SendToServer_MovePlayer(summarized_player_action);
11433 #endif
11434
11435   // summarize all actions at local players mapped input device position
11436   // (this allows using different input devices in single player mode)
11437   if (!options.network && !game.team_mode)
11438     stored_player[map_player_action[local_player->index_nr]].effective_action =
11439       summarized_player_action;
11440
11441   if (tape.recording &&
11442       setup.team_mode &&
11443       setup.input_on_focus &&
11444       game.centered_player_nr != -1)
11445   {
11446     for (i = 0; i < MAX_PLAYERS; i++)
11447       stored_player[i].effective_action =
11448         (i == game.centered_player_nr ? summarized_player_action : 0);
11449   }
11450
11451   if (recorded_player_action != NULL)
11452     for (i = 0; i < MAX_PLAYERS; i++)
11453       stored_player[i].effective_action = recorded_player_action[i];
11454
11455   for (i = 0; i < MAX_PLAYERS; i++)
11456   {
11457     tape_action[i] = stored_player[i].effective_action;
11458
11459     /* (this may happen in the RND game engine if a player was not present on
11460        the playfield on level start, but appeared later from a custom element */
11461     if (setup.team_mode &&
11462         tape.recording &&
11463         tape_action[i] &&
11464         !tape.player_participates[i])
11465       tape.player_participates[i] = TRUE;
11466   }
11467
11468   SetTapeActionFromMouseAction(tape_action,
11469                                &local_player->effective_mouse_action);
11470
11471   /* only record actions from input devices, but not programmed actions */
11472   if (tape.recording)
11473     TapeRecordAction(tape_action);
11474
11475 #if USE_NEW_PLAYER_ASSIGNMENTS
11476   // !!! also map player actions in single player mode !!!
11477   // if (game.team_mode)
11478   if (1)
11479   {
11480     byte mapped_action[MAX_PLAYERS];
11481
11482 #if DEBUG_PLAYER_ACTIONS
11483     printf(":::");
11484     for (i = 0; i < MAX_PLAYERS; i++)
11485       printf(" %d, ", stored_player[i].effective_action);
11486 #endif
11487
11488     for (i = 0; i < MAX_PLAYERS; i++)
11489       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11490
11491     for (i = 0; i < MAX_PLAYERS; i++)
11492       stored_player[i].effective_action = mapped_action[i];
11493
11494 #if DEBUG_PLAYER_ACTIONS
11495     printf(" =>");
11496     for (i = 0; i < MAX_PLAYERS; i++)
11497       printf(" %d, ", stored_player[i].effective_action);
11498     printf("\n");
11499 #endif
11500   }
11501 #if DEBUG_PLAYER_ACTIONS
11502   else
11503   {
11504     printf(":::");
11505     for (i = 0; i < MAX_PLAYERS; i++)
11506       printf(" %d, ", stored_player[i].effective_action);
11507     printf("\n");
11508   }
11509 #endif
11510 #endif
11511
11512   for (i = 0; i < MAX_PLAYERS; i++)
11513   {
11514     // allow engine snapshot in case of changed movement attempt
11515     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11516         (stored_player[i].effective_action & KEY_MOTION))
11517       game.snapshot.changed_action = TRUE;
11518
11519     // allow engine snapshot in case of snapping/dropping attempt
11520     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11521         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11522       game.snapshot.changed_action = TRUE;
11523
11524     game.snapshot.last_action[i] = stored_player[i].effective_action;
11525   }
11526
11527   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11528   {
11529     GameActions_EM_Main();
11530   }
11531   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11532   {
11533     GameActions_SP_Main();
11534   }
11535   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11536   {
11537     GameActions_MM_Main();
11538   }
11539   else
11540   {
11541     GameActions_RND_Main();
11542   }
11543
11544   BlitScreenToBitmap(backbuffer);
11545
11546   CheckLevelTime();
11547
11548   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11549
11550   if (global.show_frames_per_second)
11551   {
11552     static unsigned int fps_counter = 0;
11553     static int fps_frames = 0;
11554     unsigned int fps_delay_ms = Counter() - fps_counter;
11555
11556     fps_frames++;
11557
11558     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11559     {
11560       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11561
11562       fps_frames = 0;
11563       fps_counter = Counter();
11564
11565       /* always draw FPS to screen after FPS value was updated */
11566       redraw_mask |= REDRAW_FPS;
11567     }
11568
11569     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11570     if (GetDrawDeactivationMask() == REDRAW_NONE)
11571       redraw_mask |= REDRAW_FPS;
11572   }
11573 }
11574
11575 static void GameActions_CheckSaveEngineSnapshot()
11576 {
11577   if (!game.snapshot.save_snapshot)
11578     return;
11579
11580   // clear flag for saving snapshot _before_ saving snapshot
11581   game.snapshot.save_snapshot = FALSE;
11582
11583   SaveEngineSnapshotToList();
11584 }
11585
11586 void GameActions()
11587 {
11588   GameActionsExt();
11589
11590   GameActions_CheckSaveEngineSnapshot();
11591 }
11592
11593 void GameActions_EM_Main()
11594 {
11595   byte effective_action[MAX_PLAYERS];
11596   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11597   int i;
11598
11599   for (i = 0; i < MAX_PLAYERS; i++)
11600     effective_action[i] = stored_player[i].effective_action;
11601
11602   GameActions_EM(effective_action, warp_mode);
11603 }
11604
11605 void GameActions_SP_Main()
11606 {
11607   byte effective_action[MAX_PLAYERS];
11608   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11609   int i;
11610
11611   for (i = 0; i < MAX_PLAYERS; i++)
11612     effective_action[i] = stored_player[i].effective_action;
11613
11614   GameActions_SP(effective_action, warp_mode);
11615
11616   for (i = 0; i < MAX_PLAYERS; i++)
11617   {
11618     if (stored_player[i].force_dropping)
11619       stored_player[i].action |= KEY_BUTTON_DROP;
11620
11621     stored_player[i].force_dropping = FALSE;
11622   }
11623 }
11624
11625 void GameActions_MM_Main()
11626 {
11627   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11628
11629   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11630 }
11631
11632 void GameActions_RND_Main()
11633 {
11634   GameActions_RND();
11635 }
11636
11637 void GameActions_RND()
11638 {
11639   int magic_wall_x = 0, magic_wall_y = 0;
11640   int i, x, y, element, graphic, last_gfx_frame;
11641
11642   InitPlayfieldScanModeVars();
11643
11644   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11645   {
11646     SCAN_PLAYFIELD(x, y)
11647     {
11648       ChangeCount[x][y] = 0;
11649       ChangeEvent[x][y] = -1;
11650     }
11651   }
11652
11653   if (game.set_centered_player)
11654   {
11655     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11656
11657     /* switching to "all players" only possible if all players fit to screen */
11658     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11659     {
11660       game.centered_player_nr_next = game.centered_player_nr;
11661       game.set_centered_player = FALSE;
11662     }
11663
11664     /* do not switch focus to non-existing (or non-active) player */
11665     if (game.centered_player_nr_next >= 0 &&
11666         !stored_player[game.centered_player_nr_next].active)
11667     {
11668       game.centered_player_nr_next = game.centered_player_nr;
11669       game.set_centered_player = FALSE;
11670     }
11671   }
11672
11673   if (game.set_centered_player &&
11674       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11675   {
11676     int sx, sy;
11677
11678     if (game.centered_player_nr_next == -1)
11679     {
11680       setScreenCenteredToAllPlayers(&sx, &sy);
11681     }
11682     else
11683     {
11684       sx = stored_player[game.centered_player_nr_next].jx;
11685       sy = stored_player[game.centered_player_nr_next].jy;
11686     }
11687
11688     game.centered_player_nr = game.centered_player_nr_next;
11689     game.set_centered_player = FALSE;
11690
11691     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11692     DrawGameDoorValues();
11693   }
11694
11695   for (i = 0; i < MAX_PLAYERS; i++)
11696   {
11697     int actual_player_action = stored_player[i].effective_action;
11698
11699 #if 1
11700     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11701        - rnd_equinox_tetrachloride 048
11702        - rnd_equinox_tetrachloride_ii 096
11703        - rnd_emanuel_schmieg 002
11704        - doctor_sloan_ww 001, 020
11705     */
11706     if (stored_player[i].MovPos == 0)
11707       CheckGravityMovement(&stored_player[i]);
11708 #endif
11709
11710     /* overwrite programmed action with tape action */
11711     if (stored_player[i].programmed_action)
11712       actual_player_action = stored_player[i].programmed_action;
11713
11714     PlayerActions(&stored_player[i], actual_player_action);
11715
11716     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11717   }
11718
11719   ScrollScreen(NULL, SCROLL_GO_ON);
11720
11721   /* for backwards compatibility, the following code emulates a fixed bug that
11722      occured when pushing elements (causing elements that just made their last
11723      pushing step to already (if possible) make their first falling step in the
11724      same game frame, which is bad); this code is also needed to use the famous
11725      "spring push bug" which is used in older levels and might be wanted to be
11726      used also in newer levels, but in this case the buggy pushing code is only
11727      affecting the "spring" element and no other elements */
11728
11729   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11730   {
11731     for (i = 0; i < MAX_PLAYERS; i++)
11732     {
11733       struct PlayerInfo *player = &stored_player[i];
11734       int x = player->jx;
11735       int y = player->jy;
11736
11737       if (player->active && player->is_pushing && player->is_moving &&
11738           IS_MOVING(x, y) &&
11739           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11740            Feld[x][y] == EL_SPRING))
11741       {
11742         ContinueMoving(x, y);
11743
11744         /* continue moving after pushing (this is actually a bug) */
11745         if (!IS_MOVING(x, y))
11746           Stop[x][y] = FALSE;
11747       }
11748     }
11749   }
11750
11751   SCAN_PLAYFIELD(x, y)
11752   {
11753     ChangeCount[x][y] = 0;
11754     ChangeEvent[x][y] = -1;
11755
11756     /* this must be handled before main playfield loop */
11757     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11758     {
11759       MovDelay[x][y]--;
11760       if (MovDelay[x][y] <= 0)
11761         RemoveField(x, y);
11762     }
11763
11764     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11765     {
11766       MovDelay[x][y]--;
11767       if (MovDelay[x][y] <= 0)
11768       {
11769         RemoveField(x, y);
11770         TEST_DrawLevelField(x, y);
11771
11772         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11773       }
11774     }
11775
11776 #if DEBUG
11777     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11778     {
11779       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11780       printf("GameActions(): This should never happen!\n");
11781
11782       ChangePage[x][y] = -1;
11783     }
11784 #endif
11785
11786     Stop[x][y] = FALSE;
11787     if (WasJustMoving[x][y] > 0)
11788       WasJustMoving[x][y]--;
11789     if (WasJustFalling[x][y] > 0)
11790       WasJustFalling[x][y]--;
11791     if (CheckCollision[x][y] > 0)
11792       CheckCollision[x][y]--;
11793     if (CheckImpact[x][y] > 0)
11794       CheckImpact[x][y]--;
11795
11796     GfxFrame[x][y]++;
11797
11798     /* reset finished pushing action (not done in ContinueMoving() to allow
11799        continuous pushing animation for elements with zero push delay) */
11800     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11801     {
11802       ResetGfxAnimation(x, y);
11803       TEST_DrawLevelField(x, y);
11804     }
11805
11806 #if DEBUG
11807     if (IS_BLOCKED(x, y))
11808     {
11809       int oldx, oldy;
11810
11811       Blocked2Moving(x, y, &oldx, &oldy);
11812       if (!IS_MOVING(oldx, oldy))
11813       {
11814         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11815         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11816         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11817         printf("GameActions(): This should never happen!\n");
11818       }
11819     }
11820 #endif
11821   }
11822
11823   SCAN_PLAYFIELD(x, y)
11824   {
11825     element = Feld[x][y];
11826     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11827     last_gfx_frame = GfxFrame[x][y];
11828
11829     ResetGfxFrame(x, y);
11830
11831     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11832       DrawLevelGraphicAnimation(x, y, graphic);
11833
11834     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11835         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11836       ResetRandomAnimationValue(x, y);
11837
11838     SetRandomAnimationValue(x, y);
11839
11840     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11841
11842     if (IS_INACTIVE(element))
11843     {
11844       if (IS_ANIMATED(graphic))
11845         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11846
11847       continue;
11848     }
11849
11850     /* this may take place after moving, so 'element' may have changed */
11851     if (IS_CHANGING(x, y) &&
11852         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11853     {
11854       int page = element_info[element].event_page_nr[CE_DELAY];
11855
11856       HandleElementChange(x, y, page);
11857
11858       element = Feld[x][y];
11859       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11860     }
11861
11862     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11863     {
11864       StartMoving(x, y);
11865
11866       element = Feld[x][y];
11867       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11868
11869       if (IS_ANIMATED(graphic) &&
11870           !IS_MOVING(x, y) &&
11871           !Stop[x][y])
11872         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11873
11874       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11875         TEST_DrawTwinkleOnField(x, y);
11876     }
11877     else if (element == EL_ACID)
11878     {
11879       if (!Stop[x][y])
11880         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11881     }
11882     else if ((element == EL_EXIT_OPEN ||
11883               element == EL_EM_EXIT_OPEN ||
11884               element == EL_SP_EXIT_OPEN ||
11885               element == EL_STEEL_EXIT_OPEN ||
11886               element == EL_EM_STEEL_EXIT_OPEN ||
11887               element == EL_SP_TERMINAL ||
11888               element == EL_SP_TERMINAL_ACTIVE ||
11889               element == EL_EXTRA_TIME ||
11890               element == EL_SHIELD_NORMAL ||
11891               element == EL_SHIELD_DEADLY) &&
11892              IS_ANIMATED(graphic))
11893       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11894     else if (IS_MOVING(x, y))
11895       ContinueMoving(x, y);
11896     else if (IS_ACTIVE_BOMB(element))
11897       CheckDynamite(x, y);
11898     else if (element == EL_AMOEBA_GROWING)
11899       AmoebeWaechst(x, y);
11900     else if (element == EL_AMOEBA_SHRINKING)
11901       AmoebaDisappearing(x, y);
11902
11903 #if !USE_NEW_AMOEBA_CODE
11904     else if (IS_AMOEBALIVE(element))
11905       AmoebeAbleger(x, y);
11906 #endif
11907
11908     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11909       Life(x, y);
11910     else if (element == EL_EXIT_CLOSED)
11911       CheckExit(x, y);
11912     else if (element == EL_EM_EXIT_CLOSED)
11913       CheckExitEM(x, y);
11914     else if (element == EL_STEEL_EXIT_CLOSED)
11915       CheckExitSteel(x, y);
11916     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11917       CheckExitSteelEM(x, y);
11918     else if (element == EL_SP_EXIT_CLOSED)
11919       CheckExitSP(x, y);
11920     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11921              element == EL_EXPANDABLE_STEELWALL_GROWING)
11922       MauerWaechst(x, y);
11923     else if (element == EL_EXPANDABLE_WALL ||
11924              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11925              element == EL_EXPANDABLE_WALL_VERTICAL ||
11926              element == EL_EXPANDABLE_WALL_ANY ||
11927              element == EL_BD_EXPANDABLE_WALL)
11928       MauerAbleger(x, y);
11929     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11930              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11931              element == EL_EXPANDABLE_STEELWALL_ANY)
11932       MauerAblegerStahl(x, y);
11933     else if (element == EL_FLAMES)
11934       CheckForDragon(x, y);
11935     else if (element == EL_EXPLOSION)
11936       ; /* drawing of correct explosion animation is handled separately */
11937     else if (element == EL_ELEMENT_SNAPPING ||
11938              element == EL_DIAGONAL_SHRINKING ||
11939              element == EL_DIAGONAL_GROWING)
11940     {
11941       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11942
11943       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11944     }
11945     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11946       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11947
11948     if (IS_BELT_ACTIVE(element))
11949       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11950
11951     if (game.magic_wall_active)
11952     {
11953       int jx = local_player->jx, jy = local_player->jy;
11954
11955       /* play the element sound at the position nearest to the player */
11956       if ((element == EL_MAGIC_WALL_FULL ||
11957            element == EL_MAGIC_WALL_ACTIVE ||
11958            element == EL_MAGIC_WALL_EMPTYING ||
11959            element == EL_BD_MAGIC_WALL_FULL ||
11960            element == EL_BD_MAGIC_WALL_ACTIVE ||
11961            element == EL_BD_MAGIC_WALL_EMPTYING ||
11962            element == EL_DC_MAGIC_WALL_FULL ||
11963            element == EL_DC_MAGIC_WALL_ACTIVE ||
11964            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11965           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11966       {
11967         magic_wall_x = x;
11968         magic_wall_y = y;
11969       }
11970     }
11971   }
11972
11973 #if USE_NEW_AMOEBA_CODE
11974   /* new experimental amoeba growth stuff */
11975   if (!(FrameCounter % 8))
11976   {
11977     static unsigned int random = 1684108901;
11978
11979     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11980     {
11981       x = RND(lev_fieldx);
11982       y = RND(lev_fieldy);
11983       element = Feld[x][y];
11984
11985       if (!IS_PLAYER(x,y) &&
11986           (element == EL_EMPTY ||
11987            CAN_GROW_INTO(element) ||
11988            element == EL_QUICKSAND_EMPTY ||
11989            element == EL_QUICKSAND_FAST_EMPTY ||
11990            element == EL_ACID_SPLASH_LEFT ||
11991            element == EL_ACID_SPLASH_RIGHT))
11992       {
11993         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11994             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11995             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11996             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11997           Feld[x][y] = EL_AMOEBA_DROP;
11998       }
11999
12000       random = random * 129 + 1;
12001     }
12002   }
12003 #endif
12004
12005   game.explosions_delayed = FALSE;
12006
12007   SCAN_PLAYFIELD(x, y)
12008   {
12009     element = Feld[x][y];
12010
12011     if (ExplodeField[x][y])
12012       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12013     else if (element == EL_EXPLOSION)
12014       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12015
12016     ExplodeField[x][y] = EX_TYPE_NONE;
12017   }
12018
12019   game.explosions_delayed = TRUE;
12020
12021   if (game.magic_wall_active)
12022   {
12023     if (!(game.magic_wall_time_left % 4))
12024     {
12025       int element = Feld[magic_wall_x][magic_wall_y];
12026
12027       if (element == EL_BD_MAGIC_WALL_FULL ||
12028           element == EL_BD_MAGIC_WALL_ACTIVE ||
12029           element == EL_BD_MAGIC_WALL_EMPTYING)
12030         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12031       else if (element == EL_DC_MAGIC_WALL_FULL ||
12032                element == EL_DC_MAGIC_WALL_ACTIVE ||
12033                element == EL_DC_MAGIC_WALL_EMPTYING)
12034         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12035       else
12036         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12037     }
12038
12039     if (game.magic_wall_time_left > 0)
12040     {
12041       game.magic_wall_time_left--;
12042
12043       if (!game.magic_wall_time_left)
12044       {
12045         SCAN_PLAYFIELD(x, y)
12046         {
12047           element = Feld[x][y];
12048
12049           if (element == EL_MAGIC_WALL_ACTIVE ||
12050               element == EL_MAGIC_WALL_FULL)
12051           {
12052             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12053             TEST_DrawLevelField(x, y);
12054           }
12055           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12056                    element == EL_BD_MAGIC_WALL_FULL)
12057           {
12058             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12059             TEST_DrawLevelField(x, y);
12060           }
12061           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12062                    element == EL_DC_MAGIC_WALL_FULL)
12063           {
12064             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12065             TEST_DrawLevelField(x, y);
12066           }
12067         }
12068
12069         game.magic_wall_active = FALSE;
12070       }
12071     }
12072   }
12073
12074   if (game.light_time_left > 0)
12075   {
12076     game.light_time_left--;
12077
12078     if (game.light_time_left == 0)
12079       RedrawAllLightSwitchesAndInvisibleElements();
12080   }
12081
12082   if (game.timegate_time_left > 0)
12083   {
12084     game.timegate_time_left--;
12085
12086     if (game.timegate_time_left == 0)
12087       CloseAllOpenTimegates();
12088   }
12089
12090   if (game.lenses_time_left > 0)
12091   {
12092     game.lenses_time_left--;
12093
12094     if (game.lenses_time_left == 0)
12095       RedrawAllInvisibleElementsForLenses();
12096   }
12097
12098   if (game.magnify_time_left > 0)
12099   {
12100     game.magnify_time_left--;
12101
12102     if (game.magnify_time_left == 0)
12103       RedrawAllInvisibleElementsForMagnifier();
12104   }
12105
12106   for (i = 0; i < MAX_PLAYERS; i++)
12107   {
12108     struct PlayerInfo *player = &stored_player[i];
12109
12110     if (SHIELD_ON(player))
12111     {
12112       if (player->shield_deadly_time_left)
12113         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12114       else if (player->shield_normal_time_left)
12115         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12116     }
12117   }
12118
12119 #if USE_DELAYED_GFX_REDRAW
12120   SCAN_PLAYFIELD(x, y)
12121   {
12122     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12123     {
12124       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12125          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12126
12127       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12128         DrawLevelField(x, y);
12129
12130       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12131         DrawLevelFieldCrumbled(x, y);
12132
12133       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12134         DrawLevelFieldCrumbledNeighbours(x, y);
12135
12136       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12137         DrawTwinkleOnField(x, y);
12138     }
12139
12140     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12141   }
12142 #endif
12143
12144   DrawAllPlayers();
12145   PlayAllPlayersSound();
12146
12147   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12148   {
12149     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12150
12151     local_player->show_envelope = 0;
12152   }
12153
12154   /* use random number generator in every frame to make it less predictable */
12155   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12156     RND(1);
12157 }
12158
12159 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12160 {
12161   int min_x = x, min_y = y, max_x = x, max_y = y;
12162   int i;
12163
12164   for (i = 0; i < MAX_PLAYERS; i++)
12165   {
12166     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12167
12168     if (!stored_player[i].active || &stored_player[i] == player)
12169       continue;
12170
12171     min_x = MIN(min_x, jx);
12172     min_y = MIN(min_y, jy);
12173     max_x = MAX(max_x, jx);
12174     max_y = MAX(max_y, jy);
12175   }
12176
12177   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12178 }
12179
12180 static boolean AllPlayersInVisibleScreen()
12181 {
12182   int i;
12183
12184   for (i = 0; i < MAX_PLAYERS; i++)
12185   {
12186     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12187
12188     if (!stored_player[i].active)
12189       continue;
12190
12191     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12192       return FALSE;
12193   }
12194
12195   return TRUE;
12196 }
12197
12198 void ScrollLevel(int dx, int dy)
12199 {
12200   int scroll_offset = 2 * TILEX_VAR;
12201   int x, y;
12202
12203   BlitBitmap(drawto_field, drawto_field,
12204              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12205              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12206              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12207              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12208              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12209              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12210
12211   if (dx != 0)
12212   {
12213     x = (dx == 1 ? BX1 : BX2);
12214     for (y = BY1; y <= BY2; y++)
12215       DrawScreenField(x, y);
12216   }
12217
12218   if (dy != 0)
12219   {
12220     y = (dy == 1 ? BY1 : BY2);
12221     for (x = BX1; x <= BX2; x++)
12222       DrawScreenField(x, y);
12223   }
12224
12225   redraw_mask |= REDRAW_FIELD;
12226 }
12227
12228 static boolean canFallDown(struct PlayerInfo *player)
12229 {
12230   int jx = player->jx, jy = player->jy;
12231
12232   return (IN_LEV_FIELD(jx, jy + 1) &&
12233           (IS_FREE(jx, jy + 1) ||
12234            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12235           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12236           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12237 }
12238
12239 static boolean canPassField(int x, int y, int move_dir)
12240 {
12241   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12242   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12243   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12244   int nextx = x + dx;
12245   int nexty = y + dy;
12246   int element = Feld[x][y];
12247
12248   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12249           !CAN_MOVE(element) &&
12250           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12251           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12252           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12253 }
12254
12255 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12256 {
12257   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12258   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12259   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12260   int newx = x + dx;
12261   int newy = y + dy;
12262
12263   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12264           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12265           (IS_DIGGABLE(Feld[newx][newy]) ||
12266            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12267            canPassField(newx, newy, move_dir)));
12268 }
12269
12270 static void CheckGravityMovement(struct PlayerInfo *player)
12271 {
12272   if (player->gravity && !player->programmed_action)
12273   {
12274     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12275     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12276     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12277     int jx = player->jx, jy = player->jy;
12278     boolean player_is_moving_to_valid_field =
12279       (!player_is_snapping &&
12280        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12281         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12282     boolean player_can_fall_down = canFallDown(player);
12283
12284     if (player_can_fall_down &&
12285         !player_is_moving_to_valid_field)
12286       player->programmed_action = MV_DOWN;
12287   }
12288 }
12289
12290 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12291 {
12292   return CheckGravityMovement(player);
12293
12294   if (player->gravity && !player->programmed_action)
12295   {
12296     int jx = player->jx, jy = player->jy;
12297     boolean field_under_player_is_free =
12298       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12299     boolean player_is_standing_on_valid_field =
12300       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12301        (IS_WALKABLE(Feld[jx][jy]) &&
12302         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12303
12304     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12305       player->programmed_action = MV_DOWN;
12306   }
12307 }
12308
12309 /*
12310   MovePlayerOneStep()
12311   -----------------------------------------------------------------------------
12312   dx, dy:               direction (non-diagonal) to try to move the player to
12313   real_dx, real_dy:     direction as read from input device (can be diagonal)
12314 */
12315
12316 boolean MovePlayerOneStep(struct PlayerInfo *player,
12317                           int dx, int dy, int real_dx, int real_dy)
12318 {
12319   int jx = player->jx, jy = player->jy;
12320   int new_jx = jx + dx, new_jy = jy + dy;
12321   int can_move;
12322   boolean player_can_move = !player->cannot_move;
12323
12324   if (!player->active || (!dx && !dy))
12325     return MP_NO_ACTION;
12326
12327   player->MovDir = (dx < 0 ? MV_LEFT :
12328                     dx > 0 ? MV_RIGHT :
12329                     dy < 0 ? MV_UP :
12330                     dy > 0 ? MV_DOWN :  MV_NONE);
12331
12332   if (!IN_LEV_FIELD(new_jx, new_jy))
12333     return MP_NO_ACTION;
12334
12335   if (!player_can_move)
12336   {
12337     if (player->MovPos == 0)
12338     {
12339       player->is_moving = FALSE;
12340       player->is_digging = FALSE;
12341       player->is_collecting = FALSE;
12342       player->is_snapping = FALSE;
12343       player->is_pushing = FALSE;
12344     }
12345   }
12346
12347   if (!options.network && game.centered_player_nr == -1 &&
12348       !AllPlayersInSight(player, new_jx, new_jy))
12349     return MP_NO_ACTION;
12350
12351   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12352   if (can_move != MP_MOVING)
12353     return can_move;
12354
12355   /* check if DigField() has caused relocation of the player */
12356   if (player->jx != jx || player->jy != jy)
12357     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12358
12359   StorePlayer[jx][jy] = 0;
12360   player->last_jx = jx;
12361   player->last_jy = jy;
12362   player->jx = new_jx;
12363   player->jy = new_jy;
12364   StorePlayer[new_jx][new_jy] = player->element_nr;
12365
12366   if (player->move_delay_value_next != -1)
12367   {
12368     player->move_delay_value = player->move_delay_value_next;
12369     player->move_delay_value_next = -1;
12370   }
12371
12372   player->MovPos =
12373     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12374
12375   player->step_counter++;
12376
12377   PlayerVisit[jx][jy] = FrameCounter;
12378
12379   player->is_moving = TRUE;
12380
12381 #if 1
12382   /* should better be called in MovePlayer(), but this breaks some tapes */
12383   ScrollPlayer(player, SCROLL_INIT);
12384 #endif
12385
12386   return MP_MOVING;
12387 }
12388
12389 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12390 {
12391   int jx = player->jx, jy = player->jy;
12392   int old_jx = jx, old_jy = jy;
12393   int moved = MP_NO_ACTION;
12394
12395   if (!player->active)
12396     return FALSE;
12397
12398   if (!dx && !dy)
12399   {
12400     if (player->MovPos == 0)
12401     {
12402       player->is_moving = FALSE;
12403       player->is_digging = FALSE;
12404       player->is_collecting = FALSE;
12405       player->is_snapping = FALSE;
12406       player->is_pushing = FALSE;
12407     }
12408
12409     return FALSE;
12410   }
12411
12412   if (player->move_delay > 0)
12413     return FALSE;
12414
12415   player->move_delay = -1;              /* set to "uninitialized" value */
12416
12417   /* store if player is automatically moved to next field */
12418   player->is_auto_moving = (player->programmed_action != MV_NONE);
12419
12420   /* remove the last programmed player action */
12421   player->programmed_action = 0;
12422
12423   if (player->MovPos)
12424   {
12425     /* should only happen if pre-1.2 tape recordings are played */
12426     /* this is only for backward compatibility */
12427
12428     int original_move_delay_value = player->move_delay_value;
12429
12430 #if DEBUG
12431     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12432            tape.counter);
12433 #endif
12434
12435     /* scroll remaining steps with finest movement resolution */
12436     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12437
12438     while (player->MovPos)
12439     {
12440       ScrollPlayer(player, SCROLL_GO_ON);
12441       ScrollScreen(NULL, SCROLL_GO_ON);
12442
12443       AdvanceFrameAndPlayerCounters(player->index_nr);
12444
12445       DrawAllPlayers();
12446       BackToFront_WithFrameDelay(0);
12447     }
12448
12449     player->move_delay_value = original_move_delay_value;
12450   }
12451
12452   player->is_active = FALSE;
12453
12454   if (player->last_move_dir & MV_HORIZONTAL)
12455   {
12456     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12457       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12458   }
12459   else
12460   {
12461     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12462       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12463   }
12464
12465   if (!moved && !player->is_active)
12466   {
12467     player->is_moving = FALSE;
12468     player->is_digging = FALSE;
12469     player->is_collecting = FALSE;
12470     player->is_snapping = FALSE;
12471     player->is_pushing = FALSE;
12472   }
12473
12474   jx = player->jx;
12475   jy = player->jy;
12476
12477   if (moved & MP_MOVING && !ScreenMovPos &&
12478       (player->index_nr == game.centered_player_nr ||
12479        game.centered_player_nr == -1))
12480   {
12481     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12482     int offset = game.scroll_delay_value;
12483
12484     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12485     {
12486       /* actual player has left the screen -- scroll in that direction */
12487       if (jx != old_jx)         /* player has moved horizontally */
12488         scroll_x += (jx - old_jx);
12489       else                      /* player has moved vertically */
12490         scroll_y += (jy - old_jy);
12491     }
12492     else
12493     {
12494       if (jx != old_jx)         /* player has moved horizontally */
12495       {
12496         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12497             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12498           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12499
12500         /* don't scroll over playfield boundaries */
12501         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12502           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12503
12504         /* don't scroll more than one field at a time */
12505         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12506
12507         /* don't scroll against the player's moving direction */
12508         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12509             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12510           scroll_x = old_scroll_x;
12511       }
12512       else                      /* player has moved vertically */
12513       {
12514         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12515             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12516           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12517
12518         /* don't scroll over playfield boundaries */
12519         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12520           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12521
12522         /* don't scroll more than one field at a time */
12523         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12524
12525         /* don't scroll against the player's moving direction */
12526         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12527             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12528           scroll_y = old_scroll_y;
12529       }
12530     }
12531
12532     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12533     {
12534       if (!options.network && game.centered_player_nr == -1 &&
12535           !AllPlayersInVisibleScreen())
12536       {
12537         scroll_x = old_scroll_x;
12538         scroll_y = old_scroll_y;
12539       }
12540       else
12541       {
12542         ScrollScreen(player, SCROLL_INIT);
12543         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12544       }
12545     }
12546   }
12547
12548   player->StepFrame = 0;
12549
12550   if (moved & MP_MOVING)
12551   {
12552     if (old_jx != jx && old_jy == jy)
12553       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12554     else if (old_jx == jx && old_jy != jy)
12555       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12556
12557     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12558
12559     player->last_move_dir = player->MovDir;
12560     player->is_moving = TRUE;
12561     player->is_snapping = FALSE;
12562     player->is_switching = FALSE;
12563     player->is_dropping = FALSE;
12564     player->is_dropping_pressed = FALSE;
12565     player->drop_pressed_delay = 0;
12566
12567 #if 0
12568     /* should better be called here than above, but this breaks some tapes */
12569     ScrollPlayer(player, SCROLL_INIT);
12570 #endif
12571   }
12572   else
12573   {
12574     CheckGravityMovementWhenNotMoving(player);
12575
12576     player->is_moving = FALSE;
12577
12578     /* at this point, the player is allowed to move, but cannot move right now
12579        (e.g. because of something blocking the way) -- ensure that the player
12580        is also allowed to move in the next frame (in old versions before 3.1.1,
12581        the player was forced to wait again for eight frames before next try) */
12582
12583     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12584       player->move_delay = 0;   /* allow direct movement in the next frame */
12585   }
12586
12587   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12588     player->move_delay = player->move_delay_value;
12589
12590   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12591   {
12592     TestIfPlayerTouchesBadThing(jx, jy);
12593     TestIfPlayerTouchesCustomElement(jx, jy);
12594   }
12595
12596   if (!player->active)
12597     RemovePlayer(player);
12598
12599   return moved;
12600 }
12601
12602 void ScrollPlayer(struct PlayerInfo *player, int mode)
12603 {
12604   int jx = player->jx, jy = player->jy;
12605   int last_jx = player->last_jx, last_jy = player->last_jy;
12606   int move_stepsize = TILEX / player->move_delay_value;
12607
12608   if (!player->active)
12609     return;
12610
12611   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12612     return;
12613
12614   if (mode == SCROLL_INIT)
12615   {
12616     player->actual_frame_counter = FrameCounter;
12617     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12618
12619     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12620         Feld[last_jx][last_jy] == EL_EMPTY)
12621     {
12622       int last_field_block_delay = 0;   /* start with no blocking at all */
12623       int block_delay_adjustment = player->block_delay_adjustment;
12624
12625       /* if player blocks last field, add delay for exactly one move */
12626       if (player->block_last_field)
12627       {
12628         last_field_block_delay += player->move_delay_value;
12629
12630         /* when blocking enabled, prevent moving up despite gravity */
12631         if (player->gravity && player->MovDir == MV_UP)
12632           block_delay_adjustment = -1;
12633       }
12634
12635       /* add block delay adjustment (also possible when not blocking) */
12636       last_field_block_delay += block_delay_adjustment;
12637
12638       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12639       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12640     }
12641
12642     if (player->MovPos != 0)    /* player has not yet reached destination */
12643       return;
12644   }
12645   else if (!FrameReached(&player->actual_frame_counter, 1))
12646     return;
12647
12648   if (player->MovPos != 0)
12649   {
12650     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12651     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12652
12653     /* before DrawPlayer() to draw correct player graphic for this case */
12654     if (player->MovPos == 0)
12655       CheckGravityMovement(player);
12656   }
12657
12658   if (player->MovPos == 0)      /* player reached destination field */
12659   {
12660     if (player->move_delay_reset_counter > 0)
12661     {
12662       player->move_delay_reset_counter--;
12663
12664       if (player->move_delay_reset_counter == 0)
12665       {
12666         /* continue with normal speed after quickly moving through gate */
12667         HALVE_PLAYER_SPEED(player);
12668
12669         /* be able to make the next move without delay */
12670         player->move_delay = 0;
12671       }
12672     }
12673
12674     player->last_jx = jx;
12675     player->last_jy = jy;
12676
12677     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12678         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12679         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12680         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12681         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12682         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12683         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12684         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12685     {
12686       DrawPlayer(player);       /* needed here only to cleanup last field */
12687       RemovePlayer(player);
12688
12689       if (local_player->friends_still_needed == 0 ||
12690           IS_SP_ELEMENT(Feld[jx][jy]))
12691         PlayerWins(player);
12692     }
12693
12694     /* this breaks one level: "machine", level 000 */
12695     {
12696       int move_direction = player->MovDir;
12697       int enter_side = MV_DIR_OPPOSITE(move_direction);
12698       int leave_side = move_direction;
12699       int old_jx = last_jx;
12700       int old_jy = last_jy;
12701       int old_element = Feld[old_jx][old_jy];
12702       int new_element = Feld[jx][jy];
12703
12704       if (IS_CUSTOM_ELEMENT(old_element))
12705         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12706                                    CE_LEFT_BY_PLAYER,
12707                                    player->index_bit, leave_side);
12708
12709       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12710                                           CE_PLAYER_LEAVES_X,
12711                                           player->index_bit, leave_side);
12712
12713       if (IS_CUSTOM_ELEMENT(new_element))
12714         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12715                                    player->index_bit, enter_side);
12716
12717       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12718                                           CE_PLAYER_ENTERS_X,
12719                                           player->index_bit, enter_side);
12720
12721       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12722                                         CE_MOVE_OF_X, move_direction);
12723     }
12724
12725     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12726     {
12727       TestIfPlayerTouchesBadThing(jx, jy);
12728       TestIfPlayerTouchesCustomElement(jx, jy);
12729
12730       /* needed because pushed element has not yet reached its destination,
12731          so it would trigger a change event at its previous field location */
12732       if (!player->is_pushing)
12733         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12734
12735       if (!player->active)
12736         RemovePlayer(player);
12737     }
12738
12739     if (!local_player->LevelSolved && level.use_step_counter)
12740     {
12741       int i;
12742
12743       TimePlayed++;
12744
12745       if (TimeLeft > 0)
12746       {
12747         TimeLeft--;
12748
12749         if (TimeLeft <= 10 && setup.time_limit)
12750           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12751
12752         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12753
12754         DisplayGameControlValues();
12755
12756         if (!TimeLeft && setup.time_limit)
12757           for (i = 0; i < MAX_PLAYERS; i++)
12758             KillPlayer(&stored_player[i]);
12759       }
12760       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12761       {
12762         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12763
12764         DisplayGameControlValues();
12765       }
12766     }
12767
12768     if (tape.single_step && tape.recording && !tape.pausing &&
12769         !player->programmed_action)
12770       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12771
12772     if (!player->programmed_action)
12773       CheckSaveEngineSnapshot(player);
12774   }
12775 }
12776
12777 void ScrollScreen(struct PlayerInfo *player, int mode)
12778 {
12779   static unsigned int screen_frame_counter = 0;
12780
12781   if (mode == SCROLL_INIT)
12782   {
12783     /* set scrolling step size according to actual player's moving speed */
12784     ScrollStepSize = TILEX / player->move_delay_value;
12785
12786     screen_frame_counter = FrameCounter;
12787     ScreenMovDir = player->MovDir;
12788     ScreenMovPos = player->MovPos;
12789     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12790     return;
12791   }
12792   else if (!FrameReached(&screen_frame_counter, 1))
12793     return;
12794
12795   if (ScreenMovPos)
12796   {
12797     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12798     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12799     redraw_mask |= REDRAW_FIELD;
12800   }
12801   else
12802     ScreenMovDir = MV_NONE;
12803 }
12804
12805 void TestIfPlayerTouchesCustomElement(int x, int y)
12806 {
12807   static int xy[4][2] =
12808   {
12809     { 0, -1 },
12810     { -1, 0 },
12811     { +1, 0 },
12812     { 0, +1 }
12813   };
12814   static int trigger_sides[4][2] =
12815   {
12816     /* center side       border side */
12817     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12818     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12819     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12820     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12821   };
12822   static int touch_dir[4] =
12823   {
12824     MV_LEFT | MV_RIGHT,
12825     MV_UP   | MV_DOWN,
12826     MV_UP   | MV_DOWN,
12827     MV_LEFT | MV_RIGHT
12828   };
12829   int center_element = Feld[x][y];      /* should always be non-moving! */
12830   int i;
12831
12832   for (i = 0; i < NUM_DIRECTIONS; i++)
12833   {
12834     int xx = x + xy[i][0];
12835     int yy = y + xy[i][1];
12836     int center_side = trigger_sides[i][0];
12837     int border_side = trigger_sides[i][1];
12838     int border_element;
12839
12840     if (!IN_LEV_FIELD(xx, yy))
12841       continue;
12842
12843     if (IS_PLAYER(x, y))                /* player found at center element */
12844     {
12845       struct PlayerInfo *player = PLAYERINFO(x, y);
12846
12847       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12848         border_element = Feld[xx][yy];          /* may be moving! */
12849       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12850         border_element = Feld[xx][yy];
12851       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12852         border_element = MovingOrBlocked2Element(xx, yy);
12853       else
12854         continue;               /* center and border element do not touch */
12855
12856       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12857                                  player->index_bit, border_side);
12858       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12859                                           CE_PLAYER_TOUCHES_X,
12860                                           player->index_bit, border_side);
12861
12862       {
12863         /* use player element that is initially defined in the level playfield,
12864            not the player element that corresponds to the runtime player number
12865            (example: a level that contains EL_PLAYER_3 as the only player would
12866            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12867         int player_element = PLAYERINFO(x, y)->initial_element;
12868
12869         CheckElementChangeBySide(xx, yy, border_element, player_element,
12870                                  CE_TOUCHING_X, border_side);
12871       }
12872     }
12873     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12874     {
12875       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12876
12877       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12878       {
12879         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12880           continue;             /* center and border element do not touch */
12881       }
12882
12883       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12884                                  player->index_bit, center_side);
12885       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12886                                           CE_PLAYER_TOUCHES_X,
12887                                           player->index_bit, center_side);
12888
12889       {
12890         /* use player element that is initially defined in the level playfield,
12891            not the player element that corresponds to the runtime player number
12892            (example: a level that contains EL_PLAYER_3 as the only player would
12893            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12894         int player_element = PLAYERINFO(xx, yy)->initial_element;
12895
12896         CheckElementChangeBySide(x, y, center_element, player_element,
12897                                  CE_TOUCHING_X, center_side);
12898       }
12899
12900       break;
12901     }
12902   }
12903 }
12904
12905 void TestIfElementTouchesCustomElement(int x, int y)
12906 {
12907   static int xy[4][2] =
12908   {
12909     { 0, -1 },
12910     { -1, 0 },
12911     { +1, 0 },
12912     { 0, +1 }
12913   };
12914   static int trigger_sides[4][2] =
12915   {
12916     /* center side      border side */
12917     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12918     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12919     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12920     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12921   };
12922   static int touch_dir[4] =
12923   {
12924     MV_LEFT | MV_RIGHT,
12925     MV_UP   | MV_DOWN,
12926     MV_UP   | MV_DOWN,
12927     MV_LEFT | MV_RIGHT
12928   };
12929   boolean change_center_element = FALSE;
12930   int center_element = Feld[x][y];      /* should always be non-moving! */
12931   int border_element_old[NUM_DIRECTIONS];
12932   int i;
12933
12934   for (i = 0; i < NUM_DIRECTIONS; i++)
12935   {
12936     int xx = x + xy[i][0];
12937     int yy = y + xy[i][1];
12938     int border_element;
12939
12940     border_element_old[i] = -1;
12941
12942     if (!IN_LEV_FIELD(xx, yy))
12943       continue;
12944
12945     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12946       border_element = Feld[xx][yy];    /* may be moving! */
12947     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12948       border_element = Feld[xx][yy];
12949     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12950       border_element = MovingOrBlocked2Element(xx, yy);
12951     else
12952       continue;                 /* center and border element do not touch */
12953
12954     border_element_old[i] = border_element;
12955   }
12956
12957   for (i = 0; i < NUM_DIRECTIONS; i++)
12958   {
12959     int xx = x + xy[i][0];
12960     int yy = y + xy[i][1];
12961     int center_side = trigger_sides[i][0];
12962     int border_element = border_element_old[i];
12963
12964     if (border_element == -1)
12965       continue;
12966
12967     /* check for change of border element */
12968     CheckElementChangeBySide(xx, yy, border_element, center_element,
12969                              CE_TOUCHING_X, center_side);
12970
12971     /* (center element cannot be player, so we dont have to check this here) */
12972   }
12973
12974   for (i = 0; i < NUM_DIRECTIONS; i++)
12975   {
12976     int xx = x + xy[i][0];
12977     int yy = y + xy[i][1];
12978     int border_side = trigger_sides[i][1];
12979     int border_element = border_element_old[i];
12980
12981     if (border_element == -1)
12982       continue;
12983
12984     /* check for change of center element (but change it only once) */
12985     if (!change_center_element)
12986       change_center_element =
12987         CheckElementChangeBySide(x, y, center_element, border_element,
12988                                  CE_TOUCHING_X, border_side);
12989
12990     if (IS_PLAYER(xx, yy))
12991     {
12992       /* use player element that is initially defined in the level playfield,
12993          not the player element that corresponds to the runtime player number
12994          (example: a level that contains EL_PLAYER_3 as the only player would
12995          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12996       int player_element = PLAYERINFO(xx, yy)->initial_element;
12997
12998       CheckElementChangeBySide(x, y, center_element, player_element,
12999                                CE_TOUCHING_X, border_side);
13000     }
13001   }
13002 }
13003
13004 void TestIfElementHitsCustomElement(int x, int y, int direction)
13005 {
13006   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13007   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13008   int hitx = x + dx, hity = y + dy;
13009   int hitting_element = Feld[x][y];
13010   int touched_element;
13011
13012   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13013     return;
13014
13015   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13016                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13017
13018   if (IN_LEV_FIELD(hitx, hity))
13019   {
13020     int opposite_direction = MV_DIR_OPPOSITE(direction);
13021     int hitting_side = direction;
13022     int touched_side = opposite_direction;
13023     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13024                           MovDir[hitx][hity] != direction ||
13025                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13026
13027     object_hit = TRUE;
13028
13029     if (object_hit)
13030     {
13031       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13032                                CE_HITTING_X, touched_side);
13033
13034       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13035                                CE_HIT_BY_X, hitting_side);
13036
13037       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13038                                CE_HIT_BY_SOMETHING, opposite_direction);
13039
13040       if (IS_PLAYER(hitx, hity))
13041       {
13042         /* use player element that is initially defined in the level playfield,
13043            not the player element that corresponds to the runtime player number
13044            (example: a level that contains EL_PLAYER_3 as the only player would
13045            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13046         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13047
13048         CheckElementChangeBySide(x, y, hitting_element, player_element,
13049                                  CE_HITTING_X, touched_side);
13050       }
13051     }
13052   }
13053
13054   /* "hitting something" is also true when hitting the playfield border */
13055   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13056                            CE_HITTING_SOMETHING, direction);
13057 }
13058
13059 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13060 {
13061   int i, kill_x = -1, kill_y = -1;
13062
13063   int bad_element = -1;
13064   static int test_xy[4][2] =
13065   {
13066     { 0, -1 },
13067     { -1, 0 },
13068     { +1, 0 },
13069     { 0, +1 }
13070   };
13071   static int test_dir[4] =
13072   {
13073     MV_UP,
13074     MV_LEFT,
13075     MV_RIGHT,
13076     MV_DOWN
13077   };
13078
13079   for (i = 0; i < NUM_DIRECTIONS; i++)
13080   {
13081     int test_x, test_y, test_move_dir, test_element;
13082
13083     test_x = good_x + test_xy[i][0];
13084     test_y = good_y + test_xy[i][1];
13085
13086     if (!IN_LEV_FIELD(test_x, test_y))
13087       continue;
13088
13089     test_move_dir =
13090       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13091
13092     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13093
13094     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13095        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13096     */
13097     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13098         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13099     {
13100       kill_x = test_x;
13101       kill_y = test_y;
13102       bad_element = test_element;
13103
13104       break;
13105     }
13106   }
13107
13108   if (kill_x != -1 || kill_y != -1)
13109   {
13110     if (IS_PLAYER(good_x, good_y))
13111     {
13112       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13113
13114       if (player->shield_deadly_time_left > 0 &&
13115           !IS_INDESTRUCTIBLE(bad_element))
13116         Bang(kill_x, kill_y);
13117       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13118         KillPlayer(player);
13119     }
13120     else
13121       Bang(good_x, good_y);
13122   }
13123 }
13124
13125 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13126 {
13127   int i, kill_x = -1, kill_y = -1;
13128   int bad_element = Feld[bad_x][bad_y];
13129   static int test_xy[4][2] =
13130   {
13131     { 0, -1 },
13132     { -1, 0 },
13133     { +1, 0 },
13134     { 0, +1 }
13135   };
13136   static int touch_dir[4] =
13137   {
13138     MV_LEFT | MV_RIGHT,
13139     MV_UP   | MV_DOWN,
13140     MV_UP   | MV_DOWN,
13141     MV_LEFT | MV_RIGHT
13142   };
13143   static int test_dir[4] =
13144   {
13145     MV_UP,
13146     MV_LEFT,
13147     MV_RIGHT,
13148     MV_DOWN
13149   };
13150
13151   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13152     return;
13153
13154   for (i = 0; i < NUM_DIRECTIONS; i++)
13155   {
13156     int test_x, test_y, test_move_dir, test_element;
13157
13158     test_x = bad_x + test_xy[i][0];
13159     test_y = bad_y + test_xy[i][1];
13160
13161     if (!IN_LEV_FIELD(test_x, test_y))
13162       continue;
13163
13164     test_move_dir =
13165       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13166
13167     test_element = Feld[test_x][test_y];
13168
13169     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13170        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13171     */
13172     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13173         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13174     {
13175       /* good thing is player or penguin that does not move away */
13176       if (IS_PLAYER(test_x, test_y))
13177       {
13178         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13179
13180         if (bad_element == EL_ROBOT && player->is_moving)
13181           continue;     /* robot does not kill player if he is moving */
13182
13183         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13184         {
13185           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13186             continue;           /* center and border element do not touch */
13187         }
13188
13189         kill_x = test_x;
13190         kill_y = test_y;
13191
13192         break;
13193       }
13194       else if (test_element == EL_PENGUIN)
13195       {
13196         kill_x = test_x;
13197         kill_y = test_y;
13198
13199         break;
13200       }
13201     }
13202   }
13203
13204   if (kill_x != -1 || kill_y != -1)
13205   {
13206     if (IS_PLAYER(kill_x, kill_y))
13207     {
13208       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13209
13210       if (player->shield_deadly_time_left > 0 &&
13211           !IS_INDESTRUCTIBLE(bad_element))
13212         Bang(bad_x, bad_y);
13213       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13214         KillPlayer(player);
13215     }
13216     else
13217       Bang(kill_x, kill_y);
13218   }
13219 }
13220
13221 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13222 {
13223   int bad_element = Feld[bad_x][bad_y];
13224   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13225   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13226   int test_x = bad_x + dx, test_y = bad_y + dy;
13227   int test_move_dir, test_element;
13228   int kill_x = -1, kill_y = -1;
13229
13230   if (!IN_LEV_FIELD(test_x, test_y))
13231     return;
13232
13233   test_move_dir =
13234     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13235
13236   test_element = Feld[test_x][test_y];
13237
13238   if (test_move_dir != bad_move_dir)
13239   {
13240     /* good thing can be player or penguin that does not move away */
13241     if (IS_PLAYER(test_x, test_y))
13242     {
13243       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13244
13245       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13246          player as being hit when he is moving towards the bad thing, because
13247          the "get hit by" condition would be lost after the player stops) */
13248       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13249         return;         /* player moves away from bad thing */
13250
13251       kill_x = test_x;
13252       kill_y = test_y;
13253     }
13254     else if (test_element == EL_PENGUIN)
13255     {
13256       kill_x = test_x;
13257       kill_y = test_y;
13258     }
13259   }
13260
13261   if (kill_x != -1 || kill_y != -1)
13262   {
13263     if (IS_PLAYER(kill_x, kill_y))
13264     {
13265       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13266
13267       if (player->shield_deadly_time_left > 0 &&
13268           !IS_INDESTRUCTIBLE(bad_element))
13269         Bang(bad_x, bad_y);
13270       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13271         KillPlayer(player);
13272     }
13273     else
13274       Bang(kill_x, kill_y);
13275   }
13276 }
13277
13278 void TestIfPlayerTouchesBadThing(int x, int y)
13279 {
13280   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13281 }
13282
13283 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13284 {
13285   TestIfGoodThingHitsBadThing(x, y, move_dir);
13286 }
13287
13288 void TestIfBadThingTouchesPlayer(int x, int y)
13289 {
13290   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13291 }
13292
13293 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13294 {
13295   TestIfBadThingHitsGoodThing(x, y, move_dir);
13296 }
13297
13298 void TestIfFriendTouchesBadThing(int x, int y)
13299 {
13300   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13301 }
13302
13303 void TestIfBadThingTouchesFriend(int x, int y)
13304 {
13305   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13306 }
13307
13308 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13309 {
13310   int i, kill_x = bad_x, kill_y = bad_y;
13311   static int xy[4][2] =
13312   {
13313     { 0, -1 },
13314     { -1, 0 },
13315     { +1, 0 },
13316     { 0, +1 }
13317   };
13318
13319   for (i = 0; i < NUM_DIRECTIONS; i++)
13320   {
13321     int x, y, element;
13322
13323     x = bad_x + xy[i][0];
13324     y = bad_y + xy[i][1];
13325     if (!IN_LEV_FIELD(x, y))
13326       continue;
13327
13328     element = Feld[x][y];
13329     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13330         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13331     {
13332       kill_x = x;
13333       kill_y = y;
13334       break;
13335     }
13336   }
13337
13338   if (kill_x != bad_x || kill_y != bad_y)
13339     Bang(bad_x, bad_y);
13340 }
13341
13342 void KillPlayer(struct PlayerInfo *player)
13343 {
13344   int jx = player->jx, jy = player->jy;
13345
13346   if (!player->active)
13347     return;
13348
13349 #if 0
13350   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13351          player->killed, player->active, player->reanimated);
13352 #endif
13353
13354   /* the following code was introduced to prevent an infinite loop when calling
13355      -> Bang()
13356      -> CheckTriggeredElementChangeExt()
13357      -> ExecuteCustomElementAction()
13358      -> KillPlayer()
13359      -> (infinitely repeating the above sequence of function calls)
13360      which occurs when killing the player while having a CE with the setting
13361      "kill player X when explosion of <player X>"; the solution using a new
13362      field "player->killed" was chosen for backwards compatibility, although
13363      clever use of the fields "player->active" etc. would probably also work */
13364 #if 1
13365   if (player->killed)
13366     return;
13367 #endif
13368
13369   player->killed = TRUE;
13370
13371   /* remove accessible field at the player's position */
13372   Feld[jx][jy] = EL_EMPTY;
13373
13374   /* deactivate shield (else Bang()/Explode() would not work right) */
13375   player->shield_normal_time_left = 0;
13376   player->shield_deadly_time_left = 0;
13377
13378 #if 0
13379   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13380          player->killed, player->active, player->reanimated);
13381 #endif
13382
13383   Bang(jx, jy);
13384
13385 #if 0
13386   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13387          player->killed, player->active, player->reanimated);
13388 #endif
13389
13390   if (player->reanimated)       /* killed player may have been reanimated */
13391     player->killed = player->reanimated = FALSE;
13392   else
13393     BuryPlayer(player);
13394 }
13395
13396 static void KillPlayerUnlessEnemyProtected(int x, int y)
13397 {
13398   if (!PLAYER_ENEMY_PROTECTED(x, y))
13399     KillPlayer(PLAYERINFO(x, y));
13400 }
13401
13402 static void KillPlayerUnlessExplosionProtected(int x, int y)
13403 {
13404   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13405     KillPlayer(PLAYERINFO(x, y));
13406 }
13407
13408 void BuryPlayer(struct PlayerInfo *player)
13409 {
13410   int jx = player->jx, jy = player->jy;
13411
13412   if (!player->active)
13413     return;
13414
13415   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13416   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13417
13418   player->GameOver = TRUE;
13419   RemovePlayer(player);
13420 }
13421
13422 void RemovePlayer(struct PlayerInfo *player)
13423 {
13424   int jx = player->jx, jy = player->jy;
13425   int i, found = FALSE;
13426
13427   player->present = FALSE;
13428   player->active = FALSE;
13429
13430   if (!ExplodeField[jx][jy])
13431     StorePlayer[jx][jy] = 0;
13432
13433   if (player->is_moving)
13434     TEST_DrawLevelField(player->last_jx, player->last_jy);
13435
13436   for (i = 0; i < MAX_PLAYERS; i++)
13437     if (stored_player[i].active)
13438       found = TRUE;
13439
13440   if (!found)
13441     AllPlayersGone = TRUE;
13442
13443   ExitX = ZX = jx;
13444   ExitY = ZY = jy;
13445 }
13446
13447 static void setFieldForSnapping(int x, int y, int element, int direction)
13448 {
13449   struct ElementInfo *ei = &element_info[element];
13450   int direction_bit = MV_DIR_TO_BIT(direction);
13451   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13452   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13453                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13454
13455   Feld[x][y] = EL_ELEMENT_SNAPPING;
13456   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13457
13458   ResetGfxAnimation(x, y);
13459
13460   GfxElement[x][y] = element;
13461   GfxAction[x][y] = action;
13462   GfxDir[x][y] = direction;
13463   GfxFrame[x][y] = -1;
13464 }
13465
13466 /*
13467   =============================================================================
13468   checkDiagonalPushing()
13469   -----------------------------------------------------------------------------
13470   check if diagonal input device direction results in pushing of object
13471   (by checking if the alternative direction is walkable, diggable, ...)
13472   =============================================================================
13473 */
13474
13475 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13476                                     int x, int y, int real_dx, int real_dy)
13477 {
13478   int jx, jy, dx, dy, xx, yy;
13479
13480   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13481     return TRUE;
13482
13483   /* diagonal direction: check alternative direction */
13484   jx = player->jx;
13485   jy = player->jy;
13486   dx = x - jx;
13487   dy = y - jy;
13488   xx = jx + (dx == 0 ? real_dx : 0);
13489   yy = jy + (dy == 0 ? real_dy : 0);
13490
13491   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13492 }
13493
13494 /*
13495   =============================================================================
13496   DigField()
13497   -----------------------------------------------------------------------------
13498   x, y:                 field next to player (non-diagonal) to try to dig to
13499   real_dx, real_dy:     direction as read from input device (can be diagonal)
13500   =============================================================================
13501 */
13502
13503 static int DigField(struct PlayerInfo *player,
13504                     int oldx, int oldy, int x, int y,
13505                     int real_dx, int real_dy, int mode)
13506 {
13507   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13508   boolean player_was_pushing = player->is_pushing;
13509   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13510   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13511   int jx = oldx, jy = oldy;
13512   int dx = x - jx, dy = y - jy;
13513   int nextx = x + dx, nexty = y + dy;
13514   int move_direction = (dx == -1 ? MV_LEFT  :
13515                         dx == +1 ? MV_RIGHT :
13516                         dy == -1 ? MV_UP    :
13517                         dy == +1 ? MV_DOWN  : MV_NONE);
13518   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13519   int dig_side = MV_DIR_OPPOSITE(move_direction);
13520   int old_element = Feld[jx][jy];
13521   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13522   int collect_count;
13523
13524   if (is_player)                /* function can also be called by EL_PENGUIN */
13525   {
13526     if (player->MovPos == 0)
13527     {
13528       player->is_digging = FALSE;
13529       player->is_collecting = FALSE;
13530     }
13531
13532     if (player->MovPos == 0)    /* last pushing move finished */
13533       player->is_pushing = FALSE;
13534
13535     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13536     {
13537       player->is_switching = FALSE;
13538       player->push_delay = -1;
13539
13540       return MP_NO_ACTION;
13541     }
13542   }
13543
13544   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13545     old_element = Back[jx][jy];
13546
13547   /* in case of element dropped at player position, check background */
13548   else if (Back[jx][jy] != EL_EMPTY &&
13549            game.engine_version >= VERSION_IDENT(2,2,0,0))
13550     old_element = Back[jx][jy];
13551
13552   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13553     return MP_NO_ACTION;        /* field has no opening in this direction */
13554
13555   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13556     return MP_NO_ACTION;        /* field has no opening in this direction */
13557
13558   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13559   {
13560     SplashAcid(x, y);
13561
13562     Feld[jx][jy] = player->artwork_element;
13563     InitMovingField(jx, jy, MV_DOWN);
13564     Store[jx][jy] = EL_ACID;
13565     ContinueMoving(jx, jy);
13566     BuryPlayer(player);
13567
13568     return MP_DONT_RUN_INTO;
13569   }
13570
13571   if (player_can_move && DONT_RUN_INTO(element))
13572   {
13573     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13574
13575     return MP_DONT_RUN_INTO;
13576   }
13577
13578   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13579     return MP_NO_ACTION;
13580
13581   collect_count = element_info[element].collect_count_initial;
13582
13583   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13584     return MP_NO_ACTION;
13585
13586   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13587     player_can_move = player_can_move_or_snap;
13588
13589   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13590       game.engine_version >= VERSION_IDENT(2,2,0,0))
13591   {
13592     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13593                                player->index_bit, dig_side);
13594     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13595                                         player->index_bit, dig_side);
13596
13597     if (element == EL_DC_LANDMINE)
13598       Bang(x, y);
13599
13600     if (Feld[x][y] != element)          /* field changed by snapping */
13601       return MP_ACTION;
13602
13603     return MP_NO_ACTION;
13604   }
13605
13606   if (player->gravity && is_player && !player->is_auto_moving &&
13607       canFallDown(player) && move_direction != MV_DOWN &&
13608       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13609     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13610
13611   if (player_can_move &&
13612       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13613   {
13614     int sound_element = SND_ELEMENT(element);
13615     int sound_action = ACTION_WALKING;
13616
13617     if (IS_RND_GATE(element))
13618     {
13619       if (!player->key[RND_GATE_NR(element)])
13620         return MP_NO_ACTION;
13621     }
13622     else if (IS_RND_GATE_GRAY(element))
13623     {
13624       if (!player->key[RND_GATE_GRAY_NR(element)])
13625         return MP_NO_ACTION;
13626     }
13627     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13628     {
13629       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13630         return MP_NO_ACTION;
13631     }
13632     else if (element == EL_EXIT_OPEN ||
13633              element == EL_EM_EXIT_OPEN ||
13634              element == EL_EM_EXIT_OPENING ||
13635              element == EL_STEEL_EXIT_OPEN ||
13636              element == EL_EM_STEEL_EXIT_OPEN ||
13637              element == EL_EM_STEEL_EXIT_OPENING ||
13638              element == EL_SP_EXIT_OPEN ||
13639              element == EL_SP_EXIT_OPENING)
13640     {
13641       sound_action = ACTION_PASSING;    /* player is passing exit */
13642     }
13643     else if (element == EL_EMPTY)
13644     {
13645       sound_action = ACTION_MOVING;             /* nothing to walk on */
13646     }
13647
13648     /* play sound from background or player, whatever is available */
13649     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13650       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13651     else
13652       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13653   }
13654   else if (player_can_move &&
13655            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13656   {
13657     if (!ACCESS_FROM(element, opposite_direction))
13658       return MP_NO_ACTION;      /* field not accessible from this direction */
13659
13660     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13661       return MP_NO_ACTION;
13662
13663     if (IS_EM_GATE(element))
13664     {
13665       if (!player->key[EM_GATE_NR(element)])
13666         return MP_NO_ACTION;
13667     }
13668     else if (IS_EM_GATE_GRAY(element))
13669     {
13670       if (!player->key[EM_GATE_GRAY_NR(element)])
13671         return MP_NO_ACTION;
13672     }
13673     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13674     {
13675       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13676         return MP_NO_ACTION;
13677     }
13678     else if (IS_EMC_GATE(element))
13679     {
13680       if (!player->key[EMC_GATE_NR(element)])
13681         return MP_NO_ACTION;
13682     }
13683     else if (IS_EMC_GATE_GRAY(element))
13684     {
13685       if (!player->key[EMC_GATE_GRAY_NR(element)])
13686         return MP_NO_ACTION;
13687     }
13688     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13689     {
13690       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13691         return MP_NO_ACTION;
13692     }
13693     else if (element == EL_DC_GATE_WHITE ||
13694              element == EL_DC_GATE_WHITE_GRAY ||
13695              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13696     {
13697       if (player->num_white_keys == 0)
13698         return MP_NO_ACTION;
13699
13700       player->num_white_keys--;
13701     }
13702     else if (IS_SP_PORT(element))
13703     {
13704       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13705           element == EL_SP_GRAVITY_PORT_RIGHT ||
13706           element == EL_SP_GRAVITY_PORT_UP ||
13707           element == EL_SP_GRAVITY_PORT_DOWN)
13708         player->gravity = !player->gravity;
13709       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13710                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13711                element == EL_SP_GRAVITY_ON_PORT_UP ||
13712                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13713         player->gravity = TRUE;
13714       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13715                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13716                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13717                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13718         player->gravity = FALSE;
13719     }
13720
13721     /* automatically move to the next field with double speed */
13722     player->programmed_action = move_direction;
13723
13724     if (player->move_delay_reset_counter == 0)
13725     {
13726       player->move_delay_reset_counter = 2;     /* two double speed steps */
13727
13728       DOUBLE_PLAYER_SPEED(player);
13729     }
13730
13731     PlayLevelSoundAction(x, y, ACTION_PASSING);
13732   }
13733   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13734   {
13735     RemoveField(x, y);
13736
13737     if (mode != DF_SNAP)
13738     {
13739       GfxElement[x][y] = GFX_ELEMENT(element);
13740       player->is_digging = TRUE;
13741     }
13742
13743     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13744
13745     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13746                                         player->index_bit, dig_side);
13747
13748     if (mode == DF_SNAP)
13749     {
13750       if (level.block_snap_field)
13751         setFieldForSnapping(x, y, element, move_direction);
13752       else
13753         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13754
13755       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13756                                           player->index_bit, dig_side);
13757     }
13758   }
13759   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13760   {
13761     RemoveField(x, y);
13762
13763     if (is_player && mode != DF_SNAP)
13764     {
13765       GfxElement[x][y] = element;
13766       player->is_collecting = TRUE;
13767     }
13768
13769     if (element == EL_SPEED_PILL)
13770     {
13771       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13772     }
13773     else if (element == EL_EXTRA_TIME && level.time > 0)
13774     {
13775       TimeLeft += level.extra_time;
13776
13777       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13778
13779       DisplayGameControlValues();
13780     }
13781     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13782     {
13783       player->shield_normal_time_left += level.shield_normal_time;
13784       if (element == EL_SHIELD_DEADLY)
13785         player->shield_deadly_time_left += level.shield_deadly_time;
13786     }
13787     else if (element == EL_DYNAMITE ||
13788              element == EL_EM_DYNAMITE ||
13789              element == EL_SP_DISK_RED)
13790     {
13791       if (player->inventory_size < MAX_INVENTORY_SIZE)
13792         player->inventory_element[player->inventory_size++] = element;
13793
13794       DrawGameDoorValues();
13795     }
13796     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13797     {
13798       player->dynabomb_count++;
13799       player->dynabombs_left++;
13800     }
13801     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13802     {
13803       player->dynabomb_size++;
13804     }
13805     else if (element == EL_DYNABOMB_INCREASE_POWER)
13806     {
13807       player->dynabomb_xl = TRUE;
13808     }
13809     else if (IS_KEY(element))
13810     {
13811       player->key[KEY_NR(element)] = TRUE;
13812
13813       DrawGameDoorValues();
13814     }
13815     else if (element == EL_DC_KEY_WHITE)
13816     {
13817       player->num_white_keys++;
13818
13819       /* display white keys? */
13820       /* DrawGameDoorValues(); */
13821     }
13822     else if (IS_ENVELOPE(element))
13823     {
13824       player->show_envelope = element;
13825     }
13826     else if (element == EL_EMC_LENSES)
13827     {
13828       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13829
13830       RedrawAllInvisibleElementsForLenses();
13831     }
13832     else if (element == EL_EMC_MAGNIFIER)
13833     {
13834       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13835
13836       RedrawAllInvisibleElementsForMagnifier();
13837     }
13838     else if (IS_DROPPABLE(element) ||
13839              IS_THROWABLE(element))     /* can be collected and dropped */
13840     {
13841       int i;
13842
13843       if (collect_count == 0)
13844         player->inventory_infinite_element = element;
13845       else
13846         for (i = 0; i < collect_count; i++)
13847           if (player->inventory_size < MAX_INVENTORY_SIZE)
13848             player->inventory_element[player->inventory_size++] = element;
13849
13850       DrawGameDoorValues();
13851     }
13852     else if (collect_count > 0)
13853     {
13854       local_player->gems_still_needed -= collect_count;
13855       if (local_player->gems_still_needed < 0)
13856         local_player->gems_still_needed = 0;
13857
13858       game.snapshot.collected_item = TRUE;
13859
13860       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13861
13862       DisplayGameControlValues();
13863     }
13864
13865     RaiseScoreElement(element);
13866     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13867
13868     if (is_player)
13869       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13870                                           player->index_bit, dig_side);
13871
13872     if (mode == DF_SNAP)
13873     {
13874       if (level.block_snap_field)
13875         setFieldForSnapping(x, y, element, move_direction);
13876       else
13877         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13878
13879       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13880                                           player->index_bit, dig_side);
13881     }
13882   }
13883   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13884   {
13885     if (mode == DF_SNAP && element != EL_BD_ROCK)
13886       return MP_NO_ACTION;
13887
13888     if (CAN_FALL(element) && dy)
13889       return MP_NO_ACTION;
13890
13891     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13892         !(element == EL_SPRING && level.use_spring_bug))
13893       return MP_NO_ACTION;
13894
13895     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13896         ((move_direction & MV_VERTICAL &&
13897           ((element_info[element].move_pattern & MV_LEFT &&
13898             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13899            (element_info[element].move_pattern & MV_RIGHT &&
13900             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13901          (move_direction & MV_HORIZONTAL &&
13902           ((element_info[element].move_pattern & MV_UP &&
13903             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13904            (element_info[element].move_pattern & MV_DOWN &&
13905             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13906       return MP_NO_ACTION;
13907
13908     /* do not push elements already moving away faster than player */
13909     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13910         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13911       return MP_NO_ACTION;
13912
13913     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13914     {
13915       if (player->push_delay_value == -1 || !player_was_pushing)
13916         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13917     }
13918     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13919     {
13920       if (player->push_delay_value == -1)
13921         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13922     }
13923     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13924     {
13925       if (!player->is_pushing)
13926         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13927     }
13928
13929     player->is_pushing = TRUE;
13930     player->is_active = TRUE;
13931
13932     if (!(IN_LEV_FIELD(nextx, nexty) &&
13933           (IS_FREE(nextx, nexty) ||
13934            (IS_SB_ELEMENT(element) &&
13935             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13936            (IS_CUSTOM_ELEMENT(element) &&
13937             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13938       return MP_NO_ACTION;
13939
13940     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13941       return MP_NO_ACTION;
13942
13943     if (player->push_delay == -1)       /* new pushing; restart delay */
13944       player->push_delay = 0;
13945
13946     if (player->push_delay < player->push_delay_value &&
13947         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13948         element != EL_SPRING && element != EL_BALLOON)
13949     {
13950       /* make sure that there is no move delay before next try to push */
13951       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13952         player->move_delay = 0;
13953
13954       return MP_NO_ACTION;
13955     }
13956
13957     if (IS_CUSTOM_ELEMENT(element) &&
13958         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13959     {
13960       if (!DigFieldByCE(nextx, nexty, element))
13961         return MP_NO_ACTION;
13962     }
13963
13964     if (IS_SB_ELEMENT(element))
13965     {
13966       if (element == EL_SOKOBAN_FIELD_FULL)
13967       {
13968         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13969         local_player->sokobanfields_still_needed++;
13970       }
13971
13972       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13973       {
13974         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13975         local_player->sokobanfields_still_needed--;
13976       }
13977
13978       Feld[x][y] = EL_SOKOBAN_OBJECT;
13979
13980       if (Back[x][y] == Back[nextx][nexty])
13981         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13982       else if (Back[x][y] != 0)
13983         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13984                                     ACTION_EMPTYING);
13985       else
13986         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13987                                     ACTION_FILLING);
13988
13989       if (local_player->sokobanfields_still_needed == 0 &&
13990           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13991       {
13992         PlayerWins(player);
13993
13994         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13995       }
13996     }
13997     else
13998       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13999
14000     InitMovingField(x, y, move_direction);
14001     GfxAction[x][y] = ACTION_PUSHING;
14002
14003     if (mode == DF_SNAP)
14004       ContinueMoving(x, y);
14005     else
14006       MovPos[x][y] = (dx != 0 ? dx : dy);
14007
14008     Pushed[x][y] = TRUE;
14009     Pushed[nextx][nexty] = TRUE;
14010
14011     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14012       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14013     else
14014       player->push_delay_value = -1;    /* get new value later */
14015
14016     /* check for element change _after_ element has been pushed */
14017     if (game.use_change_when_pushing_bug)
14018     {
14019       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14020                                  player->index_bit, dig_side);
14021       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14022                                           player->index_bit, dig_side);
14023     }
14024   }
14025   else if (IS_SWITCHABLE(element))
14026   {
14027     if (PLAYER_SWITCHING(player, x, y))
14028     {
14029       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14030                                           player->index_bit, dig_side);
14031
14032       return MP_ACTION;
14033     }
14034
14035     player->is_switching = TRUE;
14036     player->switch_x = x;
14037     player->switch_y = y;
14038
14039     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14040
14041     if (element == EL_ROBOT_WHEEL)
14042     {
14043       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14044       ZX = x;
14045       ZY = y;
14046
14047       game.robot_wheel_active = TRUE;
14048
14049       TEST_DrawLevelField(x, y);
14050     }
14051     else if (element == EL_SP_TERMINAL)
14052     {
14053       int xx, yy;
14054
14055       SCAN_PLAYFIELD(xx, yy)
14056       {
14057         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14058         {
14059           Bang(xx, yy);
14060         }
14061         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14062         {
14063           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14064
14065           ResetGfxAnimation(xx, yy);
14066           TEST_DrawLevelField(xx, yy);
14067         }
14068       }
14069     }
14070     else if (IS_BELT_SWITCH(element))
14071     {
14072       ToggleBeltSwitch(x, y);
14073     }
14074     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14075              element == EL_SWITCHGATE_SWITCH_DOWN ||
14076              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14077              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14078     {
14079       ToggleSwitchgateSwitch(x, y);
14080     }
14081     else if (element == EL_LIGHT_SWITCH ||
14082              element == EL_LIGHT_SWITCH_ACTIVE)
14083     {
14084       ToggleLightSwitch(x, y);
14085     }
14086     else if (element == EL_TIMEGATE_SWITCH ||
14087              element == EL_DC_TIMEGATE_SWITCH)
14088     {
14089       ActivateTimegateSwitch(x, y);
14090     }
14091     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14092              element == EL_BALLOON_SWITCH_RIGHT ||
14093              element == EL_BALLOON_SWITCH_UP    ||
14094              element == EL_BALLOON_SWITCH_DOWN  ||
14095              element == EL_BALLOON_SWITCH_NONE  ||
14096              element == EL_BALLOON_SWITCH_ANY)
14097     {
14098       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14099                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14100                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14101                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14102                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14103                              move_direction);
14104     }
14105     else if (element == EL_LAMP)
14106     {
14107       Feld[x][y] = EL_LAMP_ACTIVE;
14108       local_player->lights_still_needed--;
14109
14110       ResetGfxAnimation(x, y);
14111       TEST_DrawLevelField(x, y);
14112     }
14113     else if (element == EL_TIME_ORB_FULL)
14114     {
14115       Feld[x][y] = EL_TIME_ORB_EMPTY;
14116
14117       if (level.time > 0 || level.use_time_orb_bug)
14118       {
14119         TimeLeft += level.time_orb_time;
14120         game.no_time_limit = FALSE;
14121
14122         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14123
14124         DisplayGameControlValues();
14125       }
14126
14127       ResetGfxAnimation(x, y);
14128       TEST_DrawLevelField(x, y);
14129     }
14130     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14131              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14132     {
14133       int xx, yy;
14134
14135       game.ball_state = !game.ball_state;
14136
14137       SCAN_PLAYFIELD(xx, yy)
14138       {
14139         int e = Feld[xx][yy];
14140
14141         if (game.ball_state)
14142         {
14143           if (e == EL_EMC_MAGIC_BALL)
14144             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14145           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14146             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14147         }
14148         else
14149         {
14150           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14151             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14152           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14153             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14154         }
14155       }
14156     }
14157
14158     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14159                                         player->index_bit, dig_side);
14160
14161     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14162                                         player->index_bit, dig_side);
14163
14164     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14165                                         player->index_bit, dig_side);
14166
14167     return MP_ACTION;
14168   }
14169   else
14170   {
14171     if (!PLAYER_SWITCHING(player, x, y))
14172     {
14173       player->is_switching = TRUE;
14174       player->switch_x = x;
14175       player->switch_y = y;
14176
14177       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14178                                  player->index_bit, dig_side);
14179       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14180                                           player->index_bit, dig_side);
14181
14182       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14183                                  player->index_bit, dig_side);
14184       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14185                                           player->index_bit, dig_side);
14186     }
14187
14188     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14189                                player->index_bit, dig_side);
14190     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14191                                         player->index_bit, dig_side);
14192
14193     return MP_NO_ACTION;
14194   }
14195
14196   player->push_delay = -1;
14197
14198   if (is_player)                /* function can also be called by EL_PENGUIN */
14199   {
14200     if (Feld[x][y] != element)          /* really digged/collected something */
14201     {
14202       player->is_collecting = !player->is_digging;
14203       player->is_active = TRUE;
14204     }
14205   }
14206
14207   return MP_MOVING;
14208 }
14209
14210 static boolean DigFieldByCE(int x, int y, int digging_element)
14211 {
14212   int element = Feld[x][y];
14213
14214   if (!IS_FREE(x, y))
14215   {
14216     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14217                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14218                   ACTION_BREAKING);
14219
14220     /* no element can dig solid indestructible elements */
14221     if (IS_INDESTRUCTIBLE(element) &&
14222         !IS_DIGGABLE(element) &&
14223         !IS_COLLECTIBLE(element))
14224       return FALSE;
14225
14226     if (AmoebaNr[x][y] &&
14227         (element == EL_AMOEBA_FULL ||
14228          element == EL_BD_AMOEBA ||
14229          element == EL_AMOEBA_GROWING))
14230     {
14231       AmoebaCnt[AmoebaNr[x][y]]--;
14232       AmoebaCnt2[AmoebaNr[x][y]]--;
14233     }
14234
14235     if (IS_MOVING(x, y))
14236       RemoveMovingField(x, y);
14237     else
14238     {
14239       RemoveField(x, y);
14240       TEST_DrawLevelField(x, y);
14241     }
14242
14243     /* if digged element was about to explode, prevent the explosion */
14244     ExplodeField[x][y] = EX_TYPE_NONE;
14245
14246     PlayLevelSoundAction(x, y, action);
14247   }
14248
14249   Store[x][y] = EL_EMPTY;
14250
14251   /* this makes it possible to leave the removed element again */
14252   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14253     Store[x][y] = element;
14254
14255   return TRUE;
14256 }
14257
14258 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14259 {
14260   int jx = player->jx, jy = player->jy;
14261   int x = jx + dx, y = jy + dy;
14262   int snap_direction = (dx == -1 ? MV_LEFT  :
14263                         dx == +1 ? MV_RIGHT :
14264                         dy == -1 ? MV_UP    :
14265                         dy == +1 ? MV_DOWN  : MV_NONE);
14266   boolean can_continue_snapping = (level.continuous_snapping &&
14267                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14268
14269   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14270     return FALSE;
14271
14272   if (!player->active || !IN_LEV_FIELD(x, y))
14273     return FALSE;
14274
14275   if (dx && dy)
14276     return FALSE;
14277
14278   if (!dx && !dy)
14279   {
14280     if (player->MovPos == 0)
14281       player->is_pushing = FALSE;
14282
14283     player->is_snapping = FALSE;
14284
14285     if (player->MovPos == 0)
14286     {
14287       player->is_moving = FALSE;
14288       player->is_digging = FALSE;
14289       player->is_collecting = FALSE;
14290     }
14291
14292     return FALSE;
14293   }
14294
14295   /* prevent snapping with already pressed snap key when not allowed */
14296   if (player->is_snapping && !can_continue_snapping)
14297     return FALSE;
14298
14299   player->MovDir = snap_direction;
14300
14301   if (player->MovPos == 0)
14302   {
14303     player->is_moving = FALSE;
14304     player->is_digging = FALSE;
14305     player->is_collecting = FALSE;
14306   }
14307
14308   player->is_dropping = FALSE;
14309   player->is_dropping_pressed = FALSE;
14310   player->drop_pressed_delay = 0;
14311
14312   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14313     return FALSE;
14314
14315   player->is_snapping = TRUE;
14316   player->is_active = TRUE;
14317
14318   if (player->MovPos == 0)
14319   {
14320     player->is_moving = FALSE;
14321     player->is_digging = FALSE;
14322     player->is_collecting = FALSE;
14323   }
14324
14325   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14326     TEST_DrawLevelField(player->last_jx, player->last_jy);
14327
14328   TEST_DrawLevelField(x, y);
14329
14330   return TRUE;
14331 }
14332
14333 static boolean DropElement(struct PlayerInfo *player)
14334 {
14335   int old_element, new_element;
14336   int dropx = player->jx, dropy = player->jy;
14337   int drop_direction = player->MovDir;
14338   int drop_side = drop_direction;
14339   int drop_element = get_next_dropped_element(player);
14340
14341   /* do not drop an element on top of another element; when holding drop key
14342      pressed without moving, dropped element must move away before the next
14343      element can be dropped (this is especially important if the next element
14344      is dynamite, which can be placed on background for historical reasons) */
14345   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14346     return MP_ACTION;
14347
14348   if (IS_THROWABLE(drop_element))
14349   {
14350     dropx += GET_DX_FROM_DIR(drop_direction);
14351     dropy += GET_DY_FROM_DIR(drop_direction);
14352
14353     if (!IN_LEV_FIELD(dropx, dropy))
14354       return FALSE;
14355   }
14356
14357   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14358   new_element = drop_element;           /* default: no change when dropping */
14359
14360   /* check if player is active, not moving and ready to drop */
14361   if (!player->active || player->MovPos || player->drop_delay > 0)
14362     return FALSE;
14363
14364   /* check if player has anything that can be dropped */
14365   if (new_element == EL_UNDEFINED)
14366     return FALSE;
14367
14368   /* only set if player has anything that can be dropped */
14369   player->is_dropping_pressed = TRUE;
14370
14371   /* check if drop key was pressed long enough for EM style dynamite */
14372   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14373     return FALSE;
14374
14375   /* check if anything can be dropped at the current position */
14376   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14377     return FALSE;
14378
14379   /* collected custom elements can only be dropped on empty fields */
14380   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14381     return FALSE;
14382
14383   if (old_element != EL_EMPTY)
14384     Back[dropx][dropy] = old_element;   /* store old element on this field */
14385
14386   ResetGfxAnimation(dropx, dropy);
14387   ResetRandomAnimationValue(dropx, dropy);
14388
14389   if (player->inventory_size > 0 ||
14390       player->inventory_infinite_element != EL_UNDEFINED)
14391   {
14392     if (player->inventory_size > 0)
14393     {
14394       player->inventory_size--;
14395
14396       DrawGameDoorValues();
14397
14398       if (new_element == EL_DYNAMITE)
14399         new_element = EL_DYNAMITE_ACTIVE;
14400       else if (new_element == EL_EM_DYNAMITE)
14401         new_element = EL_EM_DYNAMITE_ACTIVE;
14402       else if (new_element == EL_SP_DISK_RED)
14403         new_element = EL_SP_DISK_RED_ACTIVE;
14404     }
14405
14406     Feld[dropx][dropy] = new_element;
14407
14408     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14409       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14410                           el2img(Feld[dropx][dropy]), 0);
14411
14412     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14413
14414     /* needed if previous element just changed to "empty" in the last frame */
14415     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14416
14417     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14418                                player->index_bit, drop_side);
14419     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14420                                         CE_PLAYER_DROPS_X,
14421                                         player->index_bit, drop_side);
14422
14423     TestIfElementTouchesCustomElement(dropx, dropy);
14424   }
14425   else          /* player is dropping a dyna bomb */
14426   {
14427     player->dynabombs_left--;
14428
14429     Feld[dropx][dropy] = new_element;
14430
14431     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14432       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14433                           el2img(Feld[dropx][dropy]), 0);
14434
14435     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14436   }
14437
14438   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14439     InitField_WithBug1(dropx, dropy, FALSE);
14440
14441   new_element = Feld[dropx][dropy];     /* element might have changed */
14442
14443   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14444       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14445   {
14446     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14447       MovDir[dropx][dropy] = drop_direction;
14448
14449     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14450
14451     /* do not cause impact style collision by dropping elements that can fall */
14452     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14453   }
14454
14455   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14456   player->is_dropping = TRUE;
14457
14458   player->drop_pressed_delay = 0;
14459   player->is_dropping_pressed = FALSE;
14460
14461   player->drop_x = dropx;
14462   player->drop_y = dropy;
14463
14464   return TRUE;
14465 }
14466
14467 /* ------------------------------------------------------------------------- */
14468 /* game sound playing functions                                              */
14469 /* ------------------------------------------------------------------------- */
14470
14471 static int *loop_sound_frame = NULL;
14472 static int *loop_sound_volume = NULL;
14473
14474 void InitPlayLevelSound()
14475 {
14476   int num_sounds = getSoundListSize();
14477
14478   checked_free(loop_sound_frame);
14479   checked_free(loop_sound_volume);
14480
14481   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14482   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14483 }
14484
14485 static void PlayLevelSound(int x, int y, int nr)
14486 {
14487   int sx = SCREENX(x), sy = SCREENY(y);
14488   int volume, stereo_position;
14489   int max_distance = 8;
14490   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14491
14492   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14493       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14494     return;
14495
14496   if (!IN_LEV_FIELD(x, y) ||
14497       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14498       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14499     return;
14500
14501   volume = SOUND_MAX_VOLUME;
14502
14503   if (!IN_SCR_FIELD(sx, sy))
14504   {
14505     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14506     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14507
14508     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14509   }
14510
14511   stereo_position = (SOUND_MAX_LEFT +
14512                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14513                      (SCR_FIELDX + 2 * max_distance));
14514
14515   if (IS_LOOP_SOUND(nr))
14516   {
14517     /* This assures that quieter loop sounds do not overwrite louder ones,
14518        while restarting sound volume comparison with each new game frame. */
14519
14520     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14521       return;
14522
14523     loop_sound_volume[nr] = volume;
14524     loop_sound_frame[nr] = FrameCounter;
14525   }
14526
14527   PlaySoundExt(nr, volume, stereo_position, type);
14528 }
14529
14530 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14531 {
14532   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14533                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14534                  y < LEVELY(BY1) ? LEVELY(BY1) :
14535                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14536                  sound_action);
14537 }
14538
14539 static void PlayLevelSoundAction(int x, int y, int action)
14540 {
14541   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14542 }
14543
14544 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14545 {
14546   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14547
14548   if (sound_effect != SND_UNDEFINED)
14549     PlayLevelSound(x, y, sound_effect);
14550 }
14551
14552 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14553                                               int action)
14554 {
14555   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14556
14557   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14558     PlayLevelSound(x, y, sound_effect);
14559 }
14560
14561 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14562 {
14563   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14564
14565   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14566     PlayLevelSound(x, y, sound_effect);
14567 }
14568
14569 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14570 {
14571   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14572
14573   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14574     StopSound(sound_effect);
14575 }
14576
14577 static int getLevelMusicNr()
14578 {
14579   if (levelset.music[level_nr] != MUS_UNDEFINED)
14580     return levelset.music[level_nr];            /* from config file */
14581   else
14582     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14583 }
14584
14585 static void FadeLevelSounds()
14586 {
14587   FadeSounds();
14588 }
14589
14590 static void FadeLevelMusic()
14591 {
14592   int music_nr = getLevelMusicNr();
14593   char *curr_music = getCurrentlyPlayingMusicFilename();
14594   char *next_music = getMusicInfoEntryFilename(music_nr);
14595
14596   if (!strEqual(curr_music, next_music))
14597     FadeMusic();
14598 }
14599
14600 void FadeLevelSoundsAndMusic()
14601 {
14602   FadeLevelSounds();
14603   FadeLevelMusic();
14604 }
14605
14606 static void PlayLevelMusic()
14607 {
14608   int music_nr = getLevelMusicNr();
14609   char *curr_music = getCurrentlyPlayingMusicFilename();
14610   char *next_music = getMusicInfoEntryFilename(music_nr);
14611
14612   if (!strEqual(curr_music, next_music))
14613     PlayMusic(music_nr);
14614 }
14615
14616 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14617 {
14618   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14619   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14620   int x = xx - 1 - offset;
14621   int y = yy - 1 - offset;
14622
14623   switch (sample)
14624   {
14625     case SAMPLE_blank:
14626       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14627       break;
14628
14629     case SAMPLE_roll:
14630       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14631       break;
14632
14633     case SAMPLE_stone:
14634       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14635       break;
14636
14637     case SAMPLE_nut:
14638       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14639       break;
14640
14641     case SAMPLE_crack:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14643       break;
14644
14645     case SAMPLE_bug:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14647       break;
14648
14649     case SAMPLE_tank:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14651       break;
14652
14653     case SAMPLE_android_clone:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14655       break;
14656
14657     case SAMPLE_android_move:
14658       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14659       break;
14660
14661     case SAMPLE_spring:
14662       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14663       break;
14664
14665     case SAMPLE_slurp:
14666       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14667       break;
14668
14669     case SAMPLE_eater:
14670       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14671       break;
14672
14673     case SAMPLE_eater_eat:
14674       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14675       break;
14676
14677     case SAMPLE_alien:
14678       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14679       break;
14680
14681     case SAMPLE_collect:
14682       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14683       break;
14684
14685     case SAMPLE_diamond:
14686       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14687       break;
14688
14689     case SAMPLE_squash:
14690       /* !!! CHECK THIS !!! */
14691 #if 1
14692       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14693 #else
14694       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14695 #endif
14696       break;
14697
14698     case SAMPLE_wonderfall:
14699       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14700       break;
14701
14702     case SAMPLE_drip:
14703       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14704       break;
14705
14706     case SAMPLE_push:
14707       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14708       break;
14709
14710     case SAMPLE_dirt:
14711       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14712       break;
14713
14714     case SAMPLE_acid:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14716       break;
14717
14718     case SAMPLE_ball:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14720       break;
14721
14722     case SAMPLE_grow:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14724       break;
14725
14726     case SAMPLE_wonder:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14728       break;
14729
14730     case SAMPLE_door:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14732       break;
14733
14734     case SAMPLE_exit_open:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14736       break;
14737
14738     case SAMPLE_exit_leave:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14740       break;
14741
14742     case SAMPLE_dynamite:
14743       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14744       break;
14745
14746     case SAMPLE_tick:
14747       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14748       break;
14749
14750     case SAMPLE_press:
14751       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14752       break;
14753
14754     case SAMPLE_wheel:
14755       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14756       break;
14757
14758     case SAMPLE_boom:
14759       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14760       break;
14761
14762     case SAMPLE_die:
14763       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14764       break;
14765
14766     case SAMPLE_time:
14767       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14768       break;
14769
14770     default:
14771       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14772       break;
14773   }
14774 }
14775
14776 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14777 {
14778   int element = map_element_SP_to_RND(element_sp);
14779   int action = map_action_SP_to_RND(action_sp);
14780   int offset = (setup.sp_show_border_elements ? 0 : 1);
14781   int x = xx - offset;
14782   int y = yy - offset;
14783
14784   PlayLevelSoundElementAction(x, y, element, action);
14785 }
14786
14787 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14788 {
14789   int element = map_element_MM_to_RND(element_mm);
14790   int action = map_action_MM_to_RND(action_mm);
14791   int offset = 0;
14792   int x = xx - offset;
14793   int y = yy - offset;
14794
14795   if (!IS_MM_ELEMENT(element))
14796     element = EL_MM_DEFAULT;
14797
14798   PlayLevelSoundElementAction(x, y, element, action);
14799 }
14800
14801 void PlaySound_MM(int sound_mm)
14802 {
14803   int sound = map_sound_MM_to_RND(sound_mm);
14804
14805   if (sound == SND_UNDEFINED)
14806     return;
14807
14808   PlaySound(sound);
14809 }
14810
14811 void PlaySoundLoop_MM(int sound_mm)
14812 {
14813   int sound = map_sound_MM_to_RND(sound_mm);
14814
14815   if (sound == SND_UNDEFINED)
14816     return;
14817
14818   PlaySoundLoop(sound);
14819 }
14820
14821 void StopSound_MM(int sound_mm)
14822 {
14823   int sound = map_sound_MM_to_RND(sound_mm);
14824
14825   if (sound == SND_UNDEFINED)
14826     return;
14827
14828   StopSound(sound);
14829 }
14830
14831 void RaiseScore(int value)
14832 {
14833   local_player->score += value;
14834
14835   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14836
14837   DisplayGameControlValues();
14838 }
14839
14840 void RaiseScoreElement(int element)
14841 {
14842   switch (element)
14843   {
14844     case EL_EMERALD:
14845     case EL_BD_DIAMOND:
14846     case EL_EMERALD_YELLOW:
14847     case EL_EMERALD_RED:
14848     case EL_EMERALD_PURPLE:
14849     case EL_SP_INFOTRON:
14850       RaiseScore(level.score[SC_EMERALD]);
14851       break;
14852     case EL_DIAMOND:
14853       RaiseScore(level.score[SC_DIAMOND]);
14854       break;
14855     case EL_CRYSTAL:
14856       RaiseScore(level.score[SC_CRYSTAL]);
14857       break;
14858     case EL_PEARL:
14859       RaiseScore(level.score[SC_PEARL]);
14860       break;
14861     case EL_BUG:
14862     case EL_BD_BUTTERFLY:
14863     case EL_SP_ELECTRON:
14864       RaiseScore(level.score[SC_BUG]);
14865       break;
14866     case EL_SPACESHIP:
14867     case EL_BD_FIREFLY:
14868     case EL_SP_SNIKSNAK:
14869       RaiseScore(level.score[SC_SPACESHIP]);
14870       break;
14871     case EL_YAMYAM:
14872     case EL_DARK_YAMYAM:
14873       RaiseScore(level.score[SC_YAMYAM]);
14874       break;
14875     case EL_ROBOT:
14876       RaiseScore(level.score[SC_ROBOT]);
14877       break;
14878     case EL_PACMAN:
14879       RaiseScore(level.score[SC_PACMAN]);
14880       break;
14881     case EL_NUT:
14882       RaiseScore(level.score[SC_NUT]);
14883       break;
14884     case EL_DYNAMITE:
14885     case EL_EM_DYNAMITE:
14886     case EL_SP_DISK_RED:
14887     case EL_DYNABOMB_INCREASE_NUMBER:
14888     case EL_DYNABOMB_INCREASE_SIZE:
14889     case EL_DYNABOMB_INCREASE_POWER:
14890       RaiseScore(level.score[SC_DYNAMITE]);
14891       break;
14892     case EL_SHIELD_NORMAL:
14893     case EL_SHIELD_DEADLY:
14894       RaiseScore(level.score[SC_SHIELD]);
14895       break;
14896     case EL_EXTRA_TIME:
14897       RaiseScore(level.extra_time_score);
14898       break;
14899     case EL_KEY_1:
14900     case EL_KEY_2:
14901     case EL_KEY_3:
14902     case EL_KEY_4:
14903     case EL_EM_KEY_1:
14904     case EL_EM_KEY_2:
14905     case EL_EM_KEY_3:
14906     case EL_EM_KEY_4:
14907     case EL_EMC_KEY_5:
14908     case EL_EMC_KEY_6:
14909     case EL_EMC_KEY_7:
14910     case EL_EMC_KEY_8:
14911     case EL_DC_KEY_WHITE:
14912       RaiseScore(level.score[SC_KEY]);
14913       break;
14914     default:
14915       RaiseScore(element_info[element].collect_score);
14916       break;
14917   }
14918 }
14919
14920 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14921 {
14922   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14923   {
14924     /* closing door required in case of envelope style request dialogs */
14925     if (!skip_request)
14926       CloseDoor(DOOR_CLOSE_1);
14927
14928 #if defined(NETWORK_AVALIABLE)
14929     if (options.network)
14930       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14931     else
14932 #endif
14933     {
14934       if (quick_quit)
14935         FadeSkipNextFadeIn();
14936
14937       SetGameStatus(GAME_MODE_MAIN);
14938
14939       DrawMainMenu();
14940     }
14941   }
14942   else          /* continue playing the game */
14943   {
14944     if (tape.playing && tape.deactivate_display)
14945       TapeDeactivateDisplayOff(TRUE);
14946
14947     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14948
14949     if (tape.playing && tape.deactivate_display)
14950       TapeDeactivateDisplayOn();
14951   }
14952 }
14953
14954 void RequestQuitGame(boolean ask_if_really_quit)
14955 {
14956   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14957   boolean skip_request = AllPlayersGone || quick_quit;
14958
14959   RequestQuitGameExt(skip_request, quick_quit,
14960                      "Do you really want to quit the game?");
14961 }
14962
14963 void RequestRestartGame(char *message)
14964 {
14965   game.restart_game_message = NULL;
14966
14967   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14968   {
14969     StartGameActions(options.network, setup.autorecord, level.random_seed);
14970   }
14971   else
14972   {
14973     SetGameStatus(GAME_MODE_MAIN);
14974
14975     DrawMainMenu();
14976   }
14977 }
14978
14979
14980 /* ------------------------------------------------------------------------- */
14981 /* random generator functions                                                */
14982 /* ------------------------------------------------------------------------- */
14983
14984 unsigned int InitEngineRandom_RND(int seed)
14985 {
14986   game.num_random_calls = 0;
14987
14988   return InitEngineRandom(seed);
14989 }
14990
14991 unsigned int RND(int max)
14992 {
14993   if (max > 0)
14994   {
14995     game.num_random_calls++;
14996
14997     return GetEngineRandom(max);
14998   }
14999
15000   return 0;
15001 }
15002
15003
15004 /* ------------------------------------------------------------------------- */
15005 /* game engine snapshot handling functions                                   */
15006 /* ------------------------------------------------------------------------- */
15007
15008 struct EngineSnapshotInfo
15009 {
15010   /* runtime values for custom element collect score */
15011   int collect_score[NUM_CUSTOM_ELEMENTS];
15012
15013   /* runtime values for group element choice position */
15014   int choice_pos[NUM_GROUP_ELEMENTS];
15015
15016   /* runtime values for belt position animations */
15017   int belt_graphic[4][NUM_BELT_PARTS];
15018   int belt_anim_mode[4][NUM_BELT_PARTS];
15019 };
15020
15021 static struct EngineSnapshotInfo engine_snapshot_rnd;
15022 static char *snapshot_level_identifier = NULL;
15023 static int snapshot_level_nr = -1;
15024
15025 static void SaveEngineSnapshotValues_RND()
15026 {
15027   static int belt_base_active_element[4] =
15028   {
15029     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15030     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15031     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15032     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15033   };
15034   int i, j;
15035
15036   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15037   {
15038     int element = EL_CUSTOM_START + i;
15039
15040     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15041   }
15042
15043   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15044   {
15045     int element = EL_GROUP_START + i;
15046
15047     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15048   }
15049
15050   for (i = 0; i < 4; i++)
15051   {
15052     for (j = 0; j < NUM_BELT_PARTS; j++)
15053     {
15054       int element = belt_base_active_element[i] + j;
15055       int graphic = el2img(element);
15056       int anim_mode = graphic_info[graphic].anim_mode;
15057
15058       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15059       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15060     }
15061   }
15062 }
15063
15064 static void LoadEngineSnapshotValues_RND()
15065 {
15066   unsigned int num_random_calls = game.num_random_calls;
15067   int i, j;
15068
15069   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15070   {
15071     int element = EL_CUSTOM_START + i;
15072
15073     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15074   }
15075
15076   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15077   {
15078     int element = EL_GROUP_START + i;
15079
15080     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15081   }
15082
15083   for (i = 0; i < 4; i++)
15084   {
15085     for (j = 0; j < NUM_BELT_PARTS; j++)
15086     {
15087       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15088       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15089
15090       graphic_info[graphic].anim_mode = anim_mode;
15091     }
15092   }
15093
15094   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15095   {
15096     InitRND(tape.random_seed);
15097     for (i = 0; i < num_random_calls; i++)
15098       RND(1);
15099   }
15100
15101   if (game.num_random_calls != num_random_calls)
15102   {
15103     Error(ERR_INFO, "number of random calls out of sync");
15104     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15105     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15106     Error(ERR_EXIT, "this should not happen -- please debug");
15107   }
15108 }
15109
15110 void FreeEngineSnapshotSingle()
15111 {
15112   FreeSnapshotSingle();
15113
15114   setString(&snapshot_level_identifier, NULL);
15115   snapshot_level_nr = -1;
15116 }
15117
15118 void FreeEngineSnapshotList()
15119 {
15120   FreeSnapshotList();
15121 }
15122
15123 ListNode *SaveEngineSnapshotBuffers()
15124 {
15125   ListNode *buffers = NULL;
15126
15127   /* copy some special values to a structure better suited for the snapshot */
15128
15129   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15130     SaveEngineSnapshotValues_RND();
15131   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15132     SaveEngineSnapshotValues_EM();
15133   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15134     SaveEngineSnapshotValues_SP(&buffers);
15135   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15136     SaveEngineSnapshotValues_MM(&buffers);
15137
15138   /* save values stored in special snapshot structure */
15139
15140   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15141     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15142   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15143     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15144   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15145     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15146   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15147     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15148
15149   /* save further RND engine values */
15150
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15154
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15159
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15164   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15165
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15169
15170   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15171
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15173
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15176
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15181   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15183   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15186   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15187   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15192   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15195
15196   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15197   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15198
15199   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15200   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15201   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15202
15203   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15204   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15205
15206   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15207   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15208   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15209   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15210   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15211
15212   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15213   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15214
15215 #if 0
15216   ListNode *node = engine_snapshot_list_rnd;
15217   int num_bytes = 0;
15218
15219   while (node != NULL)
15220   {
15221     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15222
15223     node = node->next;
15224   }
15225
15226   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15227 #endif
15228
15229   return buffers;
15230 }
15231
15232 void SaveEngineSnapshotSingle()
15233 {
15234   ListNode *buffers = SaveEngineSnapshotBuffers();
15235
15236   /* finally save all snapshot buffers to single snapshot */
15237   SaveSnapshotSingle(buffers);
15238
15239   /* save level identification information */
15240   setString(&snapshot_level_identifier, leveldir_current->identifier);
15241   snapshot_level_nr = level_nr;
15242 }
15243
15244 boolean CheckSaveEngineSnapshotToList()
15245 {
15246   boolean save_snapshot =
15247     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15248      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15249       game.snapshot.changed_action) ||
15250      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15251       game.snapshot.collected_item));
15252
15253   game.snapshot.changed_action = FALSE;
15254   game.snapshot.collected_item = FALSE;
15255   game.snapshot.save_snapshot = save_snapshot;
15256
15257   return save_snapshot;
15258 }
15259
15260 void SaveEngineSnapshotToList()
15261 {
15262   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15263       tape.quick_resume)
15264     return;
15265
15266   ListNode *buffers = SaveEngineSnapshotBuffers();
15267
15268   /* finally save all snapshot buffers to snapshot list */
15269   SaveSnapshotToList(buffers);
15270 }
15271
15272 void SaveEngineSnapshotToListInitial()
15273 {
15274   FreeEngineSnapshotList();
15275
15276   SaveEngineSnapshotToList();
15277 }
15278
15279 void LoadEngineSnapshotValues()
15280 {
15281   /* restore special values from snapshot structure */
15282
15283   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15284     LoadEngineSnapshotValues_RND();
15285   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15286     LoadEngineSnapshotValues_EM();
15287   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15288     LoadEngineSnapshotValues_SP();
15289   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15290     LoadEngineSnapshotValues_MM();
15291 }
15292
15293 void LoadEngineSnapshotSingle()
15294 {
15295   LoadSnapshotSingle();
15296
15297   LoadEngineSnapshotValues();
15298 }
15299
15300 void LoadEngineSnapshot_Undo(int steps)
15301 {
15302   LoadSnapshotFromList_Older(steps);
15303
15304   LoadEngineSnapshotValues();
15305 }
15306
15307 void LoadEngineSnapshot_Redo(int steps)
15308 {
15309   LoadSnapshotFromList_Newer(steps);
15310
15311   LoadEngineSnapshotValues();
15312 }
15313
15314 boolean CheckEngineSnapshotSingle()
15315 {
15316   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15317           snapshot_level_nr == level_nr);
15318 }
15319
15320 boolean CheckEngineSnapshotList()
15321 {
15322   return CheckSnapshotList();
15323 }
15324
15325
15326 /* ---------- new game button stuff ---------------------------------------- */
15327
15328 static struct
15329 {
15330   int graphic;
15331   struct XY *pos;
15332   int gadget_id;
15333   boolean *setup_value;
15334   boolean allowed_on_tape;
15335   char *infotext;
15336 } gamebutton_info[NUM_GAME_BUTTONS] =
15337 {
15338   {
15339     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15340     GAME_CTRL_ID_STOP,                          NULL,
15341     TRUE,                                       "stop game"
15342   },
15343   {
15344     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15345     GAME_CTRL_ID_PAUSE,                         NULL,
15346     TRUE,                                       "pause game"
15347   },
15348   {
15349     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15350     GAME_CTRL_ID_PLAY,                          NULL,
15351     TRUE,                                       "play game"
15352   },
15353   {
15354     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15355     GAME_CTRL_ID_UNDO,                          NULL,
15356     TRUE,                                       "undo step"
15357   },
15358   {
15359     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15360     GAME_CTRL_ID_REDO,                          NULL,
15361     TRUE,                                       "redo step"
15362   },
15363   {
15364     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15365     GAME_CTRL_ID_SAVE,                          NULL,
15366     TRUE,                                       "save game"
15367   },
15368   {
15369     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15370     GAME_CTRL_ID_PAUSE2,                        NULL,
15371     TRUE,                                       "pause game"
15372   },
15373   {
15374     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15375     GAME_CTRL_ID_LOAD,                          NULL,
15376     TRUE,                                       "load game"
15377   },
15378   {
15379     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15380     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15381     FALSE,                                      "stop game"
15382   },
15383   {
15384     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15385     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15386     FALSE,                                      "pause game"
15387   },
15388   {
15389     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15390     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15391     FALSE,                                      "play game"
15392   },
15393   {
15394     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15395     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15396     TRUE,                                       "background music on/off"
15397   },
15398   {
15399     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15400     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15401     TRUE,                                       "sound loops on/off"
15402   },
15403   {
15404     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15405     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15406     TRUE,                                       "normal sounds on/off"
15407   },
15408   {
15409     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15410     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15411     FALSE,                                      "background music on/off"
15412   },
15413   {
15414     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15415     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15416     FALSE,                                      "sound loops on/off"
15417   },
15418   {
15419     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15420     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15421     FALSE,                                      "normal sounds on/off"
15422   }
15423 };
15424
15425 void CreateGameButtons()
15426 {
15427   int i;
15428
15429   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15430   {
15431     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15432     struct XY *pos = gamebutton_info[i].pos;
15433     struct GadgetInfo *gi;
15434     int button_type;
15435     boolean checked;
15436     unsigned int event_mask;
15437     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15438     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15439     int base_x = (on_tape ? VX : DX);
15440     int base_y = (on_tape ? VY : DY);
15441     int gd_x   = gfx->src_x;
15442     int gd_y   = gfx->src_y;
15443     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15444     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15445     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15446     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15447     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15448     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15449     int id = i;
15450
15451     if (gfx->bitmap == NULL)
15452     {
15453       game_gadget[id] = NULL;
15454
15455       continue;
15456     }
15457
15458     if (id == GAME_CTRL_ID_STOP ||
15459         id == GAME_CTRL_ID_PANEL_STOP ||
15460         id == GAME_CTRL_ID_PLAY ||
15461         id == GAME_CTRL_ID_PANEL_PLAY ||
15462         id == GAME_CTRL_ID_SAVE ||
15463         id == GAME_CTRL_ID_LOAD)
15464     {
15465       button_type = GD_TYPE_NORMAL_BUTTON;
15466       checked = FALSE;
15467       event_mask = GD_EVENT_RELEASED;
15468     }
15469     else if (id == GAME_CTRL_ID_UNDO ||
15470              id == GAME_CTRL_ID_REDO)
15471     {
15472       button_type = GD_TYPE_NORMAL_BUTTON;
15473       checked = FALSE;
15474       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15475     }
15476     else
15477     {
15478       button_type = GD_TYPE_CHECK_BUTTON;
15479       checked = (gamebutton_info[i].setup_value != NULL ?
15480                  *gamebutton_info[i].setup_value : FALSE);
15481       event_mask = GD_EVENT_PRESSED;
15482     }
15483
15484     gi = CreateGadget(GDI_CUSTOM_ID, id,
15485                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15486                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15487                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15488                       GDI_WIDTH, gfx->width,
15489                       GDI_HEIGHT, gfx->height,
15490                       GDI_TYPE, button_type,
15491                       GDI_STATE, GD_BUTTON_UNPRESSED,
15492                       GDI_CHECKED, checked,
15493                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15494                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15495                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15496                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15497                       GDI_DIRECT_DRAW, FALSE,
15498                       GDI_EVENT_MASK, event_mask,
15499                       GDI_CALLBACK_ACTION, HandleGameButtons,
15500                       GDI_END);
15501
15502     if (gi == NULL)
15503       Error(ERR_EXIT, "cannot create gadget");
15504
15505     game_gadget[id] = gi;
15506   }
15507 }
15508
15509 void FreeGameButtons()
15510 {
15511   int i;
15512
15513   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15514     FreeGadget(game_gadget[i]);
15515 }
15516
15517 static void UnmapGameButtonsAtSamePosition(int id)
15518 {
15519   int i;
15520
15521   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15522     if (i != id &&
15523         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15524         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15525       UnmapGadget(game_gadget[i]);
15526 }
15527
15528 static void UnmapGameButtonsAtSamePosition_All()
15529 {
15530   if (setup.show_snapshot_buttons)
15531   {
15532     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15533     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15534     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15535   }
15536   else
15537   {
15538     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15539     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15540     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15541
15542     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15543     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15544     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15545   }
15546 }
15547
15548 static void MapGameButtonsAtSamePosition(int id)
15549 {
15550   int i;
15551
15552   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15553     if (i != id &&
15554         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15555         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15556       MapGadget(game_gadget[i]);
15557
15558   UnmapGameButtonsAtSamePosition_All();
15559 }
15560
15561 void MapUndoRedoButtons()
15562 {
15563   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15564   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15565
15566   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15567   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15568
15569   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15570 }
15571
15572 void UnmapUndoRedoButtons()
15573 {
15574   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15575   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15576
15577   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15578   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15579
15580   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15581 }
15582
15583 void MapGameButtonsExt(boolean on_tape)
15584 {
15585   int i;
15586
15587   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15588     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15589         i != GAME_CTRL_ID_UNDO &&
15590         i != GAME_CTRL_ID_REDO)
15591       MapGadget(game_gadget[i]);
15592
15593   UnmapGameButtonsAtSamePosition_All();
15594
15595   RedrawGameButtons();
15596 }
15597
15598 void UnmapGameButtonsExt(boolean on_tape)
15599 {
15600   int i;
15601
15602   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15603     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15604       UnmapGadget(game_gadget[i]);
15605 }
15606
15607 void RedrawGameButtonsExt(boolean on_tape)
15608 {
15609   int i;
15610
15611   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15612     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15613       RedrawGadget(game_gadget[i]);
15614
15615   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15616   redraw_mask &= ~REDRAW_ALL;
15617 }
15618
15619 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15620 {
15621   if (gi == NULL)
15622     return;
15623
15624   gi->checked = state;
15625 }
15626
15627 void RedrawSoundButtonGadget(int id)
15628 {
15629   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15630              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15631              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15632              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15633              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15634              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15635              id);
15636
15637   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15638   RedrawGadget(game_gadget[id2]);
15639 }
15640
15641 void MapGameButtons()
15642 {
15643   MapGameButtonsExt(FALSE);
15644 }
15645
15646 void UnmapGameButtons()
15647 {
15648   UnmapGameButtonsExt(FALSE);
15649 }
15650
15651 void RedrawGameButtons()
15652 {
15653   RedrawGameButtonsExt(FALSE);
15654 }
15655
15656 void MapGameButtonsOnTape()
15657 {
15658   MapGameButtonsExt(TRUE);
15659 }
15660
15661 void UnmapGameButtonsOnTape()
15662 {
15663   UnmapGameButtonsExt(TRUE);
15664 }
15665
15666 void RedrawGameButtonsOnTape()
15667 {
15668   RedrawGameButtonsExt(TRUE);
15669 }
15670
15671 void GameUndoRedoExt()
15672 {
15673   ClearPlayerAction();
15674
15675   tape.pausing = TRUE;
15676
15677   RedrawPlayfield();
15678   UpdateAndDisplayGameControlValues();
15679
15680   DrawCompleteVideoDisplay();
15681   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15682   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15683   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15684
15685   BackToFront();
15686 }
15687
15688 void GameUndo(int steps)
15689 {
15690   if (!CheckEngineSnapshotList())
15691     return;
15692
15693   LoadEngineSnapshot_Undo(steps);
15694
15695   GameUndoRedoExt();
15696 }
15697
15698 void GameRedo(int steps)
15699 {
15700   if (!CheckEngineSnapshotList())
15701     return;
15702
15703   LoadEngineSnapshot_Redo(steps);
15704
15705   GameUndoRedoExt();
15706 }
15707
15708 static void HandleGameButtonsExt(int id, int button)
15709 {
15710   static boolean game_undo_executed = FALSE;
15711   int steps = BUTTON_STEPSIZE(button);
15712   boolean handle_game_buttons =
15713     (game_status == GAME_MODE_PLAYING ||
15714      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15715
15716   if (!handle_game_buttons)
15717     return;
15718
15719   switch (id)
15720   {
15721     case GAME_CTRL_ID_STOP:
15722     case GAME_CTRL_ID_PANEL_STOP:
15723       if (game_status == GAME_MODE_MAIN)
15724         break;
15725
15726       if (tape.playing)
15727         TapeStop();
15728       else
15729         RequestQuitGame(TRUE);
15730
15731       break;
15732
15733     case GAME_CTRL_ID_PAUSE:
15734     case GAME_CTRL_ID_PAUSE2:
15735     case GAME_CTRL_ID_PANEL_PAUSE:
15736       if (options.network && game_status == GAME_MODE_PLAYING)
15737       {
15738 #if defined(NETWORK_AVALIABLE)
15739         if (tape.pausing)
15740           SendToServer_ContinuePlaying();
15741         else
15742           SendToServer_PausePlaying();
15743 #endif
15744       }
15745       else
15746         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15747
15748       game_undo_executed = FALSE;
15749
15750       break;
15751
15752     case GAME_CTRL_ID_PLAY:
15753     case GAME_CTRL_ID_PANEL_PLAY:
15754       if (game_status == GAME_MODE_MAIN)
15755       {
15756         StartGameActions(options.network, setup.autorecord, level.random_seed);
15757       }
15758       else if (tape.pausing)
15759       {
15760 #if defined(NETWORK_AVALIABLE)
15761         if (options.network)
15762           SendToServer_ContinuePlaying();
15763         else
15764 #endif
15765           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15766       }
15767       break;
15768
15769     case GAME_CTRL_ID_UNDO:
15770       // Important: When using "save snapshot when collecting an item" mode,
15771       // load last (current) snapshot for first "undo" after pressing "pause"
15772       // (else the last-but-one snapshot would be loaded, because the snapshot
15773       // pointer already points to the last snapshot when pressing "pause",
15774       // which is fine for "every step/move" mode, but not for "every collect")
15775       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15776           !game_undo_executed)
15777         steps--;
15778
15779       game_undo_executed = TRUE;
15780
15781       GameUndo(steps);
15782       break;
15783
15784     case GAME_CTRL_ID_REDO:
15785       GameRedo(steps);
15786       break;
15787
15788     case GAME_CTRL_ID_SAVE:
15789       TapeQuickSave();
15790       break;
15791
15792     case GAME_CTRL_ID_LOAD:
15793       TapeQuickLoad();
15794       break;
15795
15796     case SOUND_CTRL_ID_MUSIC:
15797     case SOUND_CTRL_ID_PANEL_MUSIC:
15798       if (setup.sound_music)
15799       { 
15800         setup.sound_music = FALSE;
15801
15802         FadeMusic();
15803       }
15804       else if (audio.music_available)
15805       { 
15806         setup.sound = setup.sound_music = TRUE;
15807
15808         SetAudioMode(setup.sound);
15809
15810         if (game_status == GAME_MODE_PLAYING)
15811           PlayLevelMusic();
15812       }
15813
15814       RedrawSoundButtonGadget(id);
15815
15816       break;
15817
15818     case SOUND_CTRL_ID_LOOPS:
15819     case SOUND_CTRL_ID_PANEL_LOOPS:
15820       if (setup.sound_loops)
15821         setup.sound_loops = FALSE;
15822       else if (audio.loops_available)
15823       {
15824         setup.sound = setup.sound_loops = TRUE;
15825
15826         SetAudioMode(setup.sound);
15827       }
15828
15829       RedrawSoundButtonGadget(id);
15830
15831       break;
15832
15833     case SOUND_CTRL_ID_SIMPLE:
15834     case SOUND_CTRL_ID_PANEL_SIMPLE:
15835       if (setup.sound_simple)
15836         setup.sound_simple = FALSE;
15837       else if (audio.sound_available)
15838       {
15839         setup.sound = setup.sound_simple = TRUE;
15840
15841         SetAudioMode(setup.sound);
15842       }
15843
15844       RedrawSoundButtonGadget(id);
15845
15846       break;
15847
15848     default:
15849       break;
15850   }
15851 }
15852
15853 static void HandleGameButtons(struct GadgetInfo *gi)
15854 {
15855   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15856 }
15857
15858 void HandleSoundButtonKeys(Key key)
15859 {
15860   if (key == setup.shortcut.sound_simple)
15861     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15862   else if (key == setup.shortcut.sound_loops)
15863     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15864   else if (key == setup.shortcut.sound_music)
15865     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15866 }