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