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