92c54f12e73bafd1ce6e952d5cda1dfdb6ae4fcb
[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 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP               0
1007 #define GAME_CTRL_ID_PAUSE              1
1008 #define GAME_CTRL_ID_PLAY               2
1009 #define GAME_CTRL_ID_UNDO               3
1010 #define GAME_CTRL_ID_REDO               4
1011 #define GAME_CTRL_ID_SAVE               5
1012 #define GAME_CTRL_ID_PAUSE2             6
1013 #define GAME_CTRL_ID_LOAD               7
1014 #define SOUND_CTRL_ID_MUSIC             8
1015 #define SOUND_CTRL_ID_LOOPS             9
1016 #define SOUND_CTRL_ID_SIMPLE            10
1017
1018 #define NUM_GAME_BUTTONS                11
1019
1020
1021 /* forward declaration for internal use */
1022
1023 static void CreateField(int, int, int);
1024
1025 static void ResetGfxAnimation(int, int);
1026
1027 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1028 static void AdvanceFrameAndPlayerCounters(int);
1029
1030 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1031 static boolean MovePlayer(struct PlayerInfo *, int, int);
1032 static void ScrollPlayer(struct PlayerInfo *, int);
1033 static void ScrollScreen(struct PlayerInfo *, int);
1034
1035 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1036 static boolean DigFieldByCE(int, int, int);
1037 static boolean SnapField(struct PlayerInfo *, int, int);
1038 static boolean DropElement(struct PlayerInfo *);
1039
1040 static void InitBeltMovement(void);
1041 static void CloseAllOpenTimegates(void);
1042 static void CheckGravityMovement(struct PlayerInfo *);
1043 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1044 static void KillPlayerUnlessEnemyProtected(int, int);
1045 static void KillPlayerUnlessExplosionProtected(int, int);
1046
1047 static void TestIfPlayerTouchesCustomElement(int, int);
1048 static void TestIfElementTouchesCustomElement(int, int);
1049 static void TestIfElementHitsCustomElement(int, int, int);
1050
1051 static void HandleElementChange(int, int, int);
1052 static void ExecuteCustomElementAction(int, int, int, int);
1053 static boolean ChangeElement(int, int, int, int);
1054
1055 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1056 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1057         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1058 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1059         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1060 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1061         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1062 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1063         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1064
1065 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1066 #define CheckElementChange(x, y, e, te, ev)                             \
1067         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1068 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1069         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1070 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1071         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1072
1073 static void PlayLevelSound(int, int, int);
1074 static void PlayLevelSoundNearest(int, int, int);
1075 static void PlayLevelSoundAction(int, int, int);
1076 static void PlayLevelSoundElementAction(int, int, int, int);
1077 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1078 static void PlayLevelSoundActionIfLoop(int, int, int);
1079 static void StopLevelSoundActionIfLoop(int, int, int);
1080 static void PlayLevelMusic();
1081 static void FadeLevelSoundsAndMusic();
1082
1083 static void HandleGameButtons(struct GadgetInfo *);
1084
1085 int AmoebeNachbarNr(int, int);
1086 void AmoebeUmwandeln(int, int);
1087 void ContinueMoving(int, int);
1088 void Bang(int, int);
1089 void InitMovDir(int, int);
1090 void InitAmoebaNr(int, int);
1091 int NewHiScore(void);
1092
1093 void TestIfGoodThingHitsBadThing(int, int, int);
1094 void TestIfBadThingHitsGoodThing(int, int, int);
1095 void TestIfPlayerTouchesBadThing(int, int);
1096 void TestIfPlayerRunsIntoBadThing(int, int, int);
1097 void TestIfBadThingTouchesPlayer(int, int);
1098 void TestIfBadThingRunsIntoPlayer(int, int, int);
1099 void TestIfFriendTouchesBadThing(int, int);
1100 void TestIfBadThingTouchesFriend(int, int);
1101 void TestIfBadThingTouchesOtherBadThing(int, int);
1102 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1103
1104 void KillPlayer(struct PlayerInfo *);
1105 void BuryPlayer(struct PlayerInfo *);
1106 void RemovePlayer(struct PlayerInfo *);
1107
1108 static int getInvisibleActiveFromInvisibleElement(int);
1109 static int getInvisibleFromInvisibleActiveElement(int);
1110
1111 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1112
1113 /* for detection of endless loops, caused by custom element programming */
1114 /* (using maximal playfield width x 10 is just a rough approximation) */
1115 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1116
1117 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1118 {                                                                       \
1119   if (recursion_loop_detected)                                          \
1120     return (rc);                                                        \
1121                                                                         \
1122   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1123   {                                                                     \
1124     recursion_loop_detected = TRUE;                                     \
1125     recursion_loop_element = (e);                                       \
1126   }                                                                     \
1127                                                                         \
1128   recursion_loop_depth++;                                               \
1129 }
1130
1131 #define RECURSION_LOOP_DETECTION_END()                                  \
1132 {                                                                       \
1133   recursion_loop_depth--;                                               \
1134 }
1135
1136 static int recursion_loop_depth;
1137 static boolean recursion_loop_detected;
1138 static boolean recursion_loop_element;
1139
1140 static int map_player_action[MAX_PLAYERS];
1141
1142
1143 /* ------------------------------------------------------------------------- */
1144 /* definition of elements that automatically change to other elements after  */
1145 /* a specified time, eventually calling a function when changing             */
1146 /* ------------------------------------------------------------------------- */
1147
1148 /* forward declaration for changer functions */
1149 static void InitBuggyBase(int, int);
1150 static void WarnBuggyBase(int, int);
1151
1152 static void InitTrap(int, int);
1153 static void ActivateTrap(int, int);
1154 static void ChangeActiveTrap(int, int);
1155
1156 static void InitRobotWheel(int, int);
1157 static void RunRobotWheel(int, int);
1158 static void StopRobotWheel(int, int);
1159
1160 static void InitTimegateWheel(int, int);
1161 static void RunTimegateWheel(int, int);
1162
1163 static void InitMagicBallDelay(int, int);
1164 static void ActivateMagicBall(int, int);
1165
1166 struct ChangingElementInfo
1167 {
1168   int element;
1169   int target_element;
1170   int change_delay;
1171   void (*pre_change_function)(int x, int y);
1172   void (*change_function)(int x, int y);
1173   void (*post_change_function)(int x, int y);
1174 };
1175
1176 static struct ChangingElementInfo change_delay_list[] =
1177 {
1178   {
1179     EL_NUT_BREAKING,
1180     EL_EMERALD,
1181     6,
1182     NULL,
1183     NULL,
1184     NULL
1185   },
1186   {
1187     EL_PEARL_BREAKING,
1188     EL_EMPTY,
1189     8,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_EXIT_OPENING,
1196     EL_EXIT_OPEN,
1197     29,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EXIT_CLOSING,
1204     EL_EXIT_CLOSED,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_STEEL_EXIT_OPENING,
1212     EL_STEEL_EXIT_OPEN,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_STEEL_EXIT_CLOSING,
1220     EL_STEEL_EXIT_CLOSED,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EM_EXIT_OPENING,
1228     EL_EM_EXIT_OPEN,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EM_EXIT_CLOSING,
1236     EL_EMPTY,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_EM_STEEL_EXIT_OPENING,
1244     EL_EM_STEEL_EXIT_OPEN,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_CLOSING,
1252     EL_STEELWALL,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_SP_EXIT_OPENING,
1260     EL_SP_EXIT_OPEN,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_CLOSING,
1268     EL_SP_EXIT_CLOSED,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SWITCHGATE_OPENING,
1276     EL_SWITCHGATE_OPEN,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_CLOSING,
1284     EL_SWITCHGATE_CLOSED,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_TIMEGATE_OPENING,
1292     EL_TIMEGATE_OPEN,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_CLOSING,
1300     EL_TIMEGATE_CLOSED,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306
1307   {
1308     EL_ACID_SPLASH_LEFT,
1309     EL_EMPTY,
1310     8,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315   {
1316     EL_ACID_SPLASH_RIGHT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_SP_BUGGY_BASE,
1325     EL_SP_BUGGY_BASE_ACTIVATING,
1326     0,
1327     InitBuggyBase,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE_ACTIVATING,
1333     EL_SP_BUGGY_BASE_ACTIVE,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVE,
1341     EL_SP_BUGGY_BASE,
1342     0,
1343     InitBuggyBase,
1344     WarnBuggyBase,
1345     NULL
1346   },
1347   {
1348     EL_TRAP,
1349     EL_TRAP_ACTIVE,
1350     0,
1351     InitTrap,
1352     NULL,
1353     ActivateTrap
1354   },
1355   {
1356     EL_TRAP_ACTIVE,
1357     EL_TRAP,
1358     31,
1359     NULL,
1360     ChangeActiveTrap,
1361     NULL
1362   },
1363   {
1364     EL_ROBOT_WHEEL_ACTIVE,
1365     EL_ROBOT_WHEEL,
1366     0,
1367     InitRobotWheel,
1368     RunRobotWheel,
1369     StopRobotWheel
1370   },
1371   {
1372     EL_TIMEGATE_SWITCH_ACTIVE,
1373     EL_TIMEGATE_SWITCH,
1374     0,
1375     InitTimegateWheel,
1376     RunTimegateWheel,
1377     NULL
1378   },
1379   {
1380     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1381     EL_DC_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_EMC_MAGIC_BALL_ACTIVE,
1389     EL_EMC_MAGIC_BALL_ACTIVE,
1390     0,
1391     InitMagicBallDelay,
1392     NULL,
1393     ActivateMagicBall
1394   },
1395   {
1396     EL_EMC_SPRING_BUMPER_ACTIVE,
1397     EL_EMC_SPRING_BUMPER,
1398     8,
1399     NULL,
1400     NULL,
1401     NULL
1402   },
1403   {
1404     EL_DIAGONAL_SHRINKING,
1405     EL_UNDEFINED,
1406     0,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_GROWING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL,
1418   },
1419
1420   {
1421     EL_UNDEFINED,
1422     EL_UNDEFINED,
1423     -1,
1424     NULL,
1425     NULL,
1426     NULL
1427   }
1428 };
1429
1430 struct
1431 {
1432   int element;
1433   int push_delay_fixed, push_delay_random;
1434 }
1435 push_delay_list[] =
1436 {
1437   { EL_SPRING,                  0, 0 },
1438   { EL_BALLOON,                 0, 0 },
1439
1440   { EL_SOKOBAN_OBJECT,          2, 0 },
1441   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1442   { EL_SATELLITE,               2, 0 },
1443   { EL_SP_DISK_YELLOW,          2, 0 },
1444
1445   { EL_UNDEFINED,               0, 0 },
1446 };
1447
1448 struct
1449 {
1450   int element;
1451   int move_stepsize;
1452 }
1453 move_stepsize_list[] =
1454 {
1455   { EL_AMOEBA_DROP,             2 },
1456   { EL_AMOEBA_DROPPING,         2 },
1457   { EL_QUICKSAND_FILLING,       1 },
1458   { EL_QUICKSAND_EMPTYING,      1 },
1459   { EL_QUICKSAND_FAST_FILLING,  2 },
1460   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1461   { EL_MAGIC_WALL_FILLING,      2 },
1462   { EL_MAGIC_WALL_EMPTYING,     2 },
1463   { EL_BD_MAGIC_WALL_FILLING,   2 },
1464   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1465   { EL_DC_MAGIC_WALL_FILLING,   2 },
1466   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1467
1468   { EL_UNDEFINED,               0 },
1469 };
1470
1471 struct
1472 {
1473   int element;
1474   int count;
1475 }
1476 collect_count_list[] =
1477 {
1478   { EL_EMERALD,                 1 },
1479   { EL_BD_DIAMOND,              1 },
1480   { EL_EMERALD_YELLOW,          1 },
1481   { EL_EMERALD_RED,             1 },
1482   { EL_EMERALD_PURPLE,          1 },
1483   { EL_DIAMOND,                 3 },
1484   { EL_SP_INFOTRON,             1 },
1485   { EL_PEARL,                   5 },
1486   { EL_CRYSTAL,                 8 },
1487
1488   { EL_UNDEFINED,               0 },
1489 };
1490
1491 struct
1492 {
1493   int element;
1494   int direction;
1495 }
1496 access_direction_list[] =
1497 {
1498   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1499   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1500   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1501   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1502   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1503   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1504   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1505   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1506   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1507   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1508   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1509
1510   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1511   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1512   { EL_SP_PORT_UP,                                                   MV_DOWN },
1513   { EL_SP_PORT_DOWN,                                         MV_UP           },
1514   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1515   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1516   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1517   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1518   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1519   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1520   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1521   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1522   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1523   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1524   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1525   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1526   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1527   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1528   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1529
1530   { EL_UNDEFINED,                       MV_NONE                              }
1531 };
1532
1533 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1534
1535 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1536 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1537 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1538                                  IS_JUST_CHANGING(x, y))
1539
1540 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1541
1542 /* static variables for playfield scan mode (scanning forward or backward) */
1543 static int playfield_scan_start_x = 0;
1544 static int playfield_scan_start_y = 0;
1545 static int playfield_scan_delta_x = 1;
1546 static int playfield_scan_delta_y = 1;
1547
1548 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1549                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1550                                      (y) += playfield_scan_delta_y)     \
1551                                 for ((x) = playfield_scan_start_x;      \
1552                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1553                                      (x) += playfield_scan_delta_x)
1554
1555 #ifdef DEBUG
1556 void DEBUG_SetMaximumDynamite()
1557 {
1558   int i;
1559
1560   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1561     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1562       local_player->inventory_element[local_player->inventory_size++] =
1563         EL_DYNAMITE;
1564 }
1565 #endif
1566
1567 static void InitPlayfieldScanModeVars()
1568 {
1569   if (game.use_reverse_scan_direction)
1570   {
1571     playfield_scan_start_x = lev_fieldx - 1;
1572     playfield_scan_start_y = lev_fieldy - 1;
1573
1574     playfield_scan_delta_x = -1;
1575     playfield_scan_delta_y = -1;
1576   }
1577   else
1578   {
1579     playfield_scan_start_x = 0;
1580     playfield_scan_start_y = 0;
1581
1582     playfield_scan_delta_x = 1;
1583     playfield_scan_delta_y = 1;
1584   }
1585 }
1586
1587 static void InitPlayfieldScanMode(int mode)
1588 {
1589   game.use_reverse_scan_direction =
1590     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1591
1592   InitPlayfieldScanModeVars();
1593 }
1594
1595 static int get_move_delay_from_stepsize(int move_stepsize)
1596 {
1597   move_stepsize =
1598     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1599
1600   /* make sure that stepsize value is always a power of 2 */
1601   move_stepsize = (1 << log_2(move_stepsize));
1602
1603   return TILEX / move_stepsize;
1604 }
1605
1606 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1607                                boolean init_game)
1608 {
1609   int player_nr = player->index_nr;
1610   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1611   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1612
1613   /* do no immediately change move delay -- the player might just be moving */
1614   player->move_delay_value_next = move_delay;
1615
1616   /* information if player can move must be set separately */
1617   player->cannot_move = cannot_move;
1618
1619   if (init_game)
1620   {
1621     player->move_delay       = game.initial_move_delay[player_nr];
1622     player->move_delay_value = game.initial_move_delay_value[player_nr];
1623
1624     player->move_delay_value_next = -1;
1625
1626     player->move_delay_reset_counter = 0;
1627   }
1628 }
1629
1630 void GetPlayerConfig()
1631 {
1632   GameFrameDelay = setup.game_frame_delay;
1633
1634   if (!audio.sound_available)
1635     setup.sound_simple = FALSE;
1636
1637   if (!audio.loops_available)
1638     setup.sound_loops = FALSE;
1639
1640   if (!audio.music_available)
1641     setup.sound_music = FALSE;
1642
1643   if (!video.fullscreen_available)
1644     setup.fullscreen = FALSE;
1645
1646   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1647
1648   SetAudioMode(setup.sound);
1649 }
1650
1651 int GetElementFromGroupElement(int element)
1652 {
1653   if (IS_GROUP_ELEMENT(element))
1654   {
1655     struct ElementGroupInfo *group = element_info[element].group;
1656     int last_anim_random_frame = gfx.anim_random_frame;
1657     int element_pos;
1658
1659     if (group->choice_mode == ANIM_RANDOM)
1660       gfx.anim_random_frame = RND(group->num_elements_resolved);
1661
1662     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1663                                     group->choice_mode, 0,
1664                                     group->choice_pos);
1665
1666     if (group->choice_mode == ANIM_RANDOM)
1667       gfx.anim_random_frame = last_anim_random_frame;
1668
1669     group->choice_pos++;
1670
1671     element = group->element_resolved[element_pos];
1672   }
1673
1674   return element;
1675 }
1676
1677 static void InitPlayerField(int x, int y, int element, boolean init_game)
1678 {
1679   if (element == EL_SP_MURPHY)
1680   {
1681     if (init_game)
1682     {
1683       if (stored_player[0].present)
1684       {
1685         Feld[x][y] = EL_SP_MURPHY_CLONE;
1686
1687         return;
1688       }
1689       else
1690       {
1691         stored_player[0].initial_element = element;
1692         stored_player[0].use_murphy = TRUE;
1693
1694         if (!level.use_artwork_element[0])
1695           stored_player[0].artwork_element = EL_SP_MURPHY;
1696       }
1697
1698       Feld[x][y] = EL_PLAYER_1;
1699     }
1700   }
1701
1702   if (init_game)
1703   {
1704     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1705     int jx = player->jx, jy = player->jy;
1706
1707     player->present = TRUE;
1708
1709     player->block_last_field = (element == EL_SP_MURPHY ?
1710                                 level.sp_block_last_field :
1711                                 level.block_last_field);
1712
1713     /* ---------- initialize player's last field block delay --------------- */
1714
1715     /* always start with reliable default value (no adjustment needed) */
1716     player->block_delay_adjustment = 0;
1717
1718     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1719     if (player->block_last_field && element == EL_SP_MURPHY)
1720       player->block_delay_adjustment = 1;
1721
1722     /* special case 2: in game engines before 3.1.1, blocking was different */
1723     if (game.use_block_last_field_bug)
1724       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1725
1726     if (!options.network || player->connected)
1727     {
1728       player->active = TRUE;
1729
1730       /* remove potentially duplicate players */
1731       if (StorePlayer[jx][jy] == Feld[x][y])
1732         StorePlayer[jx][jy] = 0;
1733
1734       StorePlayer[x][y] = Feld[x][y];
1735
1736 #if DEBUG_INIT_PLAYER
1737       if (options.debug)
1738       {
1739         printf("- player element %d activated", player->element_nr);
1740         printf(" (local player is %d and currently %s)\n",
1741                local_player->element_nr,
1742                local_player->active ? "active" : "not active");
1743       }
1744     }
1745 #endif
1746
1747     Feld[x][y] = EL_EMPTY;
1748
1749     player->jx = player->last_jx = x;
1750     player->jy = player->last_jy = y;
1751   }
1752
1753   if (!init_game)
1754   {
1755     int player_nr = GET_PLAYER_NR(element);
1756     struct PlayerInfo *player = &stored_player[player_nr];
1757
1758     if (player->active && player->killed)
1759       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1760   }
1761 }
1762
1763 static void InitField(int x, int y, boolean init_game)
1764 {
1765   int element = Feld[x][y];
1766
1767   switch (element)
1768   {
1769     case EL_SP_MURPHY:
1770     case EL_PLAYER_1:
1771     case EL_PLAYER_2:
1772     case EL_PLAYER_3:
1773     case EL_PLAYER_4:
1774       InitPlayerField(x, y, element, init_game);
1775       break;
1776
1777     case EL_SOKOBAN_FIELD_PLAYER:
1778       element = Feld[x][y] = EL_PLAYER_1;
1779       InitField(x, y, init_game);
1780
1781       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1782       InitField(x, y, init_game);
1783       break;
1784
1785     case EL_SOKOBAN_FIELD_EMPTY:
1786       local_player->sokobanfields_still_needed++;
1787       break;
1788
1789     case EL_STONEBLOCK:
1790       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1791         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1792       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1793         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1794       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1795         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1796       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1797         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1798       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1799         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1800       break;
1801
1802     case EL_BUG:
1803     case EL_BUG_RIGHT:
1804     case EL_BUG_UP:
1805     case EL_BUG_LEFT:
1806     case EL_BUG_DOWN:
1807     case EL_SPACESHIP:
1808     case EL_SPACESHIP_RIGHT:
1809     case EL_SPACESHIP_UP:
1810     case EL_SPACESHIP_LEFT:
1811     case EL_SPACESHIP_DOWN:
1812     case EL_BD_BUTTERFLY:
1813     case EL_BD_BUTTERFLY_RIGHT:
1814     case EL_BD_BUTTERFLY_UP:
1815     case EL_BD_BUTTERFLY_LEFT:
1816     case EL_BD_BUTTERFLY_DOWN:
1817     case EL_BD_FIREFLY:
1818     case EL_BD_FIREFLY_RIGHT:
1819     case EL_BD_FIREFLY_UP:
1820     case EL_BD_FIREFLY_LEFT:
1821     case EL_BD_FIREFLY_DOWN:
1822     case EL_PACMAN_RIGHT:
1823     case EL_PACMAN_UP:
1824     case EL_PACMAN_LEFT:
1825     case EL_PACMAN_DOWN:
1826     case EL_YAMYAM:
1827     case EL_YAMYAM_LEFT:
1828     case EL_YAMYAM_RIGHT:
1829     case EL_YAMYAM_UP:
1830     case EL_YAMYAM_DOWN:
1831     case EL_DARK_YAMYAM:
1832     case EL_ROBOT:
1833     case EL_PACMAN:
1834     case EL_SP_SNIKSNAK:
1835     case EL_SP_ELECTRON:
1836     case EL_MOLE:
1837     case EL_MOLE_LEFT:
1838     case EL_MOLE_RIGHT:
1839     case EL_MOLE_UP:
1840     case EL_MOLE_DOWN:
1841       InitMovDir(x, y);
1842       break;
1843
1844     case EL_AMOEBA_FULL:
1845     case EL_BD_AMOEBA:
1846       InitAmoebaNr(x, y);
1847       break;
1848
1849     case EL_AMOEBA_DROP:
1850       if (y == lev_fieldy - 1)
1851       {
1852         Feld[x][y] = EL_AMOEBA_GROWING;
1853         Store[x][y] = EL_AMOEBA_WET;
1854       }
1855       break;
1856
1857     case EL_DYNAMITE_ACTIVE:
1858     case EL_SP_DISK_RED_ACTIVE:
1859     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1860     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1861     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1862     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1863       MovDelay[x][y] = 96;
1864       break;
1865
1866     case EL_EM_DYNAMITE_ACTIVE:
1867       MovDelay[x][y] = 32;
1868       break;
1869
1870     case EL_LAMP:
1871       local_player->lights_still_needed++;
1872       break;
1873
1874     case EL_PENGUIN:
1875       local_player->friends_still_needed++;
1876       break;
1877
1878     case EL_PIG:
1879     case EL_DRAGON:
1880       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1881       break;
1882
1883     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1884     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1885     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1886     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1887     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1888     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1889     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1890     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1891     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1892     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1895       if (init_game)
1896       {
1897         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1898         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1899         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1900
1901         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1902         {
1903           game.belt_dir[belt_nr] = belt_dir;
1904           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1905         }
1906         else    /* more than one switch -- set it like the first switch */
1907         {
1908           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1909         }
1910       }
1911       break;
1912
1913     case EL_LIGHT_SWITCH_ACTIVE:
1914       if (init_game)
1915         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1916       break;
1917
1918     case EL_INVISIBLE_STEELWALL:
1919     case EL_INVISIBLE_WALL:
1920     case EL_INVISIBLE_SAND:
1921       if (game.light_time_left > 0 ||
1922           game.lenses_time_left > 0)
1923         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1924       break;
1925
1926     case EL_EMC_MAGIC_BALL:
1927       if (game.ball_state)
1928         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1929       break;
1930
1931     case EL_EMC_MAGIC_BALL_SWITCH:
1932       if (game.ball_state)
1933         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1934       break;
1935
1936     case EL_TRIGGER_PLAYER:
1937     case EL_TRIGGER_ELEMENT:
1938     case EL_TRIGGER_CE_VALUE:
1939     case EL_TRIGGER_CE_SCORE:
1940     case EL_SELF:
1941     case EL_ANY_ELEMENT:
1942     case EL_CURRENT_CE_VALUE:
1943     case EL_CURRENT_CE_SCORE:
1944     case EL_PREV_CE_1:
1945     case EL_PREV_CE_2:
1946     case EL_PREV_CE_3:
1947     case EL_PREV_CE_4:
1948     case EL_PREV_CE_5:
1949     case EL_PREV_CE_6:
1950     case EL_PREV_CE_7:
1951     case EL_PREV_CE_8:
1952     case EL_NEXT_CE_1:
1953     case EL_NEXT_CE_2:
1954     case EL_NEXT_CE_3:
1955     case EL_NEXT_CE_4:
1956     case EL_NEXT_CE_5:
1957     case EL_NEXT_CE_6:
1958     case EL_NEXT_CE_7:
1959     case EL_NEXT_CE_8:
1960       /* reference elements should not be used on the playfield */
1961       Feld[x][y] = EL_EMPTY;
1962       break;
1963
1964     default:
1965       if (IS_CUSTOM_ELEMENT(element))
1966       {
1967         if (CAN_MOVE(element))
1968           InitMovDir(x, y);
1969
1970         if (!element_info[element].use_last_ce_value || init_game)
1971           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1972       }
1973       else if (IS_GROUP_ELEMENT(element))
1974       {
1975         Feld[x][y] = GetElementFromGroupElement(element);
1976
1977         InitField(x, y, init_game);
1978       }
1979
1980       break;
1981   }
1982
1983   if (!init_game)
1984     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1985 }
1986
1987 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1988 {
1989   InitField(x, y, init_game);
1990
1991   /* not needed to call InitMovDir() -- already done by InitField()! */
1992   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1993       CAN_MOVE(Feld[x][y]))
1994     InitMovDir(x, y);
1995 }
1996
1997 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1998 {
1999   int old_element = Feld[x][y];
2000
2001   InitField(x, y, init_game);
2002
2003   /* not needed to call InitMovDir() -- already done by InitField()! */
2004   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2005       CAN_MOVE(old_element) &&
2006       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2007     InitMovDir(x, y);
2008
2009   /* this case is in fact a combination of not less than three bugs:
2010      first, it calls InitMovDir() for elements that can move, although this is
2011      already done by InitField(); then, it checks the element that was at this
2012      field _before_ the call to InitField() (which can change it); lastly, it
2013      was not called for "mole with direction" elements, which were treated as
2014      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2015   */
2016 }
2017
2018 static int get_key_element_from_nr(int key_nr)
2019 {
2020   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2021                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2022                           EL_EM_KEY_1 : EL_KEY_1);
2023
2024   return key_base_element + key_nr;
2025 }
2026
2027 static int get_next_dropped_element(struct PlayerInfo *player)
2028 {
2029   return (player->inventory_size > 0 ?
2030           player->inventory_element[player->inventory_size - 1] :
2031           player->inventory_infinite_element != EL_UNDEFINED ?
2032           player->inventory_infinite_element :
2033           player->dynabombs_left > 0 ?
2034           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2035           EL_UNDEFINED);
2036 }
2037
2038 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2039 {
2040   /* pos >= 0: get element from bottom of the stack;
2041      pos <  0: get element from top of the stack */
2042
2043   if (pos < 0)
2044   {
2045     int min_inventory_size = -pos;
2046     int inventory_pos = player->inventory_size - min_inventory_size;
2047     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2048
2049     return (player->inventory_size >= min_inventory_size ?
2050             player->inventory_element[inventory_pos] :
2051             player->inventory_infinite_element != EL_UNDEFINED ?
2052             player->inventory_infinite_element :
2053             player->dynabombs_left >= min_dynabombs_left ?
2054             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2055             EL_UNDEFINED);
2056   }
2057   else
2058   {
2059     int min_dynabombs_left = pos + 1;
2060     int min_inventory_size = pos + 1 - player->dynabombs_left;
2061     int inventory_pos = pos - player->dynabombs_left;
2062
2063     return (player->inventory_infinite_element != EL_UNDEFINED ?
2064             player->inventory_infinite_element :
2065             player->dynabombs_left >= min_dynabombs_left ?
2066             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2067             player->inventory_size >= min_inventory_size ?
2068             player->inventory_element[inventory_pos] :
2069             EL_UNDEFINED);
2070   }
2071 }
2072
2073 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2074 {
2075   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2076   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2077   int compare_result;
2078
2079   if (gpo1->sort_priority != gpo2->sort_priority)
2080     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2081   else
2082     compare_result = gpo1->nr - gpo2->nr;
2083
2084   return compare_result;
2085 }
2086
2087 int getPlayerInventorySize(int player_nr)
2088 {
2089   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2090     return level.native_em_level->ply[player_nr]->dynamite;
2091   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2092     return level.native_sp_level->game_sp->red_disk_count;
2093   else
2094     return stored_player[player_nr].inventory_size;
2095 }
2096
2097 void InitGameControlValues()
2098 {
2099   int i;
2100
2101   for (i = 0; game_panel_controls[i].nr != -1; i++)
2102   {
2103     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2104     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2105     struct TextPosInfo *pos = gpc->pos;
2106     int nr = gpc->nr;
2107     int type = gpc->type;
2108
2109     if (nr != i)
2110     {
2111       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2112       Error(ERR_EXIT, "this should not happen -- please debug");
2113     }
2114
2115     /* force update of game controls after initialization */
2116     gpc->value = gpc->last_value = -1;
2117     gpc->frame = gpc->last_frame = -1;
2118     gpc->gfx_frame = -1;
2119
2120     /* determine panel value width for later calculation of alignment */
2121     if (type == TYPE_INTEGER || type == TYPE_STRING)
2122     {
2123       pos->width = pos->size * getFontWidth(pos->font);
2124       pos->height = getFontHeight(pos->font);
2125     }
2126     else if (type == TYPE_ELEMENT)
2127     {
2128       pos->width = pos->size;
2129       pos->height = pos->size;
2130     }
2131
2132     /* fill structure for game panel draw order */
2133     gpo->nr = gpc->nr;
2134     gpo->sort_priority = pos->sort_priority;
2135   }
2136
2137   /* sort game panel controls according to sort_priority and control number */
2138   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2139         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2140 }
2141
2142 void UpdatePlayfieldElementCount()
2143 {
2144   boolean use_element_count = FALSE;
2145   int i, j, x, y;
2146
2147   /* first check if it is needed at all to calculate playfield element count */
2148   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2149     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2150       use_element_count = TRUE;
2151
2152   if (!use_element_count)
2153     return;
2154
2155   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2156     element_info[i].element_count = 0;
2157
2158   SCAN_PLAYFIELD(x, y)
2159   {
2160     element_info[Feld[x][y]].element_count++;
2161   }
2162
2163   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2164     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2165       if (IS_IN_GROUP(j, i))
2166         element_info[EL_GROUP_START + i].element_count +=
2167           element_info[j].element_count;
2168 }
2169
2170 void UpdateGameControlValues()
2171 {
2172   int i, k;
2173   int time = (local_player->LevelSolved ?
2174               local_player->LevelSolved_CountingTime :
2175               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2176               level.native_em_level->lev->time :
2177               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2178               level.native_sp_level->game_sp->time_played :
2179               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2180               game_mm.energy_left :
2181               game.no_time_limit ? TimePlayed : TimeLeft);
2182   int score = (local_player->LevelSolved ?
2183                local_player->LevelSolved_CountingScore :
2184                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185                level.native_em_level->lev->score :
2186                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187                level.native_sp_level->game_sp->score :
2188                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189                game_mm.score :
2190                local_player->score);
2191   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2192               level.native_em_level->lev->required :
2193               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2194               level.native_sp_level->game_sp->infotrons_still_needed :
2195               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2196               game_mm.kettles_still_needed :
2197               local_player->gems_still_needed);
2198   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2199                      level.native_em_level->lev->required > 0 :
2200                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2201                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2202                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2203                      game_mm.kettles_still_needed > 0 ||
2204                      game_mm.lights_still_needed > 0 :
2205                      local_player->gems_still_needed > 0 ||
2206                      local_player->sokobanfields_still_needed > 0 ||
2207                      local_player->lights_still_needed > 0);
2208   int health = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2209                 MIN(MAX(0, 100 - game_mm.laser_overload_value), 100) : 100);
2210
2211   UpdatePlayfieldElementCount();
2212
2213   /* update game panel control values */
2214
2215   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2216   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2217
2218   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2219   for (i = 0; i < MAX_NUM_KEYS; i++)
2220     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2221   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2222   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2223
2224   if (game.centered_player_nr == -1)
2225   {
2226     for (i = 0; i < MAX_PLAYERS; i++)
2227     {
2228       /* only one player in Supaplex game engine */
2229       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2230         break;
2231
2232       for (k = 0; k < MAX_NUM_KEYS; k++)
2233       {
2234         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2235         {
2236           if (level.native_em_level->ply[i]->keys & (1 << k))
2237             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2238               get_key_element_from_nr(k);
2239         }
2240         else if (stored_player[i].key[k])
2241           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2242             get_key_element_from_nr(k);
2243       }
2244
2245       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2246         getPlayerInventorySize(i);
2247
2248       if (stored_player[i].num_white_keys > 0)
2249         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2250           EL_DC_KEY_WHITE;
2251
2252       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2253         stored_player[i].num_white_keys;
2254     }
2255   }
2256   else
2257   {
2258     int player_nr = game.centered_player_nr;
2259
2260     for (k = 0; k < MAX_NUM_KEYS; k++)
2261     {
2262       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2263       {
2264         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2265           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266             get_key_element_from_nr(k);
2267       }
2268       else if (stored_player[player_nr].key[k])
2269         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2270           get_key_element_from_nr(k);
2271     }
2272
2273     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274       getPlayerInventorySize(player_nr);
2275
2276     if (stored_player[player_nr].num_white_keys > 0)
2277       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2278
2279     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2280       stored_player[player_nr].num_white_keys;
2281   }
2282
2283   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2284   {
2285     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2286       get_inventory_element_from_pos(local_player, i);
2287     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2288       get_inventory_element_from_pos(local_player, -i - 1);
2289   }
2290
2291   game_panel_controls[GAME_PANEL_SCORE].value = score;
2292   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2293
2294   game_panel_controls[GAME_PANEL_TIME].value = time;
2295
2296   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2297   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2298   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2299
2300   if (game.no_time_limit)
2301     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2302   else
2303     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2304
2305   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2306   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2307
2308   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2309
2310   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2311     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2312      EL_EMPTY);
2313   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2314     local_player->shield_normal_time_left;
2315   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2316     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2317      EL_EMPTY);
2318   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2319     local_player->shield_deadly_time_left;
2320
2321   game_panel_controls[GAME_PANEL_EXIT].value =
2322     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2323
2324   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2325     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2326   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2327     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2328      EL_EMC_MAGIC_BALL_SWITCH);
2329
2330   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2331     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2332   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2333     game.light_time_left;
2334
2335   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2336     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2337   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2338     game.timegate_time_left;
2339
2340   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2341     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2342
2343   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2344     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2345   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2346     game.lenses_time_left;
2347
2348   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2349     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2350   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2351     game.magnify_time_left;
2352
2353   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2354     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2355      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2356      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2357      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2358      EL_BALLOON_SWITCH_NONE);
2359
2360   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2361     local_player->dynabomb_count;
2362   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2363     local_player->dynabomb_size;
2364   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2365     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2366
2367   game_panel_controls[GAME_PANEL_PENGUINS].value =
2368     local_player->friends_still_needed;
2369
2370   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2371     local_player->sokobanfields_still_needed;
2372   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2373     local_player->sokobanfields_still_needed;
2374
2375   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2376     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2377
2378   for (i = 0; i < NUM_BELTS; i++)
2379   {
2380     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2381       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2382        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2383     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2384       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2385   }
2386
2387   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2388     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2389   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2390     game.magic_wall_time_left;
2391
2392   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2393     local_player->gravity;
2394
2395   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2396     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2397
2398   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2399     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2400       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2401        game.panel.element[i].id : EL_UNDEFINED);
2402
2403   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2404     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2405       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2406        element_info[game.panel.element_count[i].id].element_count : 0);
2407
2408   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2409     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2410       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2411        element_info[game.panel.ce_score[i].id].collect_score : 0);
2412
2413   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2414     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2415       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2416        element_info[game.panel.ce_score_element[i].id].collect_score :
2417        EL_UNDEFINED);
2418
2419   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2420   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2421   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2422
2423   /* update game panel control frames */
2424
2425   for (i = 0; game_panel_controls[i].nr != -1; i++)
2426   {
2427     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2428
2429     if (gpc->type == TYPE_ELEMENT)
2430     {
2431       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2432       {
2433         int last_anim_random_frame = gfx.anim_random_frame;
2434         int element = gpc->value;
2435         int graphic = el2panelimg(element);
2436
2437         if (gpc->value != gpc->last_value)
2438         {
2439           gpc->gfx_frame = 0;
2440           gpc->gfx_random = INIT_GFX_RANDOM();
2441         }
2442         else
2443         {
2444           gpc->gfx_frame++;
2445
2446           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2447               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2448             gpc->gfx_random = INIT_GFX_RANDOM();
2449         }
2450
2451         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2452           gfx.anim_random_frame = gpc->gfx_random;
2453
2454         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2455           gpc->gfx_frame = element_info[element].collect_score;
2456
2457         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2458                                               gpc->gfx_frame);
2459
2460         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2461           gfx.anim_random_frame = last_anim_random_frame;
2462       }
2463     }
2464     else if (gpc->type == TYPE_GRAPHIC)
2465     {
2466       if (gpc->graphic != IMG_UNDEFINED)
2467       {
2468         int last_anim_random_frame = gfx.anim_random_frame;
2469         int graphic = gpc->graphic;
2470
2471         if (gpc->value != gpc->last_value)
2472         {
2473           gpc->gfx_frame = 0;
2474           gpc->gfx_random = INIT_GFX_RANDOM();
2475         }
2476         else
2477         {
2478           gpc->gfx_frame++;
2479
2480           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2481               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2482             gpc->gfx_random = INIT_GFX_RANDOM();
2483         }
2484
2485         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2486           gfx.anim_random_frame = gpc->gfx_random;
2487
2488         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2489
2490         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2491           gfx.anim_random_frame = last_anim_random_frame;
2492       }
2493     }
2494   }
2495 }
2496
2497 void DisplayGameControlValues()
2498 {
2499   boolean redraw_panel = FALSE;
2500   int i;
2501
2502   for (i = 0; game_panel_controls[i].nr != -1; i++)
2503   {
2504     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2505
2506     if (PANEL_DEACTIVATED(gpc->pos))
2507       continue;
2508
2509     if (gpc->value == gpc->last_value &&
2510         gpc->frame == gpc->last_frame)
2511       continue;
2512
2513     redraw_panel = TRUE;
2514   }
2515
2516   if (!redraw_panel)
2517     return;
2518
2519   /* copy default game door content to main double buffer */
2520
2521   /* !!! CHECK AGAIN !!! */
2522   SetPanelBackground();
2523   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2524   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2525
2526   /* redraw game control buttons */
2527   RedrawGameButtons();
2528
2529   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2530
2531   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2532   {
2533     int nr = game_panel_order[i].nr;
2534     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2535     struct TextPosInfo *pos = gpc->pos;
2536     int type = gpc->type;
2537     int value = gpc->value;
2538     int frame = gpc->frame;
2539     int size = pos->size;
2540     int font = pos->font;
2541     boolean draw_masked = pos->draw_masked;
2542     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2543
2544     if (PANEL_DEACTIVATED(pos))
2545       continue;
2546
2547     gpc->last_value = value;
2548     gpc->last_frame = frame;
2549
2550     if (type == TYPE_INTEGER)
2551     {
2552       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2553           nr == GAME_PANEL_TIME)
2554       {
2555         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2556
2557         if (use_dynamic_size)           /* use dynamic number of digits */
2558         {
2559           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2560           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2561           int size2 = size1 + 1;
2562           int font1 = pos->font;
2563           int font2 = pos->font_alt;
2564
2565           size = (value < value_change ? size1 : size2);
2566           font = (value < value_change ? font1 : font2);
2567         }
2568       }
2569
2570       /* correct text size if "digits" is zero or less */
2571       if (size <= 0)
2572         size = strlen(int2str(value, size));
2573
2574       /* dynamically correct text alignment */
2575       pos->width = size * getFontWidth(font);
2576
2577       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2578                   int2str(value, size), font, mask_mode);
2579     }
2580     else if (type == TYPE_ELEMENT)
2581     {
2582       int element, graphic;
2583       Bitmap *src_bitmap;
2584       int src_x, src_y;
2585       int width, height;
2586       int dst_x = PANEL_XPOS(pos);
2587       int dst_y = PANEL_YPOS(pos);
2588
2589       if (value != EL_UNDEFINED && value != EL_EMPTY)
2590       {
2591         element = value;
2592         graphic = el2panelimg(value);
2593
2594         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2595
2596         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2597           size = TILESIZE;
2598
2599         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2600                               &src_x, &src_y);
2601
2602         width  = graphic_info[graphic].width  * size / TILESIZE;
2603         height = graphic_info[graphic].height * size / TILESIZE;
2604
2605         if (draw_masked)
2606           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2607                            dst_x, dst_y);
2608         else
2609           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2610                      dst_x, dst_y);
2611       }
2612     }
2613     else if (type == TYPE_GRAPHIC)
2614     {
2615       int graphic        = gpc->graphic;
2616       int graphic_active = gpc->graphic_active;
2617       Bitmap *src_bitmap;
2618       int src_x, src_y;
2619       int width, height;
2620       int dst_x = PANEL_XPOS(pos);
2621       int dst_y = PANEL_YPOS(pos);
2622       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2623                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2624
2625       if (graphic != IMG_UNDEFINED && !skip)
2626       {
2627         if (pos->style == STYLE_REVERSE)
2628           value = 100 - value;
2629
2630         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2631
2632         if (pos->direction & MV_HORIZONTAL)
2633         {
2634           width  = graphic_info[graphic_active].width * value / 100;
2635           height = graphic_info[graphic_active].height;
2636
2637           if (pos->direction == MV_LEFT)
2638           {
2639             src_x += graphic_info[graphic_active].width - width;
2640             dst_x += graphic_info[graphic_active].width - width;
2641           }
2642         }
2643         else
2644         {
2645           width  = graphic_info[graphic_active].width;
2646           height = graphic_info[graphic_active].height * value / 100;
2647
2648           if (pos->direction == MV_UP)
2649           {
2650             src_y += graphic_info[graphic_active].height - height;
2651             dst_y += graphic_info[graphic_active].height - height;
2652           }
2653         }
2654
2655         if (draw_masked)
2656           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2657                            dst_x, dst_y);
2658         else
2659           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2660                      dst_x, dst_y);
2661
2662         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2663
2664         if (pos->direction & MV_HORIZONTAL)
2665         {
2666           if (pos->direction == MV_RIGHT)
2667           {
2668             src_x += width;
2669             dst_x += width;
2670           }
2671           else
2672           {
2673             dst_x = PANEL_XPOS(pos);
2674           }
2675
2676           width = graphic_info[graphic].width - width;
2677         }
2678         else
2679         {
2680           if (pos->direction == MV_DOWN)
2681           {
2682             src_y += height;
2683             dst_y += height;
2684           }
2685           else
2686           {
2687             dst_y = PANEL_YPOS(pos);
2688           }
2689
2690           height = graphic_info[graphic].height - height;
2691         }
2692
2693         if (draw_masked)
2694           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2695                            dst_x, dst_y);
2696         else
2697           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2698                      dst_x, dst_y);
2699       }
2700     }
2701     else if (type == TYPE_STRING)
2702     {
2703       boolean active = (value != 0);
2704       char *state_normal = "off";
2705       char *state_active = "on";
2706       char *state = (active ? state_active : state_normal);
2707       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2708                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2709                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2710                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2711
2712       if (nr == GAME_PANEL_GRAVITY_STATE)
2713       {
2714         int font1 = pos->font;          /* (used for normal state) */
2715         int font2 = pos->font_alt;      /* (used for active state) */
2716
2717         font = (active ? font2 : font1);
2718       }
2719
2720       if (s != NULL)
2721       {
2722         char *s_cut;
2723
2724         if (size <= 0)
2725         {
2726           /* don't truncate output if "chars" is zero or less */
2727           size = strlen(s);
2728
2729           /* dynamically correct text alignment */
2730           pos->width = size * getFontWidth(font);
2731         }
2732
2733         s_cut = getStringCopyN(s, size);
2734
2735         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2736                     s_cut, font, mask_mode);
2737
2738         free(s_cut);
2739       }
2740     }
2741
2742     redraw_mask |= REDRAW_DOOR_1;
2743   }
2744
2745   SetGameStatus(GAME_MODE_PLAYING);
2746 }
2747
2748 void UpdateAndDisplayGameControlValues()
2749 {
2750   if (tape.deactivate_display)
2751     return;
2752
2753   UpdateGameControlValues();
2754   DisplayGameControlValues();
2755 }
2756
2757 void UpdateGameDoorValues()
2758 {
2759   UpdateGameControlValues();
2760 }
2761
2762 void DrawGameDoorValues()
2763 {
2764   DisplayGameControlValues();
2765 }
2766
2767
2768 /*
2769   =============================================================================
2770   InitGameEngine()
2771   -----------------------------------------------------------------------------
2772   initialize game engine due to level / tape version number
2773   =============================================================================
2774 */
2775
2776 static void InitGameEngine()
2777 {
2778   int i, j, k, l, x, y;
2779
2780   /* set game engine from tape file when re-playing, else from level file */
2781   game.engine_version = (tape.playing ? tape.engine_version :
2782                          level.game_version);
2783
2784   /* set single or multi-player game mode (needed for re-playing tapes) */
2785   game.team_mode = setup.team_mode;
2786
2787   if (tape.playing)
2788   {
2789     int num_players = 0;
2790
2791     for (i = 0; i < MAX_PLAYERS; i++)
2792       if (tape.player_participates[i])
2793         num_players++;
2794
2795     /* multi-player tapes contain input data for more than one player */
2796     game.team_mode = (num_players > 1);
2797   }
2798
2799   /* ---------------------------------------------------------------------- */
2800   /* set flags for bugs and changes according to active game engine version */
2801   /* ---------------------------------------------------------------------- */
2802
2803   /*
2804     Summary of bugfix/change:
2805     Fixed handling for custom elements that change when pushed by the player.
2806
2807     Fixed/changed in version:
2808     3.1.0
2809
2810     Description:
2811     Before 3.1.0, custom elements that "change when pushing" changed directly
2812     after the player started pushing them (until then handled in "DigField()").
2813     Since 3.1.0, these custom elements are not changed until the "pushing"
2814     move of the element is finished (now handled in "ContinueMoving()").
2815
2816     Affected levels/tapes:
2817     The first condition is generally needed for all levels/tapes before version
2818     3.1.0, which might use the old behaviour before it was changed; known tapes
2819     that are affected are some tapes from the level set "Walpurgis Gardens" by
2820     Jamie Cullen.
2821     The second condition is an exception from the above case and is needed for
2822     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2823     above (including some development versions of 3.1.0), but before it was
2824     known that this change would break tapes like the above and was fixed in
2825     3.1.1, so that the changed behaviour was active although the engine version
2826     while recording maybe was before 3.1.0. There is at least one tape that is
2827     affected by this exception, which is the tape for the one-level set "Bug
2828     Machine" by Juergen Bonhagen.
2829   */
2830
2831   game.use_change_when_pushing_bug =
2832     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2833      !(tape.playing &&
2834        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2835        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2836
2837   /*
2838     Summary of bugfix/change:
2839     Fixed handling for blocking the field the player leaves when moving.
2840
2841     Fixed/changed in version:
2842     3.1.1
2843
2844     Description:
2845     Before 3.1.1, when "block last field when moving" was enabled, the field
2846     the player is leaving when moving was blocked for the time of the move,
2847     and was directly unblocked afterwards. This resulted in the last field
2848     being blocked for exactly one less than the number of frames of one player
2849     move. Additionally, even when blocking was disabled, the last field was
2850     blocked for exactly one frame.
2851     Since 3.1.1, due to changes in player movement handling, the last field
2852     is not blocked at all when blocking is disabled. When blocking is enabled,
2853     the last field is blocked for exactly the number of frames of one player
2854     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2855     last field is blocked for exactly one more than the number of frames of
2856     one player move.
2857
2858     Affected levels/tapes:
2859     (!!! yet to be determined -- probably many !!!)
2860   */
2861
2862   game.use_block_last_field_bug =
2863     (game.engine_version < VERSION_IDENT(3,1,1,0));
2864
2865   game_em.use_single_button =
2866     (game.engine_version > VERSION_IDENT(4,0,0,2));
2867
2868   game_em.use_snap_key_bug =
2869     (game.engine_version < VERSION_IDENT(4,0,1,0));
2870
2871   /* ---------------------------------------------------------------------- */
2872
2873   /* set maximal allowed number of custom element changes per game frame */
2874   game.max_num_changes_per_frame = 1;
2875
2876   /* default scan direction: scan playfield from top/left to bottom/right */
2877   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2878
2879   /* dynamically adjust element properties according to game engine version */
2880   InitElementPropertiesEngine(game.engine_version);
2881
2882 #if 0
2883   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2884   printf("          tape version == %06d [%s] [file: %06d]\n",
2885          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2886          tape.file_version);
2887   printf("       => game.engine_version == %06d\n", game.engine_version);
2888 #endif
2889
2890   /* ---------- initialize player's initial move delay --------------------- */
2891
2892   /* dynamically adjust player properties according to level information */
2893   for (i = 0; i < MAX_PLAYERS; i++)
2894     game.initial_move_delay_value[i] =
2895       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2896
2897   /* dynamically adjust player properties according to game engine version */
2898   for (i = 0; i < MAX_PLAYERS; i++)
2899     game.initial_move_delay[i] =
2900       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2901        game.initial_move_delay_value[i] : 0);
2902
2903   /* ---------- initialize player's initial push delay --------------------- */
2904
2905   /* dynamically adjust player properties according to game engine version */
2906   game.initial_push_delay_value =
2907     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2908
2909   /* ---------- initialize changing elements ------------------------------- */
2910
2911   /* initialize changing elements information */
2912   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2913   {
2914     struct ElementInfo *ei = &element_info[i];
2915
2916     /* this pointer might have been changed in the level editor */
2917     ei->change = &ei->change_page[0];
2918
2919     if (!IS_CUSTOM_ELEMENT(i))
2920     {
2921       ei->change->target_element = EL_EMPTY_SPACE;
2922       ei->change->delay_fixed = 0;
2923       ei->change->delay_random = 0;
2924       ei->change->delay_frames = 1;
2925     }
2926
2927     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2928     {
2929       ei->has_change_event[j] = FALSE;
2930
2931       ei->event_page_nr[j] = 0;
2932       ei->event_page[j] = &ei->change_page[0];
2933     }
2934   }
2935
2936   /* add changing elements from pre-defined list */
2937   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2938   {
2939     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2940     struct ElementInfo *ei = &element_info[ch_delay->element];
2941
2942     ei->change->target_element       = ch_delay->target_element;
2943     ei->change->delay_fixed          = ch_delay->change_delay;
2944
2945     ei->change->pre_change_function  = ch_delay->pre_change_function;
2946     ei->change->change_function      = ch_delay->change_function;
2947     ei->change->post_change_function = ch_delay->post_change_function;
2948
2949     ei->change->can_change = TRUE;
2950     ei->change->can_change_or_has_action = TRUE;
2951
2952     ei->has_change_event[CE_DELAY] = TRUE;
2953
2954     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2955     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2956   }
2957
2958   /* ---------- initialize internal run-time variables --------------------- */
2959
2960   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2961   {
2962     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2963
2964     for (j = 0; j < ei->num_change_pages; j++)
2965     {
2966       ei->change_page[j].can_change_or_has_action =
2967         (ei->change_page[j].can_change |
2968          ei->change_page[j].has_action);
2969     }
2970   }
2971
2972   /* add change events from custom element configuration */
2973   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2974   {
2975     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2976
2977     for (j = 0; j < ei->num_change_pages; j++)
2978     {
2979       if (!ei->change_page[j].can_change_or_has_action)
2980         continue;
2981
2982       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2983       {
2984         /* only add event page for the first page found with this event */
2985         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2986         {
2987           ei->has_change_event[k] = TRUE;
2988
2989           ei->event_page_nr[k] = j;
2990           ei->event_page[k] = &ei->change_page[j];
2991         }
2992       }
2993     }
2994   }
2995
2996   /* ---------- initialize reference elements in change conditions --------- */
2997
2998   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2999   {
3000     int element = EL_CUSTOM_START + i;
3001     struct ElementInfo *ei = &element_info[element];
3002
3003     for (j = 0; j < ei->num_change_pages; j++)
3004     {
3005       int trigger_element = ei->change_page[j].initial_trigger_element;
3006
3007       if (trigger_element >= EL_PREV_CE_8 &&
3008           trigger_element <= EL_NEXT_CE_8)
3009         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3010
3011       ei->change_page[j].trigger_element = trigger_element;
3012     }
3013   }
3014
3015   /* ---------- initialize run-time trigger player and element ------------- */
3016
3017   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3018   {
3019     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3020
3021     for (j = 0; j < ei->num_change_pages; j++)
3022     {
3023       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3024       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3025       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3026       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3027       ei->change_page[j].actual_trigger_ce_value = 0;
3028       ei->change_page[j].actual_trigger_ce_score = 0;
3029     }
3030   }
3031
3032   /* ---------- initialize trigger events ---------------------------------- */
3033
3034   /* initialize trigger events information */
3035   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3036     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3037       trigger_events[i][j] = FALSE;
3038
3039   /* add trigger events from element change event properties */
3040   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3041   {
3042     struct ElementInfo *ei = &element_info[i];
3043
3044     for (j = 0; j < ei->num_change_pages; j++)
3045     {
3046       if (!ei->change_page[j].can_change_or_has_action)
3047         continue;
3048
3049       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3050       {
3051         int trigger_element = ei->change_page[j].trigger_element;
3052
3053         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3054         {
3055           if (ei->change_page[j].has_event[k])
3056           {
3057             if (IS_GROUP_ELEMENT(trigger_element))
3058             {
3059               struct ElementGroupInfo *group =
3060                 element_info[trigger_element].group;
3061
3062               for (l = 0; l < group->num_elements_resolved; l++)
3063                 trigger_events[group->element_resolved[l]][k] = TRUE;
3064             }
3065             else if (trigger_element == EL_ANY_ELEMENT)
3066               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3067                 trigger_events[l][k] = TRUE;
3068             else
3069               trigger_events[trigger_element][k] = TRUE;
3070           }
3071         }
3072       }
3073     }
3074   }
3075
3076   /* ---------- initialize push delay -------------------------------------- */
3077
3078   /* initialize push delay values to default */
3079   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3080   {
3081     if (!IS_CUSTOM_ELEMENT(i))
3082     {
3083       /* set default push delay values (corrected since version 3.0.7-1) */
3084       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3085       {
3086         element_info[i].push_delay_fixed = 2;
3087         element_info[i].push_delay_random = 8;
3088       }
3089       else
3090       {
3091         element_info[i].push_delay_fixed = 8;
3092         element_info[i].push_delay_random = 8;
3093       }
3094     }
3095   }
3096
3097   /* set push delay value for certain elements from pre-defined list */
3098   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3099   {
3100     int e = push_delay_list[i].element;
3101
3102     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3103     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3104   }
3105
3106   /* set push delay value for Supaplex elements for newer engine versions */
3107   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3108   {
3109     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3110     {
3111       if (IS_SP_ELEMENT(i))
3112       {
3113         /* set SP push delay to just enough to push under a falling zonk */
3114         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3115
3116         element_info[i].push_delay_fixed  = delay;
3117         element_info[i].push_delay_random = 0;
3118       }
3119     }
3120   }
3121
3122   /* ---------- initialize move stepsize ----------------------------------- */
3123
3124   /* initialize move stepsize values to default */
3125   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3126     if (!IS_CUSTOM_ELEMENT(i))
3127       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3128
3129   /* set move stepsize value for certain elements from pre-defined list */
3130   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3131   {
3132     int e = move_stepsize_list[i].element;
3133
3134     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3135   }
3136
3137   /* ---------- initialize collect score ----------------------------------- */
3138
3139   /* initialize collect score values for custom elements from initial value */
3140   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141     if (IS_CUSTOM_ELEMENT(i))
3142       element_info[i].collect_score = element_info[i].collect_score_initial;
3143
3144   /* ---------- initialize collect count ----------------------------------- */
3145
3146   /* initialize collect count values for non-custom elements */
3147   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3148     if (!IS_CUSTOM_ELEMENT(i))
3149       element_info[i].collect_count_initial = 0;
3150
3151   /* add collect count values for all elements from pre-defined list */
3152   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3153     element_info[collect_count_list[i].element].collect_count_initial =
3154       collect_count_list[i].count;
3155
3156   /* ---------- initialize access direction -------------------------------- */
3157
3158   /* initialize access direction values to default (access from every side) */
3159   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3160     if (!IS_CUSTOM_ELEMENT(i))
3161       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3162
3163   /* set access direction value for certain elements from pre-defined list */
3164   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3165     element_info[access_direction_list[i].element].access_direction =
3166       access_direction_list[i].direction;
3167
3168   /* ---------- initialize explosion content ------------------------------- */
3169   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3170   {
3171     if (IS_CUSTOM_ELEMENT(i))
3172       continue;
3173
3174     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3175     {
3176       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3177
3178       element_info[i].content.e[x][y] =
3179         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3180          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3181          i == EL_PLAYER_3 ? EL_EMERALD :
3182          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3183          i == EL_MOLE ? EL_EMERALD_RED :
3184          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3185          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3186          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3187          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3188          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3189          i == EL_WALL_EMERALD ? EL_EMERALD :
3190          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3191          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3192          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3193          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3194          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3195          i == EL_WALL_PEARL ? EL_PEARL :
3196          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3197          EL_EMPTY);
3198     }
3199   }
3200
3201   /* ---------- initialize recursion detection ------------------------------ */
3202   recursion_loop_depth = 0;
3203   recursion_loop_detected = FALSE;
3204   recursion_loop_element = EL_UNDEFINED;
3205
3206   /* ---------- initialize graphics engine ---------------------------------- */
3207   game.scroll_delay_value =
3208     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3209      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3210   game.scroll_delay_value =
3211     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3212
3213   /* ---------- initialize game engine snapshots ---------------------------- */
3214   for (i = 0; i < MAX_PLAYERS; i++)
3215     game.snapshot.last_action[i] = 0;
3216   game.snapshot.changed_action = FALSE;
3217   game.snapshot.collected_item = FALSE;
3218   game.snapshot.mode =
3219     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3220      SNAPSHOT_MODE_EVERY_STEP :
3221      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3222      SNAPSHOT_MODE_EVERY_MOVE :
3223      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3224      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3225   game.snapshot.save_snapshot = FALSE;
3226
3227   /* ---------- initialize level time for Supaplex engine ------------------- */
3228   /* Supaplex levels with time limit currently unsupported -- should be added */
3229   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3230     level.time = 0;
3231 }
3232
3233 int get_num_special_action(int element, int action_first, int action_last)
3234 {
3235   int num_special_action = 0;
3236   int i, j;
3237
3238   for (i = action_first; i <= action_last; i++)
3239   {
3240     boolean found = FALSE;
3241
3242     for (j = 0; j < NUM_DIRECTIONS; j++)
3243       if (el_act_dir2img(element, i, j) !=
3244           el_act_dir2img(element, ACTION_DEFAULT, j))
3245         found = TRUE;
3246
3247     if (found)
3248       num_special_action++;
3249     else
3250       break;
3251   }
3252
3253   return num_special_action;
3254 }
3255
3256
3257 /*
3258   =============================================================================
3259   InitGame()
3260   -----------------------------------------------------------------------------
3261   initialize and start new game
3262   =============================================================================
3263 */
3264
3265 void InitGame()
3266 {
3267   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3268   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3269   int fade_mask = REDRAW_FIELD;
3270
3271   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3272   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3273   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3274   int initial_move_dir = MV_DOWN;
3275   int i, j, x, y;
3276
3277   // required here to update video display before fading (FIX THIS)
3278   DrawMaskedBorder(REDRAW_DOOR_2);
3279
3280   if (!game.restart_level)
3281     CloseDoor(DOOR_CLOSE_1);
3282
3283   SetGameStatus(GAME_MODE_PLAYING);
3284
3285   if (level_editor_test_game)
3286     FadeSkipNextFadeIn();
3287   else
3288     FadeSetEnterScreen();
3289
3290   if (CheckIfGlobalBorderHasChanged())
3291     fade_mask = REDRAW_ALL;
3292
3293   FadeLevelSoundsAndMusic();
3294
3295   ExpireSoundLoops(TRUE);
3296
3297   FadeOut(fade_mask);
3298
3299   /* needed if different viewport properties defined for playing */
3300   ChangeViewportPropertiesIfNeeded();
3301
3302   ClearField();
3303
3304   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3305
3306   DrawCompleteVideoDisplay();
3307
3308   InitGameEngine();
3309   InitGameControlValues();
3310
3311   /* don't play tapes over network */
3312   network_playing = (options.network && !tape.playing);
3313
3314   for (i = 0; i < MAX_PLAYERS; i++)
3315   {
3316     struct PlayerInfo *player = &stored_player[i];
3317
3318     player->index_nr = i;
3319     player->index_bit = (1 << i);
3320     player->element_nr = EL_PLAYER_1 + i;
3321
3322     player->present = FALSE;
3323     player->active = FALSE;
3324     player->mapped = FALSE;
3325
3326     player->killed = FALSE;
3327     player->reanimated = FALSE;
3328
3329     player->action = 0;
3330     player->effective_action = 0;
3331     player->programmed_action = 0;
3332
3333     player->mouse_action.lx = 0;
3334     player->mouse_action.ly = 0;
3335     player->mouse_action.button = 0;
3336
3337     player->score = 0;
3338     player->score_final = 0;
3339
3340     player->gems_still_needed = level.gems_needed;
3341     player->sokobanfields_still_needed = 0;
3342     player->lights_still_needed = 0;
3343     player->friends_still_needed = 0;
3344
3345     for (j = 0; j < MAX_NUM_KEYS; j++)
3346       player->key[j] = FALSE;
3347
3348     player->num_white_keys = 0;
3349
3350     player->dynabomb_count = 0;
3351     player->dynabomb_size = 1;
3352     player->dynabombs_left = 0;
3353     player->dynabomb_xl = FALSE;
3354
3355     player->MovDir = initial_move_dir;
3356     player->MovPos = 0;
3357     player->GfxPos = 0;
3358     player->GfxDir = initial_move_dir;
3359     player->GfxAction = ACTION_DEFAULT;
3360     player->Frame = 0;
3361     player->StepFrame = 0;
3362
3363     player->initial_element = player->element_nr;
3364     player->artwork_element =
3365       (level.use_artwork_element[i] ? level.artwork_element[i] :
3366        player->element_nr);
3367     player->use_murphy = FALSE;
3368
3369     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3370     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3371
3372     player->gravity = level.initial_player_gravity[i];
3373
3374     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3375
3376     player->actual_frame_counter = 0;
3377
3378     player->step_counter = 0;
3379
3380     player->last_move_dir = initial_move_dir;
3381
3382     player->is_active = FALSE;
3383
3384     player->is_waiting = FALSE;
3385     player->is_moving = FALSE;
3386     player->is_auto_moving = FALSE;
3387     player->is_digging = FALSE;
3388     player->is_snapping = FALSE;
3389     player->is_collecting = FALSE;
3390     player->is_pushing = FALSE;
3391     player->is_switching = FALSE;
3392     player->is_dropping = FALSE;
3393     player->is_dropping_pressed = FALSE;
3394
3395     player->is_bored = FALSE;
3396     player->is_sleeping = FALSE;
3397
3398     player->was_waiting = TRUE;
3399     player->was_moving = FALSE;
3400     player->was_snapping = FALSE;
3401     player->was_dropping = FALSE;
3402
3403     player->force_dropping = FALSE;
3404
3405     player->frame_counter_bored = -1;
3406     player->frame_counter_sleeping = -1;
3407
3408     player->anim_delay_counter = 0;
3409     player->post_delay_counter = 0;
3410
3411     player->dir_waiting = initial_move_dir;
3412     player->action_waiting = ACTION_DEFAULT;
3413     player->last_action_waiting = ACTION_DEFAULT;
3414     player->special_action_bored = ACTION_DEFAULT;
3415     player->special_action_sleeping = ACTION_DEFAULT;
3416
3417     player->switch_x = -1;
3418     player->switch_y = -1;
3419
3420     player->drop_x = -1;
3421     player->drop_y = -1;
3422
3423     player->show_envelope = 0;
3424
3425     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3426
3427     player->push_delay       = -1;      /* initialized when pushing starts */
3428     player->push_delay_value = game.initial_push_delay_value;
3429
3430     player->drop_delay = 0;
3431     player->drop_pressed_delay = 0;
3432
3433     player->last_jx = -1;
3434     player->last_jy = -1;
3435     player->jx = -1;
3436     player->jy = -1;
3437
3438     player->shield_normal_time_left = 0;
3439     player->shield_deadly_time_left = 0;
3440
3441     player->inventory_infinite_element = EL_UNDEFINED;
3442     player->inventory_size = 0;
3443
3444     if (level.use_initial_inventory[i])
3445     {
3446       for (j = 0; j < level.initial_inventory_size[i]; j++)
3447       {
3448         int element = level.initial_inventory_content[i][j];
3449         int collect_count = element_info[element].collect_count_initial;
3450         int k;
3451
3452         if (!IS_CUSTOM_ELEMENT(element))
3453           collect_count = 1;
3454
3455         if (collect_count == 0)
3456           player->inventory_infinite_element = element;
3457         else
3458           for (k = 0; k < collect_count; k++)
3459             if (player->inventory_size < MAX_INVENTORY_SIZE)
3460               player->inventory_element[player->inventory_size++] = element;
3461       }
3462     }
3463
3464     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3465     SnapField(player, 0, 0);
3466
3467     player->LevelSolved = FALSE;
3468     player->GameOver = FALSE;
3469
3470     player->LevelSolved_GameWon = FALSE;
3471     player->LevelSolved_GameEnd = FALSE;
3472     player->LevelSolved_PanelOff = FALSE;
3473     player->LevelSolved_SaveTape = FALSE;
3474     player->LevelSolved_SaveScore = FALSE;
3475     player->LevelSolved_CountingTime = 0;
3476     player->LevelSolved_CountingScore = 0;
3477
3478     map_player_action[i] = i;
3479   }
3480
3481   network_player_action_received = FALSE;
3482
3483 #if defined(NETWORK_AVALIABLE)
3484   /* initial null action */
3485   if (network_playing)
3486     SendToServer_MovePlayer(MV_NONE);
3487 #endif
3488
3489   ZX = ZY = -1;
3490   ExitX = ExitY = -1;
3491
3492   FrameCounter = 0;
3493   TimeFrames = 0;
3494   TimePlayed = 0;
3495   TimeLeft = level.time;
3496   TapeTime = 0;
3497
3498   ScreenMovDir = MV_NONE;
3499   ScreenMovPos = 0;
3500   ScreenGfxPos = 0;
3501
3502   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3503
3504   AllPlayersGone = FALSE;
3505
3506   game.no_time_limit = (level.time == 0);
3507
3508   game.yamyam_content_nr = 0;
3509   game.robot_wheel_active = FALSE;
3510   game.magic_wall_active = FALSE;
3511   game.magic_wall_time_left = 0;
3512   game.light_time_left = 0;
3513   game.timegate_time_left = 0;
3514   game.switchgate_pos = 0;
3515   game.wind_direction = level.wind_direction_initial;
3516
3517   game.lenses_time_left = 0;
3518   game.magnify_time_left = 0;
3519
3520   game.ball_state = level.ball_state_initial;
3521   game.ball_content_nr = 0;
3522
3523   game.envelope_active = FALSE;
3524
3525   /* set focus to local player for network games, else to all players */
3526   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3527   game.centered_player_nr_next = game.centered_player_nr;
3528   game.set_centered_player = FALSE;
3529
3530   if (network_playing && tape.recording)
3531   {
3532     /* store client dependent player focus when recording network games */
3533     tape.centered_player_nr_next = game.centered_player_nr_next;
3534     tape.set_centered_player = TRUE;
3535   }
3536
3537   for (i = 0; i < NUM_BELTS; i++)
3538   {
3539     game.belt_dir[i] = MV_NONE;
3540     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3541   }
3542
3543   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3544     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3545
3546 #if DEBUG_INIT_PLAYER
3547   if (options.debug)
3548   {
3549     printf("Player status at level initialization:\n");
3550   }
3551 #endif
3552
3553   SCAN_PLAYFIELD(x, y)
3554   {
3555     Feld[x][y] = level.field[x][y];
3556     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3557     ChangeDelay[x][y] = 0;
3558     ChangePage[x][y] = -1;
3559     CustomValue[x][y] = 0;              /* initialized in InitField() */
3560     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3561     AmoebaNr[x][y] = 0;
3562     WasJustMoving[x][y] = 0;
3563     WasJustFalling[x][y] = 0;
3564     CheckCollision[x][y] = 0;
3565     CheckImpact[x][y] = 0;
3566     Stop[x][y] = FALSE;
3567     Pushed[x][y] = FALSE;
3568
3569     ChangeCount[x][y] = 0;
3570     ChangeEvent[x][y] = -1;
3571
3572     ExplodePhase[x][y] = 0;
3573     ExplodeDelay[x][y] = 0;
3574     ExplodeField[x][y] = EX_TYPE_NONE;
3575
3576     RunnerVisit[x][y] = 0;
3577     PlayerVisit[x][y] = 0;
3578
3579     GfxFrame[x][y] = 0;
3580     GfxRandom[x][y] = INIT_GFX_RANDOM();
3581     GfxElement[x][y] = EL_UNDEFINED;
3582     GfxAction[x][y] = ACTION_DEFAULT;
3583     GfxDir[x][y] = MV_NONE;
3584     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3585   }
3586
3587   SCAN_PLAYFIELD(x, y)
3588   {
3589     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3590       emulate_bd = FALSE;
3591     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3592       emulate_sb = FALSE;
3593     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3594       emulate_sp = FALSE;
3595
3596     InitField(x, y, TRUE);
3597
3598     ResetGfxAnimation(x, y);
3599   }
3600
3601   InitBeltMovement();
3602
3603   for (i = 0; i < MAX_PLAYERS; i++)
3604   {
3605     struct PlayerInfo *player = &stored_player[i];
3606
3607     /* set number of special actions for bored and sleeping animation */
3608     player->num_special_action_bored =
3609       get_num_special_action(player->artwork_element,
3610                              ACTION_BORING_1, ACTION_BORING_LAST);
3611     player->num_special_action_sleeping =
3612       get_num_special_action(player->artwork_element,
3613                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3614   }
3615
3616   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3617                     emulate_sb ? EMU_SOKOBAN :
3618                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3619
3620   /* initialize type of slippery elements */
3621   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3622   {
3623     if (!IS_CUSTOM_ELEMENT(i))
3624     {
3625       /* default: elements slip down either to the left or right randomly */
3626       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3627
3628       /* SP style elements prefer to slip down on the left side */
3629       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3630         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3631
3632       /* BD style elements prefer to slip down on the left side */
3633       if (game.emulation == EMU_BOULDERDASH)
3634         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3635     }
3636   }
3637
3638   /* initialize explosion and ignition delay */
3639   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3640   {
3641     if (!IS_CUSTOM_ELEMENT(i))
3642     {
3643       int num_phase = 8;
3644       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3645                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3646                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3647       int last_phase = (num_phase + 1) * delay;
3648       int half_phase = (num_phase / 2) * delay;
3649
3650       element_info[i].explosion_delay = last_phase - 1;
3651       element_info[i].ignition_delay = half_phase;
3652
3653       if (i == EL_BLACK_ORB)
3654         element_info[i].ignition_delay = 1;
3655     }
3656   }
3657
3658   /* correct non-moving belts to start moving left */
3659   for (i = 0; i < NUM_BELTS; i++)
3660     if (game.belt_dir[i] == MV_NONE)
3661       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3662
3663 #if USE_NEW_PLAYER_ASSIGNMENTS
3664   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3665   /* choose default local player */
3666   local_player = &stored_player[0];
3667
3668   for (i = 0; i < MAX_PLAYERS; i++)
3669     stored_player[i].connected = FALSE;
3670
3671   local_player->connected = TRUE;
3672   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3673
3674   if (tape.playing)
3675   {
3676     for (i = 0; i < MAX_PLAYERS; i++)
3677       stored_player[i].connected = tape.player_participates[i];
3678   }
3679   else if (game.team_mode && !options.network)
3680   {
3681     /* try to guess locally connected team mode players (needed for correct
3682        assignment of player figures from level to locally playing players) */
3683
3684     for (i = 0; i < MAX_PLAYERS; i++)
3685       if (setup.input[i].use_joystick ||
3686           setup.input[i].key.left != KSYM_UNDEFINED)
3687         stored_player[i].connected = TRUE;
3688   }
3689
3690 #if DEBUG_INIT_PLAYER
3691   if (options.debug)
3692   {
3693     printf("Player status after level initialization:\n");
3694
3695     for (i = 0; i < MAX_PLAYERS; i++)
3696     {
3697       struct PlayerInfo *player = &stored_player[i];
3698
3699       printf("- player %d: present == %d, connected == %d, active == %d",
3700              i + 1,
3701              player->present,
3702              player->connected,
3703              player->active);
3704
3705       if (local_player == player)
3706         printf(" (local player)");
3707
3708       printf("\n");
3709     }
3710   }
3711 #endif
3712
3713 #if DEBUG_INIT_PLAYER
3714   if (options.debug)
3715     printf("Reassigning players ...\n");
3716 #endif
3717
3718   /* check if any connected player was not found in playfield */
3719   for (i = 0; i < MAX_PLAYERS; i++)
3720   {
3721     struct PlayerInfo *player = &stored_player[i];
3722
3723     if (player->connected && !player->present)
3724     {
3725       struct PlayerInfo *field_player = NULL;
3726
3727 #if DEBUG_INIT_PLAYER
3728       if (options.debug)
3729         printf("- looking for field player for player %d ...\n", i + 1);
3730 #endif
3731
3732       /* assign first free player found that is present in the playfield */
3733
3734       /* first try: look for unmapped playfield player that is not connected */
3735       for (j = 0; j < MAX_PLAYERS; j++)
3736         if (field_player == NULL &&
3737             stored_player[j].present &&
3738             !stored_player[j].mapped &&
3739             !stored_player[j].connected)
3740           field_player = &stored_player[j];
3741
3742       /* second try: look for *any* unmapped playfield player */
3743       for (j = 0; j < MAX_PLAYERS; j++)
3744         if (field_player == NULL &&
3745             stored_player[j].present &&
3746             !stored_player[j].mapped)
3747           field_player = &stored_player[j];
3748
3749       if (field_player != NULL)
3750       {
3751         int jx = field_player->jx, jy = field_player->jy;
3752
3753 #if DEBUG_INIT_PLAYER
3754         if (options.debug)
3755           printf("- found player %d\n", field_player->index_nr + 1);
3756 #endif
3757
3758         player->present = FALSE;
3759         player->active = FALSE;
3760
3761         field_player->present = TRUE;
3762         field_player->active = TRUE;
3763
3764         /*
3765         player->initial_element = field_player->initial_element;
3766         player->artwork_element = field_player->artwork_element;
3767
3768         player->block_last_field       = field_player->block_last_field;
3769         player->block_delay_adjustment = field_player->block_delay_adjustment;
3770         */
3771
3772         StorePlayer[jx][jy] = field_player->element_nr;
3773
3774         field_player->jx = field_player->last_jx = jx;
3775         field_player->jy = field_player->last_jy = jy;
3776
3777         if (local_player == player)
3778           local_player = field_player;
3779
3780         map_player_action[field_player->index_nr] = i;
3781
3782         field_player->mapped = TRUE;
3783
3784 #if DEBUG_INIT_PLAYER
3785         if (options.debug)
3786           printf("- map_player_action[%d] == %d\n",
3787                  field_player->index_nr + 1, i + 1);
3788 #endif
3789       }
3790     }
3791
3792     if (player->connected && player->present)
3793       player->mapped = TRUE;
3794   }
3795
3796 #if DEBUG_INIT_PLAYER
3797   if (options.debug)
3798   {
3799     printf("Player status after player assignment (first stage):\n");
3800
3801     for (i = 0; i < MAX_PLAYERS; i++)
3802     {
3803       struct PlayerInfo *player = &stored_player[i];
3804
3805       printf("- player %d: present == %d, connected == %d, active == %d",
3806              i + 1,
3807              player->present,
3808              player->connected,
3809              player->active);
3810
3811       if (local_player == player)
3812         printf(" (local player)");
3813
3814       printf("\n");
3815     }
3816   }
3817 #endif
3818
3819 #else
3820
3821   /* check if any connected player was not found in playfield */
3822   for (i = 0; i < MAX_PLAYERS; i++)
3823   {
3824     struct PlayerInfo *player = &stored_player[i];
3825
3826     if (player->connected && !player->present)
3827     {
3828       for (j = 0; j < MAX_PLAYERS; j++)
3829       {
3830         struct PlayerInfo *field_player = &stored_player[j];
3831         int jx = field_player->jx, jy = field_player->jy;
3832
3833         /* assign first free player found that is present in the playfield */
3834         if (field_player->present && !field_player->connected)
3835         {
3836           player->present = TRUE;
3837           player->active = TRUE;
3838
3839           field_player->present = FALSE;
3840           field_player->active = FALSE;
3841
3842           player->initial_element = field_player->initial_element;
3843           player->artwork_element = field_player->artwork_element;
3844
3845           player->block_last_field       = field_player->block_last_field;
3846           player->block_delay_adjustment = field_player->block_delay_adjustment;
3847
3848           StorePlayer[jx][jy] = player->element_nr;
3849
3850           player->jx = player->last_jx = jx;
3851           player->jy = player->last_jy = jy;
3852
3853           break;
3854         }
3855       }
3856     }
3857   }
3858 #endif
3859
3860 #if 0
3861   printf("::: local_player->present == %d\n", local_player->present);
3862 #endif
3863
3864   if (tape.playing)
3865   {
3866     /* when playing a tape, eliminate all players who do not participate */
3867
3868 #if USE_NEW_PLAYER_ASSIGNMENTS
3869
3870     if (!game.team_mode)
3871     {
3872       for (i = 0; i < MAX_PLAYERS; i++)
3873       {
3874         if (stored_player[i].active &&
3875             !tape.player_participates[map_player_action[i]])
3876         {
3877           struct PlayerInfo *player = &stored_player[i];
3878           int jx = player->jx, jy = player->jy;
3879
3880 #if DEBUG_INIT_PLAYER
3881           if (options.debug)
3882             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3883 #endif
3884
3885           player->active = FALSE;
3886           StorePlayer[jx][jy] = 0;
3887           Feld[jx][jy] = EL_EMPTY;
3888         }
3889       }
3890     }
3891
3892 #else
3893
3894     for (i = 0; i < MAX_PLAYERS; i++)
3895     {
3896       if (stored_player[i].active &&
3897           !tape.player_participates[i])
3898       {
3899         struct PlayerInfo *player = &stored_player[i];
3900         int jx = player->jx, jy = player->jy;
3901
3902         player->active = FALSE;
3903         StorePlayer[jx][jy] = 0;
3904         Feld[jx][jy] = EL_EMPTY;
3905       }
3906     }
3907 #endif
3908   }
3909   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3910   {
3911     /* when in single player mode, eliminate all but the first active player */
3912
3913     for (i = 0; i < MAX_PLAYERS; i++)
3914     {
3915       if (stored_player[i].active)
3916       {
3917         for (j = i + 1; j < MAX_PLAYERS; j++)
3918         {
3919           if (stored_player[j].active)
3920           {
3921             struct PlayerInfo *player = &stored_player[j];
3922             int jx = player->jx, jy = player->jy;
3923
3924             player->active = FALSE;
3925             player->present = FALSE;
3926
3927             StorePlayer[jx][jy] = 0;
3928             Feld[jx][jy] = EL_EMPTY;
3929           }
3930         }
3931       }
3932     }
3933   }
3934
3935   /* when recording the game, store which players take part in the game */
3936   if (tape.recording)
3937   {
3938 #if USE_NEW_PLAYER_ASSIGNMENTS
3939     for (i = 0; i < MAX_PLAYERS; i++)
3940       if (stored_player[i].connected)
3941         tape.player_participates[i] = TRUE;
3942 #else
3943     for (i = 0; i < MAX_PLAYERS; i++)
3944       if (stored_player[i].active)
3945         tape.player_participates[i] = TRUE;
3946 #endif
3947   }
3948
3949 #if DEBUG_INIT_PLAYER
3950   if (options.debug)
3951   {
3952     printf("Player status after player assignment (final stage):\n");
3953
3954     for (i = 0; i < MAX_PLAYERS; i++)
3955     {
3956       struct PlayerInfo *player = &stored_player[i];
3957
3958       printf("- player %d: present == %d, connected == %d, active == %d",
3959              i + 1,
3960              player->present,
3961              player->connected,
3962              player->active);
3963
3964       if (local_player == player)
3965         printf(" (local player)");
3966
3967       printf("\n");
3968     }
3969   }
3970 #endif
3971
3972   if (BorderElement == EL_EMPTY)
3973   {
3974     SBX_Left = 0;
3975     SBX_Right = lev_fieldx - SCR_FIELDX;
3976     SBY_Upper = 0;
3977     SBY_Lower = lev_fieldy - SCR_FIELDY;
3978   }
3979   else
3980   {
3981     SBX_Left = -1;
3982     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3983     SBY_Upper = -1;
3984     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3985   }
3986
3987   if (full_lev_fieldx <= SCR_FIELDX)
3988     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3989   if (full_lev_fieldy <= SCR_FIELDY)
3990     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3991
3992   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3993     SBX_Left--;
3994   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3995     SBY_Upper--;
3996
3997   /* if local player not found, look for custom element that might create
3998      the player (make some assumptions about the right custom element) */
3999   if (!local_player->present)
4000   {
4001     int start_x = 0, start_y = 0;
4002     int found_rating = 0;
4003     int found_element = EL_UNDEFINED;
4004     int player_nr = local_player->index_nr;
4005
4006     SCAN_PLAYFIELD(x, y)
4007     {
4008       int element = Feld[x][y];
4009       int content;
4010       int xx, yy;
4011       boolean is_player;
4012
4013       if (level.use_start_element[player_nr] &&
4014           level.start_element[player_nr] == element &&
4015           found_rating < 4)
4016       {
4017         start_x = x;
4018         start_y = y;
4019
4020         found_rating = 4;
4021         found_element = element;
4022       }
4023
4024       if (!IS_CUSTOM_ELEMENT(element))
4025         continue;
4026
4027       if (CAN_CHANGE(element))
4028       {
4029         for (i = 0; i < element_info[element].num_change_pages; i++)
4030         {
4031           /* check for player created from custom element as single target */
4032           content = element_info[element].change_page[i].target_element;
4033           is_player = ELEM_IS_PLAYER(content);
4034
4035           if (is_player && (found_rating < 3 ||
4036                             (found_rating == 3 && element < found_element)))
4037           {
4038             start_x = x;
4039             start_y = y;
4040
4041             found_rating = 3;
4042             found_element = element;
4043           }
4044         }
4045       }
4046
4047       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4048       {
4049         /* check for player created from custom element as explosion content */
4050         content = element_info[element].content.e[xx][yy];
4051         is_player = ELEM_IS_PLAYER(content);
4052
4053         if (is_player && (found_rating < 2 ||
4054                           (found_rating == 2 && element < found_element)))
4055         {
4056           start_x = x + xx - 1;
4057           start_y = y + yy - 1;
4058
4059           found_rating = 2;
4060           found_element = element;
4061         }
4062
4063         if (!CAN_CHANGE(element))
4064           continue;
4065
4066         for (i = 0; i < element_info[element].num_change_pages; i++)
4067         {
4068           /* check for player created from custom element as extended target */
4069           content =
4070             element_info[element].change_page[i].target_content.e[xx][yy];
4071
4072           is_player = ELEM_IS_PLAYER(content);
4073
4074           if (is_player && (found_rating < 1 ||
4075                             (found_rating == 1 && element < found_element)))
4076           {
4077             start_x = x + xx - 1;
4078             start_y = y + yy - 1;
4079
4080             found_rating = 1;
4081             found_element = element;
4082           }
4083         }
4084       }
4085     }
4086
4087     scroll_x = SCROLL_POSITION_X(start_x);
4088     scroll_y = SCROLL_POSITION_Y(start_y);
4089   }
4090   else
4091   {
4092     scroll_x = SCROLL_POSITION_X(local_player->jx);
4093     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4094   }
4095
4096   /* !!! FIX THIS (START) !!! */
4097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4098   {
4099     InitGameEngine_EM();
4100   }
4101   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4102   {
4103     InitGameEngine_SP();
4104   }
4105   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4106   {
4107     InitGameEngine_MM();
4108   }
4109   else
4110   {
4111     DrawLevel(REDRAW_FIELD);
4112     DrawAllPlayers();
4113
4114     /* after drawing the level, correct some elements */
4115     if (game.timegate_time_left == 0)
4116       CloseAllOpenTimegates();
4117   }
4118
4119   /* blit playfield from scroll buffer to normal back buffer for fading in */
4120   BlitScreenToBitmap(backbuffer);
4121   /* !!! FIX THIS (END) !!! */
4122
4123   DrawMaskedBorder(fade_mask);
4124
4125   FadeIn(fade_mask);
4126
4127 #if 1
4128   // full screen redraw is required at this point in the following cases:
4129   // - special editor door undrawn when game was started from level editor
4130   // - drawing area (playfield) was changed and has to be removed completely
4131   redraw_mask = REDRAW_ALL;
4132   BackToFront();
4133 #endif
4134
4135   if (!game.restart_level)
4136   {
4137     /* copy default game door content to main double buffer */
4138
4139     /* !!! CHECK AGAIN !!! */
4140     SetPanelBackground();
4141     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4142     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4143   }
4144
4145   SetPanelBackground();
4146   SetDrawBackgroundMask(REDRAW_DOOR_1);
4147
4148   UpdateAndDisplayGameControlValues();
4149
4150   if (!game.restart_level)
4151   {
4152     UnmapGameButtons();
4153     UnmapTapeButtons();
4154
4155     FreeGameButtons();
4156     CreateGameButtons();
4157
4158     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4159     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4160     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4161
4162     MapGameButtons();
4163     MapTapeButtons();
4164
4165     /* copy actual game door content to door double buffer for OpenDoor() */
4166     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4167
4168     OpenDoor(DOOR_OPEN_ALL);
4169
4170     PlaySound(SND_GAME_STARTING);
4171
4172     if (setup.sound_music)
4173       PlayLevelMusic();
4174
4175     KeyboardAutoRepeatOffUnlessAutoplay();
4176
4177 #if DEBUG_INIT_PLAYER
4178     if (options.debug)
4179     {
4180       printf("Player status (final):\n");
4181
4182       for (i = 0; i < MAX_PLAYERS; i++)
4183       {
4184         struct PlayerInfo *player = &stored_player[i];
4185
4186         printf("- player %d: present == %d, connected == %d, active == %d",
4187                i + 1,
4188                player->present,
4189                player->connected,
4190                player->active);
4191
4192         if (local_player == player)
4193           printf(" (local player)");
4194
4195         printf("\n");
4196       }
4197     }
4198 #endif
4199   }
4200
4201   UnmapAllGadgets();
4202
4203   MapGameButtons();
4204   MapTapeButtons();
4205
4206   if (!game.restart_level && !tape.playing)
4207   {
4208     LevelStats_incPlayed(level_nr);
4209
4210     SaveLevelSetup_SeriesInfo();
4211   }
4212
4213   game.restart_level = FALSE;
4214
4215   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4216     InitGameActions_MM();
4217
4218   SaveEngineSnapshotToListInitial();
4219 }
4220
4221 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4222                         int actual_player_x, int actual_player_y)
4223 {
4224   /* this is used for non-R'n'D game engines to update certain engine values */
4225
4226   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4227   {
4228     actual_player_x = correctLevelPosX_EM(actual_player_x);
4229     actual_player_y = correctLevelPosY_EM(actual_player_y);
4230   }
4231
4232   /* needed to determine if sounds are played within the visible screen area */
4233   scroll_x = actual_scroll_x;
4234   scroll_y = actual_scroll_y;
4235
4236   /* needed to get player position for "follow finger" playing input method */
4237   local_player->jx = actual_player_x;
4238   local_player->jy = actual_player_y;
4239 }
4240
4241 void InitMovDir(int x, int y)
4242 {
4243   int i, element = Feld[x][y];
4244   static int xy[4][2] =
4245   {
4246     {  0, +1 },
4247     { +1,  0 },
4248     {  0, -1 },
4249     { -1,  0 }
4250   };
4251   static int direction[3][4] =
4252   {
4253     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4254     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4255     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4256   };
4257
4258   switch (element)
4259   {
4260     case EL_BUG_RIGHT:
4261     case EL_BUG_UP:
4262     case EL_BUG_LEFT:
4263     case EL_BUG_DOWN:
4264       Feld[x][y] = EL_BUG;
4265       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4266       break;
4267
4268     case EL_SPACESHIP_RIGHT:
4269     case EL_SPACESHIP_UP:
4270     case EL_SPACESHIP_LEFT:
4271     case EL_SPACESHIP_DOWN:
4272       Feld[x][y] = EL_SPACESHIP;
4273       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4274       break;
4275
4276     case EL_BD_BUTTERFLY_RIGHT:
4277     case EL_BD_BUTTERFLY_UP:
4278     case EL_BD_BUTTERFLY_LEFT:
4279     case EL_BD_BUTTERFLY_DOWN:
4280       Feld[x][y] = EL_BD_BUTTERFLY;
4281       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4282       break;
4283
4284     case EL_BD_FIREFLY_RIGHT:
4285     case EL_BD_FIREFLY_UP:
4286     case EL_BD_FIREFLY_LEFT:
4287     case EL_BD_FIREFLY_DOWN:
4288       Feld[x][y] = EL_BD_FIREFLY;
4289       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4290       break;
4291
4292     case EL_PACMAN_RIGHT:
4293     case EL_PACMAN_UP:
4294     case EL_PACMAN_LEFT:
4295     case EL_PACMAN_DOWN:
4296       Feld[x][y] = EL_PACMAN;
4297       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4298       break;
4299
4300     case EL_YAMYAM_LEFT:
4301     case EL_YAMYAM_RIGHT:
4302     case EL_YAMYAM_UP:
4303     case EL_YAMYAM_DOWN:
4304       Feld[x][y] = EL_YAMYAM;
4305       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4306       break;
4307
4308     case EL_SP_SNIKSNAK:
4309       MovDir[x][y] = MV_UP;
4310       break;
4311
4312     case EL_SP_ELECTRON:
4313       MovDir[x][y] = MV_LEFT;
4314       break;
4315
4316     case EL_MOLE_LEFT:
4317     case EL_MOLE_RIGHT:
4318     case EL_MOLE_UP:
4319     case EL_MOLE_DOWN:
4320       Feld[x][y] = EL_MOLE;
4321       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4322       break;
4323
4324     default:
4325       if (IS_CUSTOM_ELEMENT(element))
4326       {
4327         struct ElementInfo *ei = &element_info[element];
4328         int move_direction_initial = ei->move_direction_initial;
4329         int move_pattern = ei->move_pattern;
4330
4331         if (move_direction_initial == MV_START_PREVIOUS)
4332         {
4333           if (MovDir[x][y] != MV_NONE)
4334             return;
4335
4336           move_direction_initial = MV_START_AUTOMATIC;
4337         }
4338
4339         if (move_direction_initial == MV_START_RANDOM)
4340           MovDir[x][y] = 1 << RND(4);
4341         else if (move_direction_initial & MV_ANY_DIRECTION)
4342           MovDir[x][y] = move_direction_initial;
4343         else if (move_pattern == MV_ALL_DIRECTIONS ||
4344                  move_pattern == MV_TURNING_LEFT ||
4345                  move_pattern == MV_TURNING_RIGHT ||
4346                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4347                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4348                  move_pattern == MV_TURNING_RANDOM)
4349           MovDir[x][y] = 1 << RND(4);
4350         else if (move_pattern == MV_HORIZONTAL)
4351           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4352         else if (move_pattern == MV_VERTICAL)
4353           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4354         else if (move_pattern & MV_ANY_DIRECTION)
4355           MovDir[x][y] = element_info[element].move_pattern;
4356         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4357                  move_pattern == MV_ALONG_RIGHT_SIDE)
4358         {
4359           /* use random direction as default start direction */
4360           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4361             MovDir[x][y] = 1 << RND(4);
4362
4363           for (i = 0; i < NUM_DIRECTIONS; i++)
4364           {
4365             int x1 = x + xy[i][0];
4366             int y1 = y + xy[i][1];
4367
4368             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4369             {
4370               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4371                 MovDir[x][y] = direction[0][i];
4372               else
4373                 MovDir[x][y] = direction[1][i];
4374
4375               break;
4376             }
4377           }
4378         }                
4379       }
4380       else
4381       {
4382         MovDir[x][y] = 1 << RND(4);
4383
4384         if (element != EL_BUG &&
4385             element != EL_SPACESHIP &&
4386             element != EL_BD_BUTTERFLY &&
4387             element != EL_BD_FIREFLY)
4388           break;
4389
4390         for (i = 0; i < NUM_DIRECTIONS; i++)
4391         {
4392           int x1 = x + xy[i][0];
4393           int y1 = y + xy[i][1];
4394
4395           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4396           {
4397             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4398             {
4399               MovDir[x][y] = direction[0][i];
4400               break;
4401             }
4402             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4403                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4404             {
4405               MovDir[x][y] = direction[1][i];
4406               break;
4407             }
4408           }
4409         }
4410       }
4411       break;
4412   }
4413
4414   GfxDir[x][y] = MovDir[x][y];
4415 }
4416
4417 void InitAmoebaNr(int x, int y)
4418 {
4419   int i;
4420   int group_nr = AmoebeNachbarNr(x, y);
4421
4422   if (group_nr == 0)
4423   {
4424     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4425     {
4426       if (AmoebaCnt[i] == 0)
4427       {
4428         group_nr = i;
4429         break;
4430       }
4431     }
4432   }
4433
4434   AmoebaNr[x][y] = group_nr;
4435   AmoebaCnt[group_nr]++;
4436   AmoebaCnt2[group_nr]++;
4437 }
4438
4439 static void PlayerWins(struct PlayerInfo *player)
4440 {
4441   player->LevelSolved = TRUE;
4442   player->GameOver = TRUE;
4443
4444   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4445                          level.native_em_level->lev->score :
4446                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4447                          game_mm.score :
4448                          player->score);
4449
4450   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4451                                       TimeLeft);
4452   player->LevelSolved_CountingScore = player->score_final;
4453 }
4454
4455 void GameWon()
4456 {
4457   static int time, time_final;
4458   static int score, score_final;
4459   static int game_over_delay_1 = 0;
4460   static int game_over_delay_2 = 0;
4461   int game_over_delay_value_1 = 50;
4462   int game_over_delay_value_2 = 50;
4463
4464   if (!local_player->LevelSolved_GameWon)
4465   {
4466     int i;
4467
4468     /* do not start end game actions before the player stops moving (to exit) */
4469     if (local_player->MovPos)
4470       return;
4471
4472     local_player->LevelSolved_GameWon = TRUE;
4473     local_player->LevelSolved_SaveTape = tape.recording;
4474     local_player->LevelSolved_SaveScore = !tape.playing;
4475
4476     if (!tape.playing)
4477     {
4478       LevelStats_incSolved(level_nr);
4479
4480       SaveLevelSetup_SeriesInfo();
4481     }
4482
4483     if (tape.auto_play)         /* tape might already be stopped here */
4484       tape.auto_play_level_solved = TRUE;
4485
4486     TapeStop();
4487
4488     game_over_delay_1 = game_over_delay_value_1;
4489     game_over_delay_2 = game_over_delay_value_2;
4490
4491     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4492     score = score_final = local_player->score_final;
4493
4494     if (TimeLeft > 0)
4495     {
4496       time_final = 0;
4497       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4498     }
4499     else if (game.no_time_limit && TimePlayed < 999)
4500     {
4501       time_final = 999;
4502       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4503     }
4504
4505     local_player->score_final = score_final;
4506
4507     if (level_editor_test_game)
4508     {
4509       time = time_final;
4510       score = score_final;
4511
4512       local_player->LevelSolved_CountingTime = time;
4513       local_player->LevelSolved_CountingScore = score;
4514
4515       game_panel_controls[GAME_PANEL_TIME].value = time;
4516       game_panel_controls[GAME_PANEL_SCORE].value = score;
4517
4518       DisplayGameControlValues();
4519     }
4520
4521     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4522     {
4523       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4524       {
4525         /* close exit door after last player */
4526         if ((AllPlayersGone &&
4527              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4528               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4529               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4530             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4531             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4532         {
4533           int element = Feld[ExitX][ExitY];
4534
4535           Feld[ExitX][ExitY] =
4536             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4537              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4538              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4539              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4540              EL_EM_STEEL_EXIT_CLOSING);
4541
4542           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4543         }
4544
4545         /* player disappears */
4546         DrawLevelField(ExitX, ExitY);
4547       }
4548
4549       for (i = 0; i < MAX_PLAYERS; i++)
4550       {
4551         struct PlayerInfo *player = &stored_player[i];
4552
4553         if (player->present)
4554         {
4555           RemovePlayer(player);
4556
4557           /* player disappears */
4558           DrawLevelField(player->jx, player->jy);
4559         }
4560       }
4561     }
4562
4563     PlaySound(SND_GAME_WINNING);
4564   }
4565
4566   if (game_over_delay_1 > 0)
4567   {
4568     game_over_delay_1--;
4569
4570     return;
4571   }
4572
4573   if (time != time_final)
4574   {
4575     int time_to_go = ABS(time_final - time);
4576     int time_count_dir = (time < time_final ? +1 : -1);
4577     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4578
4579     time  += time_count_steps * time_count_dir;
4580     score += time_count_steps * level.score[SC_TIME_BONUS];
4581
4582     local_player->LevelSolved_CountingTime = time;
4583     local_player->LevelSolved_CountingScore = score;
4584
4585     game_panel_controls[GAME_PANEL_TIME].value = time;
4586     game_panel_controls[GAME_PANEL_SCORE].value = score;
4587
4588     DisplayGameControlValues();
4589
4590     if (time == time_final)
4591       StopSound(SND_GAME_LEVELTIME_BONUS);
4592     else if (setup.sound_loops)
4593       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4594     else
4595       PlaySound(SND_GAME_LEVELTIME_BONUS);
4596
4597     return;
4598   }
4599
4600   local_player->LevelSolved_PanelOff = TRUE;
4601
4602   if (game_over_delay_2 > 0)
4603   {
4604     game_over_delay_2--;
4605
4606     return;
4607   }
4608
4609   GameEnd();
4610 }
4611
4612 void GameEnd()
4613 {
4614   int hi_pos;
4615   boolean raise_level = FALSE;
4616
4617   local_player->LevelSolved_GameEnd = TRUE;
4618
4619   if (!global.use_envelope_request)
4620     CloseDoor(DOOR_CLOSE_1);
4621
4622   if (local_player->LevelSolved_SaveTape)
4623   {
4624     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4625   }
4626
4627   CloseDoor(DOOR_CLOSE_ALL);
4628
4629   if (level_editor_test_game)
4630   {
4631     SetGameStatus(GAME_MODE_MAIN);
4632
4633     DrawMainMenu();
4634
4635     return;
4636   }
4637
4638   if (!local_player->LevelSolved_SaveScore)
4639   {
4640     SetGameStatus(GAME_MODE_MAIN);
4641
4642     DrawMainMenu();
4643
4644     return;
4645   }
4646
4647   if (level_nr == leveldir_current->handicap_level)
4648   {
4649     leveldir_current->handicap_level++;
4650
4651     SaveLevelSetup_SeriesInfo();
4652   }
4653
4654   if (setup.increment_levels &&
4655       level_nr < leveldir_current->last_level)
4656     raise_level = TRUE;                 /* advance to next level */
4657
4658   if ((hi_pos = NewHiScore()) >= 0) 
4659   {
4660     SetGameStatus(GAME_MODE_SCORES);
4661
4662     DrawHallOfFame(hi_pos);
4663
4664     if (raise_level)
4665     {
4666       level_nr++;
4667       TapeErase();
4668     }
4669   }
4670   else
4671   {
4672     SetGameStatus(GAME_MODE_MAIN);
4673
4674     if (raise_level)
4675     {
4676       level_nr++;
4677       TapeErase();
4678     }
4679
4680     DrawMainMenu();
4681   }
4682 }
4683
4684 int NewHiScore()
4685 {
4686   int k, l;
4687   int position = -1;
4688   boolean one_score_entry_per_name = !program.many_scores_per_name;
4689
4690   LoadScore(level_nr);
4691
4692   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4693       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4694     return -1;
4695
4696   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4697   {
4698     if (local_player->score_final > highscore[k].Score)
4699     {
4700       /* player has made it to the hall of fame */
4701
4702       if (k < MAX_SCORE_ENTRIES - 1)
4703       {
4704         int m = MAX_SCORE_ENTRIES - 1;
4705
4706         if (one_score_entry_per_name)
4707         {
4708           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4709             if (strEqual(setup.player_name, highscore[l].Name))
4710               m = l;
4711
4712           if (m == k)   /* player's new highscore overwrites his old one */
4713             goto put_into_list;
4714         }
4715
4716         for (l = m; l > k; l--)
4717         {
4718           strcpy(highscore[l].Name, highscore[l - 1].Name);
4719           highscore[l].Score = highscore[l - 1].Score;
4720         }
4721       }
4722
4723       put_into_list:
4724
4725       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4726       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4727       highscore[k].Score = local_player->score_final; 
4728       position = k;
4729
4730       break;
4731     }
4732     else if (one_score_entry_per_name &&
4733              !strncmp(setup.player_name, highscore[k].Name,
4734                       MAX_PLAYER_NAME_LEN))
4735       break;    /* player already there with a higher score */
4736   }
4737
4738   if (position >= 0) 
4739     SaveScore(level_nr);
4740
4741   return position;
4742 }
4743
4744 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4745 {
4746   int element = Feld[x][y];
4747   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4748   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4749   int horiz_move = (dx != 0);
4750   int sign = (horiz_move ? dx : dy);
4751   int step = sign * element_info[element].move_stepsize;
4752
4753   /* special values for move stepsize for spring and things on conveyor belt */
4754   if (horiz_move)
4755   {
4756     if (CAN_FALL(element) &&
4757         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4758       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4759     else if (element == EL_SPRING)
4760       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4761   }
4762
4763   return step;
4764 }
4765
4766 inline static int getElementMoveStepsize(int x, int y)
4767 {
4768   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4769 }
4770
4771 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4772 {
4773   if (player->GfxAction != action || player->GfxDir != dir)
4774   {
4775     player->GfxAction = action;
4776     player->GfxDir = dir;
4777     player->Frame = 0;
4778     player->StepFrame = 0;
4779   }
4780 }
4781
4782 static void ResetGfxFrame(int x, int y)
4783 {
4784   // profiling showed that "autotest" spends 10~20% of its time in this function
4785   if (DrawingDeactivatedField())
4786     return;
4787
4788   int element = Feld[x][y];
4789   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4790
4791   if (graphic_info[graphic].anim_global_sync)
4792     GfxFrame[x][y] = FrameCounter;
4793   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4794     GfxFrame[x][y] = CustomValue[x][y];
4795   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4796     GfxFrame[x][y] = element_info[element].collect_score;
4797   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4798     GfxFrame[x][y] = ChangeDelay[x][y];
4799 }
4800
4801 static void ResetGfxAnimation(int x, int y)
4802 {
4803   GfxAction[x][y] = ACTION_DEFAULT;
4804   GfxDir[x][y] = MovDir[x][y];
4805   GfxFrame[x][y] = 0;
4806
4807   ResetGfxFrame(x, y);
4808 }
4809
4810 static void ResetRandomAnimationValue(int x, int y)
4811 {
4812   GfxRandom[x][y] = INIT_GFX_RANDOM();
4813 }
4814
4815 void InitMovingField(int x, int y, int direction)
4816 {
4817   int element = Feld[x][y];
4818   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4819   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4820   int newx = x + dx;
4821   int newy = y + dy;
4822   boolean is_moving_before, is_moving_after;
4823
4824   /* check if element was/is moving or being moved before/after mode change */
4825   is_moving_before = (WasJustMoving[x][y] != 0);
4826   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4827
4828   /* reset animation only for moving elements which change direction of moving
4829      or which just started or stopped moving
4830      (else CEs with property "can move" / "not moving" are reset each frame) */
4831   if (is_moving_before != is_moving_after ||
4832       direction != MovDir[x][y])
4833     ResetGfxAnimation(x, y);
4834
4835   MovDir[x][y] = direction;
4836   GfxDir[x][y] = direction;
4837
4838   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4839                      direction == MV_DOWN && CAN_FALL(element) ?
4840                      ACTION_FALLING : ACTION_MOVING);
4841
4842   /* this is needed for CEs with property "can move" / "not moving" */
4843
4844   if (is_moving_after)
4845   {
4846     if (Feld[newx][newy] == EL_EMPTY)
4847       Feld[newx][newy] = EL_BLOCKED;
4848
4849     MovDir[newx][newy] = MovDir[x][y];
4850
4851     CustomValue[newx][newy] = CustomValue[x][y];
4852
4853     GfxFrame[newx][newy] = GfxFrame[x][y];
4854     GfxRandom[newx][newy] = GfxRandom[x][y];
4855     GfxAction[newx][newy] = GfxAction[x][y];
4856     GfxDir[newx][newy] = GfxDir[x][y];
4857   }
4858 }
4859
4860 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4861 {
4862   int direction = MovDir[x][y];
4863   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4864   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4865
4866   *goes_to_x = newx;
4867   *goes_to_y = newy;
4868 }
4869
4870 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4871 {
4872   int oldx = x, oldy = y;
4873   int direction = MovDir[x][y];
4874
4875   if (direction == MV_LEFT)
4876     oldx++;
4877   else if (direction == MV_RIGHT)
4878     oldx--;
4879   else if (direction == MV_UP)
4880     oldy++;
4881   else if (direction == MV_DOWN)
4882     oldy--;
4883
4884   *comes_from_x = oldx;
4885   *comes_from_y = oldy;
4886 }
4887
4888 int MovingOrBlocked2Element(int x, int y)
4889 {
4890   int element = Feld[x][y];
4891
4892   if (element == EL_BLOCKED)
4893   {
4894     int oldx, oldy;
4895
4896     Blocked2Moving(x, y, &oldx, &oldy);
4897     return Feld[oldx][oldy];
4898   }
4899   else
4900     return element;
4901 }
4902
4903 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4904 {
4905   /* like MovingOrBlocked2Element(), but if element is moving
4906      and (x,y) is the field the moving element is just leaving,
4907      return EL_BLOCKED instead of the element value */
4908   int element = Feld[x][y];
4909
4910   if (IS_MOVING(x, y))
4911   {
4912     if (element == EL_BLOCKED)
4913     {
4914       int oldx, oldy;
4915
4916       Blocked2Moving(x, y, &oldx, &oldy);
4917       return Feld[oldx][oldy];
4918     }
4919     else
4920       return EL_BLOCKED;
4921   }
4922   else
4923     return element;
4924 }
4925
4926 static void RemoveField(int x, int y)
4927 {
4928   Feld[x][y] = EL_EMPTY;
4929
4930   MovPos[x][y] = 0;
4931   MovDir[x][y] = 0;
4932   MovDelay[x][y] = 0;
4933
4934   CustomValue[x][y] = 0;
4935
4936   AmoebaNr[x][y] = 0;
4937   ChangeDelay[x][y] = 0;
4938   ChangePage[x][y] = -1;
4939   Pushed[x][y] = FALSE;
4940
4941   GfxElement[x][y] = EL_UNDEFINED;
4942   GfxAction[x][y] = ACTION_DEFAULT;
4943   GfxDir[x][y] = MV_NONE;
4944 }
4945
4946 void RemoveMovingField(int x, int y)
4947 {
4948   int oldx = x, oldy = y, newx = x, newy = y;
4949   int element = Feld[x][y];
4950   int next_element = EL_UNDEFINED;
4951
4952   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4953     return;
4954
4955   if (IS_MOVING(x, y))
4956   {
4957     Moving2Blocked(x, y, &newx, &newy);
4958
4959     if (Feld[newx][newy] != EL_BLOCKED)
4960     {
4961       /* element is moving, but target field is not free (blocked), but
4962          already occupied by something different (example: acid pool);
4963          in this case, only remove the moving field, but not the target */
4964
4965       RemoveField(oldx, oldy);
4966
4967       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4968
4969       TEST_DrawLevelField(oldx, oldy);
4970
4971       return;
4972     }
4973   }
4974   else if (element == EL_BLOCKED)
4975   {
4976     Blocked2Moving(x, y, &oldx, &oldy);
4977     if (!IS_MOVING(oldx, oldy))
4978       return;
4979   }
4980
4981   if (element == EL_BLOCKED &&
4982       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4983        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4984        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4985        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4986        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4987        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4988     next_element = get_next_element(Feld[oldx][oldy]);
4989
4990   RemoveField(oldx, oldy);
4991   RemoveField(newx, newy);
4992
4993   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4994
4995   if (next_element != EL_UNDEFINED)
4996     Feld[oldx][oldy] = next_element;
4997
4998   TEST_DrawLevelField(oldx, oldy);
4999   TEST_DrawLevelField(newx, newy);
5000 }
5001
5002 void DrawDynamite(int x, int y)
5003 {
5004   int sx = SCREENX(x), sy = SCREENY(y);
5005   int graphic = el2img(Feld[x][y]);
5006   int frame;
5007
5008   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5009     return;
5010
5011   if (IS_WALKABLE_INSIDE(Back[x][y]))
5012     return;
5013
5014   if (Back[x][y])
5015     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5016   else if (Store[x][y])
5017     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5018
5019   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5020
5021   if (Back[x][y] || Store[x][y])
5022     DrawGraphicThruMask(sx, sy, graphic, frame);
5023   else
5024     DrawGraphic(sx, sy, graphic, frame);
5025 }
5026
5027 void CheckDynamite(int x, int y)
5028 {
5029   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5030   {
5031     MovDelay[x][y]--;
5032
5033     if (MovDelay[x][y] != 0)
5034     {
5035       DrawDynamite(x, y);
5036       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5037
5038       return;
5039     }
5040   }
5041
5042   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5043
5044   Bang(x, y);
5045 }
5046
5047 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5048 {
5049   boolean num_checked_players = 0;
5050   int i;
5051
5052   for (i = 0; i < MAX_PLAYERS; i++)
5053   {
5054     if (stored_player[i].active)
5055     {
5056       int sx = stored_player[i].jx;
5057       int sy = stored_player[i].jy;
5058
5059       if (num_checked_players == 0)
5060       {
5061         *sx1 = *sx2 = sx;
5062         *sy1 = *sy2 = sy;
5063       }
5064       else
5065       {
5066         *sx1 = MIN(*sx1, sx);
5067         *sy1 = MIN(*sy1, sy);
5068         *sx2 = MAX(*sx2, sx);
5069         *sy2 = MAX(*sy2, sy);
5070       }
5071
5072       num_checked_players++;
5073     }
5074   }
5075 }
5076
5077 static boolean checkIfAllPlayersFitToScreen_RND()
5078 {
5079   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5080
5081   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5082
5083   return (sx2 - sx1 < SCR_FIELDX &&
5084           sy2 - sy1 < SCR_FIELDY);
5085 }
5086
5087 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5088 {
5089   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5090
5091   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5092
5093   *sx = (sx1 + sx2) / 2;
5094   *sy = (sy1 + sy2) / 2;
5095 }
5096
5097 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5098                         boolean center_screen, boolean quick_relocation)
5099 {
5100   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5101   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5102   boolean no_delay = (tape.warp_forward);
5103   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5104   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5105   int new_scroll_x, new_scroll_y;
5106
5107   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5108   {
5109     /* case 1: quick relocation inside visible screen (without scrolling) */
5110
5111     RedrawPlayfield();
5112
5113     return;
5114   }
5115
5116   if (!level.shifted_relocation || center_screen)
5117   {
5118     /* relocation _with_ centering of screen */
5119
5120     new_scroll_x = SCROLL_POSITION_X(x);
5121     new_scroll_y = SCROLL_POSITION_Y(y);
5122   }
5123   else
5124   {
5125     /* relocation _without_ centering of screen */
5126
5127     int center_scroll_x = SCROLL_POSITION_X(old_x);
5128     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5129     int offset_x = x + (scroll_x - center_scroll_x);
5130     int offset_y = y + (scroll_y - center_scroll_y);
5131
5132     /* for new screen position, apply previous offset to center position */
5133     new_scroll_x = SCROLL_POSITION_X(offset_x);
5134     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5135   }
5136
5137   if (quick_relocation)
5138   {
5139     /* case 2: quick relocation (redraw without visible scrolling) */
5140
5141     scroll_x = new_scroll_x;
5142     scroll_y = new_scroll_y;
5143
5144     RedrawPlayfield();
5145
5146     return;
5147   }
5148
5149   /* case 3: visible relocation (with scrolling to new position) */
5150
5151   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5152
5153   SetVideoFrameDelay(wait_delay_value);
5154
5155   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5156   {
5157     int dx = 0, dy = 0;
5158     int fx = FX, fy = FY;
5159
5160     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5161     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5162
5163     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5164       break;
5165
5166     scroll_x -= dx;
5167     scroll_y -= dy;
5168
5169     fx += dx * TILEX / 2;
5170     fy += dy * TILEY / 2;
5171
5172     ScrollLevel(dx, dy);
5173     DrawAllPlayers();
5174
5175     /* scroll in two steps of half tile size to make things smoother */
5176     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5177
5178     /* scroll second step to align at full tile size */
5179     BlitScreenToBitmap(window);
5180   }
5181
5182   DrawAllPlayers();
5183   BackToFront();
5184
5185   SetVideoFrameDelay(frame_delay_value_old);
5186 }
5187
5188 void RelocatePlayer(int jx, int jy, int el_player_raw)
5189 {
5190   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5191   int player_nr = GET_PLAYER_NR(el_player);
5192   struct PlayerInfo *player = &stored_player[player_nr];
5193   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5194   boolean no_delay = (tape.warp_forward);
5195   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5196   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5197   int old_jx = player->jx;
5198   int old_jy = player->jy;
5199   int old_element = Feld[old_jx][old_jy];
5200   int element = Feld[jx][jy];
5201   boolean player_relocated = (old_jx != jx || old_jy != jy);
5202
5203   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5204   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5205   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5206   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5207   int leave_side_horiz = move_dir_horiz;
5208   int leave_side_vert  = move_dir_vert;
5209   int enter_side = enter_side_horiz | enter_side_vert;
5210   int leave_side = leave_side_horiz | leave_side_vert;
5211
5212   if (player->GameOver)         /* do not reanimate dead player */
5213     return;
5214
5215   if (!player_relocated)        /* no need to relocate the player */
5216     return;
5217
5218   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5219   {
5220     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5221     DrawLevelField(jx, jy);
5222   }
5223
5224   if (player->present)
5225   {
5226     while (player->MovPos)
5227     {
5228       ScrollPlayer(player, SCROLL_GO_ON);
5229       ScrollScreen(NULL, SCROLL_GO_ON);
5230
5231       AdvanceFrameAndPlayerCounters(player->index_nr);
5232
5233       DrawPlayer(player);
5234
5235       BackToFront_WithFrameDelay(wait_delay_value);
5236     }
5237
5238     DrawPlayer(player);         /* needed here only to cleanup last field */
5239     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5240
5241     player->is_moving = FALSE;
5242   }
5243
5244   if (IS_CUSTOM_ELEMENT(old_element))
5245     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5246                                CE_LEFT_BY_PLAYER,
5247                                player->index_bit, leave_side);
5248
5249   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5250                                       CE_PLAYER_LEAVES_X,
5251                                       player->index_bit, leave_side);
5252
5253   Feld[jx][jy] = el_player;
5254   InitPlayerField(jx, jy, el_player, TRUE);
5255
5256   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5257      possible that the relocation target field did not contain a player element,
5258      but a walkable element, to which the new player was relocated -- in this
5259      case, restore that (already initialized!) element on the player field */
5260   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5261   {
5262     Feld[jx][jy] = element;     /* restore previously existing element */
5263   }
5264
5265   /* only visually relocate centered player */
5266   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5267                      FALSE, level.instant_relocation);
5268
5269   TestIfPlayerTouchesBadThing(jx, jy);
5270   TestIfPlayerTouchesCustomElement(jx, jy);
5271
5272   if (IS_CUSTOM_ELEMENT(element))
5273     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5274                                player->index_bit, enter_side);
5275
5276   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5277                                       player->index_bit, enter_side);
5278
5279   if (player->is_switching)
5280   {
5281     /* ensure that relocation while still switching an element does not cause
5282        a new element to be treated as also switched directly after relocation
5283        (this is important for teleporter switches that teleport the player to
5284        a place where another teleporter switch is in the same direction, which
5285        would then incorrectly be treated as immediately switched before the
5286        direction key that caused the switch was released) */
5287
5288     player->switch_x += jx - old_jx;
5289     player->switch_y += jy - old_jy;
5290   }
5291 }
5292
5293 void Explode(int ex, int ey, int phase, int mode)
5294 {
5295   int x, y;
5296   int last_phase;
5297   int border_element;
5298
5299   /* !!! eliminate this variable !!! */
5300   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5301
5302   if (game.explosions_delayed)
5303   {
5304     ExplodeField[ex][ey] = mode;
5305     return;
5306   }
5307
5308   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5309   {
5310     int center_element = Feld[ex][ey];
5311     int artwork_element, explosion_element;     /* set these values later */
5312
5313     /* remove things displayed in background while burning dynamite */
5314     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5315       Back[ex][ey] = 0;
5316
5317     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5318     {
5319       /* put moving element to center field (and let it explode there) */
5320       center_element = MovingOrBlocked2Element(ex, ey);
5321       RemoveMovingField(ex, ey);
5322       Feld[ex][ey] = center_element;
5323     }
5324
5325     /* now "center_element" is finally determined -- set related values now */
5326     artwork_element = center_element;           /* for custom player artwork */
5327     explosion_element = center_element;         /* for custom player artwork */
5328
5329     if (IS_PLAYER(ex, ey))
5330     {
5331       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5332
5333       artwork_element = stored_player[player_nr].artwork_element;
5334
5335       if (level.use_explosion_element[player_nr])
5336       {
5337         explosion_element = level.explosion_element[player_nr];
5338         artwork_element = explosion_element;
5339       }
5340     }
5341
5342     if (mode == EX_TYPE_NORMAL ||
5343         mode == EX_TYPE_CENTER ||
5344         mode == EX_TYPE_CROSS)
5345       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5346
5347     last_phase = element_info[explosion_element].explosion_delay + 1;
5348
5349     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5350     {
5351       int xx = x - ex + 1;
5352       int yy = y - ey + 1;
5353       int element;
5354
5355       if (!IN_LEV_FIELD(x, y) ||
5356           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5357           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5358         continue;
5359
5360       element = Feld[x][y];
5361
5362       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5363       {
5364         element = MovingOrBlocked2Element(x, y);
5365
5366         if (!IS_EXPLOSION_PROOF(element))
5367           RemoveMovingField(x, y);
5368       }
5369
5370       /* indestructible elements can only explode in center (but not flames) */
5371       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5372                                            mode == EX_TYPE_BORDER)) ||
5373           element == EL_FLAMES)
5374         continue;
5375
5376       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5377          behaviour, for example when touching a yamyam that explodes to rocks
5378          with active deadly shield, a rock is created under the player !!! */
5379       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5380 #if 0
5381       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5382           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5383            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5384 #else
5385       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5386 #endif
5387       {
5388         if (IS_ACTIVE_BOMB(element))
5389         {
5390           /* re-activate things under the bomb like gate or penguin */
5391           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5392           Back[x][y] = 0;
5393         }
5394
5395         continue;
5396       }
5397
5398       /* save walkable background elements while explosion on same tile */
5399       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5400           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5401         Back[x][y] = element;
5402
5403       /* ignite explodable elements reached by other explosion */
5404       if (element == EL_EXPLOSION)
5405         element = Store2[x][y];
5406
5407       if (AmoebaNr[x][y] &&
5408           (element == EL_AMOEBA_FULL ||
5409            element == EL_BD_AMOEBA ||
5410            element == EL_AMOEBA_GROWING))
5411       {
5412         AmoebaCnt[AmoebaNr[x][y]]--;
5413         AmoebaCnt2[AmoebaNr[x][y]]--;
5414       }
5415
5416       RemoveField(x, y);
5417
5418       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5419       {
5420         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5421
5422         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5423
5424         if (PLAYERINFO(ex, ey)->use_murphy)
5425           Store[x][y] = EL_EMPTY;
5426       }
5427
5428       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5429          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5430       else if (ELEM_IS_PLAYER(center_element))
5431         Store[x][y] = EL_EMPTY;
5432       else if (center_element == EL_YAMYAM)
5433         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5434       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5435         Store[x][y] = element_info[center_element].content.e[xx][yy];
5436 #if 1
5437       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5438          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5439          otherwise) -- FIX THIS !!! */
5440       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5441         Store[x][y] = element_info[element].content.e[1][1];
5442 #else
5443       else if (!CAN_EXPLODE(element))
5444         Store[x][y] = element_info[element].content.e[1][1];
5445 #endif
5446       else
5447         Store[x][y] = EL_EMPTY;
5448
5449       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5450           center_element == EL_AMOEBA_TO_DIAMOND)
5451         Store2[x][y] = element;
5452
5453       Feld[x][y] = EL_EXPLOSION;
5454       GfxElement[x][y] = artwork_element;
5455
5456       ExplodePhase[x][y] = 1;
5457       ExplodeDelay[x][y] = last_phase;
5458
5459       Stop[x][y] = TRUE;
5460     }
5461
5462     if (center_element == EL_YAMYAM)
5463       game.yamyam_content_nr =
5464         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5465
5466     return;
5467   }
5468
5469   if (Stop[ex][ey])
5470     return;
5471
5472   x = ex;
5473   y = ey;
5474
5475   if (phase == 1)
5476     GfxFrame[x][y] = 0;         /* restart explosion animation */
5477
5478   last_phase = ExplodeDelay[x][y];
5479
5480   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5481
5482   /* this can happen if the player leaves an explosion just in time */
5483   if (GfxElement[x][y] == EL_UNDEFINED)
5484     GfxElement[x][y] = EL_EMPTY;
5485
5486   border_element = Store2[x][y];
5487   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5488     border_element = StorePlayer[x][y];
5489
5490   if (phase == element_info[border_element].ignition_delay ||
5491       phase == last_phase)
5492   {
5493     boolean border_explosion = FALSE;
5494
5495     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5496         !PLAYER_EXPLOSION_PROTECTED(x, y))
5497     {
5498       KillPlayerUnlessExplosionProtected(x, y);
5499       border_explosion = TRUE;
5500     }
5501     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5502     {
5503       Feld[x][y] = Store2[x][y];
5504       Store2[x][y] = 0;
5505       Bang(x, y);
5506       border_explosion = TRUE;
5507     }
5508     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5509     {
5510       AmoebeUmwandeln(x, y);
5511       Store2[x][y] = 0;
5512       border_explosion = TRUE;
5513     }
5514
5515     /* if an element just explodes due to another explosion (chain-reaction),
5516        do not immediately end the new explosion when it was the last frame of
5517        the explosion (as it would be done in the following "if"-statement!) */
5518     if (border_explosion && phase == last_phase)
5519       return;
5520   }
5521
5522   if (phase == last_phase)
5523   {
5524     int element;
5525
5526     element = Feld[x][y] = Store[x][y];
5527     Store[x][y] = Store2[x][y] = 0;
5528     GfxElement[x][y] = EL_UNDEFINED;
5529
5530     /* player can escape from explosions and might therefore be still alive */
5531     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5532         element <= EL_PLAYER_IS_EXPLODING_4)
5533     {
5534       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5535       int explosion_element = EL_PLAYER_1 + player_nr;
5536       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5537       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5538
5539       if (level.use_explosion_element[player_nr])
5540         explosion_element = level.explosion_element[player_nr];
5541
5542       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5543                     element_info[explosion_element].content.e[xx][yy]);
5544     }
5545
5546     /* restore probably existing indestructible background element */
5547     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5548       element = Feld[x][y] = Back[x][y];
5549     Back[x][y] = 0;
5550
5551     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5552     GfxDir[x][y] = MV_NONE;
5553     ChangeDelay[x][y] = 0;
5554     ChangePage[x][y] = -1;
5555
5556     CustomValue[x][y] = 0;
5557
5558     InitField_WithBug2(x, y, FALSE);
5559
5560     TEST_DrawLevelField(x, y);
5561
5562     TestIfElementTouchesCustomElement(x, y);
5563
5564     if (GFX_CRUMBLED(element))
5565       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5566
5567     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5568       StorePlayer[x][y] = 0;
5569
5570     if (ELEM_IS_PLAYER(element))
5571       RelocatePlayer(x, y, element);
5572   }
5573   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5574   {
5575     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5576     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5577
5578     if (phase == delay)
5579       TEST_DrawLevelFieldCrumbled(x, y);
5580
5581     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5582     {
5583       DrawLevelElement(x, y, Back[x][y]);
5584       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5585     }
5586     else if (IS_WALKABLE_UNDER(Back[x][y]))
5587     {
5588       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5589       DrawLevelElementThruMask(x, y, Back[x][y]);
5590     }
5591     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5592       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5593   }
5594 }
5595
5596 void DynaExplode(int ex, int ey)
5597 {
5598   int i, j;
5599   int dynabomb_element = Feld[ex][ey];
5600   int dynabomb_size = 1;
5601   boolean dynabomb_xl = FALSE;
5602   struct PlayerInfo *player;
5603   static int xy[4][2] =
5604   {
5605     { 0, -1 },
5606     { -1, 0 },
5607     { +1, 0 },
5608     { 0, +1 }
5609   };
5610
5611   if (IS_ACTIVE_BOMB(dynabomb_element))
5612   {
5613     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5614     dynabomb_size = player->dynabomb_size;
5615     dynabomb_xl = player->dynabomb_xl;
5616     player->dynabombs_left++;
5617   }
5618
5619   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5620
5621   for (i = 0; i < NUM_DIRECTIONS; i++)
5622   {
5623     for (j = 1; j <= dynabomb_size; j++)
5624     {
5625       int x = ex + j * xy[i][0];
5626       int y = ey + j * xy[i][1];
5627       int element;
5628
5629       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5630         break;
5631
5632       element = Feld[x][y];
5633
5634       /* do not restart explosions of fields with active bombs */
5635       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5636         continue;
5637
5638       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5639
5640       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5641           !IS_DIGGABLE(element) && !dynabomb_xl)
5642         break;
5643     }
5644   }
5645 }
5646
5647 void Bang(int x, int y)
5648 {
5649   int element = MovingOrBlocked2Element(x, y);
5650   int explosion_type = EX_TYPE_NORMAL;
5651
5652   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5653   {
5654     struct PlayerInfo *player = PLAYERINFO(x, y);
5655
5656     element = Feld[x][y] = player->initial_element;
5657
5658     if (level.use_explosion_element[player->index_nr])
5659     {
5660       int explosion_element = level.explosion_element[player->index_nr];
5661
5662       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5663         explosion_type = EX_TYPE_CROSS;
5664       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5665         explosion_type = EX_TYPE_CENTER;
5666     }
5667   }
5668
5669   switch (element)
5670   {
5671     case EL_BUG:
5672     case EL_SPACESHIP:
5673     case EL_BD_BUTTERFLY:
5674     case EL_BD_FIREFLY:
5675     case EL_YAMYAM:
5676     case EL_DARK_YAMYAM:
5677     case EL_ROBOT:
5678     case EL_PACMAN:
5679     case EL_MOLE:
5680       RaiseScoreElement(element);
5681       break;
5682
5683     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5684     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5685     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5686     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5687     case EL_DYNABOMB_INCREASE_NUMBER:
5688     case EL_DYNABOMB_INCREASE_SIZE:
5689     case EL_DYNABOMB_INCREASE_POWER:
5690       explosion_type = EX_TYPE_DYNA;
5691       break;
5692
5693     case EL_DC_LANDMINE:
5694       explosion_type = EX_TYPE_CENTER;
5695       break;
5696
5697     case EL_PENGUIN:
5698     case EL_LAMP:
5699     case EL_LAMP_ACTIVE:
5700     case EL_AMOEBA_TO_DIAMOND:
5701       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5702         explosion_type = EX_TYPE_CENTER;
5703       break;
5704
5705     default:
5706       if (element_info[element].explosion_type == EXPLODES_CROSS)
5707         explosion_type = EX_TYPE_CROSS;
5708       else if (element_info[element].explosion_type == EXPLODES_1X1)
5709         explosion_type = EX_TYPE_CENTER;
5710       break;
5711   }
5712
5713   if (explosion_type == EX_TYPE_DYNA)
5714     DynaExplode(x, y);
5715   else
5716     Explode(x, y, EX_PHASE_START, explosion_type);
5717
5718   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5719 }
5720
5721 void SplashAcid(int x, int y)
5722 {
5723   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5724       (!IN_LEV_FIELD(x - 1, y - 2) ||
5725        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5726     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5727
5728   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5729       (!IN_LEV_FIELD(x + 1, y - 2) ||
5730        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5731     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5732
5733   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5734 }
5735
5736 static void InitBeltMovement()
5737 {
5738   static int belt_base_element[4] =
5739   {
5740     EL_CONVEYOR_BELT_1_LEFT,
5741     EL_CONVEYOR_BELT_2_LEFT,
5742     EL_CONVEYOR_BELT_3_LEFT,
5743     EL_CONVEYOR_BELT_4_LEFT
5744   };
5745   static int belt_base_active_element[4] =
5746   {
5747     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5748     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5749     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5750     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5751   };
5752
5753   int x, y, i, j;
5754
5755   /* set frame order for belt animation graphic according to belt direction */
5756   for (i = 0; i < NUM_BELTS; i++)
5757   {
5758     int belt_nr = i;
5759
5760     for (j = 0; j < NUM_BELT_PARTS; j++)
5761     {
5762       int element = belt_base_active_element[belt_nr] + j;
5763       int graphic_1 = el2img(element);
5764       int graphic_2 = el2panelimg(element);
5765
5766       if (game.belt_dir[i] == MV_LEFT)
5767       {
5768         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5769         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5770       }
5771       else
5772       {
5773         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5774         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5775       }
5776     }
5777   }
5778
5779   SCAN_PLAYFIELD(x, y)
5780   {
5781     int element = Feld[x][y];
5782
5783     for (i = 0; i < NUM_BELTS; i++)
5784     {
5785       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5786       {
5787         int e_belt_nr = getBeltNrFromBeltElement(element);
5788         int belt_nr = i;
5789
5790         if (e_belt_nr == belt_nr)
5791         {
5792           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5793
5794           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5795         }
5796       }
5797     }
5798   }
5799 }
5800
5801 static void ToggleBeltSwitch(int x, int y)
5802 {
5803   static int belt_base_element[4] =
5804   {
5805     EL_CONVEYOR_BELT_1_LEFT,
5806     EL_CONVEYOR_BELT_2_LEFT,
5807     EL_CONVEYOR_BELT_3_LEFT,
5808     EL_CONVEYOR_BELT_4_LEFT
5809   };
5810   static int belt_base_active_element[4] =
5811   {
5812     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5813     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5814     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5815     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5816   };
5817   static int belt_base_switch_element[4] =
5818   {
5819     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5820     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5821     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5822     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5823   };
5824   static int belt_move_dir[4] =
5825   {
5826     MV_LEFT,
5827     MV_NONE,
5828     MV_RIGHT,
5829     MV_NONE,
5830   };
5831
5832   int element = Feld[x][y];
5833   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5834   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5835   int belt_dir = belt_move_dir[belt_dir_nr];
5836   int xx, yy, i;
5837
5838   if (!IS_BELT_SWITCH(element))
5839     return;
5840
5841   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5842   game.belt_dir[belt_nr] = belt_dir;
5843
5844   if (belt_dir_nr == 3)
5845     belt_dir_nr = 1;
5846
5847   /* set frame order for belt animation graphic according to belt direction */
5848   for (i = 0; i < NUM_BELT_PARTS; i++)
5849   {
5850     int element = belt_base_active_element[belt_nr] + i;
5851     int graphic_1 = el2img(element);
5852     int graphic_2 = el2panelimg(element);
5853
5854     if (belt_dir == MV_LEFT)
5855     {
5856       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5857       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5858     }
5859     else
5860     {
5861       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5862       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5863     }
5864   }
5865
5866   SCAN_PLAYFIELD(xx, yy)
5867   {
5868     int element = Feld[xx][yy];
5869
5870     if (IS_BELT_SWITCH(element))
5871     {
5872       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5873
5874       if (e_belt_nr == belt_nr)
5875       {
5876         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5877         TEST_DrawLevelField(xx, yy);
5878       }
5879     }
5880     else if (IS_BELT(element) && belt_dir != MV_NONE)
5881     {
5882       int e_belt_nr = getBeltNrFromBeltElement(element);
5883
5884       if (e_belt_nr == belt_nr)
5885       {
5886         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5887
5888         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5889         TEST_DrawLevelField(xx, yy);
5890       }
5891     }
5892     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5893     {
5894       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5895
5896       if (e_belt_nr == belt_nr)
5897       {
5898         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5899
5900         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5901         TEST_DrawLevelField(xx, yy);
5902       }
5903     }
5904   }
5905 }
5906
5907 static void ToggleSwitchgateSwitch(int x, int y)
5908 {
5909   int xx, yy;
5910
5911   game.switchgate_pos = !game.switchgate_pos;
5912
5913   SCAN_PLAYFIELD(xx, yy)
5914   {
5915     int element = Feld[xx][yy];
5916
5917     if (element == EL_SWITCHGATE_SWITCH_UP)
5918     {
5919       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5920       TEST_DrawLevelField(xx, yy);
5921     }
5922     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5923     {
5924       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5925       TEST_DrawLevelField(xx, yy);
5926     }
5927     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5928     {
5929       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5930       TEST_DrawLevelField(xx, yy);
5931     }
5932     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5933     {
5934       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5935       TEST_DrawLevelField(xx, yy);
5936     }
5937     else if (element == EL_SWITCHGATE_OPEN ||
5938              element == EL_SWITCHGATE_OPENING)
5939     {
5940       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5941
5942       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5943     }
5944     else if (element == EL_SWITCHGATE_CLOSED ||
5945              element == EL_SWITCHGATE_CLOSING)
5946     {
5947       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5948
5949       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5950     }
5951   }
5952 }
5953
5954 static int getInvisibleActiveFromInvisibleElement(int element)
5955 {
5956   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5957           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5958           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5959           element);
5960 }
5961
5962 static int getInvisibleFromInvisibleActiveElement(int element)
5963 {
5964   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5965           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5966           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5967           element);
5968 }
5969
5970 static void RedrawAllLightSwitchesAndInvisibleElements()
5971 {
5972   int x, y;
5973
5974   SCAN_PLAYFIELD(x, y)
5975   {
5976     int element = Feld[x][y];
5977
5978     if (element == EL_LIGHT_SWITCH &&
5979         game.light_time_left > 0)
5980     {
5981       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5982       TEST_DrawLevelField(x, y);
5983     }
5984     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5985              game.light_time_left == 0)
5986     {
5987       Feld[x][y] = EL_LIGHT_SWITCH;
5988       TEST_DrawLevelField(x, y);
5989     }
5990     else if (element == EL_EMC_DRIPPER &&
5991              game.light_time_left > 0)
5992     {
5993       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5994       TEST_DrawLevelField(x, y);
5995     }
5996     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5997              game.light_time_left == 0)
5998     {
5999       Feld[x][y] = EL_EMC_DRIPPER;
6000       TEST_DrawLevelField(x, y);
6001     }
6002     else if (element == EL_INVISIBLE_STEELWALL ||
6003              element == EL_INVISIBLE_WALL ||
6004              element == EL_INVISIBLE_SAND)
6005     {
6006       if (game.light_time_left > 0)
6007         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6008
6009       TEST_DrawLevelField(x, y);
6010
6011       /* uncrumble neighbour fields, if needed */
6012       if (element == EL_INVISIBLE_SAND)
6013         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6014     }
6015     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6016              element == EL_INVISIBLE_WALL_ACTIVE ||
6017              element == EL_INVISIBLE_SAND_ACTIVE)
6018     {
6019       if (game.light_time_left == 0)
6020         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6021
6022       TEST_DrawLevelField(x, y);
6023
6024       /* re-crumble neighbour fields, if needed */
6025       if (element == EL_INVISIBLE_SAND)
6026         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6027     }
6028   }
6029 }
6030
6031 static void RedrawAllInvisibleElementsForLenses()
6032 {
6033   int x, y;
6034
6035   SCAN_PLAYFIELD(x, y)
6036   {
6037     int element = Feld[x][y];
6038
6039     if (element == EL_EMC_DRIPPER &&
6040         game.lenses_time_left > 0)
6041     {
6042       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6043       TEST_DrawLevelField(x, y);
6044     }
6045     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6046              game.lenses_time_left == 0)
6047     {
6048       Feld[x][y] = EL_EMC_DRIPPER;
6049       TEST_DrawLevelField(x, y);
6050     }
6051     else if (element == EL_INVISIBLE_STEELWALL ||
6052              element == EL_INVISIBLE_WALL ||
6053              element == EL_INVISIBLE_SAND)
6054     {
6055       if (game.lenses_time_left > 0)
6056         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6057
6058       TEST_DrawLevelField(x, y);
6059
6060       /* uncrumble neighbour fields, if needed */
6061       if (element == EL_INVISIBLE_SAND)
6062         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6063     }
6064     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6065              element == EL_INVISIBLE_WALL_ACTIVE ||
6066              element == EL_INVISIBLE_SAND_ACTIVE)
6067     {
6068       if (game.lenses_time_left == 0)
6069         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6070
6071       TEST_DrawLevelField(x, y);
6072
6073       /* re-crumble neighbour fields, if needed */
6074       if (element == EL_INVISIBLE_SAND)
6075         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6076     }
6077   }
6078 }
6079
6080 static void RedrawAllInvisibleElementsForMagnifier()
6081 {
6082   int x, y;
6083
6084   SCAN_PLAYFIELD(x, y)
6085   {
6086     int element = Feld[x][y];
6087
6088     if (element == EL_EMC_FAKE_GRASS &&
6089         game.magnify_time_left > 0)
6090     {
6091       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6092       TEST_DrawLevelField(x, y);
6093     }
6094     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6095              game.magnify_time_left == 0)
6096     {
6097       Feld[x][y] = EL_EMC_FAKE_GRASS;
6098       TEST_DrawLevelField(x, y);
6099     }
6100     else if (IS_GATE_GRAY(element) &&
6101              game.magnify_time_left > 0)
6102     {
6103       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6104                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6105                     IS_EM_GATE_GRAY(element) ?
6106                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6107                     IS_EMC_GATE_GRAY(element) ?
6108                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6109                     IS_DC_GATE_GRAY(element) ?
6110                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6111                     element);
6112       TEST_DrawLevelField(x, y);
6113     }
6114     else if (IS_GATE_GRAY_ACTIVE(element) &&
6115              game.magnify_time_left == 0)
6116     {
6117       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6118                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6119                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6120                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6121                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6122                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6123                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6124                     EL_DC_GATE_WHITE_GRAY :
6125                     element);
6126       TEST_DrawLevelField(x, y);
6127     }
6128   }
6129 }
6130
6131 static void ToggleLightSwitch(int x, int y)
6132 {
6133   int element = Feld[x][y];
6134
6135   game.light_time_left =
6136     (element == EL_LIGHT_SWITCH ?
6137      level.time_light * FRAMES_PER_SECOND : 0);
6138
6139   RedrawAllLightSwitchesAndInvisibleElements();
6140 }
6141
6142 static void ActivateTimegateSwitch(int x, int y)
6143 {
6144   int xx, yy;
6145
6146   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6147
6148   SCAN_PLAYFIELD(xx, yy)
6149   {
6150     int element = Feld[xx][yy];
6151
6152     if (element == EL_TIMEGATE_CLOSED ||
6153         element == EL_TIMEGATE_CLOSING)
6154     {
6155       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6156       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6157     }
6158
6159     /*
6160     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6161     {
6162       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6163       TEST_DrawLevelField(xx, yy);
6164     }
6165     */
6166
6167   }
6168
6169   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6170                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6171 }
6172
6173 void Impact(int x, int y)
6174 {
6175   boolean last_line = (y == lev_fieldy - 1);
6176   boolean object_hit = FALSE;
6177   boolean impact = (last_line || object_hit);
6178   int element = Feld[x][y];
6179   int smashed = EL_STEELWALL;
6180
6181   if (!last_line)       /* check if element below was hit */
6182   {
6183     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6184       return;
6185
6186     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6187                                          MovDir[x][y + 1] != MV_DOWN ||
6188                                          MovPos[x][y + 1] <= TILEY / 2));
6189
6190     /* do not smash moving elements that left the smashed field in time */
6191     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6192         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6193       object_hit = FALSE;
6194
6195 #if USE_QUICKSAND_IMPACT_BUGFIX
6196     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6197     {
6198       RemoveMovingField(x, y + 1);
6199       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6200       Feld[x][y + 2] = EL_ROCK;
6201       TEST_DrawLevelField(x, y + 2);
6202
6203       object_hit = TRUE;
6204     }
6205
6206     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6207     {
6208       RemoveMovingField(x, y + 1);
6209       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6210       Feld[x][y + 2] = EL_ROCK;
6211       TEST_DrawLevelField(x, y + 2);
6212
6213       object_hit = TRUE;
6214     }
6215 #endif
6216
6217     if (object_hit)
6218       smashed = MovingOrBlocked2Element(x, y + 1);
6219
6220     impact = (last_line || object_hit);
6221   }
6222
6223   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6224   {
6225     SplashAcid(x, y + 1);
6226     return;
6227   }
6228
6229   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6230   /* only reset graphic animation if graphic really changes after impact */
6231   if (impact &&
6232       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6233   {
6234     ResetGfxAnimation(x, y);
6235     TEST_DrawLevelField(x, y);
6236   }
6237
6238   if (impact && CAN_EXPLODE_IMPACT(element))
6239   {
6240     Bang(x, y);
6241     return;
6242   }
6243   else if (impact && element == EL_PEARL &&
6244            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6245   {
6246     ResetGfxAnimation(x, y);
6247
6248     Feld[x][y] = EL_PEARL_BREAKING;
6249     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6250     return;
6251   }
6252   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6253   {
6254     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6255
6256     return;
6257   }
6258
6259   if (impact && element == EL_AMOEBA_DROP)
6260   {
6261     if (object_hit && IS_PLAYER(x, y + 1))
6262       KillPlayerUnlessEnemyProtected(x, y + 1);
6263     else if (object_hit && smashed == EL_PENGUIN)
6264       Bang(x, y + 1);
6265     else
6266     {
6267       Feld[x][y] = EL_AMOEBA_GROWING;
6268       Store[x][y] = EL_AMOEBA_WET;
6269
6270       ResetRandomAnimationValue(x, y);
6271     }
6272     return;
6273   }
6274
6275   if (object_hit)               /* check which object was hit */
6276   {
6277     if ((CAN_PASS_MAGIC_WALL(element) && 
6278          (smashed == EL_MAGIC_WALL ||
6279           smashed == EL_BD_MAGIC_WALL)) ||
6280         (CAN_PASS_DC_MAGIC_WALL(element) &&
6281          smashed == EL_DC_MAGIC_WALL))
6282     {
6283       int xx, yy;
6284       int activated_magic_wall =
6285         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6286          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6287          EL_DC_MAGIC_WALL_ACTIVE);
6288
6289       /* activate magic wall / mill */
6290       SCAN_PLAYFIELD(xx, yy)
6291       {
6292         if (Feld[xx][yy] == smashed)
6293           Feld[xx][yy] = activated_magic_wall;
6294       }
6295
6296       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6297       game.magic_wall_active = TRUE;
6298
6299       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6300                             SND_MAGIC_WALL_ACTIVATING :
6301                             smashed == EL_BD_MAGIC_WALL ?
6302                             SND_BD_MAGIC_WALL_ACTIVATING :
6303                             SND_DC_MAGIC_WALL_ACTIVATING));
6304     }
6305
6306     if (IS_PLAYER(x, y + 1))
6307     {
6308       if (CAN_SMASH_PLAYER(element))
6309       {
6310         KillPlayerUnlessEnemyProtected(x, y + 1);
6311         return;
6312       }
6313     }
6314     else if (smashed == EL_PENGUIN)
6315     {
6316       if (CAN_SMASH_PLAYER(element))
6317       {
6318         Bang(x, y + 1);
6319         return;
6320       }
6321     }
6322     else if (element == EL_BD_DIAMOND)
6323     {
6324       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6325       {
6326         Bang(x, y + 1);
6327         return;
6328       }
6329     }
6330     else if (((element == EL_SP_INFOTRON ||
6331                element == EL_SP_ZONK) &&
6332               (smashed == EL_SP_SNIKSNAK ||
6333                smashed == EL_SP_ELECTRON ||
6334                smashed == EL_SP_DISK_ORANGE)) ||
6335              (element == EL_SP_INFOTRON &&
6336               smashed == EL_SP_DISK_YELLOW))
6337     {
6338       Bang(x, y + 1);
6339       return;
6340     }
6341     else if (CAN_SMASH_EVERYTHING(element))
6342     {
6343       if (IS_CLASSIC_ENEMY(smashed) ||
6344           CAN_EXPLODE_SMASHED(smashed))
6345       {
6346         Bang(x, y + 1);
6347         return;
6348       }
6349       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6350       {
6351         if (smashed == EL_LAMP ||
6352             smashed == EL_LAMP_ACTIVE)
6353         {
6354           Bang(x, y + 1);
6355           return;
6356         }
6357         else if (smashed == EL_NUT)
6358         {
6359           Feld[x][y + 1] = EL_NUT_BREAKING;
6360           PlayLevelSound(x, y, SND_NUT_BREAKING);
6361           RaiseScoreElement(EL_NUT);
6362           return;
6363         }
6364         else if (smashed == EL_PEARL)
6365         {
6366           ResetGfxAnimation(x, y);
6367
6368           Feld[x][y + 1] = EL_PEARL_BREAKING;
6369           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6370           return;
6371         }
6372         else if (smashed == EL_DIAMOND)
6373         {
6374           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6375           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6376           return;
6377         }
6378         else if (IS_BELT_SWITCH(smashed))
6379         {
6380           ToggleBeltSwitch(x, y + 1);
6381         }
6382         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6383                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6384                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6385                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6386         {
6387           ToggleSwitchgateSwitch(x, y + 1);
6388         }
6389         else if (smashed == EL_LIGHT_SWITCH ||
6390                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6391         {
6392           ToggleLightSwitch(x, y + 1);
6393         }
6394         else
6395         {
6396           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6397
6398           CheckElementChangeBySide(x, y + 1, smashed, element,
6399                                    CE_SWITCHED, CH_SIDE_TOP);
6400           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6401                                             CH_SIDE_TOP);
6402         }
6403       }
6404       else
6405       {
6406         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6407       }
6408     }
6409   }
6410
6411   /* play sound of magic wall / mill */
6412   if (!last_line &&
6413       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6414        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6415        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6416   {
6417     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6418       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6419     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6420       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6421     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6422       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6423
6424     return;
6425   }
6426
6427   /* play sound of object that hits the ground */
6428   if (last_line || object_hit)
6429     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6430 }
6431
6432 inline static void TurnRoundExt(int x, int y)
6433 {
6434   static struct
6435   {
6436     int dx, dy;
6437   } move_xy[] =
6438   {
6439     {  0,  0 },
6440     { -1,  0 },
6441     { +1,  0 },
6442     {  0,  0 },
6443     {  0, -1 },
6444     {  0,  0 }, { 0, 0 }, { 0, 0 },
6445     {  0, +1 }
6446   };
6447   static struct
6448   {
6449     int left, right, back;
6450   } turn[] =
6451   {
6452     { 0,        0,              0        },
6453     { MV_DOWN,  MV_UP,          MV_RIGHT },
6454     { MV_UP,    MV_DOWN,        MV_LEFT  },
6455     { 0,        0,              0        },
6456     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6457     { 0,        0,              0        },
6458     { 0,        0,              0        },
6459     { 0,        0,              0        },
6460     { MV_RIGHT, MV_LEFT,        MV_UP    }
6461   };
6462
6463   int element = Feld[x][y];
6464   int move_pattern = element_info[element].move_pattern;
6465
6466   int old_move_dir = MovDir[x][y];
6467   int left_dir  = turn[old_move_dir].left;
6468   int right_dir = turn[old_move_dir].right;
6469   int back_dir  = turn[old_move_dir].back;
6470
6471   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6472   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6473   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6474   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6475
6476   int left_x  = x + left_dx,  left_y  = y + left_dy;
6477   int right_x = x + right_dx, right_y = y + right_dy;
6478   int move_x  = x + move_dx,  move_y  = y + move_dy;
6479
6480   int xx, yy;
6481
6482   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6483   {
6484     TestIfBadThingTouchesOtherBadThing(x, y);
6485
6486     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6487       MovDir[x][y] = right_dir;
6488     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6489       MovDir[x][y] = left_dir;
6490
6491     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6492       MovDelay[x][y] = 9;
6493     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6494       MovDelay[x][y] = 1;
6495   }
6496   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6497   {
6498     TestIfBadThingTouchesOtherBadThing(x, y);
6499
6500     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6501       MovDir[x][y] = left_dir;
6502     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6503       MovDir[x][y] = right_dir;
6504
6505     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6506       MovDelay[x][y] = 9;
6507     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6508       MovDelay[x][y] = 1;
6509   }
6510   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6511   {
6512     TestIfBadThingTouchesOtherBadThing(x, y);
6513
6514     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6515       MovDir[x][y] = left_dir;
6516     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6517       MovDir[x][y] = right_dir;
6518
6519     if (MovDir[x][y] != old_move_dir)
6520       MovDelay[x][y] = 9;
6521   }
6522   else if (element == EL_YAMYAM)
6523   {
6524     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6525     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6526
6527     if (can_turn_left && can_turn_right)
6528       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6529     else if (can_turn_left)
6530       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6531     else if (can_turn_right)
6532       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6533     else
6534       MovDir[x][y] = back_dir;
6535
6536     MovDelay[x][y] = 16 + 16 * RND(3);
6537   }
6538   else if (element == EL_DARK_YAMYAM)
6539   {
6540     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6541                                                          left_x, left_y);
6542     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6543                                                          right_x, right_y);
6544
6545     if (can_turn_left && can_turn_right)
6546       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6547     else if (can_turn_left)
6548       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6549     else if (can_turn_right)
6550       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6551     else
6552       MovDir[x][y] = back_dir;
6553
6554     MovDelay[x][y] = 16 + 16 * RND(3);
6555   }
6556   else if (element == EL_PACMAN)
6557   {
6558     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6559     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6560
6561     if (can_turn_left && can_turn_right)
6562       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6563     else if (can_turn_left)
6564       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6565     else if (can_turn_right)
6566       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6567     else
6568       MovDir[x][y] = back_dir;
6569
6570     MovDelay[x][y] = 6 + RND(40);
6571   }
6572   else if (element == EL_PIG)
6573   {
6574     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6575     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6576     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6577     boolean should_turn_left, should_turn_right, should_move_on;
6578     int rnd_value = 24;
6579     int rnd = RND(rnd_value);
6580
6581     should_turn_left = (can_turn_left &&
6582                         (!can_move_on ||
6583                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6584                                                    y + back_dy + left_dy)));
6585     should_turn_right = (can_turn_right &&
6586                          (!can_move_on ||
6587                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6588                                                     y + back_dy + right_dy)));
6589     should_move_on = (can_move_on &&
6590                       (!can_turn_left ||
6591                        !can_turn_right ||
6592                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6593                                                  y + move_dy + left_dy) ||
6594                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6595                                                  y + move_dy + right_dy)));
6596
6597     if (should_turn_left || should_turn_right || should_move_on)
6598     {
6599       if (should_turn_left && should_turn_right && should_move_on)
6600         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6601                         rnd < 2 * rnd_value / 3 ? right_dir :
6602                         old_move_dir);
6603       else if (should_turn_left && should_turn_right)
6604         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6605       else if (should_turn_left && should_move_on)
6606         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6607       else if (should_turn_right && should_move_on)
6608         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6609       else if (should_turn_left)
6610         MovDir[x][y] = left_dir;
6611       else if (should_turn_right)
6612         MovDir[x][y] = right_dir;
6613       else if (should_move_on)
6614         MovDir[x][y] = old_move_dir;
6615     }
6616     else if (can_move_on && rnd > rnd_value / 8)
6617       MovDir[x][y] = old_move_dir;
6618     else if (can_turn_left && can_turn_right)
6619       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6620     else if (can_turn_left && rnd > rnd_value / 8)
6621       MovDir[x][y] = left_dir;
6622     else if (can_turn_right && rnd > rnd_value/8)
6623       MovDir[x][y] = right_dir;
6624     else
6625       MovDir[x][y] = back_dir;
6626
6627     xx = x + move_xy[MovDir[x][y]].dx;
6628     yy = y + move_xy[MovDir[x][y]].dy;
6629
6630     if (!IN_LEV_FIELD(xx, yy) ||
6631         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6632       MovDir[x][y] = old_move_dir;
6633
6634     MovDelay[x][y] = 0;
6635   }
6636   else if (element == EL_DRAGON)
6637   {
6638     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6639     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6640     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6641     int rnd_value = 24;
6642     int rnd = RND(rnd_value);
6643
6644     if (can_move_on && rnd > rnd_value / 8)
6645       MovDir[x][y] = old_move_dir;
6646     else if (can_turn_left && can_turn_right)
6647       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6648     else if (can_turn_left && rnd > rnd_value / 8)
6649       MovDir[x][y] = left_dir;
6650     else if (can_turn_right && rnd > rnd_value / 8)
6651       MovDir[x][y] = right_dir;
6652     else
6653       MovDir[x][y] = back_dir;
6654
6655     xx = x + move_xy[MovDir[x][y]].dx;
6656     yy = y + move_xy[MovDir[x][y]].dy;
6657
6658     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6659       MovDir[x][y] = old_move_dir;
6660
6661     MovDelay[x][y] = 0;
6662   }
6663   else if (element == EL_MOLE)
6664   {
6665     boolean can_move_on =
6666       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6667                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6668                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6669     if (!can_move_on)
6670     {
6671       boolean can_turn_left =
6672         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6673                               IS_AMOEBOID(Feld[left_x][left_y])));
6674
6675       boolean can_turn_right =
6676         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6677                               IS_AMOEBOID(Feld[right_x][right_y])));
6678
6679       if (can_turn_left && can_turn_right)
6680         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6681       else if (can_turn_left)
6682         MovDir[x][y] = left_dir;
6683       else
6684         MovDir[x][y] = right_dir;
6685     }
6686
6687     if (MovDir[x][y] != old_move_dir)
6688       MovDelay[x][y] = 9;
6689   }
6690   else if (element == EL_BALLOON)
6691   {
6692     MovDir[x][y] = game.wind_direction;
6693     MovDelay[x][y] = 0;
6694   }
6695   else if (element == EL_SPRING)
6696   {
6697     if (MovDir[x][y] & MV_HORIZONTAL)
6698     {
6699       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6700           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6701       {
6702         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6703         ResetGfxAnimation(move_x, move_y);
6704         TEST_DrawLevelField(move_x, move_y);
6705
6706         MovDir[x][y] = back_dir;
6707       }
6708       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6709                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6710         MovDir[x][y] = MV_NONE;
6711     }
6712
6713     MovDelay[x][y] = 0;
6714   }
6715   else if (element == EL_ROBOT ||
6716            element == EL_SATELLITE ||
6717            element == EL_PENGUIN ||
6718            element == EL_EMC_ANDROID)
6719   {
6720     int attr_x = -1, attr_y = -1;
6721
6722     if (AllPlayersGone)
6723     {
6724       attr_x = ExitX;
6725       attr_y = ExitY;
6726     }
6727     else
6728     {
6729       int i;
6730
6731       for (i = 0; i < MAX_PLAYERS; i++)
6732       {
6733         struct PlayerInfo *player = &stored_player[i];
6734         int jx = player->jx, jy = player->jy;
6735
6736         if (!player->active)
6737           continue;
6738
6739         if (attr_x == -1 ||
6740             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6741         {
6742           attr_x = jx;
6743           attr_y = jy;
6744         }
6745       }
6746     }
6747
6748     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6749         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6750          game.engine_version < VERSION_IDENT(3,1,0,0)))
6751     {
6752       attr_x = ZX;
6753       attr_y = ZY;
6754     }
6755
6756     if (element == EL_PENGUIN)
6757     {
6758       int i;
6759       static int xy[4][2] =
6760       {
6761         { 0, -1 },
6762         { -1, 0 },
6763         { +1, 0 },
6764         { 0, +1 }
6765       };
6766
6767       for (i = 0; i < NUM_DIRECTIONS; i++)
6768       {
6769         int ex = x + xy[i][0];
6770         int ey = y + xy[i][1];
6771
6772         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6773                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6774                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6775                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6776         {
6777           attr_x = ex;
6778           attr_y = ey;
6779           break;
6780         }
6781       }
6782     }
6783
6784     MovDir[x][y] = MV_NONE;
6785     if (attr_x < x)
6786       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6787     else if (attr_x > x)
6788       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6789     if (attr_y < y)
6790       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6791     else if (attr_y > y)
6792       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6793
6794     if (element == EL_ROBOT)
6795     {
6796       int newx, newy;
6797
6798       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6799         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6800       Moving2Blocked(x, y, &newx, &newy);
6801
6802       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6803         MovDelay[x][y] = 8 + 8 * !RND(3);
6804       else
6805         MovDelay[x][y] = 16;
6806     }
6807     else if (element == EL_PENGUIN)
6808     {
6809       int newx, newy;
6810
6811       MovDelay[x][y] = 1;
6812
6813       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6814       {
6815         boolean first_horiz = RND(2);
6816         int new_move_dir = MovDir[x][y];
6817
6818         MovDir[x][y] =
6819           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6820         Moving2Blocked(x, y, &newx, &newy);
6821
6822         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6823           return;
6824
6825         MovDir[x][y] =
6826           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6827         Moving2Blocked(x, y, &newx, &newy);
6828
6829         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6830           return;
6831
6832         MovDir[x][y] = old_move_dir;
6833         return;
6834       }
6835     }
6836     else if (element == EL_SATELLITE)
6837     {
6838       int newx, newy;
6839
6840       MovDelay[x][y] = 1;
6841
6842       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6843       {
6844         boolean first_horiz = RND(2);
6845         int new_move_dir = MovDir[x][y];
6846
6847         MovDir[x][y] =
6848           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6849         Moving2Blocked(x, y, &newx, &newy);
6850
6851         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6852           return;
6853
6854         MovDir[x][y] =
6855           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6856         Moving2Blocked(x, y, &newx, &newy);
6857
6858         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6859           return;
6860
6861         MovDir[x][y] = old_move_dir;
6862         return;
6863       }
6864     }
6865     else if (element == EL_EMC_ANDROID)
6866     {
6867       static int check_pos[16] =
6868       {
6869         -1,             /*  0 => (invalid)          */
6870         7,              /*  1 => MV_LEFT            */
6871         3,              /*  2 => MV_RIGHT           */
6872         -1,             /*  3 => (invalid)          */
6873         1,              /*  4 =>            MV_UP   */
6874         0,              /*  5 => MV_LEFT  | MV_UP   */
6875         2,              /*  6 => MV_RIGHT | MV_UP   */
6876         -1,             /*  7 => (invalid)          */
6877         5,              /*  8 =>            MV_DOWN */
6878         6,              /*  9 => MV_LEFT  | MV_DOWN */
6879         4,              /* 10 => MV_RIGHT | MV_DOWN */
6880         -1,             /* 11 => (invalid)          */
6881         -1,             /* 12 => (invalid)          */
6882         -1,             /* 13 => (invalid)          */
6883         -1,             /* 14 => (invalid)          */
6884         -1,             /* 15 => (invalid)          */
6885       };
6886       static struct
6887       {
6888         int dx, dy;
6889         int dir;
6890       } check_xy[8] =
6891       {
6892         { -1, -1,       MV_LEFT  | MV_UP   },
6893         {  0, -1,                  MV_UP   },
6894         { +1, -1,       MV_RIGHT | MV_UP   },
6895         { +1,  0,       MV_RIGHT           },
6896         { +1, +1,       MV_RIGHT | MV_DOWN },
6897         {  0, +1,                  MV_DOWN },
6898         { -1, +1,       MV_LEFT  | MV_DOWN },
6899         { -1,  0,       MV_LEFT            },
6900       };
6901       int start_pos, check_order;
6902       boolean can_clone = FALSE;
6903       int i;
6904
6905       /* check if there is any free field around current position */
6906       for (i = 0; i < 8; i++)
6907       {
6908         int newx = x + check_xy[i].dx;
6909         int newy = y + check_xy[i].dy;
6910
6911         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6912         {
6913           can_clone = TRUE;
6914
6915           break;
6916         }
6917       }
6918
6919       if (can_clone)            /* randomly find an element to clone */
6920       {
6921         can_clone = FALSE;
6922
6923         start_pos = check_pos[RND(8)];
6924         check_order = (RND(2) ? -1 : +1);
6925
6926         for (i = 0; i < 8; i++)
6927         {
6928           int pos_raw = start_pos + i * check_order;
6929           int pos = (pos_raw + 8) % 8;
6930           int newx = x + check_xy[pos].dx;
6931           int newy = y + check_xy[pos].dy;
6932
6933           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6934           {
6935             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6936             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6937
6938             Store[x][y] = Feld[newx][newy];
6939
6940             can_clone = TRUE;
6941
6942             break;
6943           }
6944         }
6945       }
6946
6947       if (can_clone)            /* randomly find a direction to move */
6948       {
6949         can_clone = FALSE;
6950
6951         start_pos = check_pos[RND(8)];
6952         check_order = (RND(2) ? -1 : +1);
6953
6954         for (i = 0; i < 8; i++)
6955         {
6956           int pos_raw = start_pos + i * check_order;
6957           int pos = (pos_raw + 8) % 8;
6958           int newx = x + check_xy[pos].dx;
6959           int newy = y + check_xy[pos].dy;
6960           int new_move_dir = check_xy[pos].dir;
6961
6962           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6963           {
6964             MovDir[x][y] = new_move_dir;
6965             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6966
6967             can_clone = TRUE;
6968
6969             break;
6970           }
6971         }
6972       }
6973
6974       if (can_clone)            /* cloning and moving successful */
6975         return;
6976
6977       /* cannot clone -- try to move towards player */
6978
6979       start_pos = check_pos[MovDir[x][y] & 0x0f];
6980       check_order = (RND(2) ? -1 : +1);
6981
6982       for (i = 0; i < 3; i++)
6983       {
6984         /* first check start_pos, then previous/next or (next/previous) pos */
6985         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6986         int pos = (pos_raw + 8) % 8;
6987         int newx = x + check_xy[pos].dx;
6988         int newy = y + check_xy[pos].dy;
6989         int new_move_dir = check_xy[pos].dir;
6990
6991         if (IS_PLAYER(newx, newy))
6992           break;
6993
6994         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6995         {
6996           MovDir[x][y] = new_move_dir;
6997           MovDelay[x][y] = level.android_move_time * 8 + 1;
6998
6999           break;
7000         }
7001       }
7002     }
7003   }
7004   else if (move_pattern == MV_TURNING_LEFT ||
7005            move_pattern == MV_TURNING_RIGHT ||
7006            move_pattern == MV_TURNING_LEFT_RIGHT ||
7007            move_pattern == MV_TURNING_RIGHT_LEFT ||
7008            move_pattern == MV_TURNING_RANDOM ||
7009            move_pattern == MV_ALL_DIRECTIONS)
7010   {
7011     boolean can_turn_left =
7012       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7013     boolean can_turn_right =
7014       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7015
7016     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7017       return;
7018
7019     if (move_pattern == MV_TURNING_LEFT)
7020       MovDir[x][y] = left_dir;
7021     else if (move_pattern == MV_TURNING_RIGHT)
7022       MovDir[x][y] = right_dir;
7023     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7024       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7025     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7026       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7027     else if (move_pattern == MV_TURNING_RANDOM)
7028       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7029                       can_turn_right && !can_turn_left ? right_dir :
7030                       RND(2) ? left_dir : right_dir);
7031     else if (can_turn_left && can_turn_right)
7032       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7033     else if (can_turn_left)
7034       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7035     else if (can_turn_right)
7036       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7037     else
7038       MovDir[x][y] = back_dir;
7039
7040     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7041   }
7042   else if (move_pattern == MV_HORIZONTAL ||
7043            move_pattern == MV_VERTICAL)
7044   {
7045     if (move_pattern & old_move_dir)
7046       MovDir[x][y] = back_dir;
7047     else if (move_pattern == MV_HORIZONTAL)
7048       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7049     else if (move_pattern == MV_VERTICAL)
7050       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7051
7052     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7053   }
7054   else if (move_pattern & MV_ANY_DIRECTION)
7055   {
7056     MovDir[x][y] = move_pattern;
7057     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7058   }
7059   else if (move_pattern & MV_WIND_DIRECTION)
7060   {
7061     MovDir[x][y] = game.wind_direction;
7062     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7063   }
7064   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7065   {
7066     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7067       MovDir[x][y] = left_dir;
7068     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7069       MovDir[x][y] = right_dir;
7070
7071     if (MovDir[x][y] != old_move_dir)
7072       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7073   }
7074   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7075   {
7076     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7077       MovDir[x][y] = right_dir;
7078     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7079       MovDir[x][y] = left_dir;
7080
7081     if (MovDir[x][y] != old_move_dir)
7082       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7083   }
7084   else if (move_pattern == MV_TOWARDS_PLAYER ||
7085            move_pattern == MV_AWAY_FROM_PLAYER)
7086   {
7087     int attr_x = -1, attr_y = -1;
7088     int newx, newy;
7089     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7090
7091     if (AllPlayersGone)
7092     {
7093       attr_x = ExitX;
7094       attr_y = ExitY;
7095     }
7096     else
7097     {
7098       int i;
7099
7100       for (i = 0; i < MAX_PLAYERS; i++)
7101       {
7102         struct PlayerInfo *player = &stored_player[i];
7103         int jx = player->jx, jy = player->jy;
7104
7105         if (!player->active)
7106           continue;
7107
7108         if (attr_x == -1 ||
7109             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7110         {
7111           attr_x = jx;
7112           attr_y = jy;
7113         }
7114       }
7115     }
7116
7117     MovDir[x][y] = MV_NONE;
7118     if (attr_x < x)
7119       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7120     else if (attr_x > x)
7121       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7122     if (attr_y < y)
7123       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7124     else if (attr_y > y)
7125       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7126
7127     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7128
7129     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7130     {
7131       boolean first_horiz = RND(2);
7132       int new_move_dir = MovDir[x][y];
7133
7134       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7135       {
7136         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7137         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7138
7139         return;
7140       }
7141
7142       MovDir[x][y] =
7143         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7144       Moving2Blocked(x, y, &newx, &newy);
7145
7146       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7147         return;
7148
7149       MovDir[x][y] =
7150         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7151       Moving2Blocked(x, y, &newx, &newy);
7152
7153       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7154         return;
7155
7156       MovDir[x][y] = old_move_dir;
7157     }
7158   }
7159   else if (move_pattern == MV_WHEN_PUSHED ||
7160            move_pattern == MV_WHEN_DROPPED)
7161   {
7162     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7163       MovDir[x][y] = MV_NONE;
7164
7165     MovDelay[x][y] = 0;
7166   }
7167   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7168   {
7169     static int test_xy[7][2] =
7170     {
7171       { 0, -1 },
7172       { -1, 0 },
7173       { +1, 0 },
7174       { 0, +1 },
7175       { 0, -1 },
7176       { -1, 0 },
7177       { +1, 0 },
7178     };
7179     static int test_dir[7] =
7180     {
7181       MV_UP,
7182       MV_LEFT,
7183       MV_RIGHT,
7184       MV_DOWN,
7185       MV_UP,
7186       MV_LEFT,
7187       MV_RIGHT,
7188     };
7189     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7190     int move_preference = -1000000;     /* start with very low preference */
7191     int new_move_dir = MV_NONE;
7192     int start_test = RND(4);
7193     int i;
7194
7195     for (i = 0; i < NUM_DIRECTIONS; i++)
7196     {
7197       int move_dir = test_dir[start_test + i];
7198       int move_dir_preference;
7199
7200       xx = x + test_xy[start_test + i][0];
7201       yy = y + test_xy[start_test + i][1];
7202
7203       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7204           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7205       {
7206         new_move_dir = move_dir;
7207
7208         break;
7209       }
7210
7211       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7212         continue;
7213
7214       move_dir_preference = -1 * RunnerVisit[xx][yy];
7215       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7216         move_dir_preference = PlayerVisit[xx][yy];
7217
7218       if (move_dir_preference > move_preference)
7219       {
7220         /* prefer field that has not been visited for the longest time */
7221         move_preference = move_dir_preference;
7222         new_move_dir = move_dir;
7223       }
7224       else if (move_dir_preference == move_preference &&
7225                move_dir == old_move_dir)
7226       {
7227         /* prefer last direction when all directions are preferred equally */
7228         move_preference = move_dir_preference;
7229         new_move_dir = move_dir;
7230       }
7231     }
7232
7233     MovDir[x][y] = new_move_dir;
7234     if (old_move_dir != new_move_dir)
7235       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7236   }
7237 }
7238
7239 static void TurnRound(int x, int y)
7240 {
7241   int direction = MovDir[x][y];
7242
7243   TurnRoundExt(x, y);
7244
7245   GfxDir[x][y] = MovDir[x][y];
7246
7247   if (direction != MovDir[x][y])
7248     GfxFrame[x][y] = 0;
7249
7250   if (MovDelay[x][y])
7251     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7252
7253   ResetGfxFrame(x, y);
7254 }
7255
7256 static boolean JustBeingPushed(int x, int y)
7257 {
7258   int i;
7259
7260   for (i = 0; i < MAX_PLAYERS; i++)
7261   {
7262     struct PlayerInfo *player = &stored_player[i];
7263
7264     if (player->active && player->is_pushing && player->MovPos)
7265     {
7266       int next_jx = player->jx + (player->jx - player->last_jx);
7267       int next_jy = player->jy + (player->jy - player->last_jy);
7268
7269       if (x == next_jx && y == next_jy)
7270         return TRUE;
7271     }
7272   }
7273
7274   return FALSE;
7275 }
7276
7277 void StartMoving(int x, int y)
7278 {
7279   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7280   int element = Feld[x][y];
7281
7282   if (Stop[x][y])
7283     return;
7284
7285   if (MovDelay[x][y] == 0)
7286     GfxAction[x][y] = ACTION_DEFAULT;
7287
7288   if (CAN_FALL(element) && y < lev_fieldy - 1)
7289   {
7290     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7291         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7292       if (JustBeingPushed(x, y))
7293         return;
7294
7295     if (element == EL_QUICKSAND_FULL)
7296     {
7297       if (IS_FREE(x, y + 1))
7298       {
7299         InitMovingField(x, y, MV_DOWN);
7300         started_moving = TRUE;
7301
7302         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7303 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7304         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7305           Store[x][y] = EL_ROCK;
7306 #else
7307         Store[x][y] = EL_ROCK;
7308 #endif
7309
7310         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7311       }
7312       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7313       {
7314         if (!MovDelay[x][y])
7315         {
7316           MovDelay[x][y] = TILEY + 1;
7317
7318           ResetGfxAnimation(x, y);
7319           ResetGfxAnimation(x, y + 1);
7320         }
7321
7322         if (MovDelay[x][y])
7323         {
7324           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7325           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7326
7327           MovDelay[x][y]--;
7328           if (MovDelay[x][y])
7329             return;
7330         }
7331
7332         Feld[x][y] = EL_QUICKSAND_EMPTY;
7333         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7334         Store[x][y + 1] = Store[x][y];
7335         Store[x][y] = 0;
7336
7337         PlayLevelSoundAction(x, y, ACTION_FILLING);
7338       }
7339       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7340       {
7341         if (!MovDelay[x][y])
7342         {
7343           MovDelay[x][y] = TILEY + 1;
7344
7345           ResetGfxAnimation(x, y);
7346           ResetGfxAnimation(x, y + 1);
7347         }
7348
7349         if (MovDelay[x][y])
7350         {
7351           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7352           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7353
7354           MovDelay[x][y]--;
7355           if (MovDelay[x][y])
7356             return;
7357         }
7358
7359         Feld[x][y] = EL_QUICKSAND_EMPTY;
7360         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7361         Store[x][y + 1] = Store[x][y];
7362         Store[x][y] = 0;
7363
7364         PlayLevelSoundAction(x, y, ACTION_FILLING);
7365       }
7366     }
7367     else if (element == EL_QUICKSAND_FAST_FULL)
7368     {
7369       if (IS_FREE(x, y + 1))
7370       {
7371         InitMovingField(x, y, MV_DOWN);
7372         started_moving = TRUE;
7373
7374         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7375 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7376         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7377           Store[x][y] = EL_ROCK;
7378 #else
7379         Store[x][y] = EL_ROCK;
7380 #endif
7381
7382         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7383       }
7384       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7385       {
7386         if (!MovDelay[x][y])
7387         {
7388           MovDelay[x][y] = TILEY + 1;
7389
7390           ResetGfxAnimation(x, y);
7391           ResetGfxAnimation(x, y + 1);
7392         }
7393
7394         if (MovDelay[x][y])
7395         {
7396           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7397           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7398
7399           MovDelay[x][y]--;
7400           if (MovDelay[x][y])
7401             return;
7402         }
7403
7404         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7405         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7406         Store[x][y + 1] = Store[x][y];
7407         Store[x][y] = 0;
7408
7409         PlayLevelSoundAction(x, y, ACTION_FILLING);
7410       }
7411       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7412       {
7413         if (!MovDelay[x][y])
7414         {
7415           MovDelay[x][y] = TILEY + 1;
7416
7417           ResetGfxAnimation(x, y);
7418           ResetGfxAnimation(x, y + 1);
7419         }
7420
7421         if (MovDelay[x][y])
7422         {
7423           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7424           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7425
7426           MovDelay[x][y]--;
7427           if (MovDelay[x][y])
7428             return;
7429         }
7430
7431         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7432         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7433         Store[x][y + 1] = Store[x][y];
7434         Store[x][y] = 0;
7435
7436         PlayLevelSoundAction(x, y, ACTION_FILLING);
7437       }
7438     }
7439     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7440              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7441     {
7442       InitMovingField(x, y, MV_DOWN);
7443       started_moving = TRUE;
7444
7445       Feld[x][y] = EL_QUICKSAND_FILLING;
7446       Store[x][y] = element;
7447
7448       PlayLevelSoundAction(x, y, ACTION_FILLING);
7449     }
7450     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7451              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7452     {
7453       InitMovingField(x, y, MV_DOWN);
7454       started_moving = TRUE;
7455
7456       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7457       Store[x][y] = element;
7458
7459       PlayLevelSoundAction(x, y, ACTION_FILLING);
7460     }
7461     else if (element == EL_MAGIC_WALL_FULL)
7462     {
7463       if (IS_FREE(x, y + 1))
7464       {
7465         InitMovingField(x, y, MV_DOWN);
7466         started_moving = TRUE;
7467
7468         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7469         Store[x][y] = EL_CHANGED(Store[x][y]);
7470       }
7471       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7472       {
7473         if (!MovDelay[x][y])
7474           MovDelay[x][y] = TILEY / 4 + 1;
7475
7476         if (MovDelay[x][y])
7477         {
7478           MovDelay[x][y]--;
7479           if (MovDelay[x][y])
7480             return;
7481         }
7482
7483         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7484         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7485         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7486         Store[x][y] = 0;
7487       }
7488     }
7489     else if (element == EL_BD_MAGIC_WALL_FULL)
7490     {
7491       if (IS_FREE(x, y + 1))
7492       {
7493         InitMovingField(x, y, MV_DOWN);
7494         started_moving = TRUE;
7495
7496         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7497         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7498       }
7499       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7500       {
7501         if (!MovDelay[x][y])
7502           MovDelay[x][y] = TILEY / 4 + 1;
7503
7504         if (MovDelay[x][y])
7505         {
7506           MovDelay[x][y]--;
7507           if (MovDelay[x][y])
7508             return;
7509         }
7510
7511         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7512         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7513         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7514         Store[x][y] = 0;
7515       }
7516     }
7517     else if (element == EL_DC_MAGIC_WALL_FULL)
7518     {
7519       if (IS_FREE(x, y + 1))
7520       {
7521         InitMovingField(x, y, MV_DOWN);
7522         started_moving = TRUE;
7523
7524         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7525         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7526       }
7527       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7528       {
7529         if (!MovDelay[x][y])
7530           MovDelay[x][y] = TILEY / 4 + 1;
7531
7532         if (MovDelay[x][y])
7533         {
7534           MovDelay[x][y]--;
7535           if (MovDelay[x][y])
7536             return;
7537         }
7538
7539         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7540         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7541         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7542         Store[x][y] = 0;
7543       }
7544     }
7545     else if ((CAN_PASS_MAGIC_WALL(element) &&
7546               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7547                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7548              (CAN_PASS_DC_MAGIC_WALL(element) &&
7549               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7550
7551     {
7552       InitMovingField(x, y, MV_DOWN);
7553       started_moving = TRUE;
7554
7555       Feld[x][y] =
7556         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7557          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7558          EL_DC_MAGIC_WALL_FILLING);
7559       Store[x][y] = element;
7560     }
7561     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7562     {
7563       SplashAcid(x, y + 1);
7564
7565       InitMovingField(x, y, MV_DOWN);
7566       started_moving = TRUE;
7567
7568       Store[x][y] = EL_ACID;
7569     }
7570     else if (
7571              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7572               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7573              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7574               CAN_FALL(element) && WasJustFalling[x][y] &&
7575               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7576
7577              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7578               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7579               (Feld[x][y + 1] == EL_BLOCKED)))
7580     {
7581       /* this is needed for a special case not covered by calling "Impact()"
7582          from "ContinueMoving()": if an element moves to a tile directly below
7583          another element which was just falling on that tile (which was empty
7584          in the previous frame), the falling element above would just stop
7585          instead of smashing the element below (in previous version, the above
7586          element was just checked for "moving" instead of "falling", resulting
7587          in incorrect smashes caused by horizontal movement of the above
7588          element; also, the case of the player being the element to smash was
7589          simply not covered here... :-/ ) */
7590
7591       CheckCollision[x][y] = 0;
7592       CheckImpact[x][y] = 0;
7593
7594       Impact(x, y);
7595     }
7596     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7597     {
7598       if (MovDir[x][y] == MV_NONE)
7599       {
7600         InitMovingField(x, y, MV_DOWN);
7601         started_moving = TRUE;
7602       }
7603     }
7604     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7605     {
7606       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7607         MovDir[x][y] = MV_DOWN;
7608
7609       InitMovingField(x, y, MV_DOWN);
7610       started_moving = TRUE;
7611     }
7612     else if (element == EL_AMOEBA_DROP)
7613     {
7614       Feld[x][y] = EL_AMOEBA_GROWING;
7615       Store[x][y] = EL_AMOEBA_WET;
7616     }
7617     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7618               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7619              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7620              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7621     {
7622       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7623                                 (IS_FREE(x - 1, y + 1) ||
7624                                  Feld[x - 1][y + 1] == EL_ACID));
7625       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7626                                 (IS_FREE(x + 1, y + 1) ||
7627                                  Feld[x + 1][y + 1] == EL_ACID));
7628       boolean can_fall_any  = (can_fall_left || can_fall_right);
7629       boolean can_fall_both = (can_fall_left && can_fall_right);
7630       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7631
7632       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7633       {
7634         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7635           can_fall_right = FALSE;
7636         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7637           can_fall_left = FALSE;
7638         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7639           can_fall_right = FALSE;
7640         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7641           can_fall_left = FALSE;
7642
7643         can_fall_any  = (can_fall_left || can_fall_right);
7644         can_fall_both = FALSE;
7645       }
7646
7647       if (can_fall_both)
7648       {
7649         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7650           can_fall_right = FALSE;       /* slip down on left side */
7651         else
7652           can_fall_left = !(can_fall_right = RND(2));
7653
7654         can_fall_both = FALSE;
7655       }
7656
7657       if (can_fall_any)
7658       {
7659         /* if not determined otherwise, prefer left side for slipping down */
7660         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7661         started_moving = TRUE;
7662       }
7663     }
7664     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7665     {
7666       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7667       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7668       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7669       int belt_dir = game.belt_dir[belt_nr];
7670
7671       if ((belt_dir == MV_LEFT  && left_is_free) ||
7672           (belt_dir == MV_RIGHT && right_is_free))
7673       {
7674         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7675
7676         InitMovingField(x, y, belt_dir);
7677         started_moving = TRUE;
7678
7679         Pushed[x][y] = TRUE;
7680         Pushed[nextx][y] = TRUE;
7681
7682         GfxAction[x][y] = ACTION_DEFAULT;
7683       }
7684       else
7685       {
7686         MovDir[x][y] = 0;       /* if element was moving, stop it */
7687       }
7688     }
7689   }
7690
7691   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7692   if (CAN_MOVE(element) && !started_moving)
7693   {
7694     int move_pattern = element_info[element].move_pattern;
7695     int newx, newy;
7696
7697     Moving2Blocked(x, y, &newx, &newy);
7698
7699     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7700       return;
7701
7702     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7703         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7704     {
7705       WasJustMoving[x][y] = 0;
7706       CheckCollision[x][y] = 0;
7707
7708       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7709
7710       if (Feld[x][y] != element)        /* element has changed */
7711         return;
7712     }
7713
7714     if (!MovDelay[x][y])        /* start new movement phase */
7715     {
7716       /* all objects that can change their move direction after each step
7717          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7718
7719       if (element != EL_YAMYAM &&
7720           element != EL_DARK_YAMYAM &&
7721           element != EL_PACMAN &&
7722           !(move_pattern & MV_ANY_DIRECTION) &&
7723           move_pattern != MV_TURNING_LEFT &&
7724           move_pattern != MV_TURNING_RIGHT &&
7725           move_pattern != MV_TURNING_LEFT_RIGHT &&
7726           move_pattern != MV_TURNING_RIGHT_LEFT &&
7727           move_pattern != MV_TURNING_RANDOM)
7728       {
7729         TurnRound(x, y);
7730
7731         if (MovDelay[x][y] && (element == EL_BUG ||
7732                                element == EL_SPACESHIP ||
7733                                element == EL_SP_SNIKSNAK ||
7734                                element == EL_SP_ELECTRON ||
7735                                element == EL_MOLE))
7736           TEST_DrawLevelField(x, y);
7737       }
7738     }
7739
7740     if (MovDelay[x][y])         /* wait some time before next movement */
7741     {
7742       MovDelay[x][y]--;
7743
7744       if (element == EL_ROBOT ||
7745           element == EL_YAMYAM ||
7746           element == EL_DARK_YAMYAM)
7747       {
7748         DrawLevelElementAnimationIfNeeded(x, y, element);
7749         PlayLevelSoundAction(x, y, ACTION_WAITING);
7750       }
7751       else if (element == EL_SP_ELECTRON)
7752         DrawLevelElementAnimationIfNeeded(x, y, element);
7753       else if (element == EL_DRAGON)
7754       {
7755         int i;
7756         int dir = MovDir[x][y];
7757         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7758         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7759         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7760                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7761                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7762                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7763         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7764
7765         GfxAction[x][y] = ACTION_ATTACKING;
7766
7767         if (IS_PLAYER(x, y))
7768           DrawPlayerField(x, y);
7769         else
7770           TEST_DrawLevelField(x, y);
7771
7772         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7773
7774         for (i = 1; i <= 3; i++)
7775         {
7776           int xx = x + i * dx;
7777           int yy = y + i * dy;
7778           int sx = SCREENX(xx);
7779           int sy = SCREENY(yy);
7780           int flame_graphic = graphic + (i - 1);
7781
7782           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7783             break;
7784
7785           if (MovDelay[x][y])
7786           {
7787             int flamed = MovingOrBlocked2Element(xx, yy);
7788
7789             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7790               Bang(xx, yy);
7791             else
7792               RemoveMovingField(xx, yy);
7793
7794             ChangeDelay[xx][yy] = 0;
7795
7796             Feld[xx][yy] = EL_FLAMES;
7797
7798             if (IN_SCR_FIELD(sx, sy))
7799             {
7800               TEST_DrawLevelFieldCrumbled(xx, yy);
7801               DrawGraphic(sx, sy, flame_graphic, frame);
7802             }
7803           }
7804           else
7805           {
7806             if (Feld[xx][yy] == EL_FLAMES)
7807               Feld[xx][yy] = EL_EMPTY;
7808             TEST_DrawLevelField(xx, yy);
7809           }
7810         }
7811       }
7812
7813       if (MovDelay[x][y])       /* element still has to wait some time */
7814       {
7815         PlayLevelSoundAction(x, y, ACTION_WAITING);
7816
7817         return;
7818       }
7819     }
7820
7821     /* now make next step */
7822
7823     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7824
7825     if (DONT_COLLIDE_WITH(element) &&
7826         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7827         !PLAYER_ENEMY_PROTECTED(newx, newy))
7828     {
7829       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7830
7831       return;
7832     }
7833
7834     else if (CAN_MOVE_INTO_ACID(element) &&
7835              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7836              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7837              (MovDir[x][y] == MV_DOWN ||
7838               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7839     {
7840       SplashAcid(newx, newy);
7841       Store[x][y] = EL_ACID;
7842     }
7843     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7844     {
7845       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7846           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7847           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7848           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7849       {
7850         RemoveField(x, y);
7851         TEST_DrawLevelField(x, y);
7852
7853         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7854         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7855           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7856
7857         local_player->friends_still_needed--;
7858         if (!local_player->friends_still_needed &&
7859             !local_player->GameOver && AllPlayersGone)
7860           PlayerWins(local_player);
7861
7862         return;
7863       }
7864       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7865       {
7866         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7867           TEST_DrawLevelField(newx, newy);
7868         else
7869           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7870       }
7871       else if (!IS_FREE(newx, newy))
7872       {
7873         GfxAction[x][y] = ACTION_WAITING;
7874
7875         if (IS_PLAYER(x, y))
7876           DrawPlayerField(x, y);
7877         else
7878           TEST_DrawLevelField(x, y);
7879
7880         return;
7881       }
7882     }
7883     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7884     {
7885       if (IS_FOOD_PIG(Feld[newx][newy]))
7886       {
7887         if (IS_MOVING(newx, newy))
7888           RemoveMovingField(newx, newy);
7889         else
7890         {
7891           Feld[newx][newy] = EL_EMPTY;
7892           TEST_DrawLevelField(newx, newy);
7893         }
7894
7895         PlayLevelSound(x, y, SND_PIG_DIGGING);
7896       }
7897       else if (!IS_FREE(newx, newy))
7898       {
7899         if (IS_PLAYER(x, y))
7900           DrawPlayerField(x, y);
7901         else
7902           TEST_DrawLevelField(x, y);
7903
7904         return;
7905       }
7906     }
7907     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7908     {
7909       if (Store[x][y] != EL_EMPTY)
7910       {
7911         boolean can_clone = FALSE;
7912         int xx, yy;
7913
7914         /* check if element to clone is still there */
7915         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7916         {
7917           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7918           {
7919             can_clone = TRUE;
7920
7921             break;
7922           }
7923         }
7924
7925         /* cannot clone or target field not free anymore -- do not clone */
7926         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7927           Store[x][y] = EL_EMPTY;
7928       }
7929
7930       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7931       {
7932         if (IS_MV_DIAGONAL(MovDir[x][y]))
7933         {
7934           int diagonal_move_dir = MovDir[x][y];
7935           int stored = Store[x][y];
7936           int change_delay = 8;
7937           int graphic;
7938
7939           /* android is moving diagonally */
7940
7941           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7942
7943           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7944           GfxElement[x][y] = EL_EMC_ANDROID;
7945           GfxAction[x][y] = ACTION_SHRINKING;
7946           GfxDir[x][y] = diagonal_move_dir;
7947           ChangeDelay[x][y] = change_delay;
7948
7949           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7950                                    GfxDir[x][y]);
7951
7952           DrawLevelGraphicAnimation(x, y, graphic);
7953           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7954
7955           if (Feld[newx][newy] == EL_ACID)
7956           {
7957             SplashAcid(newx, newy);
7958
7959             return;
7960           }
7961
7962           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7963
7964           Store[newx][newy] = EL_EMC_ANDROID;
7965           GfxElement[newx][newy] = EL_EMC_ANDROID;
7966           GfxAction[newx][newy] = ACTION_GROWING;
7967           GfxDir[newx][newy] = diagonal_move_dir;
7968           ChangeDelay[newx][newy] = change_delay;
7969
7970           graphic = el_act_dir2img(GfxElement[newx][newy],
7971                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7972
7973           DrawLevelGraphicAnimation(newx, newy, graphic);
7974           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7975
7976           return;
7977         }
7978         else
7979         {
7980           Feld[newx][newy] = EL_EMPTY;
7981           TEST_DrawLevelField(newx, newy);
7982
7983           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7984         }
7985       }
7986       else if (!IS_FREE(newx, newy))
7987       {
7988         return;
7989       }
7990     }
7991     else if (IS_CUSTOM_ELEMENT(element) &&
7992              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7993     {
7994       if (!DigFieldByCE(newx, newy, element))
7995         return;
7996
7997       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7998       {
7999         RunnerVisit[x][y] = FrameCounter;
8000         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8001       }
8002     }
8003     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8004     {
8005       if (!IS_FREE(newx, newy))
8006       {
8007         if (IS_PLAYER(x, y))
8008           DrawPlayerField(x, y);
8009         else
8010           TEST_DrawLevelField(x, y);
8011
8012         return;
8013       }
8014       else
8015       {
8016         boolean wanna_flame = !RND(10);
8017         int dx = newx - x, dy = newy - y;
8018         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8019         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8020         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8021                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8022         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8023                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8024
8025         if ((wanna_flame ||
8026              IS_CLASSIC_ENEMY(element1) ||
8027              IS_CLASSIC_ENEMY(element2)) &&
8028             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8029             element1 != EL_FLAMES && element2 != EL_FLAMES)
8030         {
8031           ResetGfxAnimation(x, y);
8032           GfxAction[x][y] = ACTION_ATTACKING;
8033
8034           if (IS_PLAYER(x, y))
8035             DrawPlayerField(x, y);
8036           else
8037             TEST_DrawLevelField(x, y);
8038
8039           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8040
8041           MovDelay[x][y] = 50;
8042
8043           Feld[newx][newy] = EL_FLAMES;
8044           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8045             Feld[newx1][newy1] = EL_FLAMES;
8046           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8047             Feld[newx2][newy2] = EL_FLAMES;
8048
8049           return;
8050         }
8051       }
8052     }
8053     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8054              Feld[newx][newy] == EL_DIAMOND)
8055     {
8056       if (IS_MOVING(newx, newy))
8057         RemoveMovingField(newx, newy);
8058       else
8059       {
8060         Feld[newx][newy] = EL_EMPTY;
8061         TEST_DrawLevelField(newx, newy);
8062       }
8063
8064       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8065     }
8066     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8067              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8068     {
8069       if (AmoebaNr[newx][newy])
8070       {
8071         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8072         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8073             Feld[newx][newy] == EL_BD_AMOEBA)
8074           AmoebaCnt[AmoebaNr[newx][newy]]--;
8075       }
8076
8077       if (IS_MOVING(newx, newy))
8078       {
8079         RemoveMovingField(newx, newy);
8080       }
8081       else
8082       {
8083         Feld[newx][newy] = EL_EMPTY;
8084         TEST_DrawLevelField(newx, newy);
8085       }
8086
8087       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8088     }
8089     else if ((element == EL_PACMAN || element == EL_MOLE)
8090              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8091     {
8092       if (AmoebaNr[newx][newy])
8093       {
8094         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8095         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8096             Feld[newx][newy] == EL_BD_AMOEBA)
8097           AmoebaCnt[AmoebaNr[newx][newy]]--;
8098       }
8099
8100       if (element == EL_MOLE)
8101       {
8102         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8103         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8104
8105         ResetGfxAnimation(x, y);
8106         GfxAction[x][y] = ACTION_DIGGING;
8107         TEST_DrawLevelField(x, y);
8108
8109         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8110
8111         return;                         /* wait for shrinking amoeba */
8112       }
8113       else      /* element == EL_PACMAN */
8114       {
8115         Feld[newx][newy] = EL_EMPTY;
8116         TEST_DrawLevelField(newx, newy);
8117         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8118       }
8119     }
8120     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8121              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8122               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8123     {
8124       /* wait for shrinking amoeba to completely disappear */
8125       return;
8126     }
8127     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8128     {
8129       /* object was running against a wall */
8130
8131       TurnRound(x, y);
8132
8133       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8134         DrawLevelElementAnimation(x, y, element);
8135
8136       if (DONT_TOUCH(element))
8137         TestIfBadThingTouchesPlayer(x, y);
8138
8139       return;
8140     }
8141
8142     InitMovingField(x, y, MovDir[x][y]);
8143
8144     PlayLevelSoundAction(x, y, ACTION_MOVING);
8145   }
8146
8147   if (MovDir[x][y])
8148     ContinueMoving(x, y);
8149 }
8150
8151 void ContinueMoving(int x, int y)
8152 {
8153   int element = Feld[x][y];
8154   struct ElementInfo *ei = &element_info[element];
8155   int direction = MovDir[x][y];
8156   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8157   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8158   int newx = x + dx, newy = y + dy;
8159   int stored = Store[x][y];
8160   int stored_new = Store[newx][newy];
8161   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8162   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8163   boolean last_line = (newy == lev_fieldy - 1);
8164
8165   MovPos[x][y] += getElementMoveStepsize(x, y);
8166
8167   if (pushed_by_player) /* special case: moving object pushed by player */
8168     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8169
8170   if (ABS(MovPos[x][y]) < TILEX)
8171   {
8172     TEST_DrawLevelField(x, y);
8173
8174     return;     /* element is still moving */
8175   }
8176
8177   /* element reached destination field */
8178
8179   Feld[x][y] = EL_EMPTY;
8180   Feld[newx][newy] = element;
8181   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8182
8183   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8184   {
8185     element = Feld[newx][newy] = EL_ACID;
8186   }
8187   else if (element == EL_MOLE)
8188   {
8189     Feld[x][y] = EL_SAND;
8190
8191     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8192   }
8193   else if (element == EL_QUICKSAND_FILLING)
8194   {
8195     element = Feld[newx][newy] = get_next_element(element);
8196     Store[newx][newy] = Store[x][y];
8197   }
8198   else if (element == EL_QUICKSAND_EMPTYING)
8199   {
8200     Feld[x][y] = get_next_element(element);
8201     element = Feld[newx][newy] = Store[x][y];
8202   }
8203   else if (element == EL_QUICKSAND_FAST_FILLING)
8204   {
8205     element = Feld[newx][newy] = get_next_element(element);
8206     Store[newx][newy] = Store[x][y];
8207   }
8208   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8209   {
8210     Feld[x][y] = get_next_element(element);
8211     element = Feld[newx][newy] = Store[x][y];
8212   }
8213   else if (element == EL_MAGIC_WALL_FILLING)
8214   {
8215     element = Feld[newx][newy] = get_next_element(element);
8216     if (!game.magic_wall_active)
8217       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8218     Store[newx][newy] = Store[x][y];
8219   }
8220   else if (element == EL_MAGIC_WALL_EMPTYING)
8221   {
8222     Feld[x][y] = get_next_element(element);
8223     if (!game.magic_wall_active)
8224       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8225     element = Feld[newx][newy] = Store[x][y];
8226
8227     InitField(newx, newy, FALSE);
8228   }
8229   else if (element == EL_BD_MAGIC_WALL_FILLING)
8230   {
8231     element = Feld[newx][newy] = get_next_element(element);
8232     if (!game.magic_wall_active)
8233       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8234     Store[newx][newy] = Store[x][y];
8235   }
8236   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8237   {
8238     Feld[x][y] = get_next_element(element);
8239     if (!game.magic_wall_active)
8240       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8241     element = Feld[newx][newy] = Store[x][y];
8242
8243     InitField(newx, newy, FALSE);
8244   }
8245   else if (element == EL_DC_MAGIC_WALL_FILLING)
8246   {
8247     element = Feld[newx][newy] = get_next_element(element);
8248     if (!game.magic_wall_active)
8249       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8250     Store[newx][newy] = Store[x][y];
8251   }
8252   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8253   {
8254     Feld[x][y] = get_next_element(element);
8255     if (!game.magic_wall_active)
8256       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8257     element = Feld[newx][newy] = Store[x][y];
8258
8259     InitField(newx, newy, FALSE);
8260   }
8261   else if (element == EL_AMOEBA_DROPPING)
8262   {
8263     Feld[x][y] = get_next_element(element);
8264     element = Feld[newx][newy] = Store[x][y];
8265   }
8266   else if (element == EL_SOKOBAN_OBJECT)
8267   {
8268     if (Back[x][y])
8269       Feld[x][y] = Back[x][y];
8270
8271     if (Back[newx][newy])
8272       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8273
8274     Back[x][y] = Back[newx][newy] = 0;
8275   }
8276
8277   Store[x][y] = EL_EMPTY;
8278   MovPos[x][y] = 0;
8279   MovDir[x][y] = 0;
8280   MovDelay[x][y] = 0;
8281
8282   MovDelay[newx][newy] = 0;
8283
8284   if (CAN_CHANGE_OR_HAS_ACTION(element))
8285   {
8286     /* copy element change control values to new field */
8287     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8288     ChangePage[newx][newy]  = ChangePage[x][y];
8289     ChangeCount[newx][newy] = ChangeCount[x][y];
8290     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8291   }
8292
8293   CustomValue[newx][newy] = CustomValue[x][y];
8294
8295   ChangeDelay[x][y] = 0;
8296   ChangePage[x][y] = -1;
8297   ChangeCount[x][y] = 0;
8298   ChangeEvent[x][y] = -1;
8299
8300   CustomValue[x][y] = 0;
8301
8302   /* copy animation control values to new field */
8303   GfxFrame[newx][newy]  = GfxFrame[x][y];
8304   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8305   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8306   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8307
8308   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8309
8310   /* some elements can leave other elements behind after moving */
8311   if (ei->move_leave_element != EL_EMPTY &&
8312       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8313       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8314   {
8315     int move_leave_element = ei->move_leave_element;
8316
8317     /* this makes it possible to leave the removed element again */
8318     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8319       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8320
8321     Feld[x][y] = move_leave_element;
8322
8323     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8324       MovDir[x][y] = direction;
8325
8326     InitField(x, y, FALSE);
8327
8328     if (GFX_CRUMBLED(Feld[x][y]))
8329       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8330
8331     if (ELEM_IS_PLAYER(move_leave_element))
8332       RelocatePlayer(x, y, move_leave_element);
8333   }
8334
8335   /* do this after checking for left-behind element */
8336   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8337
8338   if (!CAN_MOVE(element) ||
8339       (CAN_FALL(element) && direction == MV_DOWN &&
8340        (element == EL_SPRING ||
8341         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8342         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8343     GfxDir[x][y] = MovDir[newx][newy] = 0;
8344
8345   TEST_DrawLevelField(x, y);
8346   TEST_DrawLevelField(newx, newy);
8347
8348   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8349
8350   /* prevent pushed element from moving on in pushed direction */
8351   if (pushed_by_player && CAN_MOVE(element) &&
8352       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8353       !(element_info[element].move_pattern & direction))
8354     TurnRound(newx, newy);
8355
8356   /* prevent elements on conveyor belt from moving on in last direction */
8357   if (pushed_by_conveyor && CAN_FALL(element) &&
8358       direction & MV_HORIZONTAL)
8359     MovDir[newx][newy] = 0;
8360
8361   if (!pushed_by_player)
8362   {
8363     int nextx = newx + dx, nexty = newy + dy;
8364     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8365
8366     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8367
8368     if (CAN_FALL(element) && direction == MV_DOWN)
8369       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8370
8371     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8372       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8373
8374     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8375       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8376   }
8377
8378   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8379   {
8380     TestIfBadThingTouchesPlayer(newx, newy);
8381     TestIfBadThingTouchesFriend(newx, newy);
8382
8383     if (!IS_CUSTOM_ELEMENT(element))
8384       TestIfBadThingTouchesOtherBadThing(newx, newy);
8385   }
8386   else if (element == EL_PENGUIN)
8387     TestIfFriendTouchesBadThing(newx, newy);
8388
8389   if (DONT_GET_HIT_BY(element))
8390   {
8391     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8392   }
8393
8394   /* give the player one last chance (one more frame) to move away */
8395   if (CAN_FALL(element) && direction == MV_DOWN &&
8396       (last_line || (!IS_FREE(x, newy + 1) &&
8397                      (!IS_PLAYER(x, newy + 1) ||
8398                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8399     Impact(x, newy);
8400
8401   if (pushed_by_player && !game.use_change_when_pushing_bug)
8402   {
8403     int push_side = MV_DIR_OPPOSITE(direction);
8404     struct PlayerInfo *player = PLAYERINFO(x, y);
8405
8406     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8407                                player->index_bit, push_side);
8408     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8409                                         player->index_bit, push_side);
8410   }
8411
8412   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8413     MovDelay[newx][newy] = 1;
8414
8415   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8416
8417   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8418   TestIfElementHitsCustomElement(newx, newy, direction);
8419   TestIfPlayerTouchesCustomElement(newx, newy);
8420   TestIfElementTouchesCustomElement(newx, newy);
8421
8422   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8423       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8424     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8425                              MV_DIR_OPPOSITE(direction));
8426 }
8427
8428 int AmoebeNachbarNr(int ax, int ay)
8429 {
8430   int i;
8431   int element = Feld[ax][ay];
8432   int group_nr = 0;
8433   static int xy[4][2] =
8434   {
8435     { 0, -1 },
8436     { -1, 0 },
8437     { +1, 0 },
8438     { 0, +1 }
8439   };
8440
8441   for (i = 0; i < NUM_DIRECTIONS; i++)
8442   {
8443     int x = ax + xy[i][0];
8444     int y = ay + xy[i][1];
8445
8446     if (!IN_LEV_FIELD(x, y))
8447       continue;
8448
8449     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8450       group_nr = AmoebaNr[x][y];
8451   }
8452
8453   return group_nr;
8454 }
8455
8456 void AmoebenVereinigen(int ax, int ay)
8457 {
8458   int i, x, y, xx, yy;
8459   int new_group_nr = AmoebaNr[ax][ay];
8460   static int xy[4][2] =
8461   {
8462     { 0, -1 },
8463     { -1, 0 },
8464     { +1, 0 },
8465     { 0, +1 }
8466   };
8467
8468   if (new_group_nr == 0)
8469     return;
8470
8471   for (i = 0; i < NUM_DIRECTIONS; i++)
8472   {
8473     x = ax + xy[i][0];
8474     y = ay + xy[i][1];
8475
8476     if (!IN_LEV_FIELD(x, y))
8477       continue;
8478
8479     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8480          Feld[x][y] == EL_BD_AMOEBA ||
8481          Feld[x][y] == EL_AMOEBA_DEAD) &&
8482         AmoebaNr[x][y] != new_group_nr)
8483     {
8484       int old_group_nr = AmoebaNr[x][y];
8485
8486       if (old_group_nr == 0)
8487         return;
8488
8489       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8490       AmoebaCnt[old_group_nr] = 0;
8491       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8492       AmoebaCnt2[old_group_nr] = 0;
8493
8494       SCAN_PLAYFIELD(xx, yy)
8495       {
8496         if (AmoebaNr[xx][yy] == old_group_nr)
8497           AmoebaNr[xx][yy] = new_group_nr;
8498       }
8499     }
8500   }
8501 }
8502
8503 void AmoebeUmwandeln(int ax, int ay)
8504 {
8505   int i, x, y;
8506
8507   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8508   {
8509     int group_nr = AmoebaNr[ax][ay];
8510
8511 #ifdef DEBUG
8512     if (group_nr == 0)
8513     {
8514       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8515       printf("AmoebeUmwandeln(): This should never happen!\n");
8516       return;
8517     }
8518 #endif
8519
8520     SCAN_PLAYFIELD(x, y)
8521     {
8522       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8523       {
8524         AmoebaNr[x][y] = 0;
8525         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8526       }
8527     }
8528
8529     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8530                             SND_AMOEBA_TURNING_TO_GEM :
8531                             SND_AMOEBA_TURNING_TO_ROCK));
8532     Bang(ax, ay);
8533   }
8534   else
8535   {
8536     static int xy[4][2] =
8537     {
8538       { 0, -1 },
8539       { -1, 0 },
8540       { +1, 0 },
8541       { 0, +1 }
8542     };
8543
8544     for (i = 0; i < NUM_DIRECTIONS; i++)
8545     {
8546       x = ax + xy[i][0];
8547       y = ay + xy[i][1];
8548
8549       if (!IN_LEV_FIELD(x, y))
8550         continue;
8551
8552       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8553       {
8554         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8555                               SND_AMOEBA_TURNING_TO_GEM :
8556                               SND_AMOEBA_TURNING_TO_ROCK));
8557         Bang(x, y);
8558       }
8559     }
8560   }
8561 }
8562
8563 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8564 {
8565   int x, y;
8566   int group_nr = AmoebaNr[ax][ay];
8567   boolean done = FALSE;
8568
8569 #ifdef DEBUG
8570   if (group_nr == 0)
8571   {
8572     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8573     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8574     return;
8575   }
8576 #endif
8577
8578   SCAN_PLAYFIELD(x, y)
8579   {
8580     if (AmoebaNr[x][y] == group_nr &&
8581         (Feld[x][y] == EL_AMOEBA_DEAD ||
8582          Feld[x][y] == EL_BD_AMOEBA ||
8583          Feld[x][y] == EL_AMOEBA_GROWING))
8584     {
8585       AmoebaNr[x][y] = 0;
8586       Feld[x][y] = new_element;
8587       InitField(x, y, FALSE);
8588       TEST_DrawLevelField(x, y);
8589       done = TRUE;
8590     }
8591   }
8592
8593   if (done)
8594     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8595                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8596                             SND_BD_AMOEBA_TURNING_TO_GEM));
8597 }
8598
8599 void AmoebeWaechst(int x, int y)
8600 {
8601   static unsigned int sound_delay = 0;
8602   static unsigned int sound_delay_value = 0;
8603
8604   if (!MovDelay[x][y])          /* start new growing cycle */
8605   {
8606     MovDelay[x][y] = 7;
8607
8608     if (DelayReached(&sound_delay, sound_delay_value))
8609     {
8610       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8611       sound_delay_value = 30;
8612     }
8613   }
8614
8615   if (MovDelay[x][y])           /* wait some time before growing bigger */
8616   {
8617     MovDelay[x][y]--;
8618     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8619     {
8620       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8621                                            6 - MovDelay[x][y]);
8622
8623       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8624     }
8625
8626     if (!MovDelay[x][y])
8627     {
8628       Feld[x][y] = Store[x][y];
8629       Store[x][y] = 0;
8630       TEST_DrawLevelField(x, y);
8631     }
8632   }
8633 }
8634
8635 void AmoebaDisappearing(int x, int y)
8636 {
8637   static unsigned int sound_delay = 0;
8638   static unsigned int sound_delay_value = 0;
8639
8640   if (!MovDelay[x][y])          /* start new shrinking cycle */
8641   {
8642     MovDelay[x][y] = 7;
8643
8644     if (DelayReached(&sound_delay, sound_delay_value))
8645       sound_delay_value = 30;
8646   }
8647
8648   if (MovDelay[x][y])           /* wait some time before shrinking */
8649   {
8650     MovDelay[x][y]--;
8651     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8652     {
8653       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8654                                            6 - MovDelay[x][y]);
8655
8656       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8657     }
8658
8659     if (!MovDelay[x][y])
8660     {
8661       Feld[x][y] = EL_EMPTY;
8662       TEST_DrawLevelField(x, y);
8663
8664       /* don't let mole enter this field in this cycle;
8665          (give priority to objects falling to this field from above) */
8666       Stop[x][y] = TRUE;
8667     }
8668   }
8669 }
8670
8671 void AmoebeAbleger(int ax, int ay)
8672 {
8673   int i;
8674   int element = Feld[ax][ay];
8675   int graphic = el2img(element);
8676   int newax = ax, neway = ay;
8677   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8678   static int xy[4][2] =
8679   {
8680     { 0, -1 },
8681     { -1, 0 },
8682     { +1, 0 },
8683     { 0, +1 }
8684   };
8685
8686   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8687   {
8688     Feld[ax][ay] = EL_AMOEBA_DEAD;
8689     TEST_DrawLevelField(ax, ay);
8690     return;
8691   }
8692
8693   if (IS_ANIMATED(graphic))
8694     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8695
8696   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8697     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8698
8699   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8700   {
8701     MovDelay[ax][ay]--;
8702     if (MovDelay[ax][ay])
8703       return;
8704   }
8705
8706   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8707   {
8708     int start = RND(4);
8709     int x = ax + xy[start][0];
8710     int y = ay + xy[start][1];
8711
8712     if (!IN_LEV_FIELD(x, y))
8713       return;
8714
8715     if (IS_FREE(x, y) ||
8716         CAN_GROW_INTO(Feld[x][y]) ||
8717         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8718         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8719     {
8720       newax = x;
8721       neway = y;
8722     }
8723
8724     if (newax == ax && neway == ay)
8725       return;
8726   }
8727   else                          /* normal or "filled" (BD style) amoeba */
8728   {
8729     int start = RND(4);
8730     boolean waiting_for_player = FALSE;
8731
8732     for (i = 0; i < NUM_DIRECTIONS; i++)
8733     {
8734       int j = (start + i) % 4;
8735       int x = ax + xy[j][0];
8736       int y = ay + xy[j][1];
8737
8738       if (!IN_LEV_FIELD(x, y))
8739         continue;
8740
8741       if (IS_FREE(x, y) ||
8742           CAN_GROW_INTO(Feld[x][y]) ||
8743           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8744           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8745       {
8746         newax = x;
8747         neway = y;
8748         break;
8749       }
8750       else if (IS_PLAYER(x, y))
8751         waiting_for_player = TRUE;
8752     }
8753
8754     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8755     {
8756       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8757       {
8758         Feld[ax][ay] = EL_AMOEBA_DEAD;
8759         TEST_DrawLevelField(ax, ay);
8760         AmoebaCnt[AmoebaNr[ax][ay]]--;
8761
8762         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8763         {
8764           if (element == EL_AMOEBA_FULL)
8765             AmoebeUmwandeln(ax, ay);
8766           else if (element == EL_BD_AMOEBA)
8767             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8768         }
8769       }
8770       return;
8771     }
8772     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8773     {
8774       /* amoeba gets larger by growing in some direction */
8775
8776       int new_group_nr = AmoebaNr[ax][ay];
8777
8778 #ifdef DEBUG
8779   if (new_group_nr == 0)
8780   {
8781     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8782     printf("AmoebeAbleger(): This should never happen!\n");
8783     return;
8784   }
8785 #endif
8786
8787       AmoebaNr[newax][neway] = new_group_nr;
8788       AmoebaCnt[new_group_nr]++;
8789       AmoebaCnt2[new_group_nr]++;
8790
8791       /* if amoeba touches other amoeba(s) after growing, unify them */
8792       AmoebenVereinigen(newax, neway);
8793
8794       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8795       {
8796         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8797         return;
8798       }
8799     }
8800   }
8801
8802   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8803       (neway == lev_fieldy - 1 && newax != ax))
8804   {
8805     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8806     Store[newax][neway] = element;
8807   }
8808   else if (neway == ay || element == EL_EMC_DRIPPER)
8809   {
8810     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8811
8812     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8813   }
8814   else
8815   {
8816     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8817     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8818     Store[ax][ay] = EL_AMOEBA_DROP;
8819     ContinueMoving(ax, ay);
8820     return;
8821   }
8822
8823   TEST_DrawLevelField(newax, neway);
8824 }
8825
8826 void Life(int ax, int ay)
8827 {
8828   int x1, y1, x2, y2;
8829   int life_time = 40;
8830   int element = Feld[ax][ay];
8831   int graphic = el2img(element);
8832   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8833                          level.biomaze);
8834   boolean changed = FALSE;
8835
8836   if (IS_ANIMATED(graphic))
8837     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8838
8839   if (Stop[ax][ay])
8840     return;
8841
8842   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8843     MovDelay[ax][ay] = life_time;
8844
8845   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8846   {
8847     MovDelay[ax][ay]--;
8848     if (MovDelay[ax][ay])
8849       return;
8850   }
8851
8852   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8853   {
8854     int xx = ax+x1, yy = ay+y1;
8855     int nachbarn = 0;
8856
8857     if (!IN_LEV_FIELD(xx, yy))
8858       continue;
8859
8860     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8861     {
8862       int x = xx+x2, y = yy+y2;
8863
8864       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8865         continue;
8866
8867       if (((Feld[x][y] == element ||
8868             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8869            !Stop[x][y]) ||
8870           (IS_FREE(x, y) && Stop[x][y]))
8871         nachbarn++;
8872     }
8873
8874     if (xx == ax && yy == ay)           /* field in the middle */
8875     {
8876       if (nachbarn < life_parameter[0] ||
8877           nachbarn > life_parameter[1])
8878       {
8879         Feld[xx][yy] = EL_EMPTY;
8880         if (!Stop[xx][yy])
8881           TEST_DrawLevelField(xx, yy);
8882         Stop[xx][yy] = TRUE;
8883         changed = TRUE;
8884       }
8885     }
8886     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8887     {                                   /* free border field */
8888       if (nachbarn >= life_parameter[2] &&
8889           nachbarn <= life_parameter[3])
8890       {
8891         Feld[xx][yy] = element;
8892         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8893         if (!Stop[xx][yy])
8894           TEST_DrawLevelField(xx, yy);
8895         Stop[xx][yy] = TRUE;
8896         changed = TRUE;
8897       }
8898     }
8899   }
8900
8901   if (changed)
8902     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8903                    SND_GAME_OF_LIFE_GROWING);
8904 }
8905
8906 static void InitRobotWheel(int x, int y)
8907 {
8908   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8909 }
8910
8911 static void RunRobotWheel(int x, int y)
8912 {
8913   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8914 }
8915
8916 static void StopRobotWheel(int x, int y)
8917 {
8918   if (ZX == x && ZY == y)
8919   {
8920     ZX = ZY = -1;
8921
8922     game.robot_wheel_active = FALSE;
8923   }
8924 }
8925
8926 static void InitTimegateWheel(int x, int y)
8927 {
8928   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8929 }
8930
8931 static void RunTimegateWheel(int x, int y)
8932 {
8933   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8934 }
8935
8936 static void InitMagicBallDelay(int x, int y)
8937 {
8938   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8939 }
8940
8941 static void ActivateMagicBall(int bx, int by)
8942 {
8943   int x, y;
8944
8945   if (level.ball_random)
8946   {
8947     int pos_border = RND(8);    /* select one of the eight border elements */
8948     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8949     int xx = pos_content % 3;
8950     int yy = pos_content / 3;
8951
8952     x = bx - 1 + xx;
8953     y = by - 1 + yy;
8954
8955     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8956       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8957   }
8958   else
8959   {
8960     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8961     {
8962       int xx = x - bx + 1;
8963       int yy = y - by + 1;
8964
8965       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8966         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8967     }
8968   }
8969
8970   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8971 }
8972
8973 void CheckExit(int x, int y)
8974 {
8975   if (local_player->gems_still_needed > 0 ||
8976       local_player->sokobanfields_still_needed > 0 ||
8977       local_player->lights_still_needed > 0)
8978   {
8979     int element = Feld[x][y];
8980     int graphic = el2img(element);
8981
8982     if (IS_ANIMATED(graphic))
8983       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8984
8985     return;
8986   }
8987
8988   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8989     return;
8990
8991   Feld[x][y] = EL_EXIT_OPENING;
8992
8993   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8994 }
8995
8996 void CheckExitEM(int x, int y)
8997 {
8998   if (local_player->gems_still_needed > 0 ||
8999       local_player->sokobanfields_still_needed > 0 ||
9000       local_player->lights_still_needed > 0)
9001   {
9002     int element = Feld[x][y];
9003     int graphic = el2img(element);
9004
9005     if (IS_ANIMATED(graphic))
9006       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9007
9008     return;
9009   }
9010
9011   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9012     return;
9013
9014   Feld[x][y] = EL_EM_EXIT_OPENING;
9015
9016   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9017 }
9018
9019 void CheckExitSteel(int x, int y)
9020 {
9021   if (local_player->gems_still_needed > 0 ||
9022       local_player->sokobanfields_still_needed > 0 ||
9023       local_player->lights_still_needed > 0)
9024   {
9025     int element = Feld[x][y];
9026     int graphic = el2img(element);
9027
9028     if (IS_ANIMATED(graphic))
9029       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9030
9031     return;
9032   }
9033
9034   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9035     return;
9036
9037   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9038
9039   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9040 }
9041
9042 void CheckExitSteelEM(int x, int y)
9043 {
9044   if (local_player->gems_still_needed > 0 ||
9045       local_player->sokobanfields_still_needed > 0 ||
9046       local_player->lights_still_needed > 0)
9047   {
9048     int element = Feld[x][y];
9049     int graphic = el2img(element);
9050
9051     if (IS_ANIMATED(graphic))
9052       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9053
9054     return;
9055   }
9056
9057   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9058     return;
9059
9060   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9061
9062   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9063 }
9064
9065 void CheckExitSP(int x, int y)
9066 {
9067   if (local_player->gems_still_needed > 0)
9068   {
9069     int element = Feld[x][y];
9070     int graphic = el2img(element);
9071
9072     if (IS_ANIMATED(graphic))
9073       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9074
9075     return;
9076   }
9077
9078   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9079     return;
9080
9081   Feld[x][y] = EL_SP_EXIT_OPENING;
9082
9083   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9084 }
9085
9086 static void CloseAllOpenTimegates()
9087 {
9088   int x, y;
9089
9090   SCAN_PLAYFIELD(x, y)
9091   {
9092     int element = Feld[x][y];
9093
9094     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9095     {
9096       Feld[x][y] = EL_TIMEGATE_CLOSING;
9097
9098       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9099     }
9100   }
9101 }
9102
9103 void DrawTwinkleOnField(int x, int y)
9104 {
9105   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9106     return;
9107
9108   if (Feld[x][y] == EL_BD_DIAMOND)
9109     return;
9110
9111   if (MovDelay[x][y] == 0)      /* next animation frame */
9112     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9113
9114   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9115   {
9116     MovDelay[x][y]--;
9117
9118     DrawLevelElementAnimation(x, y, Feld[x][y]);
9119
9120     if (MovDelay[x][y] != 0)
9121     {
9122       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9123                                            10 - MovDelay[x][y]);
9124
9125       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9126     }
9127   }
9128 }
9129
9130 void MauerWaechst(int x, int y)
9131 {
9132   int delay = 6;
9133
9134   if (!MovDelay[x][y])          /* next animation frame */
9135     MovDelay[x][y] = 3 * delay;
9136
9137   if (MovDelay[x][y])           /* wait some time before next frame */
9138   {
9139     MovDelay[x][y]--;
9140
9141     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9142     {
9143       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9144       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9145
9146       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9147     }
9148
9149     if (!MovDelay[x][y])
9150     {
9151       if (MovDir[x][y] == MV_LEFT)
9152       {
9153         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9154           TEST_DrawLevelField(x - 1, y);
9155       }
9156       else if (MovDir[x][y] == MV_RIGHT)
9157       {
9158         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9159           TEST_DrawLevelField(x + 1, y);
9160       }
9161       else if (MovDir[x][y] == MV_UP)
9162       {
9163         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9164           TEST_DrawLevelField(x, y - 1);
9165       }
9166       else
9167       {
9168         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9169           TEST_DrawLevelField(x, y + 1);
9170       }
9171
9172       Feld[x][y] = Store[x][y];
9173       Store[x][y] = 0;
9174       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9175       TEST_DrawLevelField(x, y);
9176     }
9177   }
9178 }
9179
9180 void MauerAbleger(int ax, int ay)
9181 {
9182   int element = Feld[ax][ay];
9183   int graphic = el2img(element);
9184   boolean oben_frei = FALSE, unten_frei = FALSE;
9185   boolean links_frei = FALSE, rechts_frei = FALSE;
9186   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9187   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9188   boolean new_wall = FALSE;
9189
9190   if (IS_ANIMATED(graphic))
9191     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9192
9193   if (!MovDelay[ax][ay])        /* start building new wall */
9194     MovDelay[ax][ay] = 6;
9195
9196   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9197   {
9198     MovDelay[ax][ay]--;
9199     if (MovDelay[ax][ay])
9200       return;
9201   }
9202
9203   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9204     oben_frei = TRUE;
9205   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9206     unten_frei = TRUE;
9207   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9208     links_frei = TRUE;
9209   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9210     rechts_frei = TRUE;
9211
9212   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9213       element == EL_EXPANDABLE_WALL_ANY)
9214   {
9215     if (oben_frei)
9216     {
9217       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9218       Store[ax][ay-1] = element;
9219       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9220       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9221         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9222                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9223       new_wall = TRUE;
9224     }
9225     if (unten_frei)
9226     {
9227       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9228       Store[ax][ay+1] = element;
9229       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9230       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9231         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9232                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9233       new_wall = TRUE;
9234     }
9235   }
9236
9237   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9238       element == EL_EXPANDABLE_WALL_ANY ||
9239       element == EL_EXPANDABLE_WALL ||
9240       element == EL_BD_EXPANDABLE_WALL)
9241   {
9242     if (links_frei)
9243     {
9244       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9245       Store[ax-1][ay] = element;
9246       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9247       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9248         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9249                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9250       new_wall = TRUE;
9251     }
9252
9253     if (rechts_frei)
9254     {
9255       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9256       Store[ax+1][ay] = element;
9257       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9258       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9259         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9260                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9261       new_wall = TRUE;
9262     }
9263   }
9264
9265   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9266     TEST_DrawLevelField(ax, ay);
9267
9268   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9269     oben_massiv = TRUE;
9270   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9271     unten_massiv = TRUE;
9272   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9273     links_massiv = TRUE;
9274   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9275     rechts_massiv = TRUE;
9276
9277   if (((oben_massiv && unten_massiv) ||
9278        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9279        element == EL_EXPANDABLE_WALL) &&
9280       ((links_massiv && rechts_massiv) ||
9281        element == EL_EXPANDABLE_WALL_VERTICAL))
9282     Feld[ax][ay] = EL_WALL;
9283
9284   if (new_wall)
9285     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9286 }
9287
9288 void MauerAblegerStahl(int ax, int ay)
9289 {
9290   int element = Feld[ax][ay];
9291   int graphic = el2img(element);
9292   boolean oben_frei = FALSE, unten_frei = FALSE;
9293   boolean links_frei = FALSE, rechts_frei = FALSE;
9294   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9295   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9296   boolean new_wall = FALSE;
9297
9298   if (IS_ANIMATED(graphic))
9299     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9300
9301   if (!MovDelay[ax][ay])        /* start building new wall */
9302     MovDelay[ax][ay] = 6;
9303
9304   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9305   {
9306     MovDelay[ax][ay]--;
9307     if (MovDelay[ax][ay])
9308       return;
9309   }
9310
9311   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9312     oben_frei = TRUE;
9313   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9314     unten_frei = TRUE;
9315   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9316     links_frei = TRUE;
9317   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9318     rechts_frei = TRUE;
9319
9320   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9321       element == EL_EXPANDABLE_STEELWALL_ANY)
9322   {
9323     if (oben_frei)
9324     {
9325       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9326       Store[ax][ay-1] = element;
9327       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9328       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9329         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9330                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9331       new_wall = TRUE;
9332     }
9333     if (unten_frei)
9334     {
9335       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9336       Store[ax][ay+1] = element;
9337       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9338       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9339         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9340                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9341       new_wall = TRUE;
9342     }
9343   }
9344
9345   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9346       element == EL_EXPANDABLE_STEELWALL_ANY)
9347   {
9348     if (links_frei)
9349     {
9350       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9351       Store[ax-1][ay] = element;
9352       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9353       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9354         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9355                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9356       new_wall = TRUE;
9357     }
9358
9359     if (rechts_frei)
9360     {
9361       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9362       Store[ax+1][ay] = element;
9363       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9364       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9365         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9366                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9367       new_wall = TRUE;
9368     }
9369   }
9370
9371   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9372     oben_massiv = TRUE;
9373   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9374     unten_massiv = TRUE;
9375   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9376     links_massiv = TRUE;
9377   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9378     rechts_massiv = TRUE;
9379
9380   if (((oben_massiv && unten_massiv) ||
9381        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9382       ((links_massiv && rechts_massiv) ||
9383        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9384     Feld[ax][ay] = EL_STEELWALL;
9385
9386   if (new_wall)
9387     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9388 }
9389
9390 void CheckForDragon(int x, int y)
9391 {
9392   int i, j;
9393   boolean dragon_found = FALSE;
9394   static int xy[4][2] =
9395   {
9396     { 0, -1 },
9397     { -1, 0 },
9398     { +1, 0 },
9399     { 0, +1 }
9400   };
9401
9402   for (i = 0; i < NUM_DIRECTIONS; i++)
9403   {
9404     for (j = 0; j < 4; j++)
9405     {
9406       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9407
9408       if (IN_LEV_FIELD(xx, yy) &&
9409           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9410       {
9411         if (Feld[xx][yy] == EL_DRAGON)
9412           dragon_found = TRUE;
9413       }
9414       else
9415         break;
9416     }
9417   }
9418
9419   if (!dragon_found)
9420   {
9421     for (i = 0; i < NUM_DIRECTIONS; i++)
9422     {
9423       for (j = 0; j < 3; j++)
9424       {
9425         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9426   
9427         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9428         {
9429           Feld[xx][yy] = EL_EMPTY;
9430           TEST_DrawLevelField(xx, yy);
9431         }
9432         else
9433           break;
9434       }
9435     }
9436   }
9437 }
9438
9439 static void InitBuggyBase(int x, int y)
9440 {
9441   int element = Feld[x][y];
9442   int activating_delay = FRAMES_PER_SECOND / 4;
9443
9444   ChangeDelay[x][y] =
9445     (element == EL_SP_BUGGY_BASE ?
9446      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9447      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9448      activating_delay :
9449      element == EL_SP_BUGGY_BASE_ACTIVE ?
9450      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9451 }
9452
9453 static void WarnBuggyBase(int x, int y)
9454 {
9455   int i;
9456   static int xy[4][2] =
9457   {
9458     { 0, -1 },
9459     { -1, 0 },
9460     { +1, 0 },
9461     { 0, +1 }
9462   };
9463
9464   for (i = 0; i < NUM_DIRECTIONS; i++)
9465   {
9466     int xx = x + xy[i][0];
9467     int yy = y + xy[i][1];
9468
9469     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9470     {
9471       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9472
9473       break;
9474     }
9475   }
9476 }
9477
9478 static void InitTrap(int x, int y)
9479 {
9480   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9481 }
9482
9483 static void ActivateTrap(int x, int y)
9484 {
9485   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9486 }
9487
9488 static void ChangeActiveTrap(int x, int y)
9489 {
9490   int graphic = IMG_TRAP_ACTIVE;
9491
9492   /* if new animation frame was drawn, correct crumbled sand border */
9493   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9494     TEST_DrawLevelFieldCrumbled(x, y);
9495 }
9496
9497 static int getSpecialActionElement(int element, int number, int base_element)
9498 {
9499   return (element != EL_EMPTY ? element :
9500           number != -1 ? base_element + number - 1 :
9501           EL_EMPTY);
9502 }
9503
9504 static int getModifiedActionNumber(int value_old, int operator, int operand,
9505                                    int value_min, int value_max)
9506 {
9507   int value_new = (operator == CA_MODE_SET      ? operand :
9508                    operator == CA_MODE_ADD      ? value_old + operand :
9509                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9510                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9511                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9512                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9513                    value_old);
9514
9515   return (value_new < value_min ? value_min :
9516           value_new > value_max ? value_max :
9517           value_new);
9518 }
9519
9520 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9521 {
9522   struct ElementInfo *ei = &element_info[element];
9523   struct ElementChangeInfo *change = &ei->change_page[page];
9524   int target_element = change->target_element;
9525   int action_type = change->action_type;
9526   int action_mode = change->action_mode;
9527   int action_arg = change->action_arg;
9528   int action_element = change->action_element;
9529   int i;
9530
9531   if (!change->has_action)
9532     return;
9533
9534   /* ---------- determine action paramater values -------------------------- */
9535
9536   int level_time_value =
9537     (level.time > 0 ? TimeLeft :
9538      TimePlayed);
9539
9540   int action_arg_element_raw =
9541     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9542      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9543      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9544      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9545      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9546      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9547      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9548      EL_EMPTY);
9549   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9550
9551   int action_arg_direction =
9552     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9553      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9554      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9555      change->actual_trigger_side :
9556      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9557      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9558      MV_NONE);
9559
9560   int action_arg_number_min =
9561     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9562      CA_ARG_MIN);
9563
9564   int action_arg_number_max =
9565     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9566      action_type == CA_SET_LEVEL_GEMS ? 999 :
9567      action_type == CA_SET_LEVEL_TIME ? 9999 :
9568      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9569      action_type == CA_SET_CE_VALUE ? 9999 :
9570      action_type == CA_SET_CE_SCORE ? 9999 :
9571      CA_ARG_MAX);
9572
9573   int action_arg_number_reset =
9574     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9575      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9576      action_type == CA_SET_LEVEL_TIME ? level.time :
9577      action_type == CA_SET_LEVEL_SCORE ? 0 :
9578      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9579      action_type == CA_SET_CE_SCORE ? 0 :
9580      0);
9581
9582   int action_arg_number =
9583     (action_arg <= CA_ARG_MAX ? action_arg :
9584      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9585      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9586      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9587      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9588      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9589      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9590      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9591      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9592      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9593      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9594      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9595      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9596      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9597      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9598      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9599      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9600      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9601      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9602      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9603      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9604      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9605      -1);
9606
9607   int action_arg_number_old =
9608     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9609      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9610      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9611      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9612      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9613      0);
9614
9615   int action_arg_number_new =
9616     getModifiedActionNumber(action_arg_number_old,
9617                             action_mode, action_arg_number,
9618                             action_arg_number_min, action_arg_number_max);
9619
9620   int trigger_player_bits =
9621     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9622      change->actual_trigger_player_bits : change->trigger_player);
9623
9624   int action_arg_player_bits =
9625     (action_arg >= CA_ARG_PLAYER_1 &&
9626      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9627      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9628      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9629      PLAYER_BITS_ANY);
9630
9631   /* ---------- execute action  -------------------------------------------- */
9632
9633   switch (action_type)
9634   {
9635     case CA_NO_ACTION:
9636     {
9637       return;
9638     }
9639
9640     /* ---------- level actions  ------------------------------------------- */
9641
9642     case CA_RESTART_LEVEL:
9643     {
9644       game.restart_level = TRUE;
9645
9646       break;
9647     }
9648
9649     case CA_SHOW_ENVELOPE:
9650     {
9651       int element = getSpecialActionElement(action_arg_element,
9652                                             action_arg_number, EL_ENVELOPE_1);
9653
9654       if (IS_ENVELOPE(element))
9655         local_player->show_envelope = element;
9656
9657       break;
9658     }
9659
9660     case CA_SET_LEVEL_TIME:
9661     {
9662       if (level.time > 0)       /* only modify limited time value */
9663       {
9664         TimeLeft = action_arg_number_new;
9665
9666         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9667
9668         DisplayGameControlValues();
9669
9670         if (!TimeLeft && setup.time_limit)
9671           for (i = 0; i < MAX_PLAYERS; i++)
9672             KillPlayer(&stored_player[i]);
9673       }
9674
9675       break;
9676     }
9677
9678     case CA_SET_LEVEL_SCORE:
9679     {
9680       local_player->score = action_arg_number_new;
9681
9682       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9683
9684       DisplayGameControlValues();
9685
9686       break;
9687     }
9688
9689     case CA_SET_LEVEL_GEMS:
9690     {
9691       local_player->gems_still_needed = action_arg_number_new;
9692
9693       game.snapshot.collected_item = TRUE;
9694
9695       game_panel_controls[GAME_PANEL_GEMS].value =
9696         local_player->gems_still_needed;
9697
9698       DisplayGameControlValues();
9699
9700       break;
9701     }
9702
9703     case CA_SET_LEVEL_WIND:
9704     {
9705       game.wind_direction = action_arg_direction;
9706
9707       break;
9708     }
9709
9710     case CA_SET_LEVEL_RANDOM_SEED:
9711     {
9712       /* ensure that setting a new random seed while playing is predictable */
9713       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9714
9715       break;
9716     }
9717
9718     /* ---------- player actions  ------------------------------------------ */
9719
9720     case CA_MOVE_PLAYER:
9721     {
9722       /* automatically move to the next field in specified direction */
9723       for (i = 0; i < MAX_PLAYERS; i++)
9724         if (trigger_player_bits & (1 << i))
9725           stored_player[i].programmed_action = action_arg_direction;
9726
9727       break;
9728     }
9729
9730     case CA_EXIT_PLAYER:
9731     {
9732       for (i = 0; i < MAX_PLAYERS; i++)
9733         if (action_arg_player_bits & (1 << i))
9734           PlayerWins(&stored_player[i]);
9735
9736       break;
9737     }
9738
9739     case CA_KILL_PLAYER:
9740     {
9741       for (i = 0; i < MAX_PLAYERS; i++)
9742         if (action_arg_player_bits & (1 << i))
9743           KillPlayer(&stored_player[i]);
9744
9745       break;
9746     }
9747
9748     case CA_SET_PLAYER_KEYS:
9749     {
9750       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9751       int element = getSpecialActionElement(action_arg_element,
9752                                             action_arg_number, EL_KEY_1);
9753
9754       if (IS_KEY(element))
9755       {
9756         for (i = 0; i < MAX_PLAYERS; i++)
9757         {
9758           if (trigger_player_bits & (1 << i))
9759           {
9760             stored_player[i].key[KEY_NR(element)] = key_state;
9761
9762             DrawGameDoorValues();
9763           }
9764         }
9765       }
9766
9767       break;
9768     }
9769
9770     case CA_SET_PLAYER_SPEED:
9771     {
9772       for (i = 0; i < MAX_PLAYERS; i++)
9773       {
9774         if (trigger_player_bits & (1 << i))
9775         {
9776           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9777
9778           if (action_arg == CA_ARG_SPEED_FASTER &&
9779               stored_player[i].cannot_move)
9780           {
9781             action_arg_number = STEPSIZE_VERY_SLOW;
9782           }
9783           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9784                    action_arg == CA_ARG_SPEED_FASTER)
9785           {
9786             action_arg_number = 2;
9787             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9788                            CA_MODE_MULTIPLY);
9789           }
9790           else if (action_arg == CA_ARG_NUMBER_RESET)
9791           {
9792             action_arg_number = level.initial_player_stepsize[i];
9793           }
9794
9795           move_stepsize =
9796             getModifiedActionNumber(move_stepsize,
9797                                     action_mode,
9798                                     action_arg_number,
9799                                     action_arg_number_min,
9800                                     action_arg_number_max);
9801
9802           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9803         }
9804       }
9805
9806       break;
9807     }
9808
9809     case CA_SET_PLAYER_SHIELD:
9810     {
9811       for (i = 0; i < MAX_PLAYERS; i++)
9812       {
9813         if (trigger_player_bits & (1 << i))
9814         {
9815           if (action_arg == CA_ARG_SHIELD_OFF)
9816           {
9817             stored_player[i].shield_normal_time_left = 0;
9818             stored_player[i].shield_deadly_time_left = 0;
9819           }
9820           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9821           {
9822             stored_player[i].shield_normal_time_left = 999999;
9823           }
9824           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9825           {
9826             stored_player[i].shield_normal_time_left = 999999;
9827             stored_player[i].shield_deadly_time_left = 999999;
9828           }
9829         }
9830       }
9831
9832       break;
9833     }
9834
9835     case CA_SET_PLAYER_GRAVITY:
9836     {
9837       for (i = 0; i < MAX_PLAYERS; i++)
9838       {
9839         if (trigger_player_bits & (1 << i))
9840         {
9841           stored_player[i].gravity =
9842             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9843              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9844              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9845              stored_player[i].gravity);
9846         }
9847       }
9848
9849       break;
9850     }
9851
9852     case CA_SET_PLAYER_ARTWORK:
9853     {
9854       for (i = 0; i < MAX_PLAYERS; i++)
9855       {
9856         if (trigger_player_bits & (1 << i))
9857         {
9858           int artwork_element = action_arg_element;
9859
9860           if (action_arg == CA_ARG_ELEMENT_RESET)
9861             artwork_element =
9862               (level.use_artwork_element[i] ? level.artwork_element[i] :
9863                stored_player[i].element_nr);
9864
9865           if (stored_player[i].artwork_element != artwork_element)
9866             stored_player[i].Frame = 0;
9867
9868           stored_player[i].artwork_element = artwork_element;
9869
9870           SetPlayerWaiting(&stored_player[i], FALSE);
9871
9872           /* set number of special actions for bored and sleeping animation */
9873           stored_player[i].num_special_action_bored =
9874             get_num_special_action(artwork_element,
9875                                    ACTION_BORING_1, ACTION_BORING_LAST);
9876           stored_player[i].num_special_action_sleeping =
9877             get_num_special_action(artwork_element,
9878                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9879         }
9880       }
9881
9882       break;
9883     }
9884
9885     case CA_SET_PLAYER_INVENTORY:
9886     {
9887       for (i = 0; i < MAX_PLAYERS; i++)
9888       {
9889         struct PlayerInfo *player = &stored_player[i];
9890         int j, k;
9891
9892         if (trigger_player_bits & (1 << i))
9893         {
9894           int inventory_element = action_arg_element;
9895
9896           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9897               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9898               action_arg == CA_ARG_ELEMENT_ACTION)
9899           {
9900             int element = inventory_element;
9901             int collect_count = element_info[element].collect_count_initial;
9902
9903             if (!IS_CUSTOM_ELEMENT(element))
9904               collect_count = 1;
9905
9906             if (collect_count == 0)
9907               player->inventory_infinite_element = element;
9908             else
9909               for (k = 0; k < collect_count; k++)
9910                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9911                   player->inventory_element[player->inventory_size++] =
9912                     element;
9913           }
9914           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9915                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9916                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9917           {
9918             if (player->inventory_infinite_element != EL_UNDEFINED &&
9919                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9920                                      action_arg_element_raw))
9921               player->inventory_infinite_element = EL_UNDEFINED;
9922
9923             for (k = 0, j = 0; j < player->inventory_size; j++)
9924             {
9925               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9926                                         action_arg_element_raw))
9927                 player->inventory_element[k++] = player->inventory_element[j];
9928             }
9929
9930             player->inventory_size = k;
9931           }
9932           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9933           {
9934             if (player->inventory_size > 0)
9935             {
9936               for (j = 0; j < player->inventory_size - 1; j++)
9937                 player->inventory_element[j] = player->inventory_element[j + 1];
9938
9939               player->inventory_size--;
9940             }
9941           }
9942           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9943           {
9944             if (player->inventory_size > 0)
9945               player->inventory_size--;
9946           }
9947           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9948           {
9949             player->inventory_infinite_element = EL_UNDEFINED;
9950             player->inventory_size = 0;
9951           }
9952           else if (action_arg == CA_ARG_INVENTORY_RESET)
9953           {
9954             player->inventory_infinite_element = EL_UNDEFINED;
9955             player->inventory_size = 0;
9956
9957             if (level.use_initial_inventory[i])
9958             {
9959               for (j = 0; j < level.initial_inventory_size[i]; j++)
9960               {
9961                 int element = level.initial_inventory_content[i][j];
9962                 int collect_count = element_info[element].collect_count_initial;
9963
9964                 if (!IS_CUSTOM_ELEMENT(element))
9965                   collect_count = 1;
9966
9967                 if (collect_count == 0)
9968                   player->inventory_infinite_element = element;
9969                 else
9970                   for (k = 0; k < collect_count; k++)
9971                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9972                       player->inventory_element[player->inventory_size++] =
9973                         element;
9974               }
9975             }
9976           }
9977         }
9978       }
9979
9980       break;
9981     }
9982
9983     /* ---------- CE actions  ---------------------------------------------- */
9984
9985     case CA_SET_CE_VALUE:
9986     {
9987       int last_ce_value = CustomValue[x][y];
9988
9989       CustomValue[x][y] = action_arg_number_new;
9990
9991       if (CustomValue[x][y] != last_ce_value)
9992       {
9993         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9994         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9995
9996         if (CustomValue[x][y] == 0)
9997         {
9998           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9999           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10000         }
10001       }
10002
10003       break;
10004     }
10005
10006     case CA_SET_CE_SCORE:
10007     {
10008       int last_ce_score = ei->collect_score;
10009
10010       ei->collect_score = action_arg_number_new;
10011
10012       if (ei->collect_score != last_ce_score)
10013       {
10014         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10015         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10016
10017         if (ei->collect_score == 0)
10018         {
10019           int xx, yy;
10020
10021           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10022           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10023
10024           /*
10025             This is a very special case that seems to be a mixture between
10026             CheckElementChange() and CheckTriggeredElementChange(): while
10027             the first one only affects single elements that are triggered
10028             directly, the second one affects multiple elements in the playfield
10029             that are triggered indirectly by another element. This is a third
10030             case: Changing the CE score always affects multiple identical CEs,
10031             so every affected CE must be checked, not only the single CE for
10032             which the CE score was changed in the first place (as every instance
10033             of that CE shares the same CE score, and therefore also can change)!
10034           */
10035           SCAN_PLAYFIELD(xx, yy)
10036           {
10037             if (Feld[xx][yy] == element)
10038               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10039                                  CE_SCORE_GETS_ZERO);
10040           }
10041         }
10042       }
10043
10044       break;
10045     }
10046
10047     case CA_SET_CE_ARTWORK:
10048     {
10049       int artwork_element = action_arg_element;
10050       boolean reset_frame = FALSE;
10051       int xx, yy;
10052
10053       if (action_arg == CA_ARG_ELEMENT_RESET)
10054         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10055                            element);
10056
10057       if (ei->gfx_element != artwork_element)
10058         reset_frame = TRUE;
10059
10060       ei->gfx_element = artwork_element;
10061
10062       SCAN_PLAYFIELD(xx, yy)
10063       {
10064         if (Feld[xx][yy] == element)
10065         {
10066           if (reset_frame)
10067           {
10068             ResetGfxAnimation(xx, yy);
10069             ResetRandomAnimationValue(xx, yy);
10070           }
10071
10072           TEST_DrawLevelField(xx, yy);
10073         }
10074       }
10075
10076       break;
10077     }
10078
10079     /* ---------- engine actions  ------------------------------------------ */
10080
10081     case CA_SET_ENGINE_SCAN_MODE:
10082     {
10083       InitPlayfieldScanMode(action_arg);
10084
10085       break;
10086     }
10087
10088     default:
10089       break;
10090   }
10091 }
10092
10093 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10094 {
10095   int old_element = Feld[x][y];
10096   int new_element = GetElementFromGroupElement(element);
10097   int previous_move_direction = MovDir[x][y];
10098   int last_ce_value = CustomValue[x][y];
10099   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10100   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10101   boolean add_player_onto_element = (new_element_is_player &&
10102                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10103                                      IS_WALKABLE(old_element));
10104
10105   if (!add_player_onto_element)
10106   {
10107     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10108       RemoveMovingField(x, y);
10109     else
10110       RemoveField(x, y);
10111
10112     Feld[x][y] = new_element;
10113
10114     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10115       MovDir[x][y] = previous_move_direction;
10116
10117     if (element_info[new_element].use_last_ce_value)
10118       CustomValue[x][y] = last_ce_value;
10119
10120     InitField_WithBug1(x, y, FALSE);
10121
10122     new_element = Feld[x][y];   /* element may have changed */
10123
10124     ResetGfxAnimation(x, y);
10125     ResetRandomAnimationValue(x, y);
10126
10127     TEST_DrawLevelField(x, y);
10128
10129     if (GFX_CRUMBLED(new_element))
10130       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10131   }
10132
10133   /* check if element under the player changes from accessible to unaccessible
10134      (needed for special case of dropping element which then changes) */
10135   /* (must be checked after creating new element for walkable group elements) */
10136   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10137       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10138   {
10139     Bang(x, y);
10140
10141     return;
10142   }
10143
10144   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10145   if (new_element_is_player)
10146     RelocatePlayer(x, y, new_element);
10147
10148   if (is_change)
10149     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10150
10151   TestIfBadThingTouchesPlayer(x, y);
10152   TestIfPlayerTouchesCustomElement(x, y);
10153   TestIfElementTouchesCustomElement(x, y);
10154 }
10155
10156 static void CreateField(int x, int y, int element)
10157 {
10158   CreateFieldExt(x, y, element, FALSE);
10159 }
10160
10161 static void CreateElementFromChange(int x, int y, int element)
10162 {
10163   element = GET_VALID_RUNTIME_ELEMENT(element);
10164
10165   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10166   {
10167     int old_element = Feld[x][y];
10168
10169     /* prevent changed element from moving in same engine frame
10170        unless both old and new element can either fall or move */
10171     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10172         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10173       Stop[x][y] = TRUE;
10174   }
10175
10176   CreateFieldExt(x, y, element, TRUE);
10177 }
10178
10179 static boolean ChangeElement(int x, int y, int element, int page)
10180 {
10181   struct ElementInfo *ei = &element_info[element];
10182   struct ElementChangeInfo *change = &ei->change_page[page];
10183   int ce_value = CustomValue[x][y];
10184   int ce_score = ei->collect_score;
10185   int target_element;
10186   int old_element = Feld[x][y];
10187
10188   /* always use default change event to prevent running into a loop */
10189   if (ChangeEvent[x][y] == -1)
10190     ChangeEvent[x][y] = CE_DELAY;
10191
10192   if (ChangeEvent[x][y] == CE_DELAY)
10193   {
10194     /* reset actual trigger element, trigger player and action element */
10195     change->actual_trigger_element = EL_EMPTY;
10196     change->actual_trigger_player = EL_EMPTY;
10197     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10198     change->actual_trigger_side = CH_SIDE_NONE;
10199     change->actual_trigger_ce_value = 0;
10200     change->actual_trigger_ce_score = 0;
10201   }
10202
10203   /* do not change elements more than a specified maximum number of changes */
10204   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10205     return FALSE;
10206
10207   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10208
10209   if (change->explode)
10210   {
10211     Bang(x, y);
10212
10213     return TRUE;
10214   }
10215
10216   if (change->use_target_content)
10217   {
10218     boolean complete_replace = TRUE;
10219     boolean can_replace[3][3];
10220     int xx, yy;
10221
10222     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10223     {
10224       boolean is_empty;
10225       boolean is_walkable;
10226       boolean is_diggable;
10227       boolean is_collectible;
10228       boolean is_removable;
10229       boolean is_destructible;
10230       int ex = x + xx - 1;
10231       int ey = y + yy - 1;
10232       int content_element = change->target_content.e[xx][yy];
10233       int e;
10234
10235       can_replace[xx][yy] = TRUE;
10236
10237       if (ex == x && ey == y)   /* do not check changing element itself */
10238         continue;
10239
10240       if (content_element == EL_EMPTY_SPACE)
10241       {
10242         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10243
10244         continue;
10245       }
10246
10247       if (!IN_LEV_FIELD(ex, ey))
10248       {
10249         can_replace[xx][yy] = FALSE;
10250         complete_replace = FALSE;
10251
10252         continue;
10253       }
10254
10255       e = Feld[ex][ey];
10256
10257       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10258         e = MovingOrBlocked2Element(ex, ey);
10259
10260       is_empty = (IS_FREE(ex, ey) ||
10261                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10262
10263       is_walkable     = (is_empty || IS_WALKABLE(e));
10264       is_diggable     = (is_empty || IS_DIGGABLE(e));
10265       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10266       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10267       is_removable    = (is_diggable || is_collectible);
10268
10269       can_replace[xx][yy] =
10270         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10271           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10272           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10273           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10274           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10275           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10276          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10277
10278       if (!can_replace[xx][yy])
10279         complete_replace = FALSE;
10280     }
10281
10282     if (!change->only_if_complete || complete_replace)
10283     {
10284       boolean something_has_changed = FALSE;
10285
10286       if (change->only_if_complete && change->use_random_replace &&
10287           RND(100) < change->random_percentage)
10288         return FALSE;
10289
10290       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10291       {
10292         int ex = x + xx - 1;
10293         int ey = y + yy - 1;
10294         int content_element;
10295
10296         if (can_replace[xx][yy] && (!change->use_random_replace ||
10297                                     RND(100) < change->random_percentage))
10298         {
10299           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10300             RemoveMovingField(ex, ey);
10301
10302           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10303
10304           content_element = change->target_content.e[xx][yy];
10305           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10306                                               ce_value, ce_score);
10307
10308           CreateElementFromChange(ex, ey, target_element);
10309
10310           something_has_changed = TRUE;
10311
10312           /* for symmetry reasons, freeze newly created border elements */
10313           if (ex != x || ey != y)
10314             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10315         }
10316       }
10317
10318       if (something_has_changed)
10319       {
10320         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10321         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10322       }
10323     }
10324   }
10325   else
10326   {
10327     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10328                                         ce_value, ce_score);
10329
10330     if (element == EL_DIAGONAL_GROWING ||
10331         element == EL_DIAGONAL_SHRINKING)
10332     {
10333       target_element = Store[x][y];
10334
10335       Store[x][y] = EL_EMPTY;
10336     }
10337
10338     CreateElementFromChange(x, y, target_element);
10339
10340     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10341     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10342   }
10343
10344   /* this uses direct change before indirect change */
10345   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10346
10347   return TRUE;
10348 }
10349
10350 static void HandleElementChange(int x, int y, int page)
10351 {
10352   int element = MovingOrBlocked2Element(x, y);
10353   struct ElementInfo *ei = &element_info[element];
10354   struct ElementChangeInfo *change = &ei->change_page[page];
10355   boolean handle_action_before_change = FALSE;
10356
10357 #ifdef DEBUG
10358   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10359       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10360   {
10361     printf("\n\n");
10362     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10363            x, y, element, element_info[element].token_name);
10364     printf("HandleElementChange(): This should never happen!\n");
10365     printf("\n\n");
10366   }
10367 #endif
10368
10369   /* this can happen with classic bombs on walkable, changing elements */
10370   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10371   {
10372     return;
10373   }
10374
10375   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10376   {
10377     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10378
10379     if (change->can_change)
10380     {
10381       /* !!! not clear why graphic animation should be reset at all here !!! */
10382       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10383       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10384
10385       /*
10386         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10387
10388         When using an animation frame delay of 1 (this only happens with
10389         "sp_zonk.moving.left/right" in the classic graphics), the default
10390         (non-moving) animation shows wrong animation frames (while the
10391         moving animation, like "sp_zonk.moving.left/right", is correct,
10392         so this graphical bug never shows up with the classic graphics).
10393         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10394         be drawn instead of the correct frames 0,1,2,3. This is caused by
10395         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10396         an element change: First when the change delay ("ChangeDelay[][]")
10397         counter has reached zero after decrementing, then a second time in
10398         the next frame (after "GfxFrame[][]" was already incremented) when
10399         "ChangeDelay[][]" is reset to the initial delay value again.
10400
10401         This causes frame 0 to be drawn twice, while the last frame won't
10402         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10403
10404         As some animations may already be cleverly designed around this bug
10405         (at least the "Snake Bite" snake tail animation does this), it cannot
10406         simply be fixed here without breaking such existing animations.
10407         Unfortunately, it cannot easily be detected if a graphics set was
10408         designed "before" or "after" the bug was fixed. As a workaround,
10409         a new graphics set option "game.graphics_engine_version" was added
10410         to be able to specify the game's major release version for which the
10411         graphics set was designed, which can then be used to decide if the
10412         bugfix should be used (version 4 and above) or not (version 3 or
10413         below, or if no version was specified at all, as with old sets).
10414
10415         (The wrong/fixed animation frames can be tested with the test level set
10416         "test_gfxframe" and level "000", which contains a specially prepared
10417         custom element at level position (x/y) == (11/9) which uses the zonk
10418         animation mentioned above. Using "game.graphics_engine_version: 4"
10419         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10420         This can also be seen from the debug output for this test element.)
10421       */
10422
10423       /* when a custom element is about to change (for example by change delay),
10424          do not reset graphic animation when the custom element is moving */
10425       if (game.graphics_engine_version < 4 &&
10426           !IS_MOVING(x, y))
10427       {
10428         ResetGfxAnimation(x, y);
10429         ResetRandomAnimationValue(x, y);
10430       }
10431
10432       if (change->pre_change_function)
10433         change->pre_change_function(x, y);
10434     }
10435   }
10436
10437   ChangeDelay[x][y]--;
10438
10439   if (ChangeDelay[x][y] != 0)           /* continue element change */
10440   {
10441     if (change->can_change)
10442     {
10443       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10444
10445       if (IS_ANIMATED(graphic))
10446         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10447
10448       if (change->change_function)
10449         change->change_function(x, y);
10450     }
10451   }
10452   else                                  /* finish element change */
10453   {
10454     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10455     {
10456       page = ChangePage[x][y];
10457       ChangePage[x][y] = -1;
10458
10459       change = &ei->change_page[page];
10460     }
10461
10462     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10463     {
10464       ChangeDelay[x][y] = 1;            /* try change after next move step */
10465       ChangePage[x][y] = page;          /* remember page to use for change */
10466
10467       return;
10468     }
10469
10470     /* special case: set new level random seed before changing element */
10471     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10472       handle_action_before_change = TRUE;
10473
10474     if (change->has_action && handle_action_before_change)
10475       ExecuteCustomElementAction(x, y, element, page);
10476
10477     if (change->can_change)
10478     {
10479       if (ChangeElement(x, y, element, page))
10480       {
10481         if (change->post_change_function)
10482           change->post_change_function(x, y);
10483       }
10484     }
10485
10486     if (change->has_action && !handle_action_before_change)
10487       ExecuteCustomElementAction(x, y, element, page);
10488   }
10489 }
10490
10491 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10492                                               int trigger_element,
10493                                               int trigger_event,
10494                                               int trigger_player,
10495                                               int trigger_side,
10496                                               int trigger_page)
10497 {
10498   boolean change_done_any = FALSE;
10499   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10500   int i;
10501
10502   if (!(trigger_events[trigger_element][trigger_event]))
10503     return FALSE;
10504
10505   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10506
10507   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10508   {
10509     int element = EL_CUSTOM_START + i;
10510     boolean change_done = FALSE;
10511     int p;
10512
10513     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10514         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10515       continue;
10516
10517     for (p = 0; p < element_info[element].num_change_pages; p++)
10518     {
10519       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10520
10521       if (change->can_change_or_has_action &&
10522           change->has_event[trigger_event] &&
10523           change->trigger_side & trigger_side &&
10524           change->trigger_player & trigger_player &&
10525           change->trigger_page & trigger_page_bits &&
10526           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10527       {
10528         change->actual_trigger_element = trigger_element;
10529         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10530         change->actual_trigger_player_bits = trigger_player;
10531         change->actual_trigger_side = trigger_side;
10532         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10533         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10534
10535         if ((change->can_change && !change_done) || change->has_action)
10536         {
10537           int x, y;
10538
10539           SCAN_PLAYFIELD(x, y)
10540           {
10541             if (Feld[x][y] == element)
10542             {
10543               if (change->can_change && !change_done)
10544               {
10545                 /* if element already changed in this frame, not only prevent
10546                    another element change (checked in ChangeElement()), but
10547                    also prevent additional element actions for this element */
10548
10549                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10550                     !level.use_action_after_change_bug)
10551                   continue;
10552
10553                 ChangeDelay[x][y] = 1;
10554                 ChangeEvent[x][y] = trigger_event;
10555
10556                 HandleElementChange(x, y, p);
10557               }
10558               else if (change->has_action)
10559               {
10560                 /* if element already changed in this frame, not only prevent
10561                    another element change (checked in ChangeElement()), but
10562                    also prevent additional element actions for this element */
10563
10564                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10565                     !level.use_action_after_change_bug)
10566                   continue;
10567
10568                 ExecuteCustomElementAction(x, y, element, p);
10569                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10570               }
10571             }
10572           }
10573
10574           if (change->can_change)
10575           {
10576             change_done = TRUE;
10577             change_done_any = TRUE;
10578           }
10579         }
10580       }
10581     }
10582   }
10583
10584   RECURSION_LOOP_DETECTION_END();
10585
10586   return change_done_any;
10587 }
10588
10589 static boolean CheckElementChangeExt(int x, int y,
10590                                      int element,
10591                                      int trigger_element,
10592                                      int trigger_event,
10593                                      int trigger_player,
10594                                      int trigger_side)
10595 {
10596   boolean change_done = FALSE;
10597   int p;
10598
10599   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10600       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10601     return FALSE;
10602
10603   if (Feld[x][y] == EL_BLOCKED)
10604   {
10605     Blocked2Moving(x, y, &x, &y);
10606     element = Feld[x][y];
10607   }
10608
10609   /* check if element has already changed or is about to change after moving */
10610   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10611        Feld[x][y] != element) ||
10612
10613       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10614        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10615         ChangePage[x][y] != -1)))
10616     return FALSE;
10617
10618   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10619
10620   for (p = 0; p < element_info[element].num_change_pages; p++)
10621   {
10622     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10623
10624     /* check trigger element for all events where the element that is checked
10625        for changing interacts with a directly adjacent element -- this is
10626        different to element changes that affect other elements to change on the
10627        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10628     boolean check_trigger_element =
10629       (trigger_event == CE_TOUCHING_X ||
10630        trigger_event == CE_HITTING_X ||
10631        trigger_event == CE_HIT_BY_X ||
10632        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10633
10634     if (change->can_change_or_has_action &&
10635         change->has_event[trigger_event] &&
10636         change->trigger_side & trigger_side &&
10637         change->trigger_player & trigger_player &&
10638         (!check_trigger_element ||
10639          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10640     {
10641       change->actual_trigger_element = trigger_element;
10642       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10643       change->actual_trigger_player_bits = trigger_player;
10644       change->actual_trigger_side = trigger_side;
10645       change->actual_trigger_ce_value = CustomValue[x][y];
10646       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10647
10648       /* special case: trigger element not at (x,y) position for some events */
10649       if (check_trigger_element)
10650       {
10651         static struct
10652         {
10653           int dx, dy;
10654         } move_xy[] =
10655           {
10656             {  0,  0 },
10657             { -1,  0 },
10658             { +1,  0 },
10659             {  0,  0 },
10660             {  0, -1 },
10661             {  0,  0 }, { 0, 0 }, { 0, 0 },
10662             {  0, +1 }
10663           };
10664
10665         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10666         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10667
10668         change->actual_trigger_ce_value = CustomValue[xx][yy];
10669         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10670       }
10671
10672       if (change->can_change && !change_done)
10673       {
10674         ChangeDelay[x][y] = 1;
10675         ChangeEvent[x][y] = trigger_event;
10676
10677         HandleElementChange(x, y, p);
10678
10679         change_done = TRUE;
10680       }
10681       else if (change->has_action)
10682       {
10683         ExecuteCustomElementAction(x, y, element, p);
10684         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10685       }
10686     }
10687   }
10688
10689   RECURSION_LOOP_DETECTION_END();
10690
10691   return change_done;
10692 }
10693
10694 static void PlayPlayerSound(struct PlayerInfo *player)
10695 {
10696   int jx = player->jx, jy = player->jy;
10697   int sound_element = player->artwork_element;
10698   int last_action = player->last_action_waiting;
10699   int action = player->action_waiting;
10700
10701   if (player->is_waiting)
10702   {
10703     if (action != last_action)
10704       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10705     else
10706       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10707   }
10708   else
10709   {
10710     if (action != last_action)
10711       StopSound(element_info[sound_element].sound[last_action]);
10712
10713     if (last_action == ACTION_SLEEPING)
10714       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10715   }
10716 }
10717
10718 static void PlayAllPlayersSound()
10719 {
10720   int i;
10721
10722   for (i = 0; i < MAX_PLAYERS; i++)
10723     if (stored_player[i].active)
10724       PlayPlayerSound(&stored_player[i]);
10725 }
10726
10727 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10728 {
10729   boolean last_waiting = player->is_waiting;
10730   int move_dir = player->MovDir;
10731
10732   player->dir_waiting = move_dir;
10733   player->last_action_waiting = player->action_waiting;
10734
10735   if (is_waiting)
10736   {
10737     if (!last_waiting)          /* not waiting -> waiting */
10738     {
10739       player->is_waiting = TRUE;
10740
10741       player->frame_counter_bored =
10742         FrameCounter +
10743         game.player_boring_delay_fixed +
10744         GetSimpleRandom(game.player_boring_delay_random);
10745       player->frame_counter_sleeping =
10746         FrameCounter +
10747         game.player_sleeping_delay_fixed +
10748         GetSimpleRandom(game.player_sleeping_delay_random);
10749
10750       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10751     }
10752
10753     if (game.player_sleeping_delay_fixed +
10754         game.player_sleeping_delay_random > 0 &&
10755         player->anim_delay_counter == 0 &&
10756         player->post_delay_counter == 0 &&
10757         FrameCounter >= player->frame_counter_sleeping)
10758       player->is_sleeping = TRUE;
10759     else if (game.player_boring_delay_fixed +
10760              game.player_boring_delay_random > 0 &&
10761              FrameCounter >= player->frame_counter_bored)
10762       player->is_bored = TRUE;
10763
10764     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10765                               player->is_bored ? ACTION_BORING :
10766                               ACTION_WAITING);
10767
10768     if (player->is_sleeping && player->use_murphy)
10769     {
10770       /* special case for sleeping Murphy when leaning against non-free tile */
10771
10772       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10773           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10774            !IS_MOVING(player->jx - 1, player->jy)))
10775         move_dir = MV_LEFT;
10776       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10777                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10778                 !IS_MOVING(player->jx + 1, player->jy)))
10779         move_dir = MV_RIGHT;
10780       else
10781         player->is_sleeping = FALSE;
10782
10783       player->dir_waiting = move_dir;
10784     }
10785
10786     if (player->is_sleeping)
10787     {
10788       if (player->num_special_action_sleeping > 0)
10789       {
10790         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10791         {
10792           int last_special_action = player->special_action_sleeping;
10793           int num_special_action = player->num_special_action_sleeping;
10794           int special_action =
10795             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10796              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10797              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10798              last_special_action + 1 : ACTION_SLEEPING);
10799           int special_graphic =
10800             el_act_dir2img(player->artwork_element, special_action, move_dir);
10801
10802           player->anim_delay_counter =
10803             graphic_info[special_graphic].anim_delay_fixed +
10804             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10805           player->post_delay_counter =
10806             graphic_info[special_graphic].post_delay_fixed +
10807             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10808
10809           player->special_action_sleeping = special_action;
10810         }
10811
10812         if (player->anim_delay_counter > 0)
10813         {
10814           player->action_waiting = player->special_action_sleeping;
10815           player->anim_delay_counter--;
10816         }
10817         else if (player->post_delay_counter > 0)
10818         {
10819           player->post_delay_counter--;
10820         }
10821       }
10822     }
10823     else if (player->is_bored)
10824     {
10825       if (player->num_special_action_bored > 0)
10826       {
10827         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10828         {
10829           int special_action =
10830             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10831           int special_graphic =
10832             el_act_dir2img(player->artwork_element, special_action, move_dir);
10833
10834           player->anim_delay_counter =
10835             graphic_info[special_graphic].anim_delay_fixed +
10836             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10837           player->post_delay_counter =
10838             graphic_info[special_graphic].post_delay_fixed +
10839             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10840
10841           player->special_action_bored = special_action;
10842         }
10843
10844         if (player->anim_delay_counter > 0)
10845         {
10846           player->action_waiting = player->special_action_bored;
10847           player->anim_delay_counter--;
10848         }
10849         else if (player->post_delay_counter > 0)
10850         {
10851           player->post_delay_counter--;
10852         }
10853       }
10854     }
10855   }
10856   else if (last_waiting)        /* waiting -> not waiting */
10857   {
10858     player->is_waiting = FALSE;
10859     player->is_bored = FALSE;
10860     player->is_sleeping = FALSE;
10861
10862     player->frame_counter_bored = -1;
10863     player->frame_counter_sleeping = -1;
10864
10865     player->anim_delay_counter = 0;
10866     player->post_delay_counter = 0;
10867
10868     player->dir_waiting = player->MovDir;
10869     player->action_waiting = ACTION_DEFAULT;
10870
10871     player->special_action_bored = ACTION_DEFAULT;
10872     player->special_action_sleeping = ACTION_DEFAULT;
10873   }
10874 }
10875
10876 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10877 {
10878   if ((!player->is_moving  && player->was_moving) ||
10879       (player->MovPos == 0 && player->was_moving) ||
10880       (player->is_snapping && !player->was_snapping) ||
10881       (player->is_dropping && !player->was_dropping))
10882   {
10883     if (!CheckSaveEngineSnapshotToList())
10884       return;
10885
10886     player->was_moving = FALSE;
10887     player->was_snapping = TRUE;
10888     player->was_dropping = TRUE;
10889   }
10890   else
10891   {
10892     if (player->is_moving)
10893       player->was_moving = TRUE;
10894
10895     if (!player->is_snapping)
10896       player->was_snapping = FALSE;
10897
10898     if (!player->is_dropping)
10899       player->was_dropping = FALSE;
10900   }
10901 }
10902
10903 static void CheckSingleStepMode(struct PlayerInfo *player)
10904 {
10905   if (tape.single_step && tape.recording && !tape.pausing)
10906   {
10907     /* as it is called "single step mode", just return to pause mode when the
10908        player stopped moving after one tile (or never starts moving at all) */
10909     if (!player->is_moving &&
10910         !player->is_pushing &&
10911         !player->is_dropping_pressed)
10912     {
10913       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10914       SnapField(player, 0, 0);                  /* stop snapping */
10915     }
10916   }
10917
10918   CheckSaveEngineSnapshot(player);
10919 }
10920
10921 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10922 {
10923   int left      = player_action & JOY_LEFT;
10924   int right     = player_action & JOY_RIGHT;
10925   int up        = player_action & JOY_UP;
10926   int down      = player_action & JOY_DOWN;
10927   int button1   = player_action & JOY_BUTTON_1;
10928   int button2   = player_action & JOY_BUTTON_2;
10929   int dx        = (left ? -1 : right ? 1 : 0);
10930   int dy        = (up   ? -1 : down  ? 1 : 0);
10931
10932   if (!player->active || tape.pausing)
10933     return 0;
10934
10935   if (player_action)
10936   {
10937     if (button1)
10938       SnapField(player, dx, dy);
10939     else
10940     {
10941       if (button2)
10942         DropElement(player);
10943
10944       MovePlayer(player, dx, dy);
10945     }
10946
10947     CheckSingleStepMode(player);
10948
10949     SetPlayerWaiting(player, FALSE);
10950
10951     return player_action;
10952   }
10953   else
10954   {
10955     /* no actions for this player (no input at player's configured device) */
10956
10957     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10958     SnapField(player, 0, 0);
10959     CheckGravityMovementWhenNotMoving(player);
10960
10961     if (player->MovPos == 0)
10962       SetPlayerWaiting(player, TRUE);
10963
10964     if (player->MovPos == 0)    /* needed for tape.playing */
10965       player->is_moving = FALSE;
10966
10967     player->is_dropping = FALSE;
10968     player->is_dropping_pressed = FALSE;
10969     player->drop_pressed_delay = 0;
10970
10971     CheckSingleStepMode(player);
10972
10973     return 0;
10974   }
10975 }
10976
10977 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
10978                                          byte *tape_action)
10979 {
10980   if (!tape.use_mouse)
10981     return;
10982
10983   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
10984   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
10985   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
10986 }
10987
10988 static void SetTapeActionFromMouseAction(byte *tape_action,
10989                                          struct MouseActionInfo *mouse_action)
10990 {
10991   if (!tape.use_mouse)
10992     return;
10993
10994   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
10995   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
10996   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
10997 }
10998
10999 static void CheckLevelTime()
11000 {
11001   int i;
11002
11003   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11004   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11005   {
11006     if (level.native_em_level->lev->home == 0)  /* all players at home */
11007     {
11008       PlayerWins(local_player);
11009
11010       AllPlayersGone = TRUE;
11011
11012       level.native_em_level->lev->home = -1;
11013     }
11014
11015     if (level.native_em_level->ply[0]->alive == 0 &&
11016         level.native_em_level->ply[1]->alive == 0 &&
11017         level.native_em_level->ply[2]->alive == 0 &&
11018         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11019       AllPlayersGone = TRUE;
11020   }
11021   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11022   {
11023     if (game_sp.LevelSolved &&
11024         !game_sp.GameOver)                              /* game won */
11025     {
11026       PlayerWins(local_player);
11027
11028       game_sp.GameOver = TRUE;
11029
11030       AllPlayersGone = TRUE;
11031     }
11032
11033     if (game_sp.GameOver)                               /* game lost */
11034       AllPlayersGone = TRUE;
11035   }
11036   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11037   {
11038     if (game_mm.level_solved &&
11039         !game_mm.game_over)                             /* game won */
11040     {
11041       PlayerWins(local_player);
11042
11043       game_mm.game_over = TRUE;
11044
11045       AllPlayersGone = TRUE;
11046     }
11047
11048     if (game_mm.game_over)                              /* game lost */
11049       AllPlayersGone = TRUE;
11050   }
11051
11052   if (TimeFrames >= FRAMES_PER_SECOND)
11053   {
11054     TimeFrames = 0;
11055     TapeTime++;
11056
11057     for (i = 0; i < MAX_PLAYERS; i++)
11058     {
11059       struct PlayerInfo *player = &stored_player[i];
11060
11061       if (SHIELD_ON(player))
11062       {
11063         player->shield_normal_time_left--;
11064
11065         if (player->shield_deadly_time_left > 0)
11066           player->shield_deadly_time_left--;
11067       }
11068     }
11069
11070     if (!local_player->LevelSolved && !level.use_step_counter)
11071     {
11072       TimePlayed++;
11073
11074       if (TimeLeft > 0)
11075       {
11076         TimeLeft--;
11077
11078         if (TimeLeft <= 10 && setup.time_limit)
11079           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11080
11081         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11082            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11083
11084         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11085
11086         if (!TimeLeft && setup.time_limit)
11087         {
11088           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11089             level.native_em_level->lev->killed_out_of_time = TRUE;
11090           else
11091             for (i = 0; i < MAX_PLAYERS; i++)
11092               KillPlayer(&stored_player[i]);
11093         }
11094       }
11095       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11096       {
11097         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11098       }
11099
11100       level.native_em_level->lev->time =
11101         (game.no_time_limit ? TimePlayed : TimeLeft);
11102     }
11103
11104     if (tape.recording || tape.playing)
11105       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11106   }
11107
11108   if (tape.recording || tape.playing)
11109     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11110
11111   UpdateAndDisplayGameControlValues();
11112 }
11113
11114 void AdvanceFrameAndPlayerCounters(int player_nr)
11115 {
11116   int i;
11117
11118   /* advance frame counters (global frame counter and time frame counter) */
11119   FrameCounter++;
11120   TimeFrames++;
11121
11122   /* advance player counters (counters for move delay, move animation etc.) */
11123   for (i = 0; i < MAX_PLAYERS; i++)
11124   {
11125     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11126     int move_delay_value = stored_player[i].move_delay_value;
11127     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11128
11129     if (!advance_player_counters)       /* not all players may be affected */
11130       continue;
11131
11132     if (move_frames == 0)       /* less than one move per game frame */
11133     {
11134       int stepsize = TILEX / move_delay_value;
11135       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11136       int count = (stored_player[i].is_moving ?
11137                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11138
11139       if (count % delay == 0)
11140         move_frames = 1;
11141     }
11142
11143     stored_player[i].Frame += move_frames;
11144
11145     if (stored_player[i].MovPos != 0)
11146       stored_player[i].StepFrame += move_frames;
11147
11148     if (stored_player[i].move_delay > 0)
11149       stored_player[i].move_delay--;
11150
11151     /* due to bugs in previous versions, counter must count up, not down */
11152     if (stored_player[i].push_delay != -1)
11153       stored_player[i].push_delay++;
11154
11155     if (stored_player[i].drop_delay > 0)
11156       stored_player[i].drop_delay--;
11157
11158     if (stored_player[i].is_dropping_pressed)
11159       stored_player[i].drop_pressed_delay++;
11160   }
11161 }
11162
11163 void StartGameActions(boolean init_network_game, boolean record_tape,
11164                       int random_seed)
11165 {
11166   unsigned int new_random_seed = InitRND(random_seed);
11167
11168   if (record_tape)
11169     TapeStartRecording(new_random_seed);
11170
11171 #if defined(NETWORK_AVALIABLE)
11172   if (init_network_game)
11173   {
11174     SendToServer_StartPlaying();
11175
11176     return;
11177   }
11178 #endif
11179
11180   InitGame();
11181 }
11182
11183 void GameActionsExt()
11184 {
11185 #if 0
11186   static unsigned int game_frame_delay = 0;
11187 #endif
11188   unsigned int game_frame_delay_value;
11189   byte *recorded_player_action;
11190   byte summarized_player_action = 0;
11191   byte tape_action[MAX_PLAYERS];
11192   int i;
11193
11194   /* detect endless loops, caused by custom element programming */
11195   if (recursion_loop_detected && recursion_loop_depth == 0)
11196   {
11197     char *message = getStringCat3("Internal Error! Element ",
11198                                   EL_NAME(recursion_loop_element),
11199                                   " caused endless loop! Quit the game?");
11200
11201     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11202           EL_NAME(recursion_loop_element));
11203
11204     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11205
11206     recursion_loop_detected = FALSE;    /* if game should be continued */
11207
11208     free(message);
11209
11210     return;
11211   }
11212
11213   if (game.restart_level)
11214     StartGameActions(options.network, setup.autorecord, level.random_seed);
11215
11216   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11217   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11218   {
11219     if (level.native_em_level->lev->home == 0)  /* all players at home */
11220     {
11221       PlayerWins(local_player);
11222
11223       AllPlayersGone = TRUE;
11224
11225       level.native_em_level->lev->home = -1;
11226     }
11227
11228     if (level.native_em_level->ply[0]->alive == 0 &&
11229         level.native_em_level->ply[1]->alive == 0 &&
11230         level.native_em_level->ply[2]->alive == 0 &&
11231         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11232       AllPlayersGone = TRUE;
11233   }
11234   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11235   {
11236     if (game_sp.LevelSolved &&
11237         !game_sp.GameOver)                              /* game won */
11238     {
11239       PlayerWins(local_player);
11240
11241       game_sp.GameOver = TRUE;
11242
11243       AllPlayersGone = TRUE;
11244     }
11245
11246     if (game_sp.GameOver)                               /* game lost */
11247       AllPlayersGone = TRUE;
11248   }
11249   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11250   {
11251     if (game_mm.level_solved &&
11252         !game_mm.game_over)                             /* game won */
11253     {
11254       PlayerWins(local_player);
11255
11256       game_mm.game_over = TRUE;
11257
11258       AllPlayersGone = TRUE;
11259     }
11260
11261     if (game_mm.game_over)                              /* game lost */
11262       AllPlayersGone = TRUE;
11263   }
11264
11265   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11266     GameWon();
11267
11268   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11269     TapeStop();
11270
11271   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11272     return;
11273
11274   game_frame_delay_value =
11275     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11276
11277   if (tape.playing && tape.warp_forward && !tape.pausing)
11278     game_frame_delay_value = 0;
11279
11280   SetVideoFrameDelay(game_frame_delay_value);
11281
11282 #if 0
11283 #if 0
11284   /* ---------- main game synchronization point ---------- */
11285
11286   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11287
11288   printf("::: skip == %d\n", skip);
11289
11290 #else
11291   /* ---------- main game synchronization point ---------- */
11292
11293   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11294 #endif
11295 #endif
11296
11297   if (network_playing && !network_player_action_received)
11298   {
11299     /* try to get network player actions in time */
11300
11301 #if defined(NETWORK_AVALIABLE)
11302     /* last chance to get network player actions without main loop delay */
11303     HandleNetworking();
11304 #endif
11305
11306     /* game was quit by network peer */
11307     if (game_status != GAME_MODE_PLAYING)
11308       return;
11309
11310     if (!network_player_action_received)
11311       return;           /* failed to get network player actions in time */
11312
11313     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11314   }
11315
11316   if (tape.pausing)
11317     return;
11318
11319   /* at this point we know that we really continue executing the game */
11320
11321   network_player_action_received = FALSE;
11322
11323   /* when playing tape, read previously recorded player input from tape data */
11324   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11325
11326   if (recorded_player_action != NULL)
11327     SetMouseActionFromTapeAction(&local_player->mouse_action,
11328                                  recorded_player_action);
11329
11330   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11331   if (tape.pausing)
11332     return;
11333
11334   if (tape.set_centered_player)
11335   {
11336     game.centered_player_nr_next = tape.centered_player_nr_next;
11337     game.set_centered_player = TRUE;
11338   }
11339
11340   for (i = 0; i < MAX_PLAYERS; i++)
11341   {
11342     summarized_player_action |= stored_player[i].action;
11343
11344     if (!network_playing && (game.team_mode || tape.playing))
11345       stored_player[i].effective_action = stored_player[i].action;
11346   }
11347
11348 #if defined(NETWORK_AVALIABLE)
11349   if (network_playing)
11350     SendToServer_MovePlayer(summarized_player_action);
11351 #endif
11352
11353   // summarize all actions at local players mapped input device position
11354   // (this allows using different input devices in single player mode)
11355   if (!options.network && !game.team_mode)
11356     stored_player[map_player_action[local_player->index_nr]].effective_action =
11357       summarized_player_action;
11358
11359   if (tape.recording &&
11360       setup.team_mode &&
11361       setup.input_on_focus &&
11362       game.centered_player_nr != -1)
11363   {
11364     for (i = 0; i < MAX_PLAYERS; i++)
11365       stored_player[i].effective_action =
11366         (i == game.centered_player_nr ? summarized_player_action : 0);
11367   }
11368
11369   if (recorded_player_action != NULL)
11370     for (i = 0; i < MAX_PLAYERS; i++)
11371       stored_player[i].effective_action = recorded_player_action[i];
11372
11373   for (i = 0; i < MAX_PLAYERS; i++)
11374   {
11375     tape_action[i] = stored_player[i].effective_action;
11376
11377     /* (this may happen in the RND game engine if a player was not present on
11378        the playfield on level start, but appeared later from a custom element */
11379     if (setup.team_mode &&
11380         tape.recording &&
11381         tape_action[i] &&
11382         !tape.player_participates[i])
11383       tape.player_participates[i] = TRUE;
11384   }
11385
11386   SetTapeActionFromMouseAction(tape_action, &local_player->mouse_action);
11387
11388   /* only record actions from input devices, but not programmed actions */
11389   if (tape.recording)
11390     TapeRecordAction(tape_action);
11391
11392 #if USE_NEW_PLAYER_ASSIGNMENTS
11393   // !!! also map player actions in single player mode !!!
11394   // if (game.team_mode)
11395   if (1)
11396   {
11397     byte mapped_action[MAX_PLAYERS];
11398
11399 #if DEBUG_PLAYER_ACTIONS
11400     printf(":::");
11401     for (i = 0; i < MAX_PLAYERS; i++)
11402       printf(" %d, ", stored_player[i].effective_action);
11403 #endif
11404
11405     for (i = 0; i < MAX_PLAYERS; i++)
11406       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11407
11408     for (i = 0; i < MAX_PLAYERS; i++)
11409       stored_player[i].effective_action = mapped_action[i];
11410
11411 #if DEBUG_PLAYER_ACTIONS
11412     printf(" =>");
11413     for (i = 0; i < MAX_PLAYERS; i++)
11414       printf(" %d, ", stored_player[i].effective_action);
11415     printf("\n");
11416 #endif
11417   }
11418 #if DEBUG_PLAYER_ACTIONS
11419   else
11420   {
11421     printf(":::");
11422     for (i = 0; i < MAX_PLAYERS; i++)
11423       printf(" %d, ", stored_player[i].effective_action);
11424     printf("\n");
11425   }
11426 #endif
11427 #endif
11428
11429   for (i = 0; i < MAX_PLAYERS; i++)
11430   {
11431     // allow engine snapshot in case of changed movement attempt
11432     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11433         (stored_player[i].effective_action & KEY_MOTION))
11434       game.snapshot.changed_action = TRUE;
11435
11436     // allow engine snapshot in case of snapping/dropping attempt
11437     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11438         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11439       game.snapshot.changed_action = TRUE;
11440
11441     game.snapshot.last_action[i] = stored_player[i].effective_action;
11442   }
11443
11444   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11445   {
11446     GameActions_EM_Main();
11447   }
11448   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11449   {
11450     GameActions_SP_Main();
11451   }
11452   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11453   {
11454     GameActions_MM_Main();
11455   }
11456   else
11457   {
11458     GameActions_RND_Main();
11459   }
11460
11461   BlitScreenToBitmap(backbuffer);
11462
11463   CheckLevelTime();
11464
11465   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11466
11467   if (global.show_frames_per_second)
11468   {
11469     static unsigned int fps_counter = 0;
11470     static int fps_frames = 0;
11471     unsigned int fps_delay_ms = Counter() - fps_counter;
11472
11473     fps_frames++;
11474
11475     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11476     {
11477       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11478
11479       fps_frames = 0;
11480       fps_counter = Counter();
11481
11482       /* always draw FPS to screen after FPS value was updated */
11483       redraw_mask |= REDRAW_FPS;
11484     }
11485
11486     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11487     if (GetDrawDeactivationMask() == REDRAW_NONE)
11488       redraw_mask |= REDRAW_FPS;
11489   }
11490 }
11491
11492 static void GameActions_CheckSaveEngineSnapshot()
11493 {
11494   if (!game.snapshot.save_snapshot)
11495     return;
11496
11497   // clear flag for saving snapshot _before_ saving snapshot
11498   game.snapshot.save_snapshot = FALSE;
11499
11500   SaveEngineSnapshotToList();
11501 }
11502
11503 void GameActions()
11504 {
11505   GameActionsExt();
11506
11507   GameActions_CheckSaveEngineSnapshot();
11508 }
11509
11510 void GameActions_EM_Main()
11511 {
11512   byte effective_action[MAX_PLAYERS];
11513   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11514   int i;
11515
11516   for (i = 0; i < MAX_PLAYERS; i++)
11517     effective_action[i] = stored_player[i].effective_action;
11518
11519   GameActions_EM(effective_action, warp_mode);
11520 }
11521
11522 void GameActions_SP_Main()
11523 {
11524   byte effective_action[MAX_PLAYERS];
11525   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11526   int i;
11527
11528   for (i = 0; i < MAX_PLAYERS; i++)
11529     effective_action[i] = stored_player[i].effective_action;
11530
11531   GameActions_SP(effective_action, warp_mode);
11532
11533   for (i = 0; i < MAX_PLAYERS; i++)
11534   {
11535     if (stored_player[i].force_dropping)
11536       stored_player[i].action |= KEY_BUTTON_DROP;
11537
11538     stored_player[i].force_dropping = FALSE;
11539   }
11540 }
11541
11542 void GameActions_MM_Main()
11543 {
11544   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11545
11546   GameActions_MM(local_player->mouse_action, warp_mode);
11547 }
11548
11549 void GameActions_RND_Main()
11550 {
11551   GameActions_RND();
11552 }
11553
11554 void GameActions_RND()
11555 {
11556   int magic_wall_x = 0, magic_wall_y = 0;
11557   int i, x, y, element, graphic, last_gfx_frame;
11558
11559   InitPlayfieldScanModeVars();
11560
11561   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11562   {
11563     SCAN_PLAYFIELD(x, y)
11564     {
11565       ChangeCount[x][y] = 0;
11566       ChangeEvent[x][y] = -1;
11567     }
11568   }
11569
11570   if (game.set_centered_player)
11571   {
11572     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11573
11574     /* switching to "all players" only possible if all players fit to screen */
11575     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11576     {
11577       game.centered_player_nr_next = game.centered_player_nr;
11578       game.set_centered_player = FALSE;
11579     }
11580
11581     /* do not switch focus to non-existing (or non-active) player */
11582     if (game.centered_player_nr_next >= 0 &&
11583         !stored_player[game.centered_player_nr_next].active)
11584     {
11585       game.centered_player_nr_next = game.centered_player_nr;
11586       game.set_centered_player = FALSE;
11587     }
11588   }
11589
11590   if (game.set_centered_player &&
11591       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11592   {
11593     int sx, sy;
11594
11595     if (game.centered_player_nr_next == -1)
11596     {
11597       setScreenCenteredToAllPlayers(&sx, &sy);
11598     }
11599     else
11600     {
11601       sx = stored_player[game.centered_player_nr_next].jx;
11602       sy = stored_player[game.centered_player_nr_next].jy;
11603     }
11604
11605     game.centered_player_nr = game.centered_player_nr_next;
11606     game.set_centered_player = FALSE;
11607
11608     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11609     DrawGameDoorValues();
11610   }
11611
11612   for (i = 0; i < MAX_PLAYERS; i++)
11613   {
11614     int actual_player_action = stored_player[i].effective_action;
11615
11616 #if 1
11617     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11618        - rnd_equinox_tetrachloride 048
11619        - rnd_equinox_tetrachloride_ii 096
11620        - rnd_emanuel_schmieg 002
11621        - doctor_sloan_ww 001, 020
11622     */
11623     if (stored_player[i].MovPos == 0)
11624       CheckGravityMovement(&stored_player[i]);
11625 #endif
11626
11627     /* overwrite programmed action with tape action */
11628     if (stored_player[i].programmed_action)
11629       actual_player_action = stored_player[i].programmed_action;
11630
11631     PlayerActions(&stored_player[i], actual_player_action);
11632
11633     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11634   }
11635
11636   ScrollScreen(NULL, SCROLL_GO_ON);
11637
11638   /* for backwards compatibility, the following code emulates a fixed bug that
11639      occured when pushing elements (causing elements that just made their last
11640      pushing step to already (if possible) make their first falling step in the
11641      same game frame, which is bad); this code is also needed to use the famous
11642      "spring push bug" which is used in older levels and might be wanted to be
11643      used also in newer levels, but in this case the buggy pushing code is only
11644      affecting the "spring" element and no other elements */
11645
11646   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11647   {
11648     for (i = 0; i < MAX_PLAYERS; i++)
11649     {
11650       struct PlayerInfo *player = &stored_player[i];
11651       int x = player->jx;
11652       int y = player->jy;
11653
11654       if (player->active && player->is_pushing && player->is_moving &&
11655           IS_MOVING(x, y) &&
11656           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11657            Feld[x][y] == EL_SPRING))
11658       {
11659         ContinueMoving(x, y);
11660
11661         /* continue moving after pushing (this is actually a bug) */
11662         if (!IS_MOVING(x, y))
11663           Stop[x][y] = FALSE;
11664       }
11665     }
11666   }
11667
11668   SCAN_PLAYFIELD(x, y)
11669   {
11670     ChangeCount[x][y] = 0;
11671     ChangeEvent[x][y] = -1;
11672
11673     /* this must be handled before main playfield loop */
11674     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11675     {
11676       MovDelay[x][y]--;
11677       if (MovDelay[x][y] <= 0)
11678         RemoveField(x, y);
11679     }
11680
11681     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11682     {
11683       MovDelay[x][y]--;
11684       if (MovDelay[x][y] <= 0)
11685       {
11686         RemoveField(x, y);
11687         TEST_DrawLevelField(x, y);
11688
11689         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11690       }
11691     }
11692
11693 #if DEBUG
11694     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11695     {
11696       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11697       printf("GameActions(): This should never happen!\n");
11698
11699       ChangePage[x][y] = -1;
11700     }
11701 #endif
11702
11703     Stop[x][y] = FALSE;
11704     if (WasJustMoving[x][y] > 0)
11705       WasJustMoving[x][y]--;
11706     if (WasJustFalling[x][y] > 0)
11707       WasJustFalling[x][y]--;
11708     if (CheckCollision[x][y] > 0)
11709       CheckCollision[x][y]--;
11710     if (CheckImpact[x][y] > 0)
11711       CheckImpact[x][y]--;
11712
11713     GfxFrame[x][y]++;
11714
11715     /* reset finished pushing action (not done in ContinueMoving() to allow
11716        continuous pushing animation for elements with zero push delay) */
11717     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11718     {
11719       ResetGfxAnimation(x, y);
11720       TEST_DrawLevelField(x, y);
11721     }
11722
11723 #if DEBUG
11724     if (IS_BLOCKED(x, y))
11725     {
11726       int oldx, oldy;
11727
11728       Blocked2Moving(x, y, &oldx, &oldy);
11729       if (!IS_MOVING(oldx, oldy))
11730       {
11731         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11732         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11733         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11734         printf("GameActions(): This should never happen!\n");
11735       }
11736     }
11737 #endif
11738   }
11739
11740   SCAN_PLAYFIELD(x, y)
11741   {
11742     element = Feld[x][y];
11743     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11744     last_gfx_frame = GfxFrame[x][y];
11745
11746     ResetGfxFrame(x, y);
11747
11748     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11749       DrawLevelGraphicAnimation(x, y, graphic);
11750
11751     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11752         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11753       ResetRandomAnimationValue(x, y);
11754
11755     SetRandomAnimationValue(x, y);
11756
11757     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11758
11759     if (IS_INACTIVE(element))
11760     {
11761       if (IS_ANIMATED(graphic))
11762         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11763
11764       continue;
11765     }
11766
11767     /* this may take place after moving, so 'element' may have changed */
11768     if (IS_CHANGING(x, y) &&
11769         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11770     {
11771       int page = element_info[element].event_page_nr[CE_DELAY];
11772
11773       HandleElementChange(x, y, page);
11774
11775       element = Feld[x][y];
11776       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11777     }
11778
11779     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11780     {
11781       StartMoving(x, y);
11782
11783       element = Feld[x][y];
11784       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11785
11786       if (IS_ANIMATED(graphic) &&
11787           !IS_MOVING(x, y) &&
11788           !Stop[x][y])
11789         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11790
11791       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11792         TEST_DrawTwinkleOnField(x, y);
11793     }
11794     else if (element == EL_ACID)
11795     {
11796       if (!Stop[x][y])
11797         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11798     }
11799     else if ((element == EL_EXIT_OPEN ||
11800               element == EL_EM_EXIT_OPEN ||
11801               element == EL_SP_EXIT_OPEN ||
11802               element == EL_STEEL_EXIT_OPEN ||
11803               element == EL_EM_STEEL_EXIT_OPEN ||
11804               element == EL_SP_TERMINAL ||
11805               element == EL_SP_TERMINAL_ACTIVE ||
11806               element == EL_EXTRA_TIME ||
11807               element == EL_SHIELD_NORMAL ||
11808               element == EL_SHIELD_DEADLY) &&
11809              IS_ANIMATED(graphic))
11810       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11811     else if (IS_MOVING(x, y))
11812       ContinueMoving(x, y);
11813     else if (IS_ACTIVE_BOMB(element))
11814       CheckDynamite(x, y);
11815     else if (element == EL_AMOEBA_GROWING)
11816       AmoebeWaechst(x, y);
11817     else if (element == EL_AMOEBA_SHRINKING)
11818       AmoebaDisappearing(x, y);
11819
11820 #if !USE_NEW_AMOEBA_CODE
11821     else if (IS_AMOEBALIVE(element))
11822       AmoebeAbleger(x, y);
11823 #endif
11824
11825     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11826       Life(x, y);
11827     else if (element == EL_EXIT_CLOSED)
11828       CheckExit(x, y);
11829     else if (element == EL_EM_EXIT_CLOSED)
11830       CheckExitEM(x, y);
11831     else if (element == EL_STEEL_EXIT_CLOSED)
11832       CheckExitSteel(x, y);
11833     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11834       CheckExitSteelEM(x, y);
11835     else if (element == EL_SP_EXIT_CLOSED)
11836       CheckExitSP(x, y);
11837     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11838              element == EL_EXPANDABLE_STEELWALL_GROWING)
11839       MauerWaechst(x, y);
11840     else if (element == EL_EXPANDABLE_WALL ||
11841              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11842              element == EL_EXPANDABLE_WALL_VERTICAL ||
11843              element == EL_EXPANDABLE_WALL_ANY ||
11844              element == EL_BD_EXPANDABLE_WALL)
11845       MauerAbleger(x, y);
11846     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11847              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11848              element == EL_EXPANDABLE_STEELWALL_ANY)
11849       MauerAblegerStahl(x, y);
11850     else if (element == EL_FLAMES)
11851       CheckForDragon(x, y);
11852     else if (element == EL_EXPLOSION)
11853       ; /* drawing of correct explosion animation is handled separately */
11854     else if (element == EL_ELEMENT_SNAPPING ||
11855              element == EL_DIAGONAL_SHRINKING ||
11856              element == EL_DIAGONAL_GROWING)
11857     {
11858       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11859
11860       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11861     }
11862     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11863       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11864
11865     if (IS_BELT_ACTIVE(element))
11866       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11867
11868     if (game.magic_wall_active)
11869     {
11870       int jx = local_player->jx, jy = local_player->jy;
11871
11872       /* play the element sound at the position nearest to the player */
11873       if ((element == EL_MAGIC_WALL_FULL ||
11874            element == EL_MAGIC_WALL_ACTIVE ||
11875            element == EL_MAGIC_WALL_EMPTYING ||
11876            element == EL_BD_MAGIC_WALL_FULL ||
11877            element == EL_BD_MAGIC_WALL_ACTIVE ||
11878            element == EL_BD_MAGIC_WALL_EMPTYING ||
11879            element == EL_DC_MAGIC_WALL_FULL ||
11880            element == EL_DC_MAGIC_WALL_ACTIVE ||
11881            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11882           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11883       {
11884         magic_wall_x = x;
11885         magic_wall_y = y;
11886       }
11887     }
11888   }
11889
11890 #if USE_NEW_AMOEBA_CODE
11891   /* new experimental amoeba growth stuff */
11892   if (!(FrameCounter % 8))
11893   {
11894     static unsigned int random = 1684108901;
11895
11896     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11897     {
11898       x = RND(lev_fieldx);
11899       y = RND(lev_fieldy);
11900       element = Feld[x][y];
11901
11902       if (!IS_PLAYER(x,y) &&
11903           (element == EL_EMPTY ||
11904            CAN_GROW_INTO(element) ||
11905            element == EL_QUICKSAND_EMPTY ||
11906            element == EL_QUICKSAND_FAST_EMPTY ||
11907            element == EL_ACID_SPLASH_LEFT ||
11908            element == EL_ACID_SPLASH_RIGHT))
11909       {
11910         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11911             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11912             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11913             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11914           Feld[x][y] = EL_AMOEBA_DROP;
11915       }
11916
11917       random = random * 129 + 1;
11918     }
11919   }
11920 #endif
11921
11922   game.explosions_delayed = FALSE;
11923
11924   SCAN_PLAYFIELD(x, y)
11925   {
11926     element = Feld[x][y];
11927
11928     if (ExplodeField[x][y])
11929       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11930     else if (element == EL_EXPLOSION)
11931       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11932
11933     ExplodeField[x][y] = EX_TYPE_NONE;
11934   }
11935
11936   game.explosions_delayed = TRUE;
11937
11938   if (game.magic_wall_active)
11939   {
11940     if (!(game.magic_wall_time_left % 4))
11941     {
11942       int element = Feld[magic_wall_x][magic_wall_y];
11943
11944       if (element == EL_BD_MAGIC_WALL_FULL ||
11945           element == EL_BD_MAGIC_WALL_ACTIVE ||
11946           element == EL_BD_MAGIC_WALL_EMPTYING)
11947         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11948       else if (element == EL_DC_MAGIC_WALL_FULL ||
11949                element == EL_DC_MAGIC_WALL_ACTIVE ||
11950                element == EL_DC_MAGIC_WALL_EMPTYING)
11951         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11952       else
11953         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11954     }
11955
11956     if (game.magic_wall_time_left > 0)
11957     {
11958       game.magic_wall_time_left--;
11959
11960       if (!game.magic_wall_time_left)
11961       {
11962         SCAN_PLAYFIELD(x, y)
11963         {
11964           element = Feld[x][y];
11965
11966           if (element == EL_MAGIC_WALL_ACTIVE ||
11967               element == EL_MAGIC_WALL_FULL)
11968           {
11969             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11970             TEST_DrawLevelField(x, y);
11971           }
11972           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11973                    element == EL_BD_MAGIC_WALL_FULL)
11974           {
11975             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11976             TEST_DrawLevelField(x, y);
11977           }
11978           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11979                    element == EL_DC_MAGIC_WALL_FULL)
11980           {
11981             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11982             TEST_DrawLevelField(x, y);
11983           }
11984         }
11985
11986         game.magic_wall_active = FALSE;
11987       }
11988     }
11989   }
11990
11991   if (game.light_time_left > 0)
11992   {
11993     game.light_time_left--;
11994
11995     if (game.light_time_left == 0)
11996       RedrawAllLightSwitchesAndInvisibleElements();
11997   }
11998
11999   if (game.timegate_time_left > 0)
12000   {
12001     game.timegate_time_left--;
12002
12003     if (game.timegate_time_left == 0)
12004       CloseAllOpenTimegates();
12005   }
12006
12007   if (game.lenses_time_left > 0)
12008   {
12009     game.lenses_time_left--;
12010
12011     if (game.lenses_time_left == 0)
12012       RedrawAllInvisibleElementsForLenses();
12013   }
12014
12015   if (game.magnify_time_left > 0)
12016   {
12017     game.magnify_time_left--;
12018
12019     if (game.magnify_time_left == 0)
12020       RedrawAllInvisibleElementsForMagnifier();
12021   }
12022
12023   for (i = 0; i < MAX_PLAYERS; i++)
12024   {
12025     struct PlayerInfo *player = &stored_player[i];
12026
12027     if (SHIELD_ON(player))
12028     {
12029       if (player->shield_deadly_time_left)
12030         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12031       else if (player->shield_normal_time_left)
12032         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12033     }
12034   }
12035
12036 #if USE_DELAYED_GFX_REDRAW
12037   SCAN_PLAYFIELD(x, y)
12038   {
12039     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12040     {
12041       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12042          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12043
12044       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12045         DrawLevelField(x, y);
12046
12047       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12048         DrawLevelFieldCrumbled(x, y);
12049
12050       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12051         DrawLevelFieldCrumbledNeighbours(x, y);
12052
12053       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12054         DrawTwinkleOnField(x, y);
12055     }
12056
12057     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12058   }
12059 #endif
12060
12061   DrawAllPlayers();
12062   PlayAllPlayersSound();
12063
12064   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12065   {
12066     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12067
12068     local_player->show_envelope = 0;
12069   }
12070
12071   /* use random number generator in every frame to make it less predictable */
12072   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12073     RND(1);
12074 }
12075
12076 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12077 {
12078   int min_x = x, min_y = y, max_x = x, max_y = y;
12079   int i;
12080
12081   for (i = 0; i < MAX_PLAYERS; i++)
12082   {
12083     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12084
12085     if (!stored_player[i].active || &stored_player[i] == player)
12086       continue;
12087
12088     min_x = MIN(min_x, jx);
12089     min_y = MIN(min_y, jy);
12090     max_x = MAX(max_x, jx);
12091     max_y = MAX(max_y, jy);
12092   }
12093
12094   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12095 }
12096
12097 static boolean AllPlayersInVisibleScreen()
12098 {
12099   int i;
12100
12101   for (i = 0; i < MAX_PLAYERS; i++)
12102   {
12103     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12104
12105     if (!stored_player[i].active)
12106       continue;
12107
12108     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12109       return FALSE;
12110   }
12111
12112   return TRUE;
12113 }
12114
12115 void ScrollLevel(int dx, int dy)
12116 {
12117   int scroll_offset = 2 * TILEX_VAR;
12118   int x, y;
12119
12120   BlitBitmap(drawto_field, drawto_field,
12121              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12122              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12123              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12124              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12125              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12126              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12127
12128   if (dx != 0)
12129   {
12130     x = (dx == 1 ? BX1 : BX2);
12131     for (y = BY1; y <= BY2; y++)
12132       DrawScreenField(x, y);
12133   }
12134
12135   if (dy != 0)
12136   {
12137     y = (dy == 1 ? BY1 : BY2);
12138     for (x = BX1; x <= BX2; x++)
12139       DrawScreenField(x, y);
12140   }
12141
12142   redraw_mask |= REDRAW_FIELD;
12143 }
12144
12145 static boolean canFallDown(struct PlayerInfo *player)
12146 {
12147   int jx = player->jx, jy = player->jy;
12148
12149   return (IN_LEV_FIELD(jx, jy + 1) &&
12150           (IS_FREE(jx, jy + 1) ||
12151            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12152           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12153           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12154 }
12155
12156 static boolean canPassField(int x, int y, int move_dir)
12157 {
12158   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12159   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12160   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12161   int nextx = x + dx;
12162   int nexty = y + dy;
12163   int element = Feld[x][y];
12164
12165   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12166           !CAN_MOVE(element) &&
12167           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12168           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12169           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12170 }
12171
12172 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12173 {
12174   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12175   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12176   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12177   int newx = x + dx;
12178   int newy = y + dy;
12179
12180   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12181           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12182           (IS_DIGGABLE(Feld[newx][newy]) ||
12183            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12184            canPassField(newx, newy, move_dir)));
12185 }
12186
12187 static void CheckGravityMovement(struct PlayerInfo *player)
12188 {
12189   if (player->gravity && !player->programmed_action)
12190   {
12191     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12192     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12193     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12194     int jx = player->jx, jy = player->jy;
12195     boolean player_is_moving_to_valid_field =
12196       (!player_is_snapping &&
12197        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12198         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12199     boolean player_can_fall_down = canFallDown(player);
12200
12201     if (player_can_fall_down &&
12202         !player_is_moving_to_valid_field)
12203       player->programmed_action = MV_DOWN;
12204   }
12205 }
12206
12207 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12208 {
12209   return CheckGravityMovement(player);
12210
12211   if (player->gravity && !player->programmed_action)
12212   {
12213     int jx = player->jx, jy = player->jy;
12214     boolean field_under_player_is_free =
12215       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12216     boolean player_is_standing_on_valid_field =
12217       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12218        (IS_WALKABLE(Feld[jx][jy]) &&
12219         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12220
12221     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12222       player->programmed_action = MV_DOWN;
12223   }
12224 }
12225
12226 /*
12227   MovePlayerOneStep()
12228   -----------------------------------------------------------------------------
12229   dx, dy:               direction (non-diagonal) to try to move the player to
12230   real_dx, real_dy:     direction as read from input device (can be diagonal)
12231 */
12232
12233 boolean MovePlayerOneStep(struct PlayerInfo *player,
12234                           int dx, int dy, int real_dx, int real_dy)
12235 {
12236   int jx = player->jx, jy = player->jy;
12237   int new_jx = jx + dx, new_jy = jy + dy;
12238   int can_move;
12239   boolean player_can_move = !player->cannot_move;
12240
12241   if (!player->active || (!dx && !dy))
12242     return MP_NO_ACTION;
12243
12244   player->MovDir = (dx < 0 ? MV_LEFT :
12245                     dx > 0 ? MV_RIGHT :
12246                     dy < 0 ? MV_UP :
12247                     dy > 0 ? MV_DOWN :  MV_NONE);
12248
12249   if (!IN_LEV_FIELD(new_jx, new_jy))
12250     return MP_NO_ACTION;
12251
12252   if (!player_can_move)
12253   {
12254     if (player->MovPos == 0)
12255     {
12256       player->is_moving = FALSE;
12257       player->is_digging = FALSE;
12258       player->is_collecting = FALSE;
12259       player->is_snapping = FALSE;
12260       player->is_pushing = FALSE;
12261     }
12262   }
12263
12264   if (!options.network && game.centered_player_nr == -1 &&
12265       !AllPlayersInSight(player, new_jx, new_jy))
12266     return MP_NO_ACTION;
12267
12268   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12269   if (can_move != MP_MOVING)
12270     return can_move;
12271
12272   /* check if DigField() has caused relocation of the player */
12273   if (player->jx != jx || player->jy != jy)
12274     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12275
12276   StorePlayer[jx][jy] = 0;
12277   player->last_jx = jx;
12278   player->last_jy = jy;
12279   player->jx = new_jx;
12280   player->jy = new_jy;
12281   StorePlayer[new_jx][new_jy] = player->element_nr;
12282
12283   if (player->move_delay_value_next != -1)
12284   {
12285     player->move_delay_value = player->move_delay_value_next;
12286     player->move_delay_value_next = -1;
12287   }
12288
12289   player->MovPos =
12290     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12291
12292   player->step_counter++;
12293
12294   PlayerVisit[jx][jy] = FrameCounter;
12295
12296   player->is_moving = TRUE;
12297
12298 #if 1
12299   /* should better be called in MovePlayer(), but this breaks some tapes */
12300   ScrollPlayer(player, SCROLL_INIT);
12301 #endif
12302
12303   return MP_MOVING;
12304 }
12305
12306 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12307 {
12308   int jx = player->jx, jy = player->jy;
12309   int old_jx = jx, old_jy = jy;
12310   int moved = MP_NO_ACTION;
12311
12312   if (!player->active)
12313     return FALSE;
12314
12315   if (!dx && !dy)
12316   {
12317     if (player->MovPos == 0)
12318     {
12319       player->is_moving = FALSE;
12320       player->is_digging = FALSE;
12321       player->is_collecting = FALSE;
12322       player->is_snapping = FALSE;
12323       player->is_pushing = FALSE;
12324     }
12325
12326     return FALSE;
12327   }
12328
12329   if (player->move_delay > 0)
12330     return FALSE;
12331
12332   player->move_delay = -1;              /* set to "uninitialized" value */
12333
12334   /* store if player is automatically moved to next field */
12335   player->is_auto_moving = (player->programmed_action != MV_NONE);
12336
12337   /* remove the last programmed player action */
12338   player->programmed_action = 0;
12339
12340   if (player->MovPos)
12341   {
12342     /* should only happen if pre-1.2 tape recordings are played */
12343     /* this is only for backward compatibility */
12344
12345     int original_move_delay_value = player->move_delay_value;
12346
12347 #if DEBUG
12348     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12349            tape.counter);
12350 #endif
12351
12352     /* scroll remaining steps with finest movement resolution */
12353     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12354
12355     while (player->MovPos)
12356     {
12357       ScrollPlayer(player, SCROLL_GO_ON);
12358       ScrollScreen(NULL, SCROLL_GO_ON);
12359
12360       AdvanceFrameAndPlayerCounters(player->index_nr);
12361
12362       DrawAllPlayers();
12363       BackToFront_WithFrameDelay(0);
12364     }
12365
12366     player->move_delay_value = original_move_delay_value;
12367   }
12368
12369   player->is_active = FALSE;
12370
12371   if (player->last_move_dir & MV_HORIZONTAL)
12372   {
12373     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12374       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12375   }
12376   else
12377   {
12378     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12379       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12380   }
12381
12382   if (!moved && !player->is_active)
12383   {
12384     player->is_moving = FALSE;
12385     player->is_digging = FALSE;
12386     player->is_collecting = FALSE;
12387     player->is_snapping = FALSE;
12388     player->is_pushing = FALSE;
12389   }
12390
12391   jx = player->jx;
12392   jy = player->jy;
12393
12394   if (moved & MP_MOVING && !ScreenMovPos &&
12395       (player->index_nr == game.centered_player_nr ||
12396        game.centered_player_nr == -1))
12397   {
12398     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12399     int offset = game.scroll_delay_value;
12400
12401     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12402     {
12403       /* actual player has left the screen -- scroll in that direction */
12404       if (jx != old_jx)         /* player has moved horizontally */
12405         scroll_x += (jx - old_jx);
12406       else                      /* player has moved vertically */
12407         scroll_y += (jy - old_jy);
12408     }
12409     else
12410     {
12411       if (jx != old_jx)         /* player has moved horizontally */
12412       {
12413         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12414             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12415           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12416
12417         /* don't scroll over playfield boundaries */
12418         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12419           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12420
12421         /* don't scroll more than one field at a time */
12422         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12423
12424         /* don't scroll against the player's moving direction */
12425         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12426             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12427           scroll_x = old_scroll_x;
12428       }
12429       else                      /* player has moved vertically */
12430       {
12431         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12432             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12433           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12434
12435         /* don't scroll over playfield boundaries */
12436         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12437           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12438
12439         /* don't scroll more than one field at a time */
12440         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12441
12442         /* don't scroll against the player's moving direction */
12443         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12444             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12445           scroll_y = old_scroll_y;
12446       }
12447     }
12448
12449     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12450     {
12451       if (!options.network && game.centered_player_nr == -1 &&
12452           !AllPlayersInVisibleScreen())
12453       {
12454         scroll_x = old_scroll_x;
12455         scroll_y = old_scroll_y;
12456       }
12457       else
12458       {
12459         ScrollScreen(player, SCROLL_INIT);
12460         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12461       }
12462     }
12463   }
12464
12465   player->StepFrame = 0;
12466
12467   if (moved & MP_MOVING)
12468   {
12469     if (old_jx != jx && old_jy == jy)
12470       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12471     else if (old_jx == jx && old_jy != jy)
12472       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12473
12474     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12475
12476     player->last_move_dir = player->MovDir;
12477     player->is_moving = TRUE;
12478     player->is_snapping = FALSE;
12479     player->is_switching = FALSE;
12480     player->is_dropping = FALSE;
12481     player->is_dropping_pressed = FALSE;
12482     player->drop_pressed_delay = 0;
12483
12484 #if 0
12485     /* should better be called here than above, but this breaks some tapes */
12486     ScrollPlayer(player, SCROLL_INIT);
12487 #endif
12488   }
12489   else
12490   {
12491     CheckGravityMovementWhenNotMoving(player);
12492
12493     player->is_moving = FALSE;
12494
12495     /* at this point, the player is allowed to move, but cannot move right now
12496        (e.g. because of something blocking the way) -- ensure that the player
12497        is also allowed to move in the next frame (in old versions before 3.1.1,
12498        the player was forced to wait again for eight frames before next try) */
12499
12500     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12501       player->move_delay = 0;   /* allow direct movement in the next frame */
12502   }
12503
12504   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12505     player->move_delay = player->move_delay_value;
12506
12507   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12508   {
12509     TestIfPlayerTouchesBadThing(jx, jy);
12510     TestIfPlayerTouchesCustomElement(jx, jy);
12511   }
12512
12513   if (!player->active)
12514     RemovePlayer(player);
12515
12516   return moved;
12517 }
12518
12519 void ScrollPlayer(struct PlayerInfo *player, int mode)
12520 {
12521   int jx = player->jx, jy = player->jy;
12522   int last_jx = player->last_jx, last_jy = player->last_jy;
12523   int move_stepsize = TILEX / player->move_delay_value;
12524
12525   if (!player->active)
12526     return;
12527
12528   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12529     return;
12530
12531   if (mode == SCROLL_INIT)
12532   {
12533     player->actual_frame_counter = FrameCounter;
12534     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12535
12536     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12537         Feld[last_jx][last_jy] == EL_EMPTY)
12538     {
12539       int last_field_block_delay = 0;   /* start with no blocking at all */
12540       int block_delay_adjustment = player->block_delay_adjustment;
12541
12542       /* if player blocks last field, add delay for exactly one move */
12543       if (player->block_last_field)
12544       {
12545         last_field_block_delay += player->move_delay_value;
12546
12547         /* when blocking enabled, prevent moving up despite gravity */
12548         if (player->gravity && player->MovDir == MV_UP)
12549           block_delay_adjustment = -1;
12550       }
12551
12552       /* add block delay adjustment (also possible when not blocking) */
12553       last_field_block_delay += block_delay_adjustment;
12554
12555       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12556       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12557     }
12558
12559     if (player->MovPos != 0)    /* player has not yet reached destination */
12560       return;
12561   }
12562   else if (!FrameReached(&player->actual_frame_counter, 1))
12563     return;
12564
12565   if (player->MovPos != 0)
12566   {
12567     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12568     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12569
12570     /* before DrawPlayer() to draw correct player graphic for this case */
12571     if (player->MovPos == 0)
12572       CheckGravityMovement(player);
12573   }
12574
12575   if (player->MovPos == 0)      /* player reached destination field */
12576   {
12577     if (player->move_delay_reset_counter > 0)
12578     {
12579       player->move_delay_reset_counter--;
12580
12581       if (player->move_delay_reset_counter == 0)
12582       {
12583         /* continue with normal speed after quickly moving through gate */
12584         HALVE_PLAYER_SPEED(player);
12585
12586         /* be able to make the next move without delay */
12587         player->move_delay = 0;
12588       }
12589     }
12590
12591     player->last_jx = jx;
12592     player->last_jy = jy;
12593
12594     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12595         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12596         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12597         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12598         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12599         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12600         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12601         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12602     {
12603       DrawPlayer(player);       /* needed here only to cleanup last field */
12604       RemovePlayer(player);
12605
12606       if (local_player->friends_still_needed == 0 ||
12607           IS_SP_ELEMENT(Feld[jx][jy]))
12608         PlayerWins(player);
12609     }
12610
12611     /* this breaks one level: "machine", level 000 */
12612     {
12613       int move_direction = player->MovDir;
12614       int enter_side = MV_DIR_OPPOSITE(move_direction);
12615       int leave_side = move_direction;
12616       int old_jx = last_jx;
12617       int old_jy = last_jy;
12618       int old_element = Feld[old_jx][old_jy];
12619       int new_element = Feld[jx][jy];
12620
12621       if (IS_CUSTOM_ELEMENT(old_element))
12622         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12623                                    CE_LEFT_BY_PLAYER,
12624                                    player->index_bit, leave_side);
12625
12626       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12627                                           CE_PLAYER_LEAVES_X,
12628                                           player->index_bit, leave_side);
12629
12630       if (IS_CUSTOM_ELEMENT(new_element))
12631         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12632                                    player->index_bit, enter_side);
12633
12634       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12635                                           CE_PLAYER_ENTERS_X,
12636                                           player->index_bit, enter_side);
12637
12638       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12639                                         CE_MOVE_OF_X, move_direction);
12640     }
12641
12642     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12643     {
12644       TestIfPlayerTouchesBadThing(jx, jy);
12645       TestIfPlayerTouchesCustomElement(jx, jy);
12646
12647       /* needed because pushed element has not yet reached its destination,
12648          so it would trigger a change event at its previous field location */
12649       if (!player->is_pushing)
12650         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12651
12652       if (!player->active)
12653         RemovePlayer(player);
12654     }
12655
12656     if (!local_player->LevelSolved && level.use_step_counter)
12657     {
12658       int i;
12659
12660       TimePlayed++;
12661
12662       if (TimeLeft > 0)
12663       {
12664         TimeLeft--;
12665
12666         if (TimeLeft <= 10 && setup.time_limit)
12667           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12668
12669         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12670
12671         DisplayGameControlValues();
12672
12673         if (!TimeLeft && setup.time_limit)
12674           for (i = 0; i < MAX_PLAYERS; i++)
12675             KillPlayer(&stored_player[i]);
12676       }
12677       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12678       {
12679         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12680
12681         DisplayGameControlValues();
12682       }
12683     }
12684
12685     if (tape.single_step && tape.recording && !tape.pausing &&
12686         !player->programmed_action)
12687       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12688
12689     if (!player->programmed_action)
12690       CheckSaveEngineSnapshot(player);
12691   }
12692 }
12693
12694 void ScrollScreen(struct PlayerInfo *player, int mode)
12695 {
12696   static unsigned int screen_frame_counter = 0;
12697
12698   if (mode == SCROLL_INIT)
12699   {
12700     /* set scrolling step size according to actual player's moving speed */
12701     ScrollStepSize = TILEX / player->move_delay_value;
12702
12703     screen_frame_counter = FrameCounter;
12704     ScreenMovDir = player->MovDir;
12705     ScreenMovPos = player->MovPos;
12706     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12707     return;
12708   }
12709   else if (!FrameReached(&screen_frame_counter, 1))
12710     return;
12711
12712   if (ScreenMovPos)
12713   {
12714     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12715     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12716     redraw_mask |= REDRAW_FIELD;
12717   }
12718   else
12719     ScreenMovDir = MV_NONE;
12720 }
12721
12722 void TestIfPlayerTouchesCustomElement(int x, int y)
12723 {
12724   static int xy[4][2] =
12725   {
12726     { 0, -1 },
12727     { -1, 0 },
12728     { +1, 0 },
12729     { 0, +1 }
12730   };
12731   static int trigger_sides[4][2] =
12732   {
12733     /* center side       border side */
12734     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12735     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12736     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12737     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12738   };
12739   static int touch_dir[4] =
12740   {
12741     MV_LEFT | MV_RIGHT,
12742     MV_UP   | MV_DOWN,
12743     MV_UP   | MV_DOWN,
12744     MV_LEFT | MV_RIGHT
12745   };
12746   int center_element = Feld[x][y];      /* should always be non-moving! */
12747   int i;
12748
12749   for (i = 0; i < NUM_DIRECTIONS; i++)
12750   {
12751     int xx = x + xy[i][0];
12752     int yy = y + xy[i][1];
12753     int center_side = trigger_sides[i][0];
12754     int border_side = trigger_sides[i][1];
12755     int border_element;
12756
12757     if (!IN_LEV_FIELD(xx, yy))
12758       continue;
12759
12760     if (IS_PLAYER(x, y))                /* player found at center element */
12761     {
12762       struct PlayerInfo *player = PLAYERINFO(x, y);
12763
12764       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12765         border_element = Feld[xx][yy];          /* may be moving! */
12766       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12767         border_element = Feld[xx][yy];
12768       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12769         border_element = MovingOrBlocked2Element(xx, yy);
12770       else
12771         continue;               /* center and border element do not touch */
12772
12773       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12774                                  player->index_bit, border_side);
12775       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12776                                           CE_PLAYER_TOUCHES_X,
12777                                           player->index_bit, border_side);
12778
12779       {
12780         /* use player element that is initially defined in the level playfield,
12781            not the player element that corresponds to the runtime player number
12782            (example: a level that contains EL_PLAYER_3 as the only player would
12783            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12784         int player_element = PLAYERINFO(x, y)->initial_element;
12785
12786         CheckElementChangeBySide(xx, yy, border_element, player_element,
12787                                  CE_TOUCHING_X, border_side);
12788       }
12789     }
12790     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12791     {
12792       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12793
12794       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12795       {
12796         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12797           continue;             /* center and border element do not touch */
12798       }
12799
12800       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12801                                  player->index_bit, center_side);
12802       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12803                                           CE_PLAYER_TOUCHES_X,
12804                                           player->index_bit, center_side);
12805
12806       {
12807         /* use player element that is initially defined in the level playfield,
12808            not the player element that corresponds to the runtime player number
12809            (example: a level that contains EL_PLAYER_3 as the only player would
12810            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12811         int player_element = PLAYERINFO(xx, yy)->initial_element;
12812
12813         CheckElementChangeBySide(x, y, center_element, player_element,
12814                                  CE_TOUCHING_X, center_side);
12815       }
12816
12817       break;
12818     }
12819   }
12820 }
12821
12822 void TestIfElementTouchesCustomElement(int x, int y)
12823 {
12824   static int xy[4][2] =
12825   {
12826     { 0, -1 },
12827     { -1, 0 },
12828     { +1, 0 },
12829     { 0, +1 }
12830   };
12831   static int trigger_sides[4][2] =
12832   {
12833     /* center side      border side */
12834     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12835     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12836     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12837     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12838   };
12839   static int touch_dir[4] =
12840   {
12841     MV_LEFT | MV_RIGHT,
12842     MV_UP   | MV_DOWN,
12843     MV_UP   | MV_DOWN,
12844     MV_LEFT | MV_RIGHT
12845   };
12846   boolean change_center_element = FALSE;
12847   int center_element = Feld[x][y];      /* should always be non-moving! */
12848   int border_element_old[NUM_DIRECTIONS];
12849   int i;
12850
12851   for (i = 0; i < NUM_DIRECTIONS; i++)
12852   {
12853     int xx = x + xy[i][0];
12854     int yy = y + xy[i][1];
12855     int border_element;
12856
12857     border_element_old[i] = -1;
12858
12859     if (!IN_LEV_FIELD(xx, yy))
12860       continue;
12861
12862     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12863       border_element = Feld[xx][yy];    /* may be moving! */
12864     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12865       border_element = Feld[xx][yy];
12866     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12867       border_element = MovingOrBlocked2Element(xx, yy);
12868     else
12869       continue;                 /* center and border element do not touch */
12870
12871     border_element_old[i] = border_element;
12872   }
12873
12874   for (i = 0; i < NUM_DIRECTIONS; i++)
12875   {
12876     int xx = x + xy[i][0];
12877     int yy = y + xy[i][1];
12878     int center_side = trigger_sides[i][0];
12879     int border_element = border_element_old[i];
12880
12881     if (border_element == -1)
12882       continue;
12883
12884     /* check for change of border element */
12885     CheckElementChangeBySide(xx, yy, border_element, center_element,
12886                              CE_TOUCHING_X, center_side);
12887
12888     /* (center element cannot be player, so we dont have to check this here) */
12889   }
12890
12891   for (i = 0; i < NUM_DIRECTIONS; i++)
12892   {
12893     int xx = x + xy[i][0];
12894     int yy = y + xy[i][1];
12895     int border_side = trigger_sides[i][1];
12896     int border_element = border_element_old[i];
12897
12898     if (border_element == -1)
12899       continue;
12900
12901     /* check for change of center element (but change it only once) */
12902     if (!change_center_element)
12903       change_center_element =
12904         CheckElementChangeBySide(x, y, center_element, border_element,
12905                                  CE_TOUCHING_X, border_side);
12906
12907     if (IS_PLAYER(xx, yy))
12908     {
12909       /* use player element that is initially defined in the level playfield,
12910          not the player element that corresponds to the runtime player number
12911          (example: a level that contains EL_PLAYER_3 as the only player would
12912          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12913       int player_element = PLAYERINFO(xx, yy)->initial_element;
12914
12915       CheckElementChangeBySide(x, y, center_element, player_element,
12916                                CE_TOUCHING_X, border_side);
12917     }
12918   }
12919 }
12920
12921 void TestIfElementHitsCustomElement(int x, int y, int direction)
12922 {
12923   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12924   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12925   int hitx = x + dx, hity = y + dy;
12926   int hitting_element = Feld[x][y];
12927   int touched_element;
12928
12929   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12930     return;
12931
12932   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12933                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12934
12935   if (IN_LEV_FIELD(hitx, hity))
12936   {
12937     int opposite_direction = MV_DIR_OPPOSITE(direction);
12938     int hitting_side = direction;
12939     int touched_side = opposite_direction;
12940     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12941                           MovDir[hitx][hity] != direction ||
12942                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12943
12944     object_hit = TRUE;
12945
12946     if (object_hit)
12947     {
12948       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12949                                CE_HITTING_X, touched_side);
12950
12951       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12952                                CE_HIT_BY_X, hitting_side);
12953
12954       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12955                                CE_HIT_BY_SOMETHING, opposite_direction);
12956
12957       if (IS_PLAYER(hitx, hity))
12958       {
12959         /* use player element that is initially defined in the level playfield,
12960            not the player element that corresponds to the runtime player number
12961            (example: a level that contains EL_PLAYER_3 as the only player would
12962            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12963         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12964
12965         CheckElementChangeBySide(x, y, hitting_element, player_element,
12966                                  CE_HITTING_X, touched_side);
12967       }
12968     }
12969   }
12970
12971   /* "hitting something" is also true when hitting the playfield border */
12972   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12973                            CE_HITTING_SOMETHING, direction);
12974 }
12975
12976 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12977 {
12978   int i, kill_x = -1, kill_y = -1;
12979
12980   int bad_element = -1;
12981   static int test_xy[4][2] =
12982   {
12983     { 0, -1 },
12984     { -1, 0 },
12985     { +1, 0 },
12986     { 0, +1 }
12987   };
12988   static int test_dir[4] =
12989   {
12990     MV_UP,
12991     MV_LEFT,
12992     MV_RIGHT,
12993     MV_DOWN
12994   };
12995
12996   for (i = 0; i < NUM_DIRECTIONS; i++)
12997   {
12998     int test_x, test_y, test_move_dir, test_element;
12999
13000     test_x = good_x + test_xy[i][0];
13001     test_y = good_y + test_xy[i][1];
13002
13003     if (!IN_LEV_FIELD(test_x, test_y))
13004       continue;
13005
13006     test_move_dir =
13007       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13008
13009     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13010
13011     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13012        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13013     */
13014     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13015         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13016     {
13017       kill_x = test_x;
13018       kill_y = test_y;
13019       bad_element = test_element;
13020
13021       break;
13022     }
13023   }
13024
13025   if (kill_x != -1 || kill_y != -1)
13026   {
13027     if (IS_PLAYER(good_x, good_y))
13028     {
13029       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13030
13031       if (player->shield_deadly_time_left > 0 &&
13032           !IS_INDESTRUCTIBLE(bad_element))
13033         Bang(kill_x, kill_y);
13034       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13035         KillPlayer(player);
13036     }
13037     else
13038       Bang(good_x, good_y);
13039   }
13040 }
13041
13042 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13043 {
13044   int i, kill_x = -1, kill_y = -1;
13045   int bad_element = Feld[bad_x][bad_y];
13046   static int test_xy[4][2] =
13047   {
13048     { 0, -1 },
13049     { -1, 0 },
13050     { +1, 0 },
13051     { 0, +1 }
13052   };
13053   static int touch_dir[4] =
13054   {
13055     MV_LEFT | MV_RIGHT,
13056     MV_UP   | MV_DOWN,
13057     MV_UP   | MV_DOWN,
13058     MV_LEFT | MV_RIGHT
13059   };
13060   static int test_dir[4] =
13061   {
13062     MV_UP,
13063     MV_LEFT,
13064     MV_RIGHT,
13065     MV_DOWN
13066   };
13067
13068   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13069     return;
13070
13071   for (i = 0; i < NUM_DIRECTIONS; i++)
13072   {
13073     int test_x, test_y, test_move_dir, test_element;
13074
13075     test_x = bad_x + test_xy[i][0];
13076     test_y = bad_y + test_xy[i][1];
13077
13078     if (!IN_LEV_FIELD(test_x, test_y))
13079       continue;
13080
13081     test_move_dir =
13082       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13083
13084     test_element = Feld[test_x][test_y];
13085
13086     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13087        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13088     */
13089     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13090         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13091     {
13092       /* good thing is player or penguin that does not move away */
13093       if (IS_PLAYER(test_x, test_y))
13094       {
13095         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13096
13097         if (bad_element == EL_ROBOT && player->is_moving)
13098           continue;     /* robot does not kill player if he is moving */
13099
13100         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13101         {
13102           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13103             continue;           /* center and border element do not touch */
13104         }
13105
13106         kill_x = test_x;
13107         kill_y = test_y;
13108
13109         break;
13110       }
13111       else if (test_element == EL_PENGUIN)
13112       {
13113         kill_x = test_x;
13114         kill_y = test_y;
13115
13116         break;
13117       }
13118     }
13119   }
13120
13121   if (kill_x != -1 || kill_y != -1)
13122   {
13123     if (IS_PLAYER(kill_x, kill_y))
13124     {
13125       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13126
13127       if (player->shield_deadly_time_left > 0 &&
13128           !IS_INDESTRUCTIBLE(bad_element))
13129         Bang(bad_x, bad_y);
13130       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13131         KillPlayer(player);
13132     }
13133     else
13134       Bang(kill_x, kill_y);
13135   }
13136 }
13137
13138 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13139 {
13140   int bad_element = Feld[bad_x][bad_y];
13141   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13142   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13143   int test_x = bad_x + dx, test_y = bad_y + dy;
13144   int test_move_dir, test_element;
13145   int kill_x = -1, kill_y = -1;
13146
13147   if (!IN_LEV_FIELD(test_x, test_y))
13148     return;
13149
13150   test_move_dir =
13151     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13152
13153   test_element = Feld[test_x][test_y];
13154
13155   if (test_move_dir != bad_move_dir)
13156   {
13157     /* good thing can be player or penguin that does not move away */
13158     if (IS_PLAYER(test_x, test_y))
13159     {
13160       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13161
13162       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13163          player as being hit when he is moving towards the bad thing, because
13164          the "get hit by" condition would be lost after the player stops) */
13165       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13166         return;         /* player moves away from bad thing */
13167
13168       kill_x = test_x;
13169       kill_y = test_y;
13170     }
13171     else if (test_element == EL_PENGUIN)
13172     {
13173       kill_x = test_x;
13174       kill_y = test_y;
13175     }
13176   }
13177
13178   if (kill_x != -1 || kill_y != -1)
13179   {
13180     if (IS_PLAYER(kill_x, kill_y))
13181     {
13182       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13183
13184       if (player->shield_deadly_time_left > 0 &&
13185           !IS_INDESTRUCTIBLE(bad_element))
13186         Bang(bad_x, bad_y);
13187       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13188         KillPlayer(player);
13189     }
13190     else
13191       Bang(kill_x, kill_y);
13192   }
13193 }
13194
13195 void TestIfPlayerTouchesBadThing(int x, int y)
13196 {
13197   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13198 }
13199
13200 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13201 {
13202   TestIfGoodThingHitsBadThing(x, y, move_dir);
13203 }
13204
13205 void TestIfBadThingTouchesPlayer(int x, int y)
13206 {
13207   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13208 }
13209
13210 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13211 {
13212   TestIfBadThingHitsGoodThing(x, y, move_dir);
13213 }
13214
13215 void TestIfFriendTouchesBadThing(int x, int y)
13216 {
13217   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13218 }
13219
13220 void TestIfBadThingTouchesFriend(int x, int y)
13221 {
13222   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13223 }
13224
13225 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13226 {
13227   int i, kill_x = bad_x, kill_y = bad_y;
13228   static int xy[4][2] =
13229   {
13230     { 0, -1 },
13231     { -1, 0 },
13232     { +1, 0 },
13233     { 0, +1 }
13234   };
13235
13236   for (i = 0; i < NUM_DIRECTIONS; i++)
13237   {
13238     int x, y, element;
13239
13240     x = bad_x + xy[i][0];
13241     y = bad_y + xy[i][1];
13242     if (!IN_LEV_FIELD(x, y))
13243       continue;
13244
13245     element = Feld[x][y];
13246     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13247         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13248     {
13249       kill_x = x;
13250       kill_y = y;
13251       break;
13252     }
13253   }
13254
13255   if (kill_x != bad_x || kill_y != bad_y)
13256     Bang(bad_x, bad_y);
13257 }
13258
13259 void KillPlayer(struct PlayerInfo *player)
13260 {
13261   int jx = player->jx, jy = player->jy;
13262
13263   if (!player->active)
13264     return;
13265
13266 #if 0
13267   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13268          player->killed, player->active, player->reanimated);
13269 #endif
13270
13271   /* the following code was introduced to prevent an infinite loop when calling
13272      -> Bang()
13273      -> CheckTriggeredElementChangeExt()
13274      -> ExecuteCustomElementAction()
13275      -> KillPlayer()
13276      -> (infinitely repeating the above sequence of function calls)
13277      which occurs when killing the player while having a CE with the setting
13278      "kill player X when explosion of <player X>"; the solution using a new
13279      field "player->killed" was chosen for backwards compatibility, although
13280      clever use of the fields "player->active" etc. would probably also work */
13281 #if 1
13282   if (player->killed)
13283     return;
13284 #endif
13285
13286   player->killed = TRUE;
13287
13288   /* remove accessible field at the player's position */
13289   Feld[jx][jy] = EL_EMPTY;
13290
13291   /* deactivate shield (else Bang()/Explode() would not work right) */
13292   player->shield_normal_time_left = 0;
13293   player->shield_deadly_time_left = 0;
13294
13295 #if 0
13296   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13297          player->killed, player->active, player->reanimated);
13298 #endif
13299
13300   Bang(jx, jy);
13301
13302 #if 0
13303   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13304          player->killed, player->active, player->reanimated);
13305 #endif
13306
13307   if (player->reanimated)       /* killed player may have been reanimated */
13308     player->killed = player->reanimated = FALSE;
13309   else
13310     BuryPlayer(player);
13311 }
13312
13313 static void KillPlayerUnlessEnemyProtected(int x, int y)
13314 {
13315   if (!PLAYER_ENEMY_PROTECTED(x, y))
13316     KillPlayer(PLAYERINFO(x, y));
13317 }
13318
13319 static void KillPlayerUnlessExplosionProtected(int x, int y)
13320 {
13321   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13322     KillPlayer(PLAYERINFO(x, y));
13323 }
13324
13325 void BuryPlayer(struct PlayerInfo *player)
13326 {
13327   int jx = player->jx, jy = player->jy;
13328
13329   if (!player->active)
13330     return;
13331
13332   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13333   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13334
13335   player->GameOver = TRUE;
13336   RemovePlayer(player);
13337 }
13338
13339 void RemovePlayer(struct PlayerInfo *player)
13340 {
13341   int jx = player->jx, jy = player->jy;
13342   int i, found = FALSE;
13343
13344   player->present = FALSE;
13345   player->active = FALSE;
13346
13347   if (!ExplodeField[jx][jy])
13348     StorePlayer[jx][jy] = 0;
13349
13350   if (player->is_moving)
13351     TEST_DrawLevelField(player->last_jx, player->last_jy);
13352
13353   for (i = 0; i < MAX_PLAYERS; i++)
13354     if (stored_player[i].active)
13355       found = TRUE;
13356
13357   if (!found)
13358     AllPlayersGone = TRUE;
13359
13360   ExitX = ZX = jx;
13361   ExitY = ZY = jy;
13362 }
13363
13364 static void setFieldForSnapping(int x, int y, int element, int direction)
13365 {
13366   struct ElementInfo *ei = &element_info[element];
13367   int direction_bit = MV_DIR_TO_BIT(direction);
13368   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13369   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13370                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13371
13372   Feld[x][y] = EL_ELEMENT_SNAPPING;
13373   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13374
13375   ResetGfxAnimation(x, y);
13376
13377   GfxElement[x][y] = element;
13378   GfxAction[x][y] = action;
13379   GfxDir[x][y] = direction;
13380   GfxFrame[x][y] = -1;
13381 }
13382
13383 /*
13384   =============================================================================
13385   checkDiagonalPushing()
13386   -----------------------------------------------------------------------------
13387   check if diagonal input device direction results in pushing of object
13388   (by checking if the alternative direction is walkable, diggable, ...)
13389   =============================================================================
13390 */
13391
13392 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13393                                     int x, int y, int real_dx, int real_dy)
13394 {
13395   int jx, jy, dx, dy, xx, yy;
13396
13397   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13398     return TRUE;
13399
13400   /* diagonal direction: check alternative direction */
13401   jx = player->jx;
13402   jy = player->jy;
13403   dx = x - jx;
13404   dy = y - jy;
13405   xx = jx + (dx == 0 ? real_dx : 0);
13406   yy = jy + (dy == 0 ? real_dy : 0);
13407
13408   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13409 }
13410
13411 /*
13412   =============================================================================
13413   DigField()
13414   -----------------------------------------------------------------------------
13415   x, y:                 field next to player (non-diagonal) to try to dig to
13416   real_dx, real_dy:     direction as read from input device (can be diagonal)
13417   =============================================================================
13418 */
13419
13420 static int DigField(struct PlayerInfo *player,
13421                     int oldx, int oldy, int x, int y,
13422                     int real_dx, int real_dy, int mode)
13423 {
13424   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13425   boolean player_was_pushing = player->is_pushing;
13426   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13427   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13428   int jx = oldx, jy = oldy;
13429   int dx = x - jx, dy = y - jy;
13430   int nextx = x + dx, nexty = y + dy;
13431   int move_direction = (dx == -1 ? MV_LEFT  :
13432                         dx == +1 ? MV_RIGHT :
13433                         dy == -1 ? MV_UP    :
13434                         dy == +1 ? MV_DOWN  : MV_NONE);
13435   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13436   int dig_side = MV_DIR_OPPOSITE(move_direction);
13437   int old_element = Feld[jx][jy];
13438   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13439   int collect_count;
13440
13441   if (is_player)                /* function can also be called by EL_PENGUIN */
13442   {
13443     if (player->MovPos == 0)
13444     {
13445       player->is_digging = FALSE;
13446       player->is_collecting = FALSE;
13447     }
13448
13449     if (player->MovPos == 0)    /* last pushing move finished */
13450       player->is_pushing = FALSE;
13451
13452     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13453     {
13454       player->is_switching = FALSE;
13455       player->push_delay = -1;
13456
13457       return MP_NO_ACTION;
13458     }
13459   }
13460
13461   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13462     old_element = Back[jx][jy];
13463
13464   /* in case of element dropped at player position, check background */
13465   else if (Back[jx][jy] != EL_EMPTY &&
13466            game.engine_version >= VERSION_IDENT(2,2,0,0))
13467     old_element = Back[jx][jy];
13468
13469   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13470     return MP_NO_ACTION;        /* field has no opening in this direction */
13471
13472   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13473     return MP_NO_ACTION;        /* field has no opening in this direction */
13474
13475   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13476   {
13477     SplashAcid(x, y);
13478
13479     Feld[jx][jy] = player->artwork_element;
13480     InitMovingField(jx, jy, MV_DOWN);
13481     Store[jx][jy] = EL_ACID;
13482     ContinueMoving(jx, jy);
13483     BuryPlayer(player);
13484
13485     return MP_DONT_RUN_INTO;
13486   }
13487
13488   if (player_can_move && DONT_RUN_INTO(element))
13489   {
13490     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13491
13492     return MP_DONT_RUN_INTO;
13493   }
13494
13495   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13496     return MP_NO_ACTION;
13497
13498   collect_count = element_info[element].collect_count_initial;
13499
13500   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13501     return MP_NO_ACTION;
13502
13503   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13504     player_can_move = player_can_move_or_snap;
13505
13506   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13507       game.engine_version >= VERSION_IDENT(2,2,0,0))
13508   {
13509     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13510                                player->index_bit, dig_side);
13511     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13512                                         player->index_bit, dig_side);
13513
13514     if (element == EL_DC_LANDMINE)
13515       Bang(x, y);
13516
13517     if (Feld[x][y] != element)          /* field changed by snapping */
13518       return MP_ACTION;
13519
13520     return MP_NO_ACTION;
13521   }
13522
13523   if (player->gravity && is_player && !player->is_auto_moving &&
13524       canFallDown(player) && move_direction != MV_DOWN &&
13525       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13526     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13527
13528   if (player_can_move &&
13529       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13530   {
13531     int sound_element = SND_ELEMENT(element);
13532     int sound_action = ACTION_WALKING;
13533
13534     if (IS_RND_GATE(element))
13535     {
13536       if (!player->key[RND_GATE_NR(element)])
13537         return MP_NO_ACTION;
13538     }
13539     else if (IS_RND_GATE_GRAY(element))
13540     {
13541       if (!player->key[RND_GATE_GRAY_NR(element)])
13542         return MP_NO_ACTION;
13543     }
13544     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13545     {
13546       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13547         return MP_NO_ACTION;
13548     }
13549     else if (element == EL_EXIT_OPEN ||
13550              element == EL_EM_EXIT_OPEN ||
13551              element == EL_EM_EXIT_OPENING ||
13552              element == EL_STEEL_EXIT_OPEN ||
13553              element == EL_EM_STEEL_EXIT_OPEN ||
13554              element == EL_EM_STEEL_EXIT_OPENING ||
13555              element == EL_SP_EXIT_OPEN ||
13556              element == EL_SP_EXIT_OPENING)
13557     {
13558       sound_action = ACTION_PASSING;    /* player is passing exit */
13559     }
13560     else if (element == EL_EMPTY)
13561     {
13562       sound_action = ACTION_MOVING;             /* nothing to walk on */
13563     }
13564
13565     /* play sound from background or player, whatever is available */
13566     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13567       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13568     else
13569       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13570   }
13571   else if (player_can_move &&
13572            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13573   {
13574     if (!ACCESS_FROM(element, opposite_direction))
13575       return MP_NO_ACTION;      /* field not accessible from this direction */
13576
13577     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13578       return MP_NO_ACTION;
13579
13580     if (IS_EM_GATE(element))
13581     {
13582       if (!player->key[EM_GATE_NR(element)])
13583         return MP_NO_ACTION;
13584     }
13585     else if (IS_EM_GATE_GRAY(element))
13586     {
13587       if (!player->key[EM_GATE_GRAY_NR(element)])
13588         return MP_NO_ACTION;
13589     }
13590     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13591     {
13592       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13593         return MP_NO_ACTION;
13594     }
13595     else if (IS_EMC_GATE(element))
13596     {
13597       if (!player->key[EMC_GATE_NR(element)])
13598         return MP_NO_ACTION;
13599     }
13600     else if (IS_EMC_GATE_GRAY(element))
13601     {
13602       if (!player->key[EMC_GATE_GRAY_NR(element)])
13603         return MP_NO_ACTION;
13604     }
13605     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13606     {
13607       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13608         return MP_NO_ACTION;
13609     }
13610     else if (element == EL_DC_GATE_WHITE ||
13611              element == EL_DC_GATE_WHITE_GRAY ||
13612              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13613     {
13614       if (player->num_white_keys == 0)
13615         return MP_NO_ACTION;
13616
13617       player->num_white_keys--;
13618     }
13619     else if (IS_SP_PORT(element))
13620     {
13621       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13622           element == EL_SP_GRAVITY_PORT_RIGHT ||
13623           element == EL_SP_GRAVITY_PORT_UP ||
13624           element == EL_SP_GRAVITY_PORT_DOWN)
13625         player->gravity = !player->gravity;
13626       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13627                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13628                element == EL_SP_GRAVITY_ON_PORT_UP ||
13629                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13630         player->gravity = TRUE;
13631       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13632                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13633                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13634                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13635         player->gravity = FALSE;
13636     }
13637
13638     /* automatically move to the next field with double speed */
13639     player->programmed_action = move_direction;
13640
13641     if (player->move_delay_reset_counter == 0)
13642     {
13643       player->move_delay_reset_counter = 2;     /* two double speed steps */
13644
13645       DOUBLE_PLAYER_SPEED(player);
13646     }
13647
13648     PlayLevelSoundAction(x, y, ACTION_PASSING);
13649   }
13650   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13651   {
13652     RemoveField(x, y);
13653
13654     if (mode != DF_SNAP)
13655     {
13656       GfxElement[x][y] = GFX_ELEMENT(element);
13657       player->is_digging = TRUE;
13658     }
13659
13660     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13661
13662     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13663                                         player->index_bit, dig_side);
13664
13665     if (mode == DF_SNAP)
13666     {
13667       if (level.block_snap_field)
13668         setFieldForSnapping(x, y, element, move_direction);
13669       else
13670         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13671
13672       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13673                                           player->index_bit, dig_side);
13674     }
13675   }
13676   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13677   {
13678     RemoveField(x, y);
13679
13680     if (is_player && mode != DF_SNAP)
13681     {
13682       GfxElement[x][y] = element;
13683       player->is_collecting = TRUE;
13684     }
13685
13686     if (element == EL_SPEED_PILL)
13687     {
13688       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13689     }
13690     else if (element == EL_EXTRA_TIME && level.time > 0)
13691     {
13692       TimeLeft += level.extra_time;
13693
13694       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13695
13696       DisplayGameControlValues();
13697     }
13698     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13699     {
13700       player->shield_normal_time_left += level.shield_normal_time;
13701       if (element == EL_SHIELD_DEADLY)
13702         player->shield_deadly_time_left += level.shield_deadly_time;
13703     }
13704     else if (element == EL_DYNAMITE ||
13705              element == EL_EM_DYNAMITE ||
13706              element == EL_SP_DISK_RED)
13707     {
13708       if (player->inventory_size < MAX_INVENTORY_SIZE)
13709         player->inventory_element[player->inventory_size++] = element;
13710
13711       DrawGameDoorValues();
13712     }
13713     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13714     {
13715       player->dynabomb_count++;
13716       player->dynabombs_left++;
13717     }
13718     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13719     {
13720       player->dynabomb_size++;
13721     }
13722     else if (element == EL_DYNABOMB_INCREASE_POWER)
13723     {
13724       player->dynabomb_xl = TRUE;
13725     }
13726     else if (IS_KEY(element))
13727     {
13728       player->key[KEY_NR(element)] = TRUE;
13729
13730       DrawGameDoorValues();
13731     }
13732     else if (element == EL_DC_KEY_WHITE)
13733     {
13734       player->num_white_keys++;
13735
13736       /* display white keys? */
13737       /* DrawGameDoorValues(); */
13738     }
13739     else if (IS_ENVELOPE(element))
13740     {
13741       player->show_envelope = element;
13742     }
13743     else if (element == EL_EMC_LENSES)
13744     {
13745       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13746
13747       RedrawAllInvisibleElementsForLenses();
13748     }
13749     else if (element == EL_EMC_MAGNIFIER)
13750     {
13751       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13752
13753       RedrawAllInvisibleElementsForMagnifier();
13754     }
13755     else if (IS_DROPPABLE(element) ||
13756              IS_THROWABLE(element))     /* can be collected and dropped */
13757     {
13758       int i;
13759
13760       if (collect_count == 0)
13761         player->inventory_infinite_element = element;
13762       else
13763         for (i = 0; i < collect_count; i++)
13764           if (player->inventory_size < MAX_INVENTORY_SIZE)
13765             player->inventory_element[player->inventory_size++] = element;
13766
13767       DrawGameDoorValues();
13768     }
13769     else if (collect_count > 0)
13770     {
13771       local_player->gems_still_needed -= collect_count;
13772       if (local_player->gems_still_needed < 0)
13773         local_player->gems_still_needed = 0;
13774
13775       game.snapshot.collected_item = TRUE;
13776
13777       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13778
13779       DisplayGameControlValues();
13780     }
13781
13782     RaiseScoreElement(element);
13783     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13784
13785     if (is_player)
13786       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13787                                           player->index_bit, dig_side);
13788
13789     if (mode == DF_SNAP)
13790     {
13791       if (level.block_snap_field)
13792         setFieldForSnapping(x, y, element, move_direction);
13793       else
13794         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13795
13796       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13797                                           player->index_bit, dig_side);
13798     }
13799   }
13800   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13801   {
13802     if (mode == DF_SNAP && element != EL_BD_ROCK)
13803       return MP_NO_ACTION;
13804
13805     if (CAN_FALL(element) && dy)
13806       return MP_NO_ACTION;
13807
13808     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13809         !(element == EL_SPRING && level.use_spring_bug))
13810       return MP_NO_ACTION;
13811
13812     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13813         ((move_direction & MV_VERTICAL &&
13814           ((element_info[element].move_pattern & MV_LEFT &&
13815             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13816            (element_info[element].move_pattern & MV_RIGHT &&
13817             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13818          (move_direction & MV_HORIZONTAL &&
13819           ((element_info[element].move_pattern & MV_UP &&
13820             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13821            (element_info[element].move_pattern & MV_DOWN &&
13822             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13823       return MP_NO_ACTION;
13824
13825     /* do not push elements already moving away faster than player */
13826     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13827         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13828       return MP_NO_ACTION;
13829
13830     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13831     {
13832       if (player->push_delay_value == -1 || !player_was_pushing)
13833         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13834     }
13835     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13836     {
13837       if (player->push_delay_value == -1)
13838         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13839     }
13840     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13841     {
13842       if (!player->is_pushing)
13843         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13844     }
13845
13846     player->is_pushing = TRUE;
13847     player->is_active = TRUE;
13848
13849     if (!(IN_LEV_FIELD(nextx, nexty) &&
13850           (IS_FREE(nextx, nexty) ||
13851            (IS_SB_ELEMENT(element) &&
13852             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13853            (IS_CUSTOM_ELEMENT(element) &&
13854             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13855       return MP_NO_ACTION;
13856
13857     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13858       return MP_NO_ACTION;
13859
13860     if (player->push_delay == -1)       /* new pushing; restart delay */
13861       player->push_delay = 0;
13862
13863     if (player->push_delay < player->push_delay_value &&
13864         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13865         element != EL_SPRING && element != EL_BALLOON)
13866     {
13867       /* make sure that there is no move delay before next try to push */
13868       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13869         player->move_delay = 0;
13870
13871       return MP_NO_ACTION;
13872     }
13873
13874     if (IS_CUSTOM_ELEMENT(element) &&
13875         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13876     {
13877       if (!DigFieldByCE(nextx, nexty, element))
13878         return MP_NO_ACTION;
13879     }
13880
13881     if (IS_SB_ELEMENT(element))
13882     {
13883       if (element == EL_SOKOBAN_FIELD_FULL)
13884       {
13885         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13886         local_player->sokobanfields_still_needed++;
13887       }
13888
13889       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13890       {
13891         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13892         local_player->sokobanfields_still_needed--;
13893       }
13894
13895       Feld[x][y] = EL_SOKOBAN_OBJECT;
13896
13897       if (Back[x][y] == Back[nextx][nexty])
13898         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13899       else if (Back[x][y] != 0)
13900         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13901                                     ACTION_EMPTYING);
13902       else
13903         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13904                                     ACTION_FILLING);
13905
13906       if (local_player->sokobanfields_still_needed == 0 &&
13907           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13908       {
13909         PlayerWins(player);
13910
13911         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13912       }
13913     }
13914     else
13915       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13916
13917     InitMovingField(x, y, move_direction);
13918     GfxAction[x][y] = ACTION_PUSHING;
13919
13920     if (mode == DF_SNAP)
13921       ContinueMoving(x, y);
13922     else
13923       MovPos[x][y] = (dx != 0 ? dx : dy);
13924
13925     Pushed[x][y] = TRUE;
13926     Pushed[nextx][nexty] = TRUE;
13927
13928     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13929       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13930     else
13931       player->push_delay_value = -1;    /* get new value later */
13932
13933     /* check for element change _after_ element has been pushed */
13934     if (game.use_change_when_pushing_bug)
13935     {
13936       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13937                                  player->index_bit, dig_side);
13938       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13939                                           player->index_bit, dig_side);
13940     }
13941   }
13942   else if (IS_SWITCHABLE(element))
13943   {
13944     if (PLAYER_SWITCHING(player, x, y))
13945     {
13946       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13947                                           player->index_bit, dig_side);
13948
13949       return MP_ACTION;
13950     }
13951
13952     player->is_switching = TRUE;
13953     player->switch_x = x;
13954     player->switch_y = y;
13955
13956     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13957
13958     if (element == EL_ROBOT_WHEEL)
13959     {
13960       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13961       ZX = x;
13962       ZY = y;
13963
13964       game.robot_wheel_active = TRUE;
13965
13966       TEST_DrawLevelField(x, y);
13967     }
13968     else if (element == EL_SP_TERMINAL)
13969     {
13970       int xx, yy;
13971
13972       SCAN_PLAYFIELD(xx, yy)
13973       {
13974         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13975         {
13976           Bang(xx, yy);
13977         }
13978         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13979         {
13980           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13981
13982           ResetGfxAnimation(xx, yy);
13983           TEST_DrawLevelField(xx, yy);
13984         }
13985       }
13986     }
13987     else if (IS_BELT_SWITCH(element))
13988     {
13989       ToggleBeltSwitch(x, y);
13990     }
13991     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13992              element == EL_SWITCHGATE_SWITCH_DOWN ||
13993              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13994              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13995     {
13996       ToggleSwitchgateSwitch(x, y);
13997     }
13998     else if (element == EL_LIGHT_SWITCH ||
13999              element == EL_LIGHT_SWITCH_ACTIVE)
14000     {
14001       ToggleLightSwitch(x, y);
14002     }
14003     else if (element == EL_TIMEGATE_SWITCH ||
14004              element == EL_DC_TIMEGATE_SWITCH)
14005     {
14006       ActivateTimegateSwitch(x, y);
14007     }
14008     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14009              element == EL_BALLOON_SWITCH_RIGHT ||
14010              element == EL_BALLOON_SWITCH_UP    ||
14011              element == EL_BALLOON_SWITCH_DOWN  ||
14012              element == EL_BALLOON_SWITCH_NONE  ||
14013              element == EL_BALLOON_SWITCH_ANY)
14014     {
14015       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14016                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14017                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14018                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14019                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14020                              move_direction);
14021     }
14022     else if (element == EL_LAMP)
14023     {
14024       Feld[x][y] = EL_LAMP_ACTIVE;
14025       local_player->lights_still_needed--;
14026
14027       ResetGfxAnimation(x, y);
14028       TEST_DrawLevelField(x, y);
14029     }
14030     else if (element == EL_TIME_ORB_FULL)
14031     {
14032       Feld[x][y] = EL_TIME_ORB_EMPTY;
14033
14034       if (level.time > 0 || level.use_time_orb_bug)
14035       {
14036         TimeLeft += level.time_orb_time;
14037         game.no_time_limit = FALSE;
14038
14039         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14040
14041         DisplayGameControlValues();
14042       }
14043
14044       ResetGfxAnimation(x, y);
14045       TEST_DrawLevelField(x, y);
14046     }
14047     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14048              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14049     {
14050       int xx, yy;
14051
14052       game.ball_state = !game.ball_state;
14053
14054       SCAN_PLAYFIELD(xx, yy)
14055       {
14056         int e = Feld[xx][yy];
14057
14058         if (game.ball_state)
14059         {
14060           if (e == EL_EMC_MAGIC_BALL)
14061             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14062           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14063             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14064         }
14065         else
14066         {
14067           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14068             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14069           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14070             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14071         }
14072       }
14073     }
14074
14075     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14076                                         player->index_bit, dig_side);
14077
14078     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14079                                         player->index_bit, dig_side);
14080
14081     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14082                                         player->index_bit, dig_side);
14083
14084     return MP_ACTION;
14085   }
14086   else
14087   {
14088     if (!PLAYER_SWITCHING(player, x, y))
14089     {
14090       player->is_switching = TRUE;
14091       player->switch_x = x;
14092       player->switch_y = y;
14093
14094       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14095                                  player->index_bit, dig_side);
14096       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14097                                           player->index_bit, dig_side);
14098
14099       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14100                                  player->index_bit, dig_side);
14101       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14102                                           player->index_bit, dig_side);
14103     }
14104
14105     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14106                                player->index_bit, dig_side);
14107     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14108                                         player->index_bit, dig_side);
14109
14110     return MP_NO_ACTION;
14111   }
14112
14113   player->push_delay = -1;
14114
14115   if (is_player)                /* function can also be called by EL_PENGUIN */
14116   {
14117     if (Feld[x][y] != element)          /* really digged/collected something */
14118     {
14119       player->is_collecting = !player->is_digging;
14120       player->is_active = TRUE;
14121     }
14122   }
14123
14124   return MP_MOVING;
14125 }
14126
14127 static boolean DigFieldByCE(int x, int y, int digging_element)
14128 {
14129   int element = Feld[x][y];
14130
14131   if (!IS_FREE(x, y))
14132   {
14133     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14134                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14135                   ACTION_BREAKING);
14136
14137     /* no element can dig solid indestructible elements */
14138     if (IS_INDESTRUCTIBLE(element) &&
14139         !IS_DIGGABLE(element) &&
14140         !IS_COLLECTIBLE(element))
14141       return FALSE;
14142
14143     if (AmoebaNr[x][y] &&
14144         (element == EL_AMOEBA_FULL ||
14145          element == EL_BD_AMOEBA ||
14146          element == EL_AMOEBA_GROWING))
14147     {
14148       AmoebaCnt[AmoebaNr[x][y]]--;
14149       AmoebaCnt2[AmoebaNr[x][y]]--;
14150     }
14151
14152     if (IS_MOVING(x, y))
14153       RemoveMovingField(x, y);
14154     else
14155     {
14156       RemoveField(x, y);
14157       TEST_DrawLevelField(x, y);
14158     }
14159
14160     /* if digged element was about to explode, prevent the explosion */
14161     ExplodeField[x][y] = EX_TYPE_NONE;
14162
14163     PlayLevelSoundAction(x, y, action);
14164   }
14165
14166   Store[x][y] = EL_EMPTY;
14167
14168   /* this makes it possible to leave the removed element again */
14169   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14170     Store[x][y] = element;
14171
14172   return TRUE;
14173 }
14174
14175 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14176 {
14177   int jx = player->jx, jy = player->jy;
14178   int x = jx + dx, y = jy + dy;
14179   int snap_direction = (dx == -1 ? MV_LEFT  :
14180                         dx == +1 ? MV_RIGHT :
14181                         dy == -1 ? MV_UP    :
14182                         dy == +1 ? MV_DOWN  : MV_NONE);
14183   boolean can_continue_snapping = (level.continuous_snapping &&
14184                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14185
14186   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14187     return FALSE;
14188
14189   if (!player->active || !IN_LEV_FIELD(x, y))
14190     return FALSE;
14191
14192   if (dx && dy)
14193     return FALSE;
14194
14195   if (!dx && !dy)
14196   {
14197     if (player->MovPos == 0)
14198       player->is_pushing = FALSE;
14199
14200     player->is_snapping = FALSE;
14201
14202     if (player->MovPos == 0)
14203     {
14204       player->is_moving = FALSE;
14205       player->is_digging = FALSE;
14206       player->is_collecting = FALSE;
14207     }
14208
14209     return FALSE;
14210   }
14211
14212   /* prevent snapping with already pressed snap key when not allowed */
14213   if (player->is_snapping && !can_continue_snapping)
14214     return FALSE;
14215
14216   player->MovDir = snap_direction;
14217
14218   if (player->MovPos == 0)
14219   {
14220     player->is_moving = FALSE;
14221     player->is_digging = FALSE;
14222     player->is_collecting = FALSE;
14223   }
14224
14225   player->is_dropping = FALSE;
14226   player->is_dropping_pressed = FALSE;
14227   player->drop_pressed_delay = 0;
14228
14229   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14230     return FALSE;
14231
14232   player->is_snapping = TRUE;
14233   player->is_active = TRUE;
14234
14235   if (player->MovPos == 0)
14236   {
14237     player->is_moving = FALSE;
14238     player->is_digging = FALSE;
14239     player->is_collecting = FALSE;
14240   }
14241
14242   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14243     TEST_DrawLevelField(player->last_jx, player->last_jy);
14244
14245   TEST_DrawLevelField(x, y);
14246
14247   return TRUE;
14248 }
14249
14250 static boolean DropElement(struct PlayerInfo *player)
14251 {
14252   int old_element, new_element;
14253   int dropx = player->jx, dropy = player->jy;
14254   int drop_direction = player->MovDir;
14255   int drop_side = drop_direction;
14256   int drop_element = get_next_dropped_element(player);
14257
14258   /* do not drop an element on top of another element; when holding drop key
14259      pressed without moving, dropped element must move away before the next
14260      element can be dropped (this is especially important if the next element
14261      is dynamite, which can be placed on background for historical reasons) */
14262   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14263     return MP_ACTION;
14264
14265   if (IS_THROWABLE(drop_element))
14266   {
14267     dropx += GET_DX_FROM_DIR(drop_direction);
14268     dropy += GET_DY_FROM_DIR(drop_direction);
14269
14270     if (!IN_LEV_FIELD(dropx, dropy))
14271       return FALSE;
14272   }
14273
14274   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14275   new_element = drop_element;           /* default: no change when dropping */
14276
14277   /* check if player is active, not moving and ready to drop */
14278   if (!player->active || player->MovPos || player->drop_delay > 0)
14279     return FALSE;
14280
14281   /* check if player has anything that can be dropped */
14282   if (new_element == EL_UNDEFINED)
14283     return FALSE;
14284
14285   /* only set if player has anything that can be dropped */
14286   player->is_dropping_pressed = TRUE;
14287
14288   /* check if drop key was pressed long enough for EM style dynamite */
14289   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14290     return FALSE;
14291
14292   /* check if anything can be dropped at the current position */
14293   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14294     return FALSE;
14295
14296   /* collected custom elements can only be dropped on empty fields */
14297   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14298     return FALSE;
14299
14300   if (old_element != EL_EMPTY)
14301     Back[dropx][dropy] = old_element;   /* store old element on this field */
14302
14303   ResetGfxAnimation(dropx, dropy);
14304   ResetRandomAnimationValue(dropx, dropy);
14305
14306   if (player->inventory_size > 0 ||
14307       player->inventory_infinite_element != EL_UNDEFINED)
14308   {
14309     if (player->inventory_size > 0)
14310     {
14311       player->inventory_size--;
14312
14313       DrawGameDoorValues();
14314
14315       if (new_element == EL_DYNAMITE)
14316         new_element = EL_DYNAMITE_ACTIVE;
14317       else if (new_element == EL_EM_DYNAMITE)
14318         new_element = EL_EM_DYNAMITE_ACTIVE;
14319       else if (new_element == EL_SP_DISK_RED)
14320         new_element = EL_SP_DISK_RED_ACTIVE;
14321     }
14322
14323     Feld[dropx][dropy] = new_element;
14324
14325     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14326       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14327                           el2img(Feld[dropx][dropy]), 0);
14328
14329     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14330
14331     /* needed if previous element just changed to "empty" in the last frame */
14332     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14333
14334     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14335                                player->index_bit, drop_side);
14336     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14337                                         CE_PLAYER_DROPS_X,
14338                                         player->index_bit, drop_side);
14339
14340     TestIfElementTouchesCustomElement(dropx, dropy);
14341   }
14342   else          /* player is dropping a dyna bomb */
14343   {
14344     player->dynabombs_left--;
14345
14346     Feld[dropx][dropy] = new_element;
14347
14348     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14349       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14350                           el2img(Feld[dropx][dropy]), 0);
14351
14352     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14353   }
14354
14355   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14356     InitField_WithBug1(dropx, dropy, FALSE);
14357
14358   new_element = Feld[dropx][dropy];     /* element might have changed */
14359
14360   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14361       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14362   {
14363     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14364       MovDir[dropx][dropy] = drop_direction;
14365
14366     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14367
14368     /* do not cause impact style collision by dropping elements that can fall */
14369     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14370   }
14371
14372   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14373   player->is_dropping = TRUE;
14374
14375   player->drop_pressed_delay = 0;
14376   player->is_dropping_pressed = FALSE;
14377
14378   player->drop_x = dropx;
14379   player->drop_y = dropy;
14380
14381   return TRUE;
14382 }
14383
14384 /* ------------------------------------------------------------------------- */
14385 /* game sound playing functions                                              */
14386 /* ------------------------------------------------------------------------- */
14387
14388 static int *loop_sound_frame = NULL;
14389 static int *loop_sound_volume = NULL;
14390
14391 void InitPlayLevelSound()
14392 {
14393   int num_sounds = getSoundListSize();
14394
14395   checked_free(loop_sound_frame);
14396   checked_free(loop_sound_volume);
14397
14398   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14399   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14400 }
14401
14402 static void PlayLevelSound(int x, int y, int nr)
14403 {
14404   int sx = SCREENX(x), sy = SCREENY(y);
14405   int volume, stereo_position;
14406   int max_distance = 8;
14407   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14408
14409   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14410       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14411     return;
14412
14413   if (!IN_LEV_FIELD(x, y) ||
14414       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14415       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14416     return;
14417
14418   volume = SOUND_MAX_VOLUME;
14419
14420   if (!IN_SCR_FIELD(sx, sy))
14421   {
14422     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14423     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14424
14425     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14426   }
14427
14428   stereo_position = (SOUND_MAX_LEFT +
14429                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14430                      (SCR_FIELDX + 2 * max_distance));
14431
14432   if (IS_LOOP_SOUND(nr))
14433   {
14434     /* This assures that quieter loop sounds do not overwrite louder ones,
14435        while restarting sound volume comparison with each new game frame. */
14436
14437     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14438       return;
14439
14440     loop_sound_volume[nr] = volume;
14441     loop_sound_frame[nr] = FrameCounter;
14442   }
14443
14444   PlaySoundExt(nr, volume, stereo_position, type);
14445 }
14446
14447 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14448 {
14449   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14450                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14451                  y < LEVELY(BY1) ? LEVELY(BY1) :
14452                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14453                  sound_action);
14454 }
14455
14456 static void PlayLevelSoundAction(int x, int y, int action)
14457 {
14458   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14459 }
14460
14461 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14462 {
14463   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14464
14465   if (sound_effect != SND_UNDEFINED)
14466     PlayLevelSound(x, y, sound_effect);
14467 }
14468
14469 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14470                                               int action)
14471 {
14472   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14473
14474   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14475     PlayLevelSound(x, y, sound_effect);
14476 }
14477
14478 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14479 {
14480   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14481
14482   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14483     PlayLevelSound(x, y, sound_effect);
14484 }
14485
14486 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14487 {
14488   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14489
14490   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14491     StopSound(sound_effect);
14492 }
14493
14494 static int getLevelMusicNr()
14495 {
14496   if (levelset.music[level_nr] != MUS_UNDEFINED)
14497     return levelset.music[level_nr];            /* from config file */
14498   else
14499     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14500 }
14501
14502 static void FadeLevelSounds()
14503 {
14504   FadeSounds();
14505 }
14506
14507 static void FadeLevelMusic()
14508 {
14509   int music_nr = getLevelMusicNr();
14510   char *curr_music = getCurrentlyPlayingMusicFilename();
14511   char *next_music = getMusicInfoEntryFilename(music_nr);
14512
14513   if (!strEqual(curr_music, next_music))
14514     FadeMusic();
14515 }
14516
14517 void FadeLevelSoundsAndMusic()
14518 {
14519   FadeLevelSounds();
14520   FadeLevelMusic();
14521 }
14522
14523 static void PlayLevelMusic()
14524 {
14525   int music_nr = getLevelMusicNr();
14526   char *curr_music = getCurrentlyPlayingMusicFilename();
14527   char *next_music = getMusicInfoEntryFilename(music_nr);
14528
14529   if (!strEqual(curr_music, next_music))
14530     PlayMusic(music_nr);
14531 }
14532
14533 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14534 {
14535   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14536   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14537   int x = xx - 1 - offset;
14538   int y = yy - 1 - offset;
14539
14540   switch (sample)
14541   {
14542     case SAMPLE_blank:
14543       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14544       break;
14545
14546     case SAMPLE_roll:
14547       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14548       break;
14549
14550     case SAMPLE_stone:
14551       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14552       break;
14553
14554     case SAMPLE_nut:
14555       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14556       break;
14557
14558     case SAMPLE_crack:
14559       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14560       break;
14561
14562     case SAMPLE_bug:
14563       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14564       break;
14565
14566     case SAMPLE_tank:
14567       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14568       break;
14569
14570     case SAMPLE_android_clone:
14571       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14572       break;
14573
14574     case SAMPLE_android_move:
14575       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14576       break;
14577
14578     case SAMPLE_spring:
14579       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14580       break;
14581
14582     case SAMPLE_slurp:
14583       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14584       break;
14585
14586     case SAMPLE_eater:
14587       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14588       break;
14589
14590     case SAMPLE_eater_eat:
14591       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14592       break;
14593
14594     case SAMPLE_alien:
14595       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14596       break;
14597
14598     case SAMPLE_collect:
14599       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14600       break;
14601
14602     case SAMPLE_diamond:
14603       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14604       break;
14605
14606     case SAMPLE_squash:
14607       /* !!! CHECK THIS !!! */
14608 #if 1
14609       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14610 #else
14611       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14612 #endif
14613       break;
14614
14615     case SAMPLE_wonderfall:
14616       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14617       break;
14618
14619     case SAMPLE_drip:
14620       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14621       break;
14622
14623     case SAMPLE_push:
14624       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14625       break;
14626
14627     case SAMPLE_dirt:
14628       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14629       break;
14630
14631     case SAMPLE_acid:
14632       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14633       break;
14634
14635     case SAMPLE_ball:
14636       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14637       break;
14638
14639     case SAMPLE_grow:
14640       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14641       break;
14642
14643     case SAMPLE_wonder:
14644       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14645       break;
14646
14647     case SAMPLE_door:
14648       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14649       break;
14650
14651     case SAMPLE_exit_open:
14652       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14653       break;
14654
14655     case SAMPLE_exit_leave:
14656       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14657       break;
14658
14659     case SAMPLE_dynamite:
14660       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14661       break;
14662
14663     case SAMPLE_tick:
14664       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14665       break;
14666
14667     case SAMPLE_press:
14668       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14669       break;
14670
14671     case SAMPLE_wheel:
14672       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14673       break;
14674
14675     case SAMPLE_boom:
14676       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14677       break;
14678
14679     case SAMPLE_die:
14680       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14681       break;
14682
14683     case SAMPLE_time:
14684       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14685       break;
14686
14687     default:
14688       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14689       break;
14690   }
14691 }
14692
14693 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14694 {
14695   int element = map_element_SP_to_RND(element_sp);
14696   int action = map_action_SP_to_RND(action_sp);
14697   int offset = (setup.sp_show_border_elements ? 0 : 1);
14698   int x = xx - offset;
14699   int y = yy - offset;
14700
14701   PlayLevelSoundElementAction(x, y, element, action);
14702 }
14703
14704 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14705 {
14706   int element = map_element_MM_to_RND(element_mm);
14707   int action = map_action_MM_to_RND(action_mm);
14708   int offset = 0;
14709   int x = xx - offset;
14710   int y = yy - offset;
14711
14712   if (!IS_MM_ELEMENT(element))
14713     element = EL_MM_DEFAULT;
14714
14715   PlayLevelSoundElementAction(x, y, element, action);
14716 }
14717
14718 void PlaySound_MM(int sound_mm)
14719 {
14720   int sound = map_sound_MM_to_RND(sound_mm);
14721
14722   if (sound == SND_UNDEFINED)
14723     return;
14724
14725   PlaySound(sound);
14726 }
14727
14728 void PlaySoundLoop_MM(int sound_mm)
14729 {
14730   int sound = map_sound_MM_to_RND(sound_mm);
14731
14732   if (sound == SND_UNDEFINED)
14733     return;
14734
14735   PlaySoundLoop(sound);
14736 }
14737
14738 void StopSound_MM(int sound_mm)
14739 {
14740   int sound = map_sound_MM_to_RND(sound_mm);
14741
14742   if (sound == SND_UNDEFINED)
14743     return;
14744
14745   StopSound(sound);
14746 }
14747
14748 void RaiseScore(int value)
14749 {
14750   local_player->score += value;
14751
14752   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14753
14754   DisplayGameControlValues();
14755 }
14756
14757 void RaiseScoreElement(int element)
14758 {
14759   switch (element)
14760   {
14761     case EL_EMERALD:
14762     case EL_BD_DIAMOND:
14763     case EL_EMERALD_YELLOW:
14764     case EL_EMERALD_RED:
14765     case EL_EMERALD_PURPLE:
14766     case EL_SP_INFOTRON:
14767       RaiseScore(level.score[SC_EMERALD]);
14768       break;
14769     case EL_DIAMOND:
14770       RaiseScore(level.score[SC_DIAMOND]);
14771       break;
14772     case EL_CRYSTAL:
14773       RaiseScore(level.score[SC_CRYSTAL]);
14774       break;
14775     case EL_PEARL:
14776       RaiseScore(level.score[SC_PEARL]);
14777       break;
14778     case EL_BUG:
14779     case EL_BD_BUTTERFLY:
14780     case EL_SP_ELECTRON:
14781       RaiseScore(level.score[SC_BUG]);
14782       break;
14783     case EL_SPACESHIP:
14784     case EL_BD_FIREFLY:
14785     case EL_SP_SNIKSNAK:
14786       RaiseScore(level.score[SC_SPACESHIP]);
14787       break;
14788     case EL_YAMYAM:
14789     case EL_DARK_YAMYAM:
14790       RaiseScore(level.score[SC_YAMYAM]);
14791       break;
14792     case EL_ROBOT:
14793       RaiseScore(level.score[SC_ROBOT]);
14794       break;
14795     case EL_PACMAN:
14796       RaiseScore(level.score[SC_PACMAN]);
14797       break;
14798     case EL_NUT:
14799       RaiseScore(level.score[SC_NUT]);
14800       break;
14801     case EL_DYNAMITE:
14802     case EL_EM_DYNAMITE:
14803     case EL_SP_DISK_RED:
14804     case EL_DYNABOMB_INCREASE_NUMBER:
14805     case EL_DYNABOMB_INCREASE_SIZE:
14806     case EL_DYNABOMB_INCREASE_POWER:
14807       RaiseScore(level.score[SC_DYNAMITE]);
14808       break;
14809     case EL_SHIELD_NORMAL:
14810     case EL_SHIELD_DEADLY:
14811       RaiseScore(level.score[SC_SHIELD]);
14812       break;
14813     case EL_EXTRA_TIME:
14814       RaiseScore(level.extra_time_score);
14815       break;
14816     case EL_KEY_1:
14817     case EL_KEY_2:
14818     case EL_KEY_3:
14819     case EL_KEY_4:
14820     case EL_EM_KEY_1:
14821     case EL_EM_KEY_2:
14822     case EL_EM_KEY_3:
14823     case EL_EM_KEY_4:
14824     case EL_EMC_KEY_5:
14825     case EL_EMC_KEY_6:
14826     case EL_EMC_KEY_7:
14827     case EL_EMC_KEY_8:
14828     case EL_DC_KEY_WHITE:
14829       RaiseScore(level.score[SC_KEY]);
14830       break;
14831     default:
14832       RaiseScore(element_info[element].collect_score);
14833       break;
14834   }
14835 }
14836
14837 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14838 {
14839   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14840   {
14841     /* closing door required in case of envelope style request dialogs */
14842     if (!skip_request)
14843       CloseDoor(DOOR_CLOSE_1);
14844
14845 #if defined(NETWORK_AVALIABLE)
14846     if (options.network)
14847       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14848     else
14849 #endif
14850     {
14851       if (quick_quit)
14852         FadeSkipNextFadeIn();
14853
14854       SetGameStatus(GAME_MODE_MAIN);
14855
14856       DrawMainMenu();
14857     }
14858   }
14859   else          /* continue playing the game */
14860   {
14861     if (tape.playing && tape.deactivate_display)
14862       TapeDeactivateDisplayOff(TRUE);
14863
14864     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14865
14866     if (tape.playing && tape.deactivate_display)
14867       TapeDeactivateDisplayOn();
14868   }
14869 }
14870
14871 void RequestQuitGame(boolean ask_if_really_quit)
14872 {
14873   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14874   boolean skip_request = AllPlayersGone || quick_quit;
14875
14876   RequestQuitGameExt(skip_request, quick_quit,
14877                      "Do you really want to quit the game?");
14878 }
14879
14880
14881 /* ------------------------------------------------------------------------- */
14882 /* random generator functions                                                */
14883 /* ------------------------------------------------------------------------- */
14884
14885 unsigned int InitEngineRandom_RND(int seed)
14886 {
14887   game.num_random_calls = 0;
14888
14889   return InitEngineRandom(seed);
14890 }
14891
14892 unsigned int RND(int max)
14893 {
14894   if (max > 0)
14895   {
14896     game.num_random_calls++;
14897
14898     return GetEngineRandom(max);
14899   }
14900
14901   return 0;
14902 }
14903
14904
14905 /* ------------------------------------------------------------------------- */
14906 /* game engine snapshot handling functions                                   */
14907 /* ------------------------------------------------------------------------- */
14908
14909 struct EngineSnapshotInfo
14910 {
14911   /* runtime values for custom element collect score */
14912   int collect_score[NUM_CUSTOM_ELEMENTS];
14913
14914   /* runtime values for group element choice position */
14915   int choice_pos[NUM_GROUP_ELEMENTS];
14916
14917   /* runtime values for belt position animations */
14918   int belt_graphic[4][NUM_BELT_PARTS];
14919   int belt_anim_mode[4][NUM_BELT_PARTS];
14920 };
14921
14922 static struct EngineSnapshotInfo engine_snapshot_rnd;
14923 static char *snapshot_level_identifier = NULL;
14924 static int snapshot_level_nr = -1;
14925
14926 static void SaveEngineSnapshotValues_RND()
14927 {
14928   static int belt_base_active_element[4] =
14929   {
14930     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14931     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14932     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14933     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14934   };
14935   int i, j;
14936
14937   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14938   {
14939     int element = EL_CUSTOM_START + i;
14940
14941     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14942   }
14943
14944   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14945   {
14946     int element = EL_GROUP_START + i;
14947
14948     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14949   }
14950
14951   for (i = 0; i < 4; i++)
14952   {
14953     for (j = 0; j < NUM_BELT_PARTS; j++)
14954     {
14955       int element = belt_base_active_element[i] + j;
14956       int graphic = el2img(element);
14957       int anim_mode = graphic_info[graphic].anim_mode;
14958
14959       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14960       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14961     }
14962   }
14963 }
14964
14965 static void LoadEngineSnapshotValues_RND()
14966 {
14967   unsigned int num_random_calls = game.num_random_calls;
14968   int i, j;
14969
14970   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14971   {
14972     int element = EL_CUSTOM_START + i;
14973
14974     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14975   }
14976
14977   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14978   {
14979     int element = EL_GROUP_START + i;
14980
14981     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14982   }
14983
14984   for (i = 0; i < 4; i++)
14985   {
14986     for (j = 0; j < NUM_BELT_PARTS; j++)
14987     {
14988       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14989       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14990
14991       graphic_info[graphic].anim_mode = anim_mode;
14992     }
14993   }
14994
14995   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14996   {
14997     InitRND(tape.random_seed);
14998     for (i = 0; i < num_random_calls; i++)
14999       RND(1);
15000   }
15001
15002   if (game.num_random_calls != num_random_calls)
15003   {
15004     Error(ERR_INFO, "number of random calls out of sync");
15005     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15006     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15007     Error(ERR_EXIT, "this should not happen -- please debug");
15008   }
15009 }
15010
15011 void FreeEngineSnapshotSingle()
15012 {
15013   FreeSnapshotSingle();
15014
15015   setString(&snapshot_level_identifier, NULL);
15016   snapshot_level_nr = -1;
15017 }
15018
15019 void FreeEngineSnapshotList()
15020 {
15021   FreeSnapshotList();
15022 }
15023
15024 ListNode *SaveEngineSnapshotBuffers()
15025 {
15026   ListNode *buffers = NULL;
15027
15028   /* copy some special values to a structure better suited for the snapshot */
15029
15030   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15031     SaveEngineSnapshotValues_RND();
15032   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15033     SaveEngineSnapshotValues_EM();
15034   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15035     SaveEngineSnapshotValues_SP(&buffers);
15036   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15037     SaveEngineSnapshotValues_MM(&buffers);
15038
15039   /* save values stored in special snapshot structure */
15040
15041   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15042     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15043   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15044     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15045   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15046     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15047   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15048     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15049
15050   /* save further RND engine values */
15051
15052   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15053   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15054   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15055
15056   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15057   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15058   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15059   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15060
15061   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15062   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15063   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15064   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15065   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15066
15067   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15068   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15069   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15070
15071   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15072
15073   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15074
15075   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15076   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15077
15078   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15079   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15080   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15081   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15082   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15083   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15084   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15085   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15086   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15087   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15088   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15089   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15090   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15091   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15092   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15093   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15094   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15095   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15096
15097   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15098   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15099
15100   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15101   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15102   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15103
15104   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15105   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15106
15107   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15108   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15109   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15110   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15111   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15112
15113   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15114   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15115
15116 #if 0
15117   ListNode *node = engine_snapshot_list_rnd;
15118   int num_bytes = 0;
15119
15120   while (node != NULL)
15121   {
15122     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15123
15124     node = node->next;
15125   }
15126
15127   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15128 #endif
15129
15130   return buffers;
15131 }
15132
15133 void SaveEngineSnapshotSingle()
15134 {
15135   ListNode *buffers = SaveEngineSnapshotBuffers();
15136
15137   /* finally save all snapshot buffers to single snapshot */
15138   SaveSnapshotSingle(buffers);
15139
15140   /* save level identification information */
15141   setString(&snapshot_level_identifier, leveldir_current->identifier);
15142   snapshot_level_nr = level_nr;
15143 }
15144
15145 boolean CheckSaveEngineSnapshotToList()
15146 {
15147   boolean save_snapshot =
15148     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15149      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15150       game.snapshot.changed_action) ||
15151      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15152       game.snapshot.collected_item));
15153
15154   game.snapshot.changed_action = FALSE;
15155   game.snapshot.collected_item = FALSE;
15156   game.snapshot.save_snapshot = save_snapshot;
15157
15158   return save_snapshot;
15159 }
15160
15161 void SaveEngineSnapshotToList()
15162 {
15163   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15164       tape.quick_resume)
15165     return;
15166
15167   ListNode *buffers = SaveEngineSnapshotBuffers();
15168
15169   /* finally save all snapshot buffers to snapshot list */
15170   SaveSnapshotToList(buffers);
15171 }
15172
15173 void SaveEngineSnapshotToListInitial()
15174 {
15175   FreeEngineSnapshotList();
15176
15177   SaveEngineSnapshotToList();
15178 }
15179
15180 void LoadEngineSnapshotValues()
15181 {
15182   /* restore special values from snapshot structure */
15183
15184   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15185     LoadEngineSnapshotValues_RND();
15186   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15187     LoadEngineSnapshotValues_EM();
15188   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15189     LoadEngineSnapshotValues_SP();
15190   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15191     LoadEngineSnapshotValues_MM();
15192 }
15193
15194 void LoadEngineSnapshotSingle()
15195 {
15196   LoadSnapshotSingle();
15197
15198   LoadEngineSnapshotValues();
15199 }
15200
15201 void LoadEngineSnapshot_Undo(int steps)
15202 {
15203   LoadSnapshotFromList_Older(steps);
15204
15205   LoadEngineSnapshotValues();
15206 }
15207
15208 void LoadEngineSnapshot_Redo(int steps)
15209 {
15210   LoadSnapshotFromList_Newer(steps);
15211
15212   LoadEngineSnapshotValues();
15213 }
15214
15215 boolean CheckEngineSnapshotSingle()
15216 {
15217   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15218           snapshot_level_nr == level_nr);
15219 }
15220
15221 boolean CheckEngineSnapshotList()
15222 {
15223   return CheckSnapshotList();
15224 }
15225
15226
15227 /* ---------- new game button stuff ---------------------------------------- */
15228
15229 static struct
15230 {
15231   int graphic;
15232   struct XY *pos;
15233   int gadget_id;
15234   char *infotext;
15235 } gamebutton_info[NUM_GAME_BUTTONS] =
15236 {
15237   {
15238     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
15239     GAME_CTRL_ID_STOP,                  "stop game"
15240   },
15241   {
15242     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
15243     GAME_CTRL_ID_PAUSE,                 "pause game"
15244   },
15245   {
15246     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
15247     GAME_CTRL_ID_PLAY,                  "play game"
15248   },
15249   {
15250     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
15251     GAME_CTRL_ID_UNDO,                  "undo step"
15252   },
15253   {
15254     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
15255     GAME_CTRL_ID_REDO,                  "redo step"
15256   },
15257   {
15258     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
15259     GAME_CTRL_ID_SAVE,                  "save game"
15260   },
15261   {
15262     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
15263     GAME_CTRL_ID_PAUSE2,                "pause game"
15264   },
15265   {
15266     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
15267     GAME_CTRL_ID_LOAD,                  "load game"
15268   },
15269   {
15270     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
15271     SOUND_CTRL_ID_MUSIC,                "background music on/off"
15272   },
15273   {
15274     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
15275     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
15276   },
15277   {
15278     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
15279     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
15280   }
15281 };
15282
15283 void CreateGameButtons()
15284 {
15285   int i;
15286
15287   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15288   {
15289     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15290     struct XY *pos = gamebutton_info[i].pos;
15291     struct GadgetInfo *gi;
15292     int button_type;
15293     boolean checked;
15294     unsigned int event_mask;
15295     int base_x = (tape.show_game_buttons ? VX : DX);
15296     int base_y = (tape.show_game_buttons ? VY : DY);
15297     int gd_x   = gfx->src_x;
15298     int gd_y   = gfx->src_y;
15299     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15300     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15301     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15302     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15303     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15304     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15305     int id = i;
15306
15307     if (gfx->bitmap == NULL)
15308     {
15309       game_gadget[id] = NULL;
15310
15311       continue;
15312     }
15313
15314     if (id == GAME_CTRL_ID_STOP ||
15315         id == GAME_CTRL_ID_PLAY ||
15316         id == GAME_CTRL_ID_SAVE ||
15317         id == GAME_CTRL_ID_LOAD)
15318     {
15319       button_type = GD_TYPE_NORMAL_BUTTON;
15320       checked = FALSE;
15321       event_mask = GD_EVENT_RELEASED;
15322     }
15323     else if (id == GAME_CTRL_ID_UNDO ||
15324              id == GAME_CTRL_ID_REDO)
15325     {
15326       button_type = GD_TYPE_NORMAL_BUTTON;
15327       checked = FALSE;
15328       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15329     }
15330     else
15331     {
15332       button_type = GD_TYPE_CHECK_BUTTON;
15333       checked =
15334         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15335          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15336          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15337       event_mask = GD_EVENT_PRESSED;
15338     }
15339
15340     gi = CreateGadget(GDI_CUSTOM_ID, id,
15341                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15342                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15343                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15344                       GDI_WIDTH, gfx->width,
15345                       GDI_HEIGHT, gfx->height,
15346                       GDI_TYPE, button_type,
15347                       GDI_STATE, GD_BUTTON_UNPRESSED,
15348                       GDI_CHECKED, checked,
15349                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15350                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15351                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15352                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15353                       GDI_DIRECT_DRAW, FALSE,
15354                       GDI_EVENT_MASK, event_mask,
15355                       GDI_CALLBACK_ACTION, HandleGameButtons,
15356                       GDI_END);
15357
15358     if (gi == NULL)
15359       Error(ERR_EXIT, "cannot create gadget");
15360
15361     game_gadget[id] = gi;
15362   }
15363 }
15364
15365 void FreeGameButtons()
15366 {
15367   int i;
15368
15369   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15370     FreeGadget(game_gadget[i]);
15371 }
15372
15373 static void UnmapGameButtonsAtSamePosition(int id)
15374 {
15375   int i;
15376
15377   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15378     if (i != id &&
15379         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15380         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15381       UnmapGadget(game_gadget[i]);
15382 }
15383
15384 static void UnmapGameButtonsAtSamePosition_All()
15385 {
15386   if (setup.show_snapshot_buttons)
15387   {
15388     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15389     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15390     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15391   }
15392   else
15393   {
15394     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15395     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15396     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15397   }
15398 }
15399
15400 static void MapGameButtonsAtSamePosition(int id)
15401 {
15402   int i;
15403
15404   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15405     if (i != id &&
15406         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15407         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15408       MapGadget(game_gadget[i]);
15409
15410   UnmapGameButtonsAtSamePosition_All();
15411 }
15412
15413 void MapUndoRedoButtons()
15414 {
15415   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15416   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15417
15418   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15419   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15420
15421   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15422 }
15423
15424 void UnmapUndoRedoButtons()
15425 {
15426   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15427   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15428
15429   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15430   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15431
15432   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15433 }
15434
15435 void MapGameButtons()
15436 {
15437   int i;
15438
15439   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15440     if (i != GAME_CTRL_ID_UNDO &&
15441         i != GAME_CTRL_ID_REDO)
15442       MapGadget(game_gadget[i]);
15443
15444   UnmapGameButtonsAtSamePosition_All();
15445
15446   RedrawGameButtons();
15447 }
15448
15449 void UnmapGameButtons()
15450 {
15451   int i;
15452
15453   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15454     UnmapGadget(game_gadget[i]);
15455 }
15456
15457 void RedrawGameButtons()
15458 {
15459   int i;
15460
15461   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15462     RedrawGadget(game_gadget[i]);
15463
15464   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15465   redraw_mask &= ~REDRAW_ALL;
15466 }
15467
15468 void GameUndoRedoExt()
15469 {
15470   ClearPlayerAction();
15471
15472   tape.pausing = TRUE;
15473
15474   RedrawPlayfield();
15475   UpdateAndDisplayGameControlValues();
15476
15477   DrawCompleteVideoDisplay();
15478   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15479   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15480   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15481
15482   BackToFront();
15483 }
15484
15485 void GameUndo(int steps)
15486 {
15487   if (!CheckEngineSnapshotList())
15488     return;
15489
15490   LoadEngineSnapshot_Undo(steps);
15491
15492   GameUndoRedoExt();
15493 }
15494
15495 void GameRedo(int steps)
15496 {
15497   if (!CheckEngineSnapshotList())
15498     return;
15499
15500   LoadEngineSnapshot_Redo(steps);
15501
15502   GameUndoRedoExt();
15503 }
15504
15505 static void HandleGameButtonsExt(int id, int button)
15506 {
15507   static boolean game_undo_executed = FALSE;
15508   int steps = BUTTON_STEPSIZE(button);
15509   boolean handle_game_buttons =
15510     (game_status == GAME_MODE_PLAYING ||
15511      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15512
15513   if (!handle_game_buttons)
15514     return;
15515
15516   switch (id)
15517   {
15518     case GAME_CTRL_ID_STOP:
15519       if (game_status == GAME_MODE_MAIN)
15520         break;
15521
15522       if (tape.playing)
15523         TapeStop();
15524       else
15525         RequestQuitGame(TRUE);
15526
15527       break;
15528
15529     case GAME_CTRL_ID_PAUSE:
15530     case GAME_CTRL_ID_PAUSE2:
15531       if (options.network && game_status == GAME_MODE_PLAYING)
15532       {
15533 #if defined(NETWORK_AVALIABLE)
15534         if (tape.pausing)
15535           SendToServer_ContinuePlaying();
15536         else
15537           SendToServer_PausePlaying();
15538 #endif
15539       }
15540       else
15541         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15542
15543       game_undo_executed = FALSE;
15544
15545       break;
15546
15547     case GAME_CTRL_ID_PLAY:
15548       if (game_status == GAME_MODE_MAIN)
15549       {
15550         StartGameActions(options.network, setup.autorecord, level.random_seed);
15551       }
15552       else if (tape.pausing)
15553       {
15554 #if defined(NETWORK_AVALIABLE)
15555         if (options.network)
15556           SendToServer_ContinuePlaying();
15557         else
15558 #endif
15559           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15560       }
15561       break;
15562
15563     case GAME_CTRL_ID_UNDO:
15564       // Important: When using "save snapshot when collecting an item" mode,
15565       // load last (current) snapshot for first "undo" after pressing "pause"
15566       // (else the last-but-one snapshot would be loaded, because the snapshot
15567       // pointer already points to the last snapshot when pressing "pause",
15568       // which is fine for "every step/move" mode, but not for "every collect")
15569       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15570           !game_undo_executed)
15571         steps--;
15572
15573       game_undo_executed = TRUE;
15574
15575       GameUndo(steps);
15576       break;
15577
15578     case GAME_CTRL_ID_REDO:
15579       GameRedo(steps);
15580       break;
15581
15582     case GAME_CTRL_ID_SAVE:
15583       TapeQuickSave();
15584       break;
15585
15586     case GAME_CTRL_ID_LOAD:
15587       TapeQuickLoad();
15588       break;
15589
15590     case SOUND_CTRL_ID_MUSIC:
15591       if (setup.sound_music)
15592       { 
15593         setup.sound_music = FALSE;
15594
15595         FadeMusic();
15596       }
15597       else if (audio.music_available)
15598       { 
15599         setup.sound = setup.sound_music = TRUE;
15600
15601         SetAudioMode(setup.sound);
15602
15603         PlayLevelMusic();
15604       }
15605       break;
15606
15607     case SOUND_CTRL_ID_LOOPS:
15608       if (setup.sound_loops)
15609         setup.sound_loops = FALSE;
15610       else if (audio.loops_available)
15611       {
15612         setup.sound = setup.sound_loops = TRUE;
15613
15614         SetAudioMode(setup.sound);
15615       }
15616       break;
15617
15618     case SOUND_CTRL_ID_SIMPLE:
15619       if (setup.sound_simple)
15620         setup.sound_simple = FALSE;
15621       else if (audio.sound_available)
15622       {
15623         setup.sound = setup.sound_simple = TRUE;
15624
15625         SetAudioMode(setup.sound);
15626       }
15627       break;
15628
15629     default:
15630       break;
15631   }
15632 }
15633
15634 static void HandleGameButtons(struct GadgetInfo *gi)
15635 {
15636   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15637 }
15638
15639 void HandleSoundButtonKeys(Key key)
15640 {
15641
15642   if (key == setup.shortcut.sound_simple)
15643     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15644   else if (key == setup.shortcut.sound_loops)
15645     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15646   else if (key == setup.shortcut.sound_music)
15647     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15648 }