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