fixed support for wrap-around levels in EM engine for old tapes
[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()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebeNachbarNr(int, int);
1096 void AmoebeUmwandeln(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Feld[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Feld[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Feld[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Feld[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       if (options.debug)
1773       {
1774         printf("- player element %d activated", player->element_nr);
1775         printf(" (local player is %d and currently %s)\n",
1776                local_player->element_nr,
1777                local_player->active ? "active" : "not active");
1778       }
1779     }
1780 #endif
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   // always check if player was just killed and should be reanimated
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Feld[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Feld[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Feld[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Feld[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Feld[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Feld[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Feld[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2151       Error(ERR_EXIT, "this should not happen -- please debug");
2152     }
2153
2154     // force update of game controls after initialization
2155     gpc->value = gpc->last_value = -1;
2156     gpc->frame = gpc->last_frame = -1;
2157     gpc->gfx_frame = -1;
2158
2159     // determine panel value width for later calculation of alignment
2160     if (type == TYPE_INTEGER || type == TYPE_STRING)
2161     {
2162       pos->width = pos->size * getFontWidth(pos->font);
2163       pos->height = getFontHeight(pos->font);
2164     }
2165     else if (type == TYPE_ELEMENT)
2166     {
2167       pos->width = pos->size;
2168       pos->height = pos->size;
2169     }
2170
2171     // fill structure for game panel draw order
2172     gpo->nr = gpc->nr;
2173     gpo->sort_priority = pos->sort_priority;
2174   }
2175
2176   // sort game panel controls according to sort_priority and control number
2177   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2178         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2179 }
2180
2181 static void UpdatePlayfieldElementCount(void)
2182 {
2183   boolean use_element_count = FALSE;
2184   int i, j, x, y;
2185
2186   // first check if it is needed at all to calculate playfield element count
2187   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2188     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2189       use_element_count = TRUE;
2190
2191   if (!use_element_count)
2192     return;
2193
2194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195     element_info[i].element_count = 0;
2196
2197   SCAN_PLAYFIELD(x, y)
2198   {
2199     element_info[Feld[x][y]].element_count++;
2200   }
2201
2202   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2203     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2204       if (IS_IN_GROUP(j, i))
2205         element_info[EL_GROUP_START + i].element_count +=
2206           element_info[j].element_count;
2207 }
2208
2209 static void UpdateGameControlValues(void)
2210 {
2211   int i, k;
2212   int time = (game.LevelSolved ?
2213               game.LevelSolved_CountingTime :
2214               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215               game_em.lev->time :
2216               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217               game_sp.time_played :
2218               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219               game_mm.energy_left :
2220               game.no_time_limit ? TimePlayed : TimeLeft);
2221   int score = (game.LevelSolved ?
2222                game.LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                game_em.lev->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2226                game_sp.score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2228                game_mm.score :
2229                game.score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               game_em.lev->gems_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233               game_sp.infotrons_still_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235               game_mm.kettles_still_needed :
2236               game.gems_still_needed);
2237   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                      game_em.lev->gems_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                      game_sp.infotrons_still_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                      game_mm.kettles_still_needed > 0 ||
2243                      game_mm.lights_still_needed > 0 :
2244                      game.gems_still_needed > 0 ||
2245                      game.sokoban_fields_still_needed > 0 ||
2246                      game.sokoban_objects_still_needed > 0 ||
2247                      game.lights_still_needed > 0);
2248   int health = (game.LevelSolved ?
2249                 game.LevelSolved_CountingHealth :
2250                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2251                 MM_HEALTH(game_mm.laser_overload_value) :
2252                 game.health);
2253
2254   UpdatePlayfieldElementCount();
2255
2256   // update game panel control values
2257
2258   // used instead of "level_nr" (for network games)
2259   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2260   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2261
2262   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2263   for (i = 0; i < MAX_NUM_KEYS; i++)
2264     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2265   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2267
2268   if (game.centered_player_nr == -1)
2269   {
2270     for (i = 0; i < MAX_PLAYERS; i++)
2271     {
2272       // only one player in Supaplex game engine
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274         break;
2275
2276       for (k = 0; k < MAX_NUM_KEYS; k++)
2277       {
2278         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279         {
2280           if (game_em.ply[i]->keys & (1 << k))
2281             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282               get_key_element_from_nr(k);
2283         }
2284         else if (stored_player[i].key[k])
2285           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286             get_key_element_from_nr(k);
2287       }
2288
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         getPlayerInventorySize(i);
2291
2292       if (stored_player[i].num_white_keys > 0)
2293         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2294           EL_DC_KEY_WHITE;
2295
2296       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2297         stored_player[i].num_white_keys;
2298     }
2299   }
2300   else
2301   {
2302     int player_nr = game.centered_player_nr;
2303
2304     for (k = 0; k < MAX_NUM_KEYS; k++)
2305     {
2306       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2307       {
2308         if (game_em.ply[player_nr]->keys & (1 << k))
2309           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310             get_key_element_from_nr(k);
2311       }
2312       else if (stored_player[player_nr].key[k])
2313         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314           get_key_element_from_nr(k);
2315     }
2316
2317     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318       getPlayerInventorySize(player_nr);
2319
2320     if (stored_player[player_nr].num_white_keys > 0)
2321       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2322
2323     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324       stored_player[player_nr].num_white_keys;
2325   }
2326
2327   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, i);
2331     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, -i - 1);
2333   }
2334
2335   game_panel_controls[GAME_PANEL_SCORE].value = score;
2336   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2337
2338   game_panel_controls[GAME_PANEL_TIME].value = time;
2339
2340   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2343
2344   if (level.time == 0)
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2346   else
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2348
2349   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2351
2352   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2353
2354   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2356      EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358     local_player->shield_normal_time_left;
2359   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2361      EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363     local_player->shield_deadly_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EXIT].value =
2366     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372      EL_EMC_MAGIC_BALL_SWITCH);
2373
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377     game.light_time_left;
2378
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382     game.timegate_time_left;
2383
2384   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2386
2387   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390     game.lenses_time_left;
2391
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395     game.magnify_time_left;
2396
2397   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2399      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2401      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2402      EL_BALLOON_SWITCH_NONE);
2403
2404   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405     local_player->dynabomb_count;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407     local_player->dynabomb_size;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2410
2411   game_panel_controls[GAME_PANEL_PENGUINS].value =
2412     game.friends_still_needed;
2413
2414   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415     game.sokoban_objects_still_needed;
2416   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417     game.sokoban_fields_still_needed;
2418
2419   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2421
2422   for (i = 0; i < NUM_BELTS; i++)
2423   {
2424     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2429   }
2430
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434     game.magic_wall_time_left;
2435
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437     local_player->gravity;
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   // update game panel control frames
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508     else if (gpc->type == TYPE_GRAPHIC)
2509     {
2510       if (gpc->graphic != IMG_UNDEFINED)
2511       {
2512         int last_anim_random_frame = gfx.anim_random_frame;
2513         int graphic = gpc->graphic;
2514
2515         if (gpc->value != gpc->last_value)
2516         {
2517           gpc->gfx_frame = 0;
2518           gpc->gfx_random = INIT_GFX_RANDOM();
2519         }
2520         else
2521         {
2522           gpc->gfx_frame++;
2523
2524           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526             gpc->gfx_random = INIT_GFX_RANDOM();
2527         }
2528
2529         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530           gfx.anim_random_frame = gpc->gfx_random;
2531
2532         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2533
2534         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535           gfx.anim_random_frame = last_anim_random_frame;
2536       }
2537     }
2538   }
2539 }
2540
2541 static void DisplayGameControlValues(void)
2542 {
2543   boolean redraw_panel = FALSE;
2544   int i;
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (PANEL_DEACTIVATED(gpc->pos))
2551       continue;
2552
2553     if (gpc->value == gpc->last_value &&
2554         gpc->frame == gpc->last_frame)
2555       continue;
2556
2557     redraw_panel = TRUE;
2558   }
2559
2560   if (!redraw_panel)
2561     return;
2562
2563   // copy default game door content to main double buffer
2564
2565   // !!! CHECK AGAIN !!!
2566   SetPanelBackground();
2567   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2569
2570   // redraw game control buttons
2571   RedrawGameButtons();
2572
2573   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2574
2575   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2576   {
2577     int nr = game_panel_order[i].nr;
2578     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579     struct TextPosInfo *pos = gpc->pos;
2580     int type = gpc->type;
2581     int value = gpc->value;
2582     int frame = gpc->frame;
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591     gpc->last_value = value;
2592     gpc->last_frame = frame;
2593
2594     if (type == TYPE_INTEGER)
2595     {
2596       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597           nr == GAME_PANEL_TIME)
2598       {
2599         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2600
2601         if (use_dynamic_size)           // use dynamic number of digits
2602         {
2603           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605           int size2 = size1 + 1;
2606           int font1 = pos->font;
2607           int font2 = pos->font_alt;
2608
2609           size = (value < value_change ? size1 : size2);
2610           font = (value < value_change ? font1 : font2);
2611         }
2612       }
2613
2614       // correct text size if "digits" is zero or less
2615       if (size <= 0)
2616         size = strlen(int2str(value, size));
2617
2618       // dynamically correct text alignment
2619       pos->width = size * getFontWidth(font);
2620
2621       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622                   int2str(value, size), font, mask_mode);
2623     }
2624     else if (type == TYPE_ELEMENT)
2625     {
2626       int element, graphic;
2627       Bitmap *src_bitmap;
2628       int src_x, src_y;
2629       int width, height;
2630       int dst_x = PANEL_XPOS(pos);
2631       int dst_y = PANEL_YPOS(pos);
2632
2633       if (value != EL_UNDEFINED && value != EL_EMPTY)
2634       {
2635         element = value;
2636         graphic = el2panelimg(value);
2637
2638         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2639
2640         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2641           size = TILESIZE;
2642
2643         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2644                               &src_x, &src_y);
2645
2646         width  = graphic_info[graphic].width  * size / TILESIZE;
2647         height = graphic_info[graphic].height * size / TILESIZE;
2648
2649         if (draw_masked)
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         else
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655       }
2656     }
2657     else if (type == TYPE_GRAPHIC)
2658     {
2659       int graphic        = gpc->graphic;
2660       int graphic_active = gpc->graphic_active;
2661       Bitmap *src_bitmap;
2662       int src_x, src_y;
2663       int width, height;
2664       int dst_x = PANEL_XPOS(pos);
2665       int dst_y = PANEL_YPOS(pos);
2666       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2667                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2668
2669       if (graphic != IMG_UNDEFINED && !skip)
2670       {
2671         if (pos->style == STYLE_REVERSE)
2672           value = 100 - value;
2673
2674         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           width  = graphic_info[graphic_active].width * value / 100;
2679           height = graphic_info[graphic_active].height;
2680
2681           if (pos->direction == MV_LEFT)
2682           {
2683             src_x += graphic_info[graphic_active].width - width;
2684             dst_x += graphic_info[graphic_active].width - width;
2685           }
2686         }
2687         else
2688         {
2689           width  = graphic_info[graphic_active].width;
2690           height = graphic_info[graphic_active].height * value / 100;
2691
2692           if (pos->direction == MV_UP)
2693           {
2694             src_y += graphic_info[graphic_active].height - height;
2695             dst_y += graphic_info[graphic_active].height - height;
2696           }
2697         }
2698
2699         if (draw_masked)
2700           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2701                            dst_x, dst_y);
2702         else
2703           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2704                      dst_x, dst_y);
2705
2706         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2707
2708         if (pos->direction & MV_HORIZONTAL)
2709         {
2710           if (pos->direction == MV_RIGHT)
2711           {
2712             src_x += width;
2713             dst_x += width;
2714           }
2715           else
2716           {
2717             dst_x = PANEL_XPOS(pos);
2718           }
2719
2720           width = graphic_info[graphic].width - width;
2721         }
2722         else
2723         {
2724           if (pos->direction == MV_DOWN)
2725           {
2726             src_y += height;
2727             dst_y += height;
2728           }
2729           else
2730           {
2731             dst_y = PANEL_YPOS(pos);
2732           }
2733
2734           height = graphic_info[graphic].height - height;
2735         }
2736
2737         if (draw_masked)
2738           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2739                            dst_x, dst_y);
2740         else
2741           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2742                      dst_x, dst_y);
2743       }
2744     }
2745     else if (type == TYPE_STRING)
2746     {
2747       boolean active = (value != 0);
2748       char *state_normal = "off";
2749       char *state_active = "on";
2750       char *state = (active ? state_active : state_normal);
2751       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2752                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2753                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2754                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2755
2756       if (nr == GAME_PANEL_GRAVITY_STATE)
2757       {
2758         int font1 = pos->font;          // (used for normal state)
2759         int font2 = pos->font_alt;      // (used for active state)
2760
2761         font = (active ? font2 : font1);
2762       }
2763
2764       if (s != NULL)
2765       {
2766         char *s_cut;
2767
2768         if (size <= 0)
2769         {
2770           // don't truncate output if "chars" is zero or less
2771           size = strlen(s);
2772
2773           // dynamically correct text alignment
2774           pos->width = size * getFontWidth(font);
2775         }
2776
2777         s_cut = getStringCopyN(s, size);
2778
2779         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2780                     s_cut, font, mask_mode);
2781
2782         free(s_cut);
2783       }
2784     }
2785
2786     redraw_mask |= REDRAW_DOOR_1;
2787   }
2788
2789   SetGameStatus(GAME_MODE_PLAYING);
2790 }
2791
2792 void UpdateAndDisplayGameControlValues(void)
2793 {
2794   if (tape.deactivate_display)
2795     return;
2796
2797   UpdateGameControlValues();
2798   DisplayGameControlValues();
2799 }
2800
2801 #if 0
2802 static void UpdateGameDoorValues(void)
2803 {
2804   UpdateGameControlValues();
2805 }
2806 #endif
2807
2808 void DrawGameDoorValues(void)
2809 {
2810   DisplayGameControlValues();
2811 }
2812
2813
2814 // ============================================================================
2815 // InitGameEngine()
2816 // ----------------------------------------------------------------------------
2817 // initialize game engine due to level / tape version number
2818 // ============================================================================
2819
2820 static void InitGameEngine(void)
2821 {
2822   int i, j, k, l, x, y;
2823
2824   // set game engine from tape file when re-playing, else from level file
2825   game.engine_version = (tape.playing ? tape.engine_version :
2826                          level.game_version);
2827
2828   // set single or multi-player game mode (needed for re-playing tapes)
2829   game.team_mode = setup.team_mode;
2830
2831   if (tape.playing)
2832   {
2833     int num_players = 0;
2834
2835     for (i = 0; i < MAX_PLAYERS; i++)
2836       if (tape.player_participates[i])
2837         num_players++;
2838
2839     // multi-player tapes contain input data for more than one player
2840     game.team_mode = (num_players > 1);
2841   }
2842
2843 #if 0
2844   printf("level %d: level.game_version  == %06d\n", level_nr,
2845          level.game_version);
2846   printf("          tape.file_version   == %06d\n",
2847          tape.file_version);
2848   printf("          tape.game_version   == %06d\n",
2849          tape.game_version);
2850   printf("          tape.engine_version == %06d\n",
2851          tape.engine_version);
2852   printf("       => game.engine_version == %06d [tape mode: %s]\n",
2853          game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2854 #endif
2855
2856   // --------------------------------------------------------------------------
2857   // set flags for bugs and changes according to active game engine version
2858   // --------------------------------------------------------------------------
2859
2860   /*
2861     Summary of bugfix:
2862     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2863
2864     Bug was introduced in version:
2865     2.0.1
2866
2867     Bug was fixed in version:
2868     4.1.4.2
2869
2870     Description:
2871     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2872     but the property "can fall" was missing, which caused some levels to be
2873     unsolvable. This was fixed in version 4.1.4.2.
2874
2875     Affected levels/tapes:
2876     An example for a tape that was fixed by this bugfix is tape 029 from the
2877     level set "rnd_sam_bateman".
2878     The wrong behaviour will still be used for all levels or tapes that were
2879     created/recorded with it. An example for this is tape 023 from the level
2880     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2881   */
2882
2883   boolean use_amoeba_dropping_cannot_fall_bug =
2884     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2885       game.engine_version <= VERSION_IDENT(4,1,4,1)) ||
2886      (tape.playing &&
2887       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2888       tape.game_version <= VERSION_IDENT(4,1,4,1)));
2889
2890   /*
2891     Summary of bugfix/change:
2892     Fixed move speed of elements entering or leaving magic wall.
2893
2894     Fixed/changed in version:
2895     2.0.1
2896
2897     Description:
2898     Before 2.0.1, move speed of elements entering or leaving magic wall was
2899     twice as fast as it is now.
2900     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2901
2902     Affected levels/tapes:
2903     The first condition is generally needed for all levels/tapes before version
2904     2.0.1, which might use the old behaviour before it was changed; known tapes
2905     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2906     The second condition is an exception from the above case and is needed for
2907     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2908     above, but before it was known that this change would break tapes like the
2909     above and was fixed in 4.1.4.2, so that the changed behaviour was active
2910     although the engine version while recording maybe was before 2.0.1. There
2911     are a lot of tapes that are affected by this exception, like tape 006 from
2912     the level set "rnd_conor_mancone".
2913   */
2914
2915   boolean use_old_move_stepsize_for_magic_wall =
2916     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2917      !(tape.playing &&
2918        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2919        tape.game_version <  VERSION_IDENT(4,1,4,2)));
2920
2921   /*
2922     Summary of bugfix/change:
2923     Fixed handling for custom elements that change when pushed by the player.
2924
2925     Fixed/changed in version:
2926     3.1.0
2927
2928     Description:
2929     Before 3.1.0, custom elements that "change when pushing" changed directly
2930     after the player started pushing them (until then handled in "DigField()").
2931     Since 3.1.0, these custom elements are not changed until the "pushing"
2932     move of the element is finished (now handled in "ContinueMoving()").
2933
2934     Affected levels/tapes:
2935     The first condition is generally needed for all levels/tapes before version
2936     3.1.0, which might use the old behaviour before it was changed; known tapes
2937     that are affected are some tapes from the level set "Walpurgis Gardens" by
2938     Jamie Cullen.
2939     The second condition is an exception from the above case and is needed for
2940     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2941     above (including some development versions of 3.1.0), but before it was
2942     known that this change would break tapes like the above and was fixed in
2943     3.1.1, so that the changed behaviour was active although the engine version
2944     while recording maybe was before 3.1.0. There is at least one tape that is
2945     affected by this exception, which is the tape for the one-level set "Bug
2946     Machine" by Juergen Bonhagen.
2947   */
2948
2949   game.use_change_when_pushing_bug =
2950     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2951      !(tape.playing &&
2952        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2953        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2954
2955   /*
2956     Summary of bugfix/change:
2957     Fixed handling for blocking the field the player leaves when moving.
2958
2959     Fixed/changed in version:
2960     3.1.1
2961
2962     Description:
2963     Before 3.1.1, when "block last field when moving" was enabled, the field
2964     the player is leaving when moving was blocked for the time of the move,
2965     and was directly unblocked afterwards. This resulted in the last field
2966     being blocked for exactly one less than the number of frames of one player
2967     move. Additionally, even when blocking was disabled, the last field was
2968     blocked for exactly one frame.
2969     Since 3.1.1, due to changes in player movement handling, the last field
2970     is not blocked at all when blocking is disabled. When blocking is enabled,
2971     the last field is blocked for exactly the number of frames of one player
2972     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2973     last field is blocked for exactly one more than the number of frames of
2974     one player move.
2975
2976     Affected levels/tapes:
2977     (!!! yet to be determined -- probably many !!!)
2978   */
2979
2980   game.use_block_last_field_bug =
2981     (game.engine_version < VERSION_IDENT(3,1,1,0));
2982
2983   /* various special flags and settings for native Emerald Mine game engine */
2984
2985   game_em.use_single_button =
2986     (game.engine_version > VERSION_IDENT(4,0,0,2));
2987
2988   game_em.use_snap_key_bug =
2989     (game.engine_version < VERSION_IDENT(4,0,1,0));
2990
2991   game_em.use_old_explosions =
2992     (game.engine_version < VERSION_IDENT(4,1,4,2));
2993
2994   game_em.use_wrap_around =
2995     (game.engine_version > VERSION_IDENT(4,1,4,1));
2996
2997   // --------------------------------------------------------------------------
2998
2999   // set maximal allowed number of custom element changes per game frame
3000   game.max_num_changes_per_frame = 1;
3001
3002   // default scan direction: scan playfield from top/left to bottom/right
3003   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3004
3005   // dynamically adjust element properties according to game engine version
3006   InitElementPropertiesEngine(game.engine_version);
3007
3008   // ---------- initialize special element properties -------------------------
3009
3010   // "EL_AMOEBA_DROPPING" missed property "can fall" between 2.0.1 and 4.1.4.1
3011   if (use_amoeba_dropping_cannot_fall_bug)
3012     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3013
3014   // ---------- initialize player's initial move delay ------------------------
3015
3016   // dynamically adjust player properties according to level information
3017   for (i = 0; i < MAX_PLAYERS; i++)
3018     game.initial_move_delay_value[i] =
3019       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3020
3021   // dynamically adjust player properties according to game engine version
3022   for (i = 0; i < MAX_PLAYERS; i++)
3023     game.initial_move_delay[i] =
3024       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3025        game.initial_move_delay_value[i] : 0);
3026
3027   // ---------- initialize player's initial push delay ------------------------
3028
3029   // dynamically adjust player properties according to game engine version
3030   game.initial_push_delay_value =
3031     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3032
3033   // ---------- initialize changing elements ----------------------------------
3034
3035   // initialize changing elements information
3036   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3037   {
3038     struct ElementInfo *ei = &element_info[i];
3039
3040     // this pointer might have been changed in the level editor
3041     ei->change = &ei->change_page[0];
3042
3043     if (!IS_CUSTOM_ELEMENT(i))
3044     {
3045       ei->change->target_element = EL_EMPTY_SPACE;
3046       ei->change->delay_fixed = 0;
3047       ei->change->delay_random = 0;
3048       ei->change->delay_frames = 1;
3049     }
3050
3051     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3052     {
3053       ei->has_change_event[j] = FALSE;
3054
3055       ei->event_page_nr[j] = 0;
3056       ei->event_page[j] = &ei->change_page[0];
3057     }
3058   }
3059
3060   // add changing elements from pre-defined list
3061   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3062   {
3063     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3064     struct ElementInfo *ei = &element_info[ch_delay->element];
3065
3066     ei->change->target_element       = ch_delay->target_element;
3067     ei->change->delay_fixed          = ch_delay->change_delay;
3068
3069     ei->change->pre_change_function  = ch_delay->pre_change_function;
3070     ei->change->change_function      = ch_delay->change_function;
3071     ei->change->post_change_function = ch_delay->post_change_function;
3072
3073     ei->change->can_change = TRUE;
3074     ei->change->can_change_or_has_action = TRUE;
3075
3076     ei->has_change_event[CE_DELAY] = TRUE;
3077
3078     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3079     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3080   }
3081
3082   // ---------- initialize internal run-time variables ------------------------
3083
3084   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3085   {
3086     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3087
3088     for (j = 0; j < ei->num_change_pages; j++)
3089     {
3090       ei->change_page[j].can_change_or_has_action =
3091         (ei->change_page[j].can_change |
3092          ei->change_page[j].has_action);
3093     }
3094   }
3095
3096   // add change events from custom element configuration
3097   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3098   {
3099     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3100
3101     for (j = 0; j < ei->num_change_pages; j++)
3102     {
3103       if (!ei->change_page[j].can_change_or_has_action)
3104         continue;
3105
3106       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3107       {
3108         // only add event page for the first page found with this event
3109         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3110         {
3111           ei->has_change_event[k] = TRUE;
3112
3113           ei->event_page_nr[k] = j;
3114           ei->event_page[k] = &ei->change_page[j];
3115         }
3116       }
3117     }
3118   }
3119
3120   // ---------- initialize reference elements in change conditions ------------
3121
3122   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3123   {
3124     int element = EL_CUSTOM_START + i;
3125     struct ElementInfo *ei = &element_info[element];
3126
3127     for (j = 0; j < ei->num_change_pages; j++)
3128     {
3129       int trigger_element = ei->change_page[j].initial_trigger_element;
3130
3131       if (trigger_element >= EL_PREV_CE_8 &&
3132           trigger_element <= EL_NEXT_CE_8)
3133         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3134
3135       ei->change_page[j].trigger_element = trigger_element;
3136     }
3137   }
3138
3139   // ---------- initialize run-time trigger player and element ----------------
3140
3141   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3142   {
3143     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3144
3145     for (j = 0; j < ei->num_change_pages; j++)
3146     {
3147       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3148       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3149       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3150       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3151       ei->change_page[j].actual_trigger_ce_value = 0;
3152       ei->change_page[j].actual_trigger_ce_score = 0;
3153     }
3154   }
3155
3156   // ---------- initialize trigger events -------------------------------------
3157
3158   // initialize trigger events information
3159   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3160     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3161       trigger_events[i][j] = FALSE;
3162
3163   // add trigger events from element change event properties
3164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3165   {
3166     struct ElementInfo *ei = &element_info[i];
3167
3168     for (j = 0; j < ei->num_change_pages; j++)
3169     {
3170       if (!ei->change_page[j].can_change_or_has_action)
3171         continue;
3172
3173       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3174       {
3175         int trigger_element = ei->change_page[j].trigger_element;
3176
3177         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3178         {
3179           if (ei->change_page[j].has_event[k])
3180           {
3181             if (IS_GROUP_ELEMENT(trigger_element))
3182             {
3183               struct ElementGroupInfo *group =
3184                 element_info[trigger_element].group;
3185
3186               for (l = 0; l < group->num_elements_resolved; l++)
3187                 trigger_events[group->element_resolved[l]][k] = TRUE;
3188             }
3189             else if (trigger_element == EL_ANY_ELEMENT)
3190               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3191                 trigger_events[l][k] = TRUE;
3192             else
3193               trigger_events[trigger_element][k] = TRUE;
3194           }
3195         }
3196       }
3197     }
3198   }
3199
3200   // ---------- initialize push delay -----------------------------------------
3201
3202   // initialize push delay values to default
3203   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3204   {
3205     if (!IS_CUSTOM_ELEMENT(i))
3206     {
3207       // set default push delay values (corrected since version 3.0.7-1)
3208       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3209       {
3210         element_info[i].push_delay_fixed = 2;
3211         element_info[i].push_delay_random = 8;
3212       }
3213       else
3214       {
3215         element_info[i].push_delay_fixed = 8;
3216         element_info[i].push_delay_random = 8;
3217       }
3218     }
3219   }
3220
3221   // set push delay value for certain elements from pre-defined list
3222   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3223   {
3224     int e = push_delay_list[i].element;
3225
3226     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3227     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3228   }
3229
3230   // set push delay value for Supaplex elements for newer engine versions
3231   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3232   {
3233     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3234     {
3235       if (IS_SP_ELEMENT(i))
3236       {
3237         // set SP push delay to just enough to push under a falling zonk
3238         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3239
3240         element_info[i].push_delay_fixed  = delay;
3241         element_info[i].push_delay_random = 0;
3242       }
3243     }
3244   }
3245
3246   // ---------- initialize move stepsize --------------------------------------
3247
3248   // initialize move stepsize values to default
3249   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3250     if (!IS_CUSTOM_ELEMENT(i))
3251       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3252
3253   // set move stepsize value for certain elements from pre-defined list
3254   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3255   {
3256     int e = move_stepsize_list[i].element;
3257
3258     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3259
3260     // set move stepsize value for certain elements for older engine versions
3261     if (use_old_move_stepsize_for_magic_wall)
3262     {
3263       if (e == EL_MAGIC_WALL_FILLING ||
3264           e == EL_MAGIC_WALL_EMPTYING ||
3265           e == EL_BD_MAGIC_WALL_FILLING ||
3266           e == EL_BD_MAGIC_WALL_EMPTYING)
3267         element_info[e].move_stepsize *= 2;
3268     }
3269   }
3270
3271   // ---------- initialize collect score --------------------------------------
3272
3273   // initialize collect score values for custom elements from initial value
3274   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3275     if (IS_CUSTOM_ELEMENT(i))
3276       element_info[i].collect_score = element_info[i].collect_score_initial;
3277
3278   // ---------- initialize collect count --------------------------------------
3279
3280   // initialize collect count values for non-custom elements
3281   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3282     if (!IS_CUSTOM_ELEMENT(i))
3283       element_info[i].collect_count_initial = 0;
3284
3285   // add collect count values for all elements from pre-defined list
3286   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3287     element_info[collect_count_list[i].element].collect_count_initial =
3288       collect_count_list[i].count;
3289
3290   // ---------- initialize access direction -----------------------------------
3291
3292   // initialize access direction values to default (access from every side)
3293   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3294     if (!IS_CUSTOM_ELEMENT(i))
3295       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3296
3297   // set access direction value for certain elements from pre-defined list
3298   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3299     element_info[access_direction_list[i].element].access_direction =
3300       access_direction_list[i].direction;
3301
3302   // ---------- initialize explosion content ----------------------------------
3303   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3304   {
3305     if (IS_CUSTOM_ELEMENT(i))
3306       continue;
3307
3308     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3309     {
3310       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3311
3312       element_info[i].content.e[x][y] =
3313         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3314          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3315          i == EL_PLAYER_3 ? EL_EMERALD :
3316          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3317          i == EL_MOLE ? EL_EMERALD_RED :
3318          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3319          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3320          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3321          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3322          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3323          i == EL_WALL_EMERALD ? EL_EMERALD :
3324          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3325          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3326          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3327          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3328          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3329          i == EL_WALL_PEARL ? EL_PEARL :
3330          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3331          EL_EMPTY);
3332     }
3333   }
3334
3335   // ---------- initialize recursion detection --------------------------------
3336   recursion_loop_depth = 0;
3337   recursion_loop_detected = FALSE;
3338   recursion_loop_element = EL_UNDEFINED;
3339
3340   // ---------- initialize graphics engine ------------------------------------
3341   game.scroll_delay_value =
3342     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3343      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3344      !setup.forced_scroll_delay           ? 0 :
3345      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3346   game.scroll_delay_value =
3347     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3348
3349   // ---------- initialize game engine snapshots ------------------------------
3350   for (i = 0; i < MAX_PLAYERS; i++)
3351     game.snapshot.last_action[i] = 0;
3352   game.snapshot.changed_action = FALSE;
3353   game.snapshot.collected_item = FALSE;
3354   game.snapshot.mode =
3355     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3356      SNAPSHOT_MODE_EVERY_STEP :
3357      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3358      SNAPSHOT_MODE_EVERY_MOVE :
3359      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3360      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3361   game.snapshot.save_snapshot = FALSE;
3362
3363   // ---------- initialize level time for Supaplex engine ---------------------
3364   // Supaplex levels with time limit currently unsupported -- should be added
3365   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3366     level.time = 0;
3367
3368   // ---------- initialize flags for handling game actions --------------------
3369
3370   // set flags for game actions to default values
3371   game.use_key_actions = TRUE;
3372   game.use_mouse_actions = FALSE;
3373
3374   // when using Mirror Magic game engine, handle mouse events only
3375   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3376   {
3377     game.use_key_actions = FALSE;
3378     game.use_mouse_actions = TRUE;
3379   }
3380
3381   // check for custom elements with mouse click events
3382   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3383   {
3384     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3385     {
3386       int element = EL_CUSTOM_START + i;
3387
3388       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3389           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3390           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3391           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3392         game.use_mouse_actions = TRUE;
3393     }
3394   }
3395 }
3396
3397 static int get_num_special_action(int element, int action_first,
3398                                   int action_last)
3399 {
3400   int num_special_action = 0;
3401   int i, j;
3402
3403   for (i = action_first; i <= action_last; i++)
3404   {
3405     boolean found = FALSE;
3406
3407     for (j = 0; j < NUM_DIRECTIONS; j++)
3408       if (el_act_dir2img(element, i, j) !=
3409           el_act_dir2img(element, ACTION_DEFAULT, j))
3410         found = TRUE;
3411
3412     if (found)
3413       num_special_action++;
3414     else
3415       break;
3416   }
3417
3418   return num_special_action;
3419 }
3420
3421
3422 // ============================================================================
3423 // InitGame()
3424 // ----------------------------------------------------------------------------
3425 // initialize and start new game
3426 // ============================================================================
3427
3428 #if DEBUG_INIT_PLAYER
3429 static void DebugPrintPlayerStatus(char *message)
3430 {
3431   int i;
3432
3433   if (!options.debug)
3434     return;
3435
3436   printf("%s:\n", message);
3437
3438   for (i = 0; i < MAX_PLAYERS; i++)
3439   {
3440     struct PlayerInfo *player = &stored_player[i];
3441
3442     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3443            i + 1,
3444            player->present,
3445            player->connected,
3446            player->connected_locally,
3447            player->connected_network,
3448            player->active);
3449
3450     if (local_player == player)
3451       printf(" (local player)");
3452
3453     printf("\n");
3454   }
3455 }
3456 #endif
3457
3458 void InitGame(void)
3459 {
3460   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3461   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3462   int fade_mask = REDRAW_FIELD;
3463
3464   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3465   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3466   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3467   int initial_move_dir = MV_DOWN;
3468   int i, j, x, y;
3469
3470   // required here to update video display before fading (FIX THIS)
3471   DrawMaskedBorder(REDRAW_DOOR_2);
3472
3473   if (!game.restart_level)
3474     CloseDoor(DOOR_CLOSE_1);
3475
3476   SetGameStatus(GAME_MODE_PLAYING);
3477
3478   if (level_editor_test_game)
3479     FadeSkipNextFadeOut();
3480   else
3481     FadeSetEnterScreen();
3482
3483   if (CheckFadeAll())
3484     fade_mask = REDRAW_ALL;
3485
3486   FadeLevelSoundsAndMusic();
3487
3488   ExpireSoundLoops(TRUE);
3489
3490   FadeOut(fade_mask);
3491
3492   if (level_editor_test_game)
3493     FadeSkipNextFadeIn();
3494
3495   // needed if different viewport properties defined for playing
3496   ChangeViewportPropertiesIfNeeded();
3497
3498   ClearField();
3499
3500   DrawCompleteVideoDisplay();
3501
3502   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3503
3504   InitGameEngine();
3505   InitGameControlValues();
3506
3507   // initialize tape actions from game when recording tape
3508   if (tape.recording)
3509   {
3510     tape.use_key_actions   = game.use_key_actions;
3511     tape.use_mouse_actions = game.use_mouse_actions;
3512   }
3513
3514   // don't play tapes over network
3515   network_playing = (network.enabled && !tape.playing);
3516
3517   for (i = 0; i < MAX_PLAYERS; i++)
3518   {
3519     struct PlayerInfo *player = &stored_player[i];
3520
3521     player->index_nr = i;
3522     player->index_bit = (1 << i);
3523     player->element_nr = EL_PLAYER_1 + i;
3524
3525     player->present = FALSE;
3526     player->active = FALSE;
3527     player->mapped = FALSE;
3528
3529     player->killed = FALSE;
3530     player->reanimated = FALSE;
3531     player->buried = FALSE;
3532
3533     player->action = 0;
3534     player->effective_action = 0;
3535     player->programmed_action = 0;
3536     player->snap_action = 0;
3537
3538     player->mouse_action.lx = 0;
3539     player->mouse_action.ly = 0;
3540     player->mouse_action.button = 0;
3541     player->mouse_action.button_hint = 0;
3542
3543     player->effective_mouse_action.lx = 0;
3544     player->effective_mouse_action.ly = 0;
3545     player->effective_mouse_action.button = 0;
3546     player->effective_mouse_action.button_hint = 0;
3547
3548     for (j = 0; j < MAX_NUM_KEYS; j++)
3549       player->key[j] = FALSE;
3550
3551     player->num_white_keys = 0;
3552
3553     player->dynabomb_count = 0;
3554     player->dynabomb_size = 1;
3555     player->dynabombs_left = 0;
3556     player->dynabomb_xl = FALSE;
3557
3558     player->MovDir = initial_move_dir;
3559     player->MovPos = 0;
3560     player->GfxPos = 0;
3561     player->GfxDir = initial_move_dir;
3562     player->GfxAction = ACTION_DEFAULT;
3563     player->Frame = 0;
3564     player->StepFrame = 0;
3565
3566     player->initial_element = player->element_nr;
3567     player->artwork_element =
3568       (level.use_artwork_element[i] ? level.artwork_element[i] :
3569        player->element_nr);
3570     player->use_murphy = FALSE;
3571
3572     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3573     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3574
3575     player->gravity = level.initial_player_gravity[i];
3576
3577     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3578
3579     player->actual_frame_counter = 0;
3580
3581     player->step_counter = 0;
3582
3583     player->last_move_dir = initial_move_dir;
3584
3585     player->is_active = FALSE;
3586
3587     player->is_waiting = FALSE;
3588     player->is_moving = FALSE;
3589     player->is_auto_moving = FALSE;
3590     player->is_digging = FALSE;
3591     player->is_snapping = FALSE;
3592     player->is_collecting = FALSE;
3593     player->is_pushing = FALSE;
3594     player->is_switching = FALSE;
3595     player->is_dropping = FALSE;
3596     player->is_dropping_pressed = FALSE;
3597
3598     player->is_bored = FALSE;
3599     player->is_sleeping = FALSE;
3600
3601     player->was_waiting = TRUE;
3602     player->was_moving = FALSE;
3603     player->was_snapping = FALSE;
3604     player->was_dropping = FALSE;
3605
3606     player->force_dropping = FALSE;
3607
3608     player->frame_counter_bored = -1;
3609     player->frame_counter_sleeping = -1;
3610
3611     player->anim_delay_counter = 0;
3612     player->post_delay_counter = 0;
3613
3614     player->dir_waiting = initial_move_dir;
3615     player->action_waiting = ACTION_DEFAULT;
3616     player->last_action_waiting = ACTION_DEFAULT;
3617     player->special_action_bored = ACTION_DEFAULT;
3618     player->special_action_sleeping = ACTION_DEFAULT;
3619
3620     player->switch_x = -1;
3621     player->switch_y = -1;
3622
3623     player->drop_x = -1;
3624     player->drop_y = -1;
3625
3626     player->show_envelope = 0;
3627
3628     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3629
3630     player->push_delay       = -1;      // initialized when pushing starts
3631     player->push_delay_value = game.initial_push_delay_value;
3632
3633     player->drop_delay = 0;
3634     player->drop_pressed_delay = 0;
3635
3636     player->last_jx = -1;
3637     player->last_jy = -1;
3638     player->jx = -1;
3639     player->jy = -1;
3640
3641     player->shield_normal_time_left = 0;
3642     player->shield_deadly_time_left = 0;
3643
3644     player->inventory_infinite_element = EL_UNDEFINED;
3645     player->inventory_size = 0;
3646
3647     if (level.use_initial_inventory[i])
3648     {
3649       for (j = 0; j < level.initial_inventory_size[i]; j++)
3650       {
3651         int element = level.initial_inventory_content[i][j];
3652         int collect_count = element_info[element].collect_count_initial;
3653         int k;
3654
3655         if (!IS_CUSTOM_ELEMENT(element))
3656           collect_count = 1;
3657
3658         if (collect_count == 0)
3659           player->inventory_infinite_element = element;
3660         else
3661           for (k = 0; k < collect_count; k++)
3662             if (player->inventory_size < MAX_INVENTORY_SIZE)
3663               player->inventory_element[player->inventory_size++] = element;
3664       }
3665     }
3666
3667     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3668     SnapField(player, 0, 0);
3669
3670     map_player_action[i] = i;
3671   }
3672
3673   network_player_action_received = FALSE;
3674
3675   // initial null action
3676   if (network_playing)
3677     SendToServer_MovePlayer(MV_NONE);
3678
3679   FrameCounter = 0;
3680   TimeFrames = 0;
3681   TimePlayed = 0;
3682   TimeLeft = level.time;
3683   TapeTime = 0;
3684
3685   ScreenMovDir = MV_NONE;
3686   ScreenMovPos = 0;
3687   ScreenGfxPos = 0;
3688
3689   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3690
3691   game.robot_wheel_x = -1;
3692   game.robot_wheel_y = -1;
3693
3694   game.exit_x = -1;
3695   game.exit_y = -1;
3696
3697   game.all_players_gone = FALSE;
3698
3699   game.LevelSolved = FALSE;
3700   game.GameOver = FALSE;
3701
3702   game.GamePlayed = !tape.playing;
3703
3704   game.LevelSolved_GameWon = FALSE;
3705   game.LevelSolved_GameEnd = FALSE;
3706   game.LevelSolved_SaveTape = FALSE;
3707   game.LevelSolved_SaveScore = FALSE;
3708
3709   game.LevelSolved_CountingTime = 0;
3710   game.LevelSolved_CountingScore = 0;
3711   game.LevelSolved_CountingHealth = 0;
3712
3713   game.panel.active = TRUE;
3714
3715   game.no_time_limit = (level.time == 0);
3716
3717   game.yamyam_content_nr = 0;
3718   game.robot_wheel_active = FALSE;
3719   game.magic_wall_active = FALSE;
3720   game.magic_wall_time_left = 0;
3721   game.light_time_left = 0;
3722   game.timegate_time_left = 0;
3723   game.switchgate_pos = 0;
3724   game.wind_direction = level.wind_direction_initial;
3725
3726   game.score = 0;
3727   game.score_final = 0;
3728
3729   game.health = MAX_HEALTH;
3730   game.health_final = MAX_HEALTH;
3731
3732   game.gems_still_needed = level.gems_needed;
3733   game.sokoban_fields_still_needed = 0;
3734   game.sokoban_objects_still_needed = 0;
3735   game.lights_still_needed = 0;
3736   game.players_still_needed = 0;
3737   game.friends_still_needed = 0;
3738
3739   game.lenses_time_left = 0;
3740   game.magnify_time_left = 0;
3741
3742   game.ball_active = level.ball_active_initial;
3743   game.ball_content_nr = 0;
3744
3745   game.explosions_delayed = TRUE;
3746
3747   game.envelope_active = FALSE;
3748
3749   for (i = 0; i < NUM_BELTS; i++)
3750   {
3751     game.belt_dir[i] = MV_NONE;
3752     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3753   }
3754
3755   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3756     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3757
3758 #if DEBUG_INIT_PLAYER
3759   DebugPrintPlayerStatus("Player status at level initialization");
3760 #endif
3761
3762   SCAN_PLAYFIELD(x, y)
3763   {
3764     Feld[x][y] = Last[x][y] = level.field[x][y];
3765     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3766     ChangeDelay[x][y] = 0;
3767     ChangePage[x][y] = -1;
3768     CustomValue[x][y] = 0;              // initialized in InitField()
3769     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3770     AmoebaNr[x][y] = 0;
3771     WasJustMoving[x][y] = 0;
3772     WasJustFalling[x][y] = 0;
3773     CheckCollision[x][y] = 0;
3774     CheckImpact[x][y] = 0;
3775     Stop[x][y] = FALSE;
3776     Pushed[x][y] = FALSE;
3777
3778     ChangeCount[x][y] = 0;
3779     ChangeEvent[x][y] = -1;
3780
3781     ExplodePhase[x][y] = 0;
3782     ExplodeDelay[x][y] = 0;
3783     ExplodeField[x][y] = EX_TYPE_NONE;
3784
3785     RunnerVisit[x][y] = 0;
3786     PlayerVisit[x][y] = 0;
3787
3788     GfxFrame[x][y] = 0;
3789     GfxRandom[x][y] = INIT_GFX_RANDOM();
3790     GfxElement[x][y] = EL_UNDEFINED;
3791     GfxAction[x][y] = ACTION_DEFAULT;
3792     GfxDir[x][y] = MV_NONE;
3793     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3794   }
3795
3796   SCAN_PLAYFIELD(x, y)
3797   {
3798     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3799       emulate_bd = FALSE;
3800     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3801       emulate_sb = FALSE;
3802     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3803       emulate_sp = FALSE;
3804
3805     InitField(x, y, TRUE);
3806
3807     ResetGfxAnimation(x, y);
3808   }
3809
3810   InitBeltMovement();
3811
3812   for (i = 0; i < MAX_PLAYERS; i++)
3813   {
3814     struct PlayerInfo *player = &stored_player[i];
3815
3816     // set number of special actions for bored and sleeping animation
3817     player->num_special_action_bored =
3818       get_num_special_action(player->artwork_element,
3819                              ACTION_BORING_1, ACTION_BORING_LAST);
3820     player->num_special_action_sleeping =
3821       get_num_special_action(player->artwork_element,
3822                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3823   }
3824
3825   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3826                     emulate_sb ? EMU_SOKOBAN :
3827                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3828
3829   // initialize type of slippery elements
3830   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3831   {
3832     if (!IS_CUSTOM_ELEMENT(i))
3833     {
3834       // default: elements slip down either to the left or right randomly
3835       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3836
3837       // SP style elements prefer to slip down on the left side
3838       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3839         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3840
3841       // BD style elements prefer to slip down on the left side
3842       if (game.emulation == EMU_BOULDERDASH)
3843         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3844     }
3845   }
3846
3847   // initialize explosion and ignition delay
3848   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3849   {
3850     if (!IS_CUSTOM_ELEMENT(i))
3851     {
3852       int num_phase = 8;
3853       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3854                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3855                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3856       int last_phase = (num_phase + 1) * delay;
3857       int half_phase = (num_phase / 2) * delay;
3858
3859       element_info[i].explosion_delay = last_phase - 1;
3860       element_info[i].ignition_delay = half_phase;
3861
3862       if (i == EL_BLACK_ORB)
3863         element_info[i].ignition_delay = 1;
3864     }
3865   }
3866
3867   // correct non-moving belts to start moving left
3868   for (i = 0; i < NUM_BELTS; i++)
3869     if (game.belt_dir[i] == MV_NONE)
3870       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3871
3872 #if USE_NEW_PLAYER_ASSIGNMENTS
3873   // use preferred player also in local single-player mode
3874   if (!network.enabled && !game.team_mode)
3875   {
3876     int new_index_nr = setup.network_player_nr;
3877
3878     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3879     {
3880       for (i = 0; i < MAX_PLAYERS; i++)
3881         stored_player[i].connected_locally = FALSE;
3882
3883       stored_player[new_index_nr].connected_locally = TRUE;
3884     }
3885   }
3886
3887   for (i = 0; i < MAX_PLAYERS; i++)
3888   {
3889     stored_player[i].connected = FALSE;
3890
3891     // in network game mode, the local player might not be the first player
3892     if (stored_player[i].connected_locally)
3893       local_player = &stored_player[i];
3894   }
3895
3896   if (!network.enabled)
3897     local_player->connected = TRUE;
3898
3899   if (tape.playing)
3900   {
3901     for (i = 0; i < MAX_PLAYERS; i++)
3902       stored_player[i].connected = tape.player_participates[i];
3903   }
3904   else if (network.enabled)
3905   {
3906     // add team mode players connected over the network (needed for correct
3907     // assignment of player figures from level to locally playing players)
3908
3909     for (i = 0; i < MAX_PLAYERS; i++)
3910       if (stored_player[i].connected_network)
3911         stored_player[i].connected = TRUE;
3912   }
3913   else if (game.team_mode)
3914   {
3915     // try to guess locally connected team mode players (needed for correct
3916     // assignment of player figures from level to locally playing players)
3917
3918     for (i = 0; i < MAX_PLAYERS; i++)
3919       if (setup.input[i].use_joystick ||
3920           setup.input[i].key.left != KSYM_UNDEFINED)
3921         stored_player[i].connected = TRUE;
3922   }
3923
3924 #if DEBUG_INIT_PLAYER
3925   DebugPrintPlayerStatus("Player status after level initialization");
3926 #endif
3927
3928 #if DEBUG_INIT_PLAYER
3929   if (options.debug)
3930     printf("Reassigning players ...\n");
3931 #endif
3932
3933   // check if any connected player was not found in playfield
3934   for (i = 0; i < MAX_PLAYERS; i++)
3935   {
3936     struct PlayerInfo *player = &stored_player[i];
3937
3938     if (player->connected && !player->present)
3939     {
3940       struct PlayerInfo *field_player = NULL;
3941
3942 #if DEBUG_INIT_PLAYER
3943       if (options.debug)
3944         printf("- looking for field player for player %d ...\n", i + 1);
3945 #endif
3946
3947       // assign first free player found that is present in the playfield
3948
3949       // first try: look for unmapped playfield player that is not connected
3950       for (j = 0; j < MAX_PLAYERS; j++)
3951         if (field_player == NULL &&
3952             stored_player[j].present &&
3953             !stored_player[j].mapped &&
3954             !stored_player[j].connected)
3955           field_player = &stored_player[j];
3956
3957       // second try: look for *any* unmapped playfield player
3958       for (j = 0; j < MAX_PLAYERS; j++)
3959         if (field_player == NULL &&
3960             stored_player[j].present &&
3961             !stored_player[j].mapped)
3962           field_player = &stored_player[j];
3963
3964       if (field_player != NULL)
3965       {
3966         int jx = field_player->jx, jy = field_player->jy;
3967
3968 #if DEBUG_INIT_PLAYER
3969         if (options.debug)
3970           printf("- found player %d\n", field_player->index_nr + 1);
3971 #endif
3972
3973         player->present = FALSE;
3974         player->active = FALSE;
3975
3976         field_player->present = TRUE;
3977         field_player->active = TRUE;
3978
3979         /*
3980         player->initial_element = field_player->initial_element;
3981         player->artwork_element = field_player->artwork_element;
3982
3983         player->block_last_field       = field_player->block_last_field;
3984         player->block_delay_adjustment = field_player->block_delay_adjustment;
3985         */
3986
3987         StorePlayer[jx][jy] = field_player->element_nr;
3988
3989         field_player->jx = field_player->last_jx = jx;
3990         field_player->jy = field_player->last_jy = jy;
3991
3992         if (local_player == player)
3993           local_player = field_player;
3994
3995         map_player_action[field_player->index_nr] = i;
3996
3997         field_player->mapped = TRUE;
3998
3999 #if DEBUG_INIT_PLAYER
4000         if (options.debug)
4001           printf("- map_player_action[%d] == %d\n",
4002                  field_player->index_nr + 1, i + 1);
4003 #endif
4004       }
4005     }
4006
4007     if (player->connected && player->present)
4008       player->mapped = TRUE;
4009   }
4010
4011 #if DEBUG_INIT_PLAYER
4012   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4013 #endif
4014
4015 #else
4016
4017   // check if any connected player was not found in playfield
4018   for (i = 0; i < MAX_PLAYERS; i++)
4019   {
4020     struct PlayerInfo *player = &stored_player[i];
4021
4022     if (player->connected && !player->present)
4023     {
4024       for (j = 0; j < MAX_PLAYERS; j++)
4025       {
4026         struct PlayerInfo *field_player = &stored_player[j];
4027         int jx = field_player->jx, jy = field_player->jy;
4028
4029         // assign first free player found that is present in the playfield
4030         if (field_player->present && !field_player->connected)
4031         {
4032           player->present = TRUE;
4033           player->active = TRUE;
4034
4035           field_player->present = FALSE;
4036           field_player->active = FALSE;
4037
4038           player->initial_element = field_player->initial_element;
4039           player->artwork_element = field_player->artwork_element;
4040
4041           player->block_last_field       = field_player->block_last_field;
4042           player->block_delay_adjustment = field_player->block_delay_adjustment;
4043
4044           StorePlayer[jx][jy] = player->element_nr;
4045
4046           player->jx = player->last_jx = jx;
4047           player->jy = player->last_jy = jy;
4048
4049           break;
4050         }
4051       }
4052     }
4053   }
4054 #endif
4055
4056 #if 0
4057   printf("::: local_player->present == %d\n", local_player->present);
4058 #endif
4059
4060   // set focus to local player for network games, else to all players
4061   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4062   game.centered_player_nr_next = game.centered_player_nr;
4063   game.set_centered_player = FALSE;
4064   game.set_centered_player_wrap = FALSE;
4065
4066   if (network_playing && tape.recording)
4067   {
4068     // store client dependent player focus when recording network games
4069     tape.centered_player_nr_next = game.centered_player_nr_next;
4070     tape.set_centered_player = TRUE;
4071   }
4072
4073   if (tape.playing)
4074   {
4075     // when playing a tape, eliminate all players who do not participate
4076
4077 #if USE_NEW_PLAYER_ASSIGNMENTS
4078
4079     if (!game.team_mode)
4080     {
4081       for (i = 0; i < MAX_PLAYERS; i++)
4082       {
4083         if (stored_player[i].active &&
4084             !tape.player_participates[map_player_action[i]])
4085         {
4086           struct PlayerInfo *player = &stored_player[i];
4087           int jx = player->jx, jy = player->jy;
4088
4089 #if DEBUG_INIT_PLAYER
4090           if (options.debug)
4091             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4092 #endif
4093
4094           player->active = FALSE;
4095           StorePlayer[jx][jy] = 0;
4096           Feld[jx][jy] = EL_EMPTY;
4097         }
4098       }
4099     }
4100
4101 #else
4102
4103     for (i = 0; i < MAX_PLAYERS; i++)
4104     {
4105       if (stored_player[i].active &&
4106           !tape.player_participates[i])
4107       {
4108         struct PlayerInfo *player = &stored_player[i];
4109         int jx = player->jx, jy = player->jy;
4110
4111         player->active = FALSE;
4112         StorePlayer[jx][jy] = 0;
4113         Feld[jx][jy] = EL_EMPTY;
4114       }
4115     }
4116 #endif
4117   }
4118   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4119   {
4120     // when in single player mode, eliminate all but the local player
4121
4122     for (i = 0; i < MAX_PLAYERS; i++)
4123     {
4124       struct PlayerInfo *player = &stored_player[i];
4125
4126       if (player->active && player != local_player)
4127       {
4128         int jx = player->jx, jy = player->jy;
4129
4130         player->active = FALSE;
4131         player->present = FALSE;
4132
4133         StorePlayer[jx][jy] = 0;
4134         Feld[jx][jy] = EL_EMPTY;
4135       }
4136     }
4137   }
4138
4139   for (i = 0; i < MAX_PLAYERS; i++)
4140     if (stored_player[i].active)
4141       game.players_still_needed++;
4142
4143   if (level.solved_by_one_player)
4144     game.players_still_needed = 1;
4145
4146   // when recording the game, store which players take part in the game
4147   if (tape.recording)
4148   {
4149 #if USE_NEW_PLAYER_ASSIGNMENTS
4150     for (i = 0; i < MAX_PLAYERS; i++)
4151       if (stored_player[i].connected)
4152         tape.player_participates[i] = TRUE;
4153 #else
4154     for (i = 0; i < MAX_PLAYERS; i++)
4155       if (stored_player[i].active)
4156         tape.player_participates[i] = TRUE;
4157 #endif
4158   }
4159
4160 #if DEBUG_INIT_PLAYER
4161   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4162 #endif
4163
4164   if (BorderElement == EL_EMPTY)
4165   {
4166     SBX_Left = 0;
4167     SBX_Right = lev_fieldx - SCR_FIELDX;
4168     SBY_Upper = 0;
4169     SBY_Lower = lev_fieldy - SCR_FIELDY;
4170   }
4171   else
4172   {
4173     SBX_Left = -1;
4174     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4175     SBY_Upper = -1;
4176     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4177   }
4178
4179   if (full_lev_fieldx <= SCR_FIELDX)
4180     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4181   if (full_lev_fieldy <= SCR_FIELDY)
4182     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4183
4184   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4185     SBX_Left--;
4186   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4187     SBY_Upper--;
4188
4189   // if local player not found, look for custom element that might create
4190   // the player (make some assumptions about the right custom element)
4191   if (!local_player->present)
4192   {
4193     int start_x = 0, start_y = 0;
4194     int found_rating = 0;
4195     int found_element = EL_UNDEFINED;
4196     int player_nr = local_player->index_nr;
4197
4198     SCAN_PLAYFIELD(x, y)
4199     {
4200       int element = Feld[x][y];
4201       int content;
4202       int xx, yy;
4203       boolean is_player;
4204
4205       if (level.use_start_element[player_nr] &&
4206           level.start_element[player_nr] == element &&
4207           found_rating < 4)
4208       {
4209         start_x = x;
4210         start_y = y;
4211
4212         found_rating = 4;
4213         found_element = element;
4214       }
4215
4216       if (!IS_CUSTOM_ELEMENT(element))
4217         continue;
4218
4219       if (CAN_CHANGE(element))
4220       {
4221         for (i = 0; i < element_info[element].num_change_pages; i++)
4222         {
4223           // check for player created from custom element as single target
4224           content = element_info[element].change_page[i].target_element;
4225           is_player = ELEM_IS_PLAYER(content);
4226
4227           if (is_player && (found_rating < 3 ||
4228                             (found_rating == 3 && element < found_element)))
4229           {
4230             start_x = x;
4231             start_y = y;
4232
4233             found_rating = 3;
4234             found_element = element;
4235           }
4236         }
4237       }
4238
4239       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4240       {
4241         // check for player created from custom element as explosion content
4242         content = element_info[element].content.e[xx][yy];
4243         is_player = ELEM_IS_PLAYER(content);
4244
4245         if (is_player && (found_rating < 2 ||
4246                           (found_rating == 2 && element < found_element)))
4247         {
4248           start_x = x + xx - 1;
4249           start_y = y + yy - 1;
4250
4251           found_rating = 2;
4252           found_element = element;
4253         }
4254
4255         if (!CAN_CHANGE(element))
4256           continue;
4257
4258         for (i = 0; i < element_info[element].num_change_pages; i++)
4259         {
4260           // check for player created from custom element as extended target
4261           content =
4262             element_info[element].change_page[i].target_content.e[xx][yy];
4263
4264           is_player = ELEM_IS_PLAYER(content);
4265
4266           if (is_player && (found_rating < 1 ||
4267                             (found_rating == 1 && element < found_element)))
4268           {
4269             start_x = x + xx - 1;
4270             start_y = y + yy - 1;
4271
4272             found_rating = 1;
4273             found_element = element;
4274           }
4275         }
4276       }
4277     }
4278
4279     scroll_x = SCROLL_POSITION_X(start_x);
4280     scroll_y = SCROLL_POSITION_Y(start_y);
4281   }
4282   else
4283   {
4284     scroll_x = SCROLL_POSITION_X(local_player->jx);
4285     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4286   }
4287
4288   // !!! FIX THIS (START) !!!
4289   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4290   {
4291     InitGameEngine_EM();
4292   }
4293   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4294   {
4295     InitGameEngine_SP();
4296   }
4297   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4298   {
4299     InitGameEngine_MM();
4300   }
4301   else
4302   {
4303     DrawLevel(REDRAW_FIELD);
4304     DrawAllPlayers();
4305
4306     // after drawing the level, correct some elements
4307     if (game.timegate_time_left == 0)
4308       CloseAllOpenTimegates();
4309   }
4310
4311   // blit playfield from scroll buffer to normal back buffer for fading in
4312   BlitScreenToBitmap(backbuffer);
4313   // !!! FIX THIS (END) !!!
4314
4315   DrawMaskedBorder(fade_mask);
4316
4317   FadeIn(fade_mask);
4318
4319 #if 1
4320   // full screen redraw is required at this point in the following cases:
4321   // - special editor door undrawn when game was started from level editor
4322   // - drawing area (playfield) was changed and has to be removed completely
4323   redraw_mask = REDRAW_ALL;
4324   BackToFront();
4325 #endif
4326
4327   if (!game.restart_level)
4328   {
4329     // copy default game door content to main double buffer
4330
4331     // !!! CHECK AGAIN !!!
4332     SetPanelBackground();
4333     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4334     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4335   }
4336
4337   SetPanelBackground();
4338   SetDrawBackgroundMask(REDRAW_DOOR_1);
4339
4340   UpdateAndDisplayGameControlValues();
4341
4342   if (!game.restart_level)
4343   {
4344     UnmapGameButtons();
4345     UnmapTapeButtons();
4346
4347     FreeGameButtons();
4348     CreateGameButtons();
4349
4350     MapGameButtons();
4351     MapTapeButtons();
4352
4353     // copy actual game door content to door double buffer for OpenDoor()
4354     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4355
4356     OpenDoor(DOOR_OPEN_ALL);
4357
4358     KeyboardAutoRepeatOffUnlessAutoplay();
4359
4360 #if DEBUG_INIT_PLAYER
4361     DebugPrintPlayerStatus("Player status (final)");
4362 #endif
4363   }
4364
4365   UnmapAllGadgets();
4366
4367   MapGameButtons();
4368   MapTapeButtons();
4369
4370   if (!game.restart_level && !tape.playing)
4371   {
4372     LevelStats_incPlayed(level_nr);
4373
4374     SaveLevelSetup_SeriesInfo();
4375   }
4376
4377   game.restart_level = FALSE;
4378   game.restart_game_message = NULL;
4379   game.request_active = FALSE;
4380
4381   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4382     InitGameActions_MM();
4383
4384   SaveEngineSnapshotToListInitial();
4385
4386   if (!game.restart_level)
4387   {
4388     PlaySound(SND_GAME_STARTING);
4389
4390     if (setup.sound_music)
4391       PlayLevelMusic();
4392   }
4393 }
4394
4395 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4396                         int actual_player_x, int actual_player_y)
4397 {
4398   // this is used for non-R'n'D game engines to update certain engine values
4399
4400   // needed to determine if sounds are played within the visible screen area
4401   scroll_x = actual_scroll_x;
4402   scroll_y = actual_scroll_y;
4403
4404   // needed to get player position for "follow finger" playing input method
4405   local_player->jx = actual_player_x;
4406   local_player->jy = actual_player_y;
4407 }
4408
4409 void InitMovDir(int x, int y)
4410 {
4411   int i, element = Feld[x][y];
4412   static int xy[4][2] =
4413   {
4414     {  0, +1 },
4415     { +1,  0 },
4416     {  0, -1 },
4417     { -1,  0 }
4418   };
4419   static int direction[3][4] =
4420   {
4421     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4422     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4423     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4424   };
4425
4426   switch (element)
4427   {
4428     case EL_BUG_RIGHT:
4429     case EL_BUG_UP:
4430     case EL_BUG_LEFT:
4431     case EL_BUG_DOWN:
4432       Feld[x][y] = EL_BUG;
4433       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4434       break;
4435
4436     case EL_SPACESHIP_RIGHT:
4437     case EL_SPACESHIP_UP:
4438     case EL_SPACESHIP_LEFT:
4439     case EL_SPACESHIP_DOWN:
4440       Feld[x][y] = EL_SPACESHIP;
4441       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4442       break;
4443
4444     case EL_BD_BUTTERFLY_RIGHT:
4445     case EL_BD_BUTTERFLY_UP:
4446     case EL_BD_BUTTERFLY_LEFT:
4447     case EL_BD_BUTTERFLY_DOWN:
4448       Feld[x][y] = EL_BD_BUTTERFLY;
4449       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4450       break;
4451
4452     case EL_BD_FIREFLY_RIGHT:
4453     case EL_BD_FIREFLY_UP:
4454     case EL_BD_FIREFLY_LEFT:
4455     case EL_BD_FIREFLY_DOWN:
4456       Feld[x][y] = EL_BD_FIREFLY;
4457       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4458       break;
4459
4460     case EL_PACMAN_RIGHT:
4461     case EL_PACMAN_UP:
4462     case EL_PACMAN_LEFT:
4463     case EL_PACMAN_DOWN:
4464       Feld[x][y] = EL_PACMAN;
4465       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4466       break;
4467
4468     case EL_YAMYAM_LEFT:
4469     case EL_YAMYAM_RIGHT:
4470     case EL_YAMYAM_UP:
4471     case EL_YAMYAM_DOWN:
4472       Feld[x][y] = EL_YAMYAM;
4473       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4474       break;
4475
4476     case EL_SP_SNIKSNAK:
4477       MovDir[x][y] = MV_UP;
4478       break;
4479
4480     case EL_SP_ELECTRON:
4481       MovDir[x][y] = MV_LEFT;
4482       break;
4483
4484     case EL_MOLE_LEFT:
4485     case EL_MOLE_RIGHT:
4486     case EL_MOLE_UP:
4487     case EL_MOLE_DOWN:
4488       Feld[x][y] = EL_MOLE;
4489       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4490       break;
4491
4492     default:
4493       if (IS_CUSTOM_ELEMENT(element))
4494       {
4495         struct ElementInfo *ei = &element_info[element];
4496         int move_direction_initial = ei->move_direction_initial;
4497         int move_pattern = ei->move_pattern;
4498
4499         if (move_direction_initial == MV_START_PREVIOUS)
4500         {
4501           if (MovDir[x][y] != MV_NONE)
4502             return;
4503
4504           move_direction_initial = MV_START_AUTOMATIC;
4505         }
4506
4507         if (move_direction_initial == MV_START_RANDOM)
4508           MovDir[x][y] = 1 << RND(4);
4509         else if (move_direction_initial & MV_ANY_DIRECTION)
4510           MovDir[x][y] = move_direction_initial;
4511         else if (move_pattern == MV_ALL_DIRECTIONS ||
4512                  move_pattern == MV_TURNING_LEFT ||
4513                  move_pattern == MV_TURNING_RIGHT ||
4514                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4515                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4516                  move_pattern == MV_TURNING_RANDOM)
4517           MovDir[x][y] = 1 << RND(4);
4518         else if (move_pattern == MV_HORIZONTAL)
4519           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4520         else if (move_pattern == MV_VERTICAL)
4521           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4522         else if (move_pattern & MV_ANY_DIRECTION)
4523           MovDir[x][y] = element_info[element].move_pattern;
4524         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4525                  move_pattern == MV_ALONG_RIGHT_SIDE)
4526         {
4527           // use random direction as default start direction
4528           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4529             MovDir[x][y] = 1 << RND(4);
4530
4531           for (i = 0; i < NUM_DIRECTIONS; i++)
4532           {
4533             int x1 = x + xy[i][0];
4534             int y1 = y + xy[i][1];
4535
4536             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4537             {
4538               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4539                 MovDir[x][y] = direction[0][i];
4540               else
4541                 MovDir[x][y] = direction[1][i];
4542
4543               break;
4544             }
4545           }
4546         }                
4547       }
4548       else
4549       {
4550         MovDir[x][y] = 1 << RND(4);
4551
4552         if (element != EL_BUG &&
4553             element != EL_SPACESHIP &&
4554             element != EL_BD_BUTTERFLY &&
4555             element != EL_BD_FIREFLY)
4556           break;
4557
4558         for (i = 0; i < NUM_DIRECTIONS; i++)
4559         {
4560           int x1 = x + xy[i][0];
4561           int y1 = y + xy[i][1];
4562
4563           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4564           {
4565             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4566             {
4567               MovDir[x][y] = direction[0][i];
4568               break;
4569             }
4570             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4571                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4572             {
4573               MovDir[x][y] = direction[1][i];
4574               break;
4575             }
4576           }
4577         }
4578       }
4579       break;
4580   }
4581
4582   GfxDir[x][y] = MovDir[x][y];
4583 }
4584
4585 void InitAmoebaNr(int x, int y)
4586 {
4587   int i;
4588   int group_nr = AmoebeNachbarNr(x, y);
4589
4590   if (group_nr == 0)
4591   {
4592     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4593     {
4594       if (AmoebaCnt[i] == 0)
4595       {
4596         group_nr = i;
4597         break;
4598       }
4599     }
4600   }
4601
4602   AmoebaNr[x][y] = group_nr;
4603   AmoebaCnt[group_nr]++;
4604   AmoebaCnt2[group_nr]++;
4605 }
4606
4607 static void LevelSolved(void)
4608 {
4609   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4610       game.players_still_needed > 0)
4611     return;
4612
4613   game.LevelSolved = TRUE;
4614   game.GameOver = TRUE;
4615
4616   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4617                       game_em.lev->score :
4618                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4619                       game_mm.score :
4620                       game.score);
4621   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4622                        MM_HEALTH(game_mm.laser_overload_value) :
4623                        game.health);
4624
4625   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4626   game.LevelSolved_CountingScore = game.score_final;
4627   game.LevelSolved_CountingHealth = game.health_final;
4628 }
4629
4630 void GameWon(void)
4631 {
4632   static int time_count_steps;
4633   static int time, time_final;
4634   static int score, score_final;
4635   static int health, health_final;
4636   static int game_over_delay_1 = 0;
4637   static int game_over_delay_2 = 0;
4638   static int game_over_delay_3 = 0;
4639   int game_over_delay_value_1 = 50;
4640   int game_over_delay_value_2 = 25;
4641   int game_over_delay_value_3 = 50;
4642
4643   if (!game.LevelSolved_GameWon)
4644   {
4645     int i;
4646
4647     // do not start end game actions before the player stops moving (to exit)
4648     if (local_player->active && local_player->MovPos)
4649       return;
4650
4651     game.LevelSolved_GameWon = TRUE;
4652     game.LevelSolved_SaveTape = tape.recording;
4653     game.LevelSolved_SaveScore = !tape.playing;
4654
4655     if (!tape.playing)
4656     {
4657       LevelStats_incSolved(level_nr);
4658
4659       SaveLevelSetup_SeriesInfo();
4660     }
4661
4662     if (tape.auto_play)         // tape might already be stopped here
4663       tape.auto_play_level_solved = TRUE;
4664
4665     TapeStop();
4666
4667     game_over_delay_1 = 0;
4668     game_over_delay_2 = 0;
4669     game_over_delay_3 = game_over_delay_value_3;
4670
4671     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4672     score = score_final = game.score_final;
4673     health = health_final = game.health_final;
4674
4675     if (level.score[SC_TIME_BONUS] > 0)
4676     {
4677       if (TimeLeft > 0)
4678       {
4679         time_final = 0;
4680         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4681       }
4682       else if (game.no_time_limit && TimePlayed < 999)
4683       {
4684         time_final = 999;
4685         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4686       }
4687
4688       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4689
4690       game_over_delay_1 = game_over_delay_value_1;
4691
4692       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4693       {
4694         health_final = 0;
4695         score_final += health * level.score[SC_TIME_BONUS];
4696
4697         game_over_delay_2 = game_over_delay_value_2;
4698       }
4699
4700       game.score_final = score_final;
4701       game.health_final = health_final;
4702     }
4703
4704     if (level_editor_test_game)
4705     {
4706       time = time_final;
4707       score = score_final;
4708
4709       game.LevelSolved_CountingTime = time;
4710       game.LevelSolved_CountingScore = score;
4711
4712       game_panel_controls[GAME_PANEL_TIME].value = time;
4713       game_panel_controls[GAME_PANEL_SCORE].value = score;
4714
4715       DisplayGameControlValues();
4716     }
4717
4718     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4719     {
4720       // check if last player has left the level
4721       if (game.exit_x >= 0 &&
4722           game.exit_y >= 0)
4723       {
4724         int x = game.exit_x;
4725         int y = game.exit_y;
4726         int element = Feld[x][y];
4727
4728         // close exit door after last player
4729         if ((game.all_players_gone &&
4730              (element == EL_EXIT_OPEN ||
4731               element == EL_SP_EXIT_OPEN ||
4732               element == EL_STEEL_EXIT_OPEN)) ||
4733             element == EL_EM_EXIT_OPEN ||
4734             element == EL_EM_STEEL_EXIT_OPEN)
4735         {
4736
4737           Feld[x][y] =
4738             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4739              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4740              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4741              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4742              EL_EM_STEEL_EXIT_CLOSING);
4743
4744           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4745         }
4746
4747         // player disappears
4748         DrawLevelField(x, y);
4749       }
4750
4751       for (i = 0; i < MAX_PLAYERS; i++)
4752       {
4753         struct PlayerInfo *player = &stored_player[i];
4754
4755         if (player->present)
4756         {
4757           RemovePlayer(player);
4758
4759           // player disappears
4760           DrawLevelField(player->jx, player->jy);
4761         }
4762       }
4763     }
4764
4765     PlaySound(SND_GAME_WINNING);
4766   }
4767
4768   if (game_over_delay_1 > 0)
4769   {
4770     game_over_delay_1--;
4771
4772     return;
4773   }
4774
4775   if (time != time_final)
4776   {
4777     int time_to_go = ABS(time_final - time);
4778     int time_count_dir = (time < time_final ? +1 : -1);
4779
4780     if (time_to_go < time_count_steps)
4781       time_count_steps = 1;
4782
4783     time  += time_count_steps * time_count_dir;
4784     score += time_count_steps * level.score[SC_TIME_BONUS];
4785
4786     game.LevelSolved_CountingTime = time;
4787     game.LevelSolved_CountingScore = score;
4788
4789     game_panel_controls[GAME_PANEL_TIME].value = time;
4790     game_panel_controls[GAME_PANEL_SCORE].value = score;
4791
4792     DisplayGameControlValues();
4793
4794     if (time == time_final)
4795       StopSound(SND_GAME_LEVELTIME_BONUS);
4796     else if (setup.sound_loops)
4797       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4798     else
4799       PlaySound(SND_GAME_LEVELTIME_BONUS);
4800
4801     return;
4802   }
4803
4804   if (game_over_delay_2 > 0)
4805   {
4806     game_over_delay_2--;
4807
4808     return;
4809   }
4810
4811   if (health != health_final)
4812   {
4813     int health_count_dir = (health < health_final ? +1 : -1);
4814
4815     health += health_count_dir;
4816     score  += level.score[SC_TIME_BONUS];
4817
4818     game.LevelSolved_CountingHealth = health;
4819     game.LevelSolved_CountingScore = score;
4820
4821     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4822     game_panel_controls[GAME_PANEL_SCORE].value = score;
4823
4824     DisplayGameControlValues();
4825
4826     if (health == health_final)
4827       StopSound(SND_GAME_LEVELTIME_BONUS);
4828     else if (setup.sound_loops)
4829       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4830     else
4831       PlaySound(SND_GAME_LEVELTIME_BONUS);
4832
4833     return;
4834   }
4835
4836   game.panel.active = FALSE;
4837
4838   if (game_over_delay_3 > 0)
4839   {
4840     game_over_delay_3--;
4841
4842     return;
4843   }
4844
4845   GameEnd();
4846 }
4847
4848 void GameEnd(void)
4849 {
4850   // used instead of "level_nr" (needed for network games)
4851   int last_level_nr = levelset.level_nr;
4852   int hi_pos;
4853
4854   game.LevelSolved_GameEnd = TRUE;
4855
4856   if (game.LevelSolved_SaveTape)
4857   {
4858     // make sure that request dialog to save tape does not open door again
4859     if (!global.use_envelope_request)
4860       CloseDoor(DOOR_CLOSE_1);
4861
4862     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4863   }
4864
4865   // if no tape is to be saved, close both doors simultaneously
4866   CloseDoor(DOOR_CLOSE_ALL);
4867
4868   if (level_editor_test_game)
4869   {
4870     SetGameStatus(GAME_MODE_MAIN);
4871
4872     DrawMainMenu();
4873
4874     return;
4875   }
4876
4877   if (!game.LevelSolved_SaveScore)
4878   {
4879     SetGameStatus(GAME_MODE_MAIN);
4880
4881     DrawMainMenu();
4882
4883     return;
4884   }
4885
4886   if (level_nr == leveldir_current->handicap_level)
4887   {
4888     leveldir_current->handicap_level++;
4889
4890     SaveLevelSetup_SeriesInfo();
4891   }
4892
4893   if (setup.increment_levels &&
4894       level_nr < leveldir_current->last_level &&
4895       !network_playing)
4896   {
4897     level_nr++;         // advance to next level
4898     TapeErase();        // start with empty tape
4899
4900     if (setup.auto_play_next_level)
4901     {
4902       LoadLevel(level_nr);
4903
4904       SaveLevelSetup_SeriesInfo();
4905     }
4906   }
4907
4908   hi_pos = NewHiScore(last_level_nr);
4909
4910   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4911   {
4912     SetGameStatus(GAME_MODE_SCORES);
4913
4914     DrawHallOfFame(last_level_nr, hi_pos);
4915   }
4916   else if (setup.auto_play_next_level && setup.increment_levels &&
4917            last_level_nr < leveldir_current->last_level &&
4918            !network_playing)
4919   {
4920     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4921   }
4922   else
4923   {
4924     SetGameStatus(GAME_MODE_MAIN);
4925
4926     DrawMainMenu();
4927   }
4928 }
4929
4930 int NewHiScore(int level_nr)
4931 {
4932   int k, l;
4933   int position = -1;
4934   boolean one_score_entry_per_name = !program.many_scores_per_name;
4935
4936   LoadScore(level_nr);
4937
4938   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4939       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4940     return -1;
4941
4942   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4943   {
4944     if (game.score_final > highscore[k].Score)
4945     {
4946       // player has made it to the hall of fame
4947
4948       if (k < MAX_SCORE_ENTRIES - 1)
4949       {
4950         int m = MAX_SCORE_ENTRIES - 1;
4951
4952         if (one_score_entry_per_name)
4953         {
4954           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4955             if (strEqual(setup.player_name, highscore[l].Name))
4956               m = l;
4957
4958           if (m == k)   // player's new highscore overwrites his old one
4959             goto put_into_list;
4960         }
4961
4962         for (l = m; l > k; l--)
4963         {
4964           strcpy(highscore[l].Name, highscore[l - 1].Name);
4965           highscore[l].Score = highscore[l - 1].Score;
4966         }
4967       }
4968
4969       put_into_list:
4970
4971       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4972       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4973       highscore[k].Score = game.score_final;
4974       position = k;
4975
4976       break;
4977     }
4978     else if (one_score_entry_per_name &&
4979              !strncmp(setup.player_name, highscore[k].Name,
4980                       MAX_PLAYER_NAME_LEN))
4981       break;    // player already there with a higher score
4982   }
4983
4984   if (position >= 0) 
4985     SaveScore(level_nr);
4986
4987   return position;
4988 }
4989
4990 static int getElementMoveStepsizeExt(int x, int y, int direction)
4991 {
4992   int element = Feld[x][y];
4993   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4994   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4995   int horiz_move = (dx != 0);
4996   int sign = (horiz_move ? dx : dy);
4997   int step = sign * element_info[element].move_stepsize;
4998
4999   // special values for move stepsize for spring and things on conveyor belt
5000   if (horiz_move)
5001   {
5002     if (CAN_FALL(element) &&
5003         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5004       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5005     else if (element == EL_SPRING)
5006       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5007   }
5008
5009   return step;
5010 }
5011
5012 static int getElementMoveStepsize(int x, int y)
5013 {
5014   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5015 }
5016
5017 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5018 {
5019   if (player->GfxAction != action || player->GfxDir != dir)
5020   {
5021     player->GfxAction = action;
5022     player->GfxDir = dir;
5023     player->Frame = 0;
5024     player->StepFrame = 0;
5025   }
5026 }
5027
5028 static void ResetGfxFrame(int x, int y)
5029 {
5030   // profiling showed that "autotest" spends 10~20% of its time in this function
5031   if (DrawingDeactivatedField())
5032     return;
5033
5034   int element = Feld[x][y];
5035   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5036
5037   if (graphic_info[graphic].anim_global_sync)
5038     GfxFrame[x][y] = FrameCounter;
5039   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5040     GfxFrame[x][y] = CustomValue[x][y];
5041   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5042     GfxFrame[x][y] = element_info[element].collect_score;
5043   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5044     GfxFrame[x][y] = ChangeDelay[x][y];
5045 }
5046
5047 static void ResetGfxAnimation(int x, int y)
5048 {
5049   GfxAction[x][y] = ACTION_DEFAULT;
5050   GfxDir[x][y] = MovDir[x][y];
5051   GfxFrame[x][y] = 0;
5052
5053   ResetGfxFrame(x, y);
5054 }
5055
5056 static void ResetRandomAnimationValue(int x, int y)
5057 {
5058   GfxRandom[x][y] = INIT_GFX_RANDOM();
5059 }
5060
5061 static void InitMovingField(int x, int y, int direction)
5062 {
5063   int element = Feld[x][y];
5064   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5065   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5066   int newx = x + dx;
5067   int newy = y + dy;
5068   boolean is_moving_before, is_moving_after;
5069
5070   // check if element was/is moving or being moved before/after mode change
5071   is_moving_before = (WasJustMoving[x][y] != 0);
5072   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5073
5074   // reset animation only for moving elements which change direction of moving
5075   // or which just started or stopped moving
5076   // (else CEs with property "can move" / "not moving" are reset each frame)
5077   if (is_moving_before != is_moving_after ||
5078       direction != MovDir[x][y])
5079     ResetGfxAnimation(x, y);
5080
5081   MovDir[x][y] = direction;
5082   GfxDir[x][y] = direction;
5083
5084   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5085                      direction == MV_DOWN && CAN_FALL(element) ?
5086                      ACTION_FALLING : ACTION_MOVING);
5087
5088   // this is needed for CEs with property "can move" / "not moving"
5089
5090   if (is_moving_after)
5091   {
5092     if (Feld[newx][newy] == EL_EMPTY)
5093       Feld[newx][newy] = EL_BLOCKED;
5094
5095     MovDir[newx][newy] = MovDir[x][y];
5096
5097     CustomValue[newx][newy] = CustomValue[x][y];
5098
5099     GfxFrame[newx][newy] = GfxFrame[x][y];
5100     GfxRandom[newx][newy] = GfxRandom[x][y];
5101     GfxAction[newx][newy] = GfxAction[x][y];
5102     GfxDir[newx][newy] = GfxDir[x][y];
5103   }
5104 }
5105
5106 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5107 {
5108   int direction = MovDir[x][y];
5109   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5110   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5111
5112   *goes_to_x = newx;
5113   *goes_to_y = newy;
5114 }
5115
5116 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5117 {
5118   int oldx = x, oldy = y;
5119   int direction = MovDir[x][y];
5120
5121   if (direction == MV_LEFT)
5122     oldx++;
5123   else if (direction == MV_RIGHT)
5124     oldx--;
5125   else if (direction == MV_UP)
5126     oldy++;
5127   else if (direction == MV_DOWN)
5128     oldy--;
5129
5130   *comes_from_x = oldx;
5131   *comes_from_y = oldy;
5132 }
5133
5134 static int MovingOrBlocked2Element(int x, int y)
5135 {
5136   int element = Feld[x][y];
5137
5138   if (element == EL_BLOCKED)
5139   {
5140     int oldx, oldy;
5141
5142     Blocked2Moving(x, y, &oldx, &oldy);
5143     return Feld[oldx][oldy];
5144   }
5145   else
5146     return element;
5147 }
5148
5149 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5150 {
5151   // like MovingOrBlocked2Element(), but if element is moving
5152   // and (x,y) is the field the moving element is just leaving,
5153   // return EL_BLOCKED instead of the element value
5154   int element = Feld[x][y];
5155
5156   if (IS_MOVING(x, y))
5157   {
5158     if (element == EL_BLOCKED)
5159     {
5160       int oldx, oldy;
5161
5162       Blocked2Moving(x, y, &oldx, &oldy);
5163       return Feld[oldx][oldy];
5164     }
5165     else
5166       return EL_BLOCKED;
5167   }
5168   else
5169     return element;
5170 }
5171
5172 static void RemoveField(int x, int y)
5173 {
5174   Feld[x][y] = EL_EMPTY;
5175
5176   MovPos[x][y] = 0;
5177   MovDir[x][y] = 0;
5178   MovDelay[x][y] = 0;
5179
5180   CustomValue[x][y] = 0;
5181
5182   AmoebaNr[x][y] = 0;
5183   ChangeDelay[x][y] = 0;
5184   ChangePage[x][y] = -1;
5185   Pushed[x][y] = FALSE;
5186
5187   GfxElement[x][y] = EL_UNDEFINED;
5188   GfxAction[x][y] = ACTION_DEFAULT;
5189   GfxDir[x][y] = MV_NONE;
5190 }
5191
5192 static void RemoveMovingField(int x, int y)
5193 {
5194   int oldx = x, oldy = y, newx = x, newy = y;
5195   int element = Feld[x][y];
5196   int next_element = EL_UNDEFINED;
5197
5198   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5199     return;
5200
5201   if (IS_MOVING(x, y))
5202   {
5203     Moving2Blocked(x, y, &newx, &newy);
5204
5205     if (Feld[newx][newy] != EL_BLOCKED)
5206     {
5207       // element is moving, but target field is not free (blocked), but
5208       // already occupied by something different (example: acid pool);
5209       // in this case, only remove the moving field, but not the target
5210
5211       RemoveField(oldx, oldy);
5212
5213       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5214
5215       TEST_DrawLevelField(oldx, oldy);
5216
5217       return;
5218     }
5219   }
5220   else if (element == EL_BLOCKED)
5221   {
5222     Blocked2Moving(x, y, &oldx, &oldy);
5223     if (!IS_MOVING(oldx, oldy))
5224       return;
5225   }
5226
5227   if (element == EL_BLOCKED &&
5228       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5229        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5230        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5231        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5232        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5233        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5234     next_element = get_next_element(Feld[oldx][oldy]);
5235
5236   RemoveField(oldx, oldy);
5237   RemoveField(newx, newy);
5238
5239   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5240
5241   if (next_element != EL_UNDEFINED)
5242     Feld[oldx][oldy] = next_element;
5243
5244   TEST_DrawLevelField(oldx, oldy);
5245   TEST_DrawLevelField(newx, newy);
5246 }
5247
5248 void DrawDynamite(int x, int y)
5249 {
5250   int sx = SCREENX(x), sy = SCREENY(y);
5251   int graphic = el2img(Feld[x][y]);
5252   int frame;
5253
5254   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5255     return;
5256
5257   if (IS_WALKABLE_INSIDE(Back[x][y]))
5258     return;
5259
5260   if (Back[x][y])
5261     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5262   else if (Store[x][y])
5263     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5264
5265   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5266
5267   if (Back[x][y] || Store[x][y])
5268     DrawGraphicThruMask(sx, sy, graphic, frame);
5269   else
5270     DrawGraphic(sx, sy, graphic, frame);
5271 }
5272
5273 static void CheckDynamite(int x, int y)
5274 {
5275   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5276   {
5277     MovDelay[x][y]--;
5278
5279     if (MovDelay[x][y] != 0)
5280     {
5281       DrawDynamite(x, y);
5282       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5283
5284       return;
5285     }
5286   }
5287
5288   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5289
5290   Bang(x, y);
5291 }
5292
5293 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5294 {
5295   boolean num_checked_players = 0;
5296   int i;
5297
5298   for (i = 0; i < MAX_PLAYERS; i++)
5299   {
5300     if (stored_player[i].active)
5301     {
5302       int sx = stored_player[i].jx;
5303       int sy = stored_player[i].jy;
5304
5305       if (num_checked_players == 0)
5306       {
5307         *sx1 = *sx2 = sx;
5308         *sy1 = *sy2 = sy;
5309       }
5310       else
5311       {
5312         *sx1 = MIN(*sx1, sx);
5313         *sy1 = MIN(*sy1, sy);
5314         *sx2 = MAX(*sx2, sx);
5315         *sy2 = MAX(*sy2, sy);
5316       }
5317
5318       num_checked_players++;
5319     }
5320   }
5321 }
5322
5323 static boolean checkIfAllPlayersFitToScreen_RND(void)
5324 {
5325   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5326
5327   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5328
5329   return (sx2 - sx1 < SCR_FIELDX &&
5330           sy2 - sy1 < SCR_FIELDY);
5331 }
5332
5333 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5334 {
5335   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5336
5337   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5338
5339   *sx = (sx1 + sx2) / 2;
5340   *sy = (sy1 + sy2) / 2;
5341 }
5342
5343 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5344                                boolean center_screen, boolean quick_relocation)
5345 {
5346   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5347   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5348   boolean no_delay = (tape.warp_forward);
5349   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5350   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5351   int new_scroll_x, new_scroll_y;
5352
5353   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5354   {
5355     // case 1: quick relocation inside visible screen (without scrolling)
5356
5357     RedrawPlayfield();
5358
5359     return;
5360   }
5361
5362   if (!level.shifted_relocation || center_screen)
5363   {
5364     // relocation _with_ centering of screen
5365
5366     new_scroll_x = SCROLL_POSITION_X(x);
5367     new_scroll_y = SCROLL_POSITION_Y(y);
5368   }
5369   else
5370   {
5371     // relocation _without_ centering of screen
5372
5373     int center_scroll_x = SCROLL_POSITION_X(old_x);
5374     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5375     int offset_x = x + (scroll_x - center_scroll_x);
5376     int offset_y = y + (scroll_y - center_scroll_y);
5377
5378     // for new screen position, apply previous offset to center position
5379     new_scroll_x = SCROLL_POSITION_X(offset_x);
5380     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5381   }
5382
5383   if (quick_relocation)
5384   {
5385     // case 2: quick relocation (redraw without visible scrolling)
5386
5387     scroll_x = new_scroll_x;
5388     scroll_y = new_scroll_y;
5389
5390     RedrawPlayfield();
5391
5392     return;
5393   }
5394
5395   // case 3: visible relocation (with scrolling to new position)
5396
5397   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5398
5399   SetVideoFrameDelay(wait_delay_value);
5400
5401   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5402   {
5403     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5404     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5405
5406     if (dx == 0 && dy == 0)             // no scrolling needed at all
5407       break;
5408
5409     scroll_x -= dx;
5410     scroll_y -= dy;
5411
5412     // set values for horizontal/vertical screen scrolling (half tile size)
5413     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5414     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5415     int pos_x = dx * TILEX / 2;
5416     int pos_y = dy * TILEY / 2;
5417     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5418     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5419
5420     ScrollLevel(dx, dy);
5421     DrawAllPlayers();
5422
5423     // scroll in two steps of half tile size to make things smoother
5424     BlitScreenToBitmapExt_RND(window, fx, fy);
5425
5426     // scroll second step to align at full tile size
5427     BlitScreenToBitmap(window);
5428   }
5429
5430   DrawAllPlayers();
5431   BackToFront();
5432
5433   SetVideoFrameDelay(frame_delay_value_old);
5434 }
5435
5436 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5437 {
5438   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5439   int player_nr = GET_PLAYER_NR(el_player);
5440   struct PlayerInfo *player = &stored_player[player_nr];
5441   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5442   boolean no_delay = (tape.warp_forward);
5443   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5444   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5445   int old_jx = player->jx;
5446   int old_jy = player->jy;
5447   int old_element = Feld[old_jx][old_jy];
5448   int element = Feld[jx][jy];
5449   boolean player_relocated = (old_jx != jx || old_jy != jy);
5450
5451   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5452   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5453   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5454   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5455   int leave_side_horiz = move_dir_horiz;
5456   int leave_side_vert  = move_dir_vert;
5457   int enter_side = enter_side_horiz | enter_side_vert;
5458   int leave_side = leave_side_horiz | leave_side_vert;
5459
5460   if (player->buried)           // do not reanimate dead player
5461     return;
5462
5463   if (!player_relocated)        // no need to relocate the player
5464     return;
5465
5466   if (IS_PLAYER(jx, jy))        // player already placed at new position
5467   {
5468     RemoveField(jx, jy);        // temporarily remove newly placed player
5469     DrawLevelField(jx, jy);
5470   }
5471
5472   if (player->present)
5473   {
5474     while (player->MovPos)
5475     {
5476       ScrollPlayer(player, SCROLL_GO_ON);
5477       ScrollScreen(NULL, SCROLL_GO_ON);
5478
5479       AdvanceFrameAndPlayerCounters(player->index_nr);
5480
5481       DrawPlayer(player);
5482
5483       BackToFront_WithFrameDelay(wait_delay_value);
5484     }
5485
5486     DrawPlayer(player);         // needed here only to cleanup last field
5487     DrawLevelField(player->jx, player->jy);     // remove player graphic
5488
5489     player->is_moving = FALSE;
5490   }
5491
5492   if (IS_CUSTOM_ELEMENT(old_element))
5493     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5494                                CE_LEFT_BY_PLAYER,
5495                                player->index_bit, leave_side);
5496
5497   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5498                                       CE_PLAYER_LEAVES_X,
5499                                       player->index_bit, leave_side);
5500
5501   Feld[jx][jy] = el_player;
5502   InitPlayerField(jx, jy, el_player, TRUE);
5503
5504   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5505      possible that the relocation target field did not contain a player element,
5506      but a walkable element, to which the new player was relocated -- in this
5507      case, restore that (already initialized!) element on the player field */
5508   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5509   {
5510     Feld[jx][jy] = element;     // restore previously existing element
5511   }
5512
5513   // only visually relocate centered player
5514   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5515                      FALSE, level.instant_relocation);
5516
5517   TestIfPlayerTouchesBadThing(jx, jy);
5518   TestIfPlayerTouchesCustomElement(jx, jy);
5519
5520   if (IS_CUSTOM_ELEMENT(element))
5521     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5522                                player->index_bit, enter_side);
5523
5524   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5525                                       player->index_bit, enter_side);
5526
5527   if (player->is_switching)
5528   {
5529     /* ensure that relocation while still switching an element does not cause
5530        a new element to be treated as also switched directly after relocation
5531        (this is important for teleporter switches that teleport the player to
5532        a place where another teleporter switch is in the same direction, which
5533        would then incorrectly be treated as immediately switched before the
5534        direction key that caused the switch was released) */
5535
5536     player->switch_x += jx - old_jx;
5537     player->switch_y += jy - old_jy;
5538   }
5539 }
5540
5541 static void Explode(int ex, int ey, int phase, int mode)
5542 {
5543   int x, y;
5544   int last_phase;
5545   int border_element;
5546
5547   // !!! eliminate this variable !!!
5548   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5549
5550   if (game.explosions_delayed)
5551   {
5552     ExplodeField[ex][ey] = mode;
5553     return;
5554   }
5555
5556   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5557   {
5558     int center_element = Feld[ex][ey];
5559     int artwork_element, explosion_element;     // set these values later
5560
5561     // remove things displayed in background while burning dynamite
5562     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5563       Back[ex][ey] = 0;
5564
5565     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5566     {
5567       // put moving element to center field (and let it explode there)
5568       center_element = MovingOrBlocked2Element(ex, ey);
5569       RemoveMovingField(ex, ey);
5570       Feld[ex][ey] = center_element;
5571     }
5572
5573     // now "center_element" is finally determined -- set related values now
5574     artwork_element = center_element;           // for custom player artwork
5575     explosion_element = center_element;         // for custom player artwork
5576
5577     if (IS_PLAYER(ex, ey))
5578     {
5579       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5580
5581       artwork_element = stored_player[player_nr].artwork_element;
5582
5583       if (level.use_explosion_element[player_nr])
5584       {
5585         explosion_element = level.explosion_element[player_nr];
5586         artwork_element = explosion_element;
5587       }
5588     }
5589
5590     if (mode == EX_TYPE_NORMAL ||
5591         mode == EX_TYPE_CENTER ||
5592         mode == EX_TYPE_CROSS)
5593       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5594
5595     last_phase = element_info[explosion_element].explosion_delay + 1;
5596
5597     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5598     {
5599       int xx = x - ex + 1;
5600       int yy = y - ey + 1;
5601       int element;
5602
5603       if (!IN_LEV_FIELD(x, y) ||
5604           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5605           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5606         continue;
5607
5608       element = Feld[x][y];
5609
5610       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5611       {
5612         element = MovingOrBlocked2Element(x, y);
5613
5614         if (!IS_EXPLOSION_PROOF(element))
5615           RemoveMovingField(x, y);
5616       }
5617
5618       // indestructible elements can only explode in center (but not flames)
5619       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5620                                            mode == EX_TYPE_BORDER)) ||
5621           element == EL_FLAMES)
5622         continue;
5623
5624       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5625          behaviour, for example when touching a yamyam that explodes to rocks
5626          with active deadly shield, a rock is created under the player !!! */
5627       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5628 #if 0
5629       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5630           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5631            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5632 #else
5633       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5634 #endif
5635       {
5636         if (IS_ACTIVE_BOMB(element))
5637         {
5638           // re-activate things under the bomb like gate or penguin
5639           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5640           Back[x][y] = 0;
5641         }
5642
5643         continue;
5644       }
5645
5646       // save walkable background elements while explosion on same tile
5647       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5648           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5649         Back[x][y] = element;
5650
5651       // ignite explodable elements reached by other explosion
5652       if (element == EL_EXPLOSION)
5653         element = Store2[x][y];
5654
5655       if (AmoebaNr[x][y] &&
5656           (element == EL_AMOEBA_FULL ||
5657            element == EL_BD_AMOEBA ||
5658            element == EL_AMOEBA_GROWING))
5659       {
5660         AmoebaCnt[AmoebaNr[x][y]]--;
5661         AmoebaCnt2[AmoebaNr[x][y]]--;
5662       }
5663
5664       RemoveField(x, y);
5665
5666       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5667       {
5668         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5669
5670         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5671
5672         if (PLAYERINFO(ex, ey)->use_murphy)
5673           Store[x][y] = EL_EMPTY;
5674       }
5675
5676       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5677       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5678       else if (ELEM_IS_PLAYER(center_element))
5679         Store[x][y] = EL_EMPTY;
5680       else if (center_element == EL_YAMYAM)
5681         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5682       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5683         Store[x][y] = element_info[center_element].content.e[xx][yy];
5684 #if 1
5685       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5686       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5687       // otherwise) -- FIX THIS !!!
5688       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5689         Store[x][y] = element_info[element].content.e[1][1];
5690 #else
5691       else if (!CAN_EXPLODE(element))
5692         Store[x][y] = element_info[element].content.e[1][1];
5693 #endif
5694       else
5695         Store[x][y] = EL_EMPTY;
5696
5697       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5698           center_element == EL_AMOEBA_TO_DIAMOND)
5699         Store2[x][y] = element;
5700
5701       Feld[x][y] = EL_EXPLOSION;
5702       GfxElement[x][y] = artwork_element;
5703
5704       ExplodePhase[x][y] = 1;
5705       ExplodeDelay[x][y] = last_phase;
5706
5707       Stop[x][y] = TRUE;
5708     }
5709
5710     if (center_element == EL_YAMYAM)
5711       game.yamyam_content_nr =
5712         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5713
5714     return;
5715   }
5716
5717   if (Stop[ex][ey])
5718     return;
5719
5720   x = ex;
5721   y = ey;
5722
5723   if (phase == 1)
5724     GfxFrame[x][y] = 0;         // restart explosion animation
5725
5726   last_phase = ExplodeDelay[x][y];
5727
5728   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5729
5730   // this can happen if the player leaves an explosion just in time
5731   if (GfxElement[x][y] == EL_UNDEFINED)
5732     GfxElement[x][y] = EL_EMPTY;
5733
5734   border_element = Store2[x][y];
5735   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5736     border_element = StorePlayer[x][y];
5737
5738   if (phase == element_info[border_element].ignition_delay ||
5739       phase == last_phase)
5740   {
5741     boolean border_explosion = FALSE;
5742
5743     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5744         !PLAYER_EXPLOSION_PROTECTED(x, y))
5745     {
5746       KillPlayerUnlessExplosionProtected(x, y);
5747       border_explosion = TRUE;
5748     }
5749     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5750     {
5751       Feld[x][y] = Store2[x][y];
5752       Store2[x][y] = 0;
5753       Bang(x, y);
5754       border_explosion = TRUE;
5755     }
5756     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5757     {
5758       AmoebeUmwandeln(x, y);
5759       Store2[x][y] = 0;
5760       border_explosion = TRUE;
5761     }
5762
5763     // if an element just explodes due to another explosion (chain-reaction),
5764     // do not immediately end the new explosion when it was the last frame of
5765     // the explosion (as it would be done in the following "if"-statement!)
5766     if (border_explosion && phase == last_phase)
5767       return;
5768   }
5769
5770   if (phase == last_phase)
5771   {
5772     int element;
5773
5774     element = Feld[x][y] = Store[x][y];
5775     Store[x][y] = Store2[x][y] = 0;
5776     GfxElement[x][y] = EL_UNDEFINED;
5777
5778     // player can escape from explosions and might therefore be still alive
5779     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5780         element <= EL_PLAYER_IS_EXPLODING_4)
5781     {
5782       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5783       int explosion_element = EL_PLAYER_1 + player_nr;
5784       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5785       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5786
5787       if (level.use_explosion_element[player_nr])
5788         explosion_element = level.explosion_element[player_nr];
5789
5790       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5791                     element_info[explosion_element].content.e[xx][yy]);
5792     }
5793
5794     // restore probably existing indestructible background element
5795     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5796       element = Feld[x][y] = Back[x][y];
5797     Back[x][y] = 0;
5798
5799     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5800     GfxDir[x][y] = MV_NONE;
5801     ChangeDelay[x][y] = 0;
5802     ChangePage[x][y] = -1;
5803
5804     CustomValue[x][y] = 0;
5805
5806     InitField_WithBug2(x, y, FALSE);
5807
5808     TEST_DrawLevelField(x, y);
5809
5810     TestIfElementTouchesCustomElement(x, y);
5811
5812     if (GFX_CRUMBLED(element))
5813       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5814
5815     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5816       StorePlayer[x][y] = 0;
5817
5818     if (ELEM_IS_PLAYER(element))
5819       RelocatePlayer(x, y, element);
5820   }
5821   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5822   {
5823     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5824     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5825
5826     if (phase == delay)
5827       TEST_DrawLevelFieldCrumbled(x, y);
5828
5829     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5830     {
5831       DrawLevelElement(x, y, Back[x][y]);
5832       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5833     }
5834     else if (IS_WALKABLE_UNDER(Back[x][y]))
5835     {
5836       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5837       DrawLevelElementThruMask(x, y, Back[x][y]);
5838     }
5839     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5840       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5841   }
5842 }
5843
5844 static void DynaExplode(int ex, int ey)
5845 {
5846   int i, j;
5847   int dynabomb_element = Feld[ex][ey];
5848   int dynabomb_size = 1;
5849   boolean dynabomb_xl = FALSE;
5850   struct PlayerInfo *player;
5851   static int xy[4][2] =
5852   {
5853     { 0, -1 },
5854     { -1, 0 },
5855     { +1, 0 },
5856     { 0, +1 }
5857   };
5858
5859   if (IS_ACTIVE_BOMB(dynabomb_element))
5860   {
5861     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5862     dynabomb_size = player->dynabomb_size;
5863     dynabomb_xl = player->dynabomb_xl;
5864     player->dynabombs_left++;
5865   }
5866
5867   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5868
5869   for (i = 0; i < NUM_DIRECTIONS; i++)
5870   {
5871     for (j = 1; j <= dynabomb_size; j++)
5872     {
5873       int x = ex + j * xy[i][0];
5874       int y = ey + j * xy[i][1];
5875       int element;
5876
5877       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5878         break;
5879
5880       element = Feld[x][y];
5881
5882       // do not restart explosions of fields with active bombs
5883       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5884         continue;
5885
5886       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5887
5888       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5889           !IS_DIGGABLE(element) && !dynabomb_xl)
5890         break;
5891     }
5892   }
5893 }
5894
5895 void Bang(int x, int y)
5896 {
5897   int element = MovingOrBlocked2Element(x, y);
5898   int explosion_type = EX_TYPE_NORMAL;
5899
5900   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5901   {
5902     struct PlayerInfo *player = PLAYERINFO(x, y);
5903
5904     element = Feld[x][y] = player->initial_element;
5905
5906     if (level.use_explosion_element[player->index_nr])
5907     {
5908       int explosion_element = level.explosion_element[player->index_nr];
5909
5910       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5911         explosion_type = EX_TYPE_CROSS;
5912       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5913         explosion_type = EX_TYPE_CENTER;
5914     }
5915   }
5916
5917   switch (element)
5918   {
5919     case EL_BUG:
5920     case EL_SPACESHIP:
5921     case EL_BD_BUTTERFLY:
5922     case EL_BD_FIREFLY:
5923     case EL_YAMYAM:
5924     case EL_DARK_YAMYAM:
5925     case EL_ROBOT:
5926     case EL_PACMAN:
5927     case EL_MOLE:
5928       RaiseScoreElement(element);
5929       break;
5930
5931     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5932     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5933     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5934     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5935     case EL_DYNABOMB_INCREASE_NUMBER:
5936     case EL_DYNABOMB_INCREASE_SIZE:
5937     case EL_DYNABOMB_INCREASE_POWER:
5938       explosion_type = EX_TYPE_DYNA;
5939       break;
5940
5941     case EL_DC_LANDMINE:
5942       explosion_type = EX_TYPE_CENTER;
5943       break;
5944
5945     case EL_PENGUIN:
5946     case EL_LAMP:
5947     case EL_LAMP_ACTIVE:
5948     case EL_AMOEBA_TO_DIAMOND:
5949       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5950         explosion_type = EX_TYPE_CENTER;
5951       break;
5952
5953     default:
5954       if (element_info[element].explosion_type == EXPLODES_CROSS)
5955         explosion_type = EX_TYPE_CROSS;
5956       else if (element_info[element].explosion_type == EXPLODES_1X1)
5957         explosion_type = EX_TYPE_CENTER;
5958       break;
5959   }
5960
5961   if (explosion_type == EX_TYPE_DYNA)
5962     DynaExplode(x, y);
5963   else
5964     Explode(x, y, EX_PHASE_START, explosion_type);
5965
5966   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5967 }
5968
5969 static void SplashAcid(int x, int y)
5970 {
5971   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5972       (!IN_LEV_FIELD(x - 1, y - 2) ||
5973        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5974     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5975
5976   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5977       (!IN_LEV_FIELD(x + 1, y - 2) ||
5978        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5979     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5980
5981   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5982 }
5983
5984 static void InitBeltMovement(void)
5985 {
5986   static int belt_base_element[4] =
5987   {
5988     EL_CONVEYOR_BELT_1_LEFT,
5989     EL_CONVEYOR_BELT_2_LEFT,
5990     EL_CONVEYOR_BELT_3_LEFT,
5991     EL_CONVEYOR_BELT_4_LEFT
5992   };
5993   static int belt_base_active_element[4] =
5994   {
5995     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5996     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5997     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5998     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5999   };
6000
6001   int x, y, i, j;
6002
6003   // set frame order for belt animation graphic according to belt direction
6004   for (i = 0; i < NUM_BELTS; i++)
6005   {
6006     int belt_nr = i;
6007
6008     for (j = 0; j < NUM_BELT_PARTS; j++)
6009     {
6010       int element = belt_base_active_element[belt_nr] + j;
6011       int graphic_1 = el2img(element);
6012       int graphic_2 = el2panelimg(element);
6013
6014       if (game.belt_dir[i] == MV_LEFT)
6015       {
6016         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6017         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6018       }
6019       else
6020       {
6021         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6022         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6023       }
6024     }
6025   }
6026
6027   SCAN_PLAYFIELD(x, y)
6028   {
6029     int element = Feld[x][y];
6030
6031     for (i = 0; i < NUM_BELTS; i++)
6032     {
6033       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6034       {
6035         int e_belt_nr = getBeltNrFromBeltElement(element);
6036         int belt_nr = i;
6037
6038         if (e_belt_nr == belt_nr)
6039         {
6040           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6041
6042           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6043         }
6044       }
6045     }
6046   }
6047 }
6048
6049 static void ToggleBeltSwitch(int x, int y)
6050 {
6051   static int belt_base_element[4] =
6052   {
6053     EL_CONVEYOR_BELT_1_LEFT,
6054     EL_CONVEYOR_BELT_2_LEFT,
6055     EL_CONVEYOR_BELT_3_LEFT,
6056     EL_CONVEYOR_BELT_4_LEFT
6057   };
6058   static int belt_base_active_element[4] =
6059   {
6060     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6061     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6062     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6063     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6064   };
6065   static int belt_base_switch_element[4] =
6066   {
6067     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6068     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6069     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6070     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6071   };
6072   static int belt_move_dir[4] =
6073   {
6074     MV_LEFT,
6075     MV_NONE,
6076     MV_RIGHT,
6077     MV_NONE,
6078   };
6079
6080   int element = Feld[x][y];
6081   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6082   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6083   int belt_dir = belt_move_dir[belt_dir_nr];
6084   int xx, yy, i;
6085
6086   if (!IS_BELT_SWITCH(element))
6087     return;
6088
6089   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6090   game.belt_dir[belt_nr] = belt_dir;
6091
6092   if (belt_dir_nr == 3)
6093     belt_dir_nr = 1;
6094
6095   // set frame order for belt animation graphic according to belt direction
6096   for (i = 0; i < NUM_BELT_PARTS; i++)
6097   {
6098     int element = belt_base_active_element[belt_nr] + i;
6099     int graphic_1 = el2img(element);
6100     int graphic_2 = el2panelimg(element);
6101
6102     if (belt_dir == MV_LEFT)
6103     {
6104       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6105       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6106     }
6107     else
6108     {
6109       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6110       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6111     }
6112   }
6113
6114   SCAN_PLAYFIELD(xx, yy)
6115   {
6116     int element = Feld[xx][yy];
6117
6118     if (IS_BELT_SWITCH(element))
6119     {
6120       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6121
6122       if (e_belt_nr == belt_nr)
6123       {
6124         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6125         TEST_DrawLevelField(xx, yy);
6126       }
6127     }
6128     else if (IS_BELT(element) && belt_dir != MV_NONE)
6129     {
6130       int e_belt_nr = getBeltNrFromBeltElement(element);
6131
6132       if (e_belt_nr == belt_nr)
6133       {
6134         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6135
6136         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6137         TEST_DrawLevelField(xx, yy);
6138       }
6139     }
6140     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6141     {
6142       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6143
6144       if (e_belt_nr == belt_nr)
6145       {
6146         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6147
6148         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6149         TEST_DrawLevelField(xx, yy);
6150       }
6151     }
6152   }
6153 }
6154
6155 static void ToggleSwitchgateSwitch(int x, int y)
6156 {
6157   int xx, yy;
6158
6159   game.switchgate_pos = !game.switchgate_pos;
6160
6161   SCAN_PLAYFIELD(xx, yy)
6162   {
6163     int element = Feld[xx][yy];
6164
6165     if (element == EL_SWITCHGATE_SWITCH_UP)
6166     {
6167       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6168       TEST_DrawLevelField(xx, yy);
6169     }
6170     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6171     {
6172       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6173       TEST_DrawLevelField(xx, yy);
6174     }
6175     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6176     {
6177       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6178       TEST_DrawLevelField(xx, yy);
6179     }
6180     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6181     {
6182       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6183       TEST_DrawLevelField(xx, yy);
6184     }
6185     else if (element == EL_SWITCHGATE_OPEN ||
6186              element == EL_SWITCHGATE_OPENING)
6187     {
6188       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6189
6190       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6191     }
6192     else if (element == EL_SWITCHGATE_CLOSED ||
6193              element == EL_SWITCHGATE_CLOSING)
6194     {
6195       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6196
6197       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6198     }
6199   }
6200 }
6201
6202 static int getInvisibleActiveFromInvisibleElement(int element)
6203 {
6204   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6205           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6206           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6207           element);
6208 }
6209
6210 static int getInvisibleFromInvisibleActiveElement(int element)
6211 {
6212   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6213           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6214           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6215           element);
6216 }
6217
6218 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6219 {
6220   int x, y;
6221
6222   SCAN_PLAYFIELD(x, y)
6223   {
6224     int element = Feld[x][y];
6225
6226     if (element == EL_LIGHT_SWITCH &&
6227         game.light_time_left > 0)
6228     {
6229       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6230       TEST_DrawLevelField(x, y);
6231     }
6232     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6233              game.light_time_left == 0)
6234     {
6235       Feld[x][y] = EL_LIGHT_SWITCH;
6236       TEST_DrawLevelField(x, y);
6237     }
6238     else if (element == EL_EMC_DRIPPER &&
6239              game.light_time_left > 0)
6240     {
6241       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6242       TEST_DrawLevelField(x, y);
6243     }
6244     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6245              game.light_time_left == 0)
6246     {
6247       Feld[x][y] = EL_EMC_DRIPPER;
6248       TEST_DrawLevelField(x, y);
6249     }
6250     else if (element == EL_INVISIBLE_STEELWALL ||
6251              element == EL_INVISIBLE_WALL ||
6252              element == EL_INVISIBLE_SAND)
6253     {
6254       if (game.light_time_left > 0)
6255         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6256
6257       TEST_DrawLevelField(x, y);
6258
6259       // uncrumble neighbour fields, if needed
6260       if (element == EL_INVISIBLE_SAND)
6261         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6262     }
6263     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6264              element == EL_INVISIBLE_WALL_ACTIVE ||
6265              element == EL_INVISIBLE_SAND_ACTIVE)
6266     {
6267       if (game.light_time_left == 0)
6268         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6269
6270       TEST_DrawLevelField(x, y);
6271
6272       // re-crumble neighbour fields, if needed
6273       if (element == EL_INVISIBLE_SAND)
6274         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6275     }
6276   }
6277 }
6278
6279 static void RedrawAllInvisibleElementsForLenses(void)
6280 {
6281   int x, y;
6282
6283   SCAN_PLAYFIELD(x, y)
6284   {
6285     int element = Feld[x][y];
6286
6287     if (element == EL_EMC_DRIPPER &&
6288         game.lenses_time_left > 0)
6289     {
6290       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6291       TEST_DrawLevelField(x, y);
6292     }
6293     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6294              game.lenses_time_left == 0)
6295     {
6296       Feld[x][y] = EL_EMC_DRIPPER;
6297       TEST_DrawLevelField(x, y);
6298     }
6299     else if (element == EL_INVISIBLE_STEELWALL ||
6300              element == EL_INVISIBLE_WALL ||
6301              element == EL_INVISIBLE_SAND)
6302     {
6303       if (game.lenses_time_left > 0)
6304         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6305
6306       TEST_DrawLevelField(x, y);
6307
6308       // uncrumble neighbour fields, if needed
6309       if (element == EL_INVISIBLE_SAND)
6310         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6311     }
6312     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6313              element == EL_INVISIBLE_WALL_ACTIVE ||
6314              element == EL_INVISIBLE_SAND_ACTIVE)
6315     {
6316       if (game.lenses_time_left == 0)
6317         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6318
6319       TEST_DrawLevelField(x, y);
6320
6321       // re-crumble neighbour fields, if needed
6322       if (element == EL_INVISIBLE_SAND)
6323         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6324     }
6325   }
6326 }
6327
6328 static void RedrawAllInvisibleElementsForMagnifier(void)
6329 {
6330   int x, y;
6331
6332   SCAN_PLAYFIELD(x, y)
6333   {
6334     int element = Feld[x][y];
6335
6336     if (element == EL_EMC_FAKE_GRASS &&
6337         game.magnify_time_left > 0)
6338     {
6339       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6340       TEST_DrawLevelField(x, y);
6341     }
6342     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6343              game.magnify_time_left == 0)
6344     {
6345       Feld[x][y] = EL_EMC_FAKE_GRASS;
6346       TEST_DrawLevelField(x, y);
6347     }
6348     else if (IS_GATE_GRAY(element) &&
6349              game.magnify_time_left > 0)
6350     {
6351       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6352                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6353                     IS_EM_GATE_GRAY(element) ?
6354                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6355                     IS_EMC_GATE_GRAY(element) ?
6356                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6357                     IS_DC_GATE_GRAY(element) ?
6358                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6359                     element);
6360       TEST_DrawLevelField(x, y);
6361     }
6362     else if (IS_GATE_GRAY_ACTIVE(element) &&
6363              game.magnify_time_left == 0)
6364     {
6365       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6366                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6367                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6368                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6369                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6370                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6371                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6372                     EL_DC_GATE_WHITE_GRAY :
6373                     element);
6374       TEST_DrawLevelField(x, y);
6375     }
6376   }
6377 }
6378
6379 static void ToggleLightSwitch(int x, int y)
6380 {
6381   int element = Feld[x][y];
6382
6383   game.light_time_left =
6384     (element == EL_LIGHT_SWITCH ?
6385      level.time_light * FRAMES_PER_SECOND : 0);
6386
6387   RedrawAllLightSwitchesAndInvisibleElements();
6388 }
6389
6390 static void ActivateTimegateSwitch(int x, int y)
6391 {
6392   int xx, yy;
6393
6394   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6395
6396   SCAN_PLAYFIELD(xx, yy)
6397   {
6398     int element = Feld[xx][yy];
6399
6400     if (element == EL_TIMEGATE_CLOSED ||
6401         element == EL_TIMEGATE_CLOSING)
6402     {
6403       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6404       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6405     }
6406
6407     /*
6408     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6409     {
6410       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6411       TEST_DrawLevelField(xx, yy);
6412     }
6413     */
6414
6415   }
6416
6417   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6418                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6419 }
6420
6421 static void Impact(int x, int y)
6422 {
6423   boolean last_line = (y == lev_fieldy - 1);
6424   boolean object_hit = FALSE;
6425   boolean impact = (last_line || object_hit);
6426   int element = Feld[x][y];
6427   int smashed = EL_STEELWALL;
6428
6429   if (!last_line)       // check if element below was hit
6430   {
6431     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6432       return;
6433
6434     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6435                                          MovDir[x][y + 1] != MV_DOWN ||
6436                                          MovPos[x][y + 1] <= TILEY / 2));
6437
6438     // do not smash moving elements that left the smashed field in time
6439     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6440         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6441       object_hit = FALSE;
6442
6443 #if USE_QUICKSAND_IMPACT_BUGFIX
6444     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6445     {
6446       RemoveMovingField(x, y + 1);
6447       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6448       Feld[x][y + 2] = EL_ROCK;
6449       TEST_DrawLevelField(x, y + 2);
6450
6451       object_hit = TRUE;
6452     }
6453
6454     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6455     {
6456       RemoveMovingField(x, y + 1);
6457       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6458       Feld[x][y + 2] = EL_ROCK;
6459       TEST_DrawLevelField(x, y + 2);
6460
6461       object_hit = TRUE;
6462     }
6463 #endif
6464
6465     if (object_hit)
6466       smashed = MovingOrBlocked2Element(x, y + 1);
6467
6468     impact = (last_line || object_hit);
6469   }
6470
6471   if (!last_line && smashed == EL_ACID) // element falls into acid
6472   {
6473     SplashAcid(x, y + 1);
6474     return;
6475   }
6476
6477   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6478   // only reset graphic animation if graphic really changes after impact
6479   if (impact &&
6480       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6481   {
6482     ResetGfxAnimation(x, y);
6483     TEST_DrawLevelField(x, y);
6484   }
6485
6486   if (impact && CAN_EXPLODE_IMPACT(element))
6487   {
6488     Bang(x, y);
6489     return;
6490   }
6491   else if (impact && element == EL_PEARL &&
6492            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6493   {
6494     ResetGfxAnimation(x, y);
6495
6496     Feld[x][y] = EL_PEARL_BREAKING;
6497     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6498     return;
6499   }
6500   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6501   {
6502     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6503
6504     return;
6505   }
6506
6507   if (impact && element == EL_AMOEBA_DROP)
6508   {
6509     if (object_hit && IS_PLAYER(x, y + 1))
6510       KillPlayerUnlessEnemyProtected(x, y + 1);
6511     else if (object_hit && smashed == EL_PENGUIN)
6512       Bang(x, y + 1);
6513     else
6514     {
6515       Feld[x][y] = EL_AMOEBA_GROWING;
6516       Store[x][y] = EL_AMOEBA_WET;
6517
6518       ResetRandomAnimationValue(x, y);
6519     }
6520     return;
6521   }
6522
6523   if (object_hit)               // check which object was hit
6524   {
6525     if ((CAN_PASS_MAGIC_WALL(element) && 
6526          (smashed == EL_MAGIC_WALL ||
6527           smashed == EL_BD_MAGIC_WALL)) ||
6528         (CAN_PASS_DC_MAGIC_WALL(element) &&
6529          smashed == EL_DC_MAGIC_WALL))
6530     {
6531       int xx, yy;
6532       int activated_magic_wall =
6533         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6534          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6535          EL_DC_MAGIC_WALL_ACTIVE);
6536
6537       // activate magic wall / mill
6538       SCAN_PLAYFIELD(xx, yy)
6539       {
6540         if (Feld[xx][yy] == smashed)
6541           Feld[xx][yy] = activated_magic_wall;
6542       }
6543
6544       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6545       game.magic_wall_active = TRUE;
6546
6547       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6548                             SND_MAGIC_WALL_ACTIVATING :
6549                             smashed == EL_BD_MAGIC_WALL ?
6550                             SND_BD_MAGIC_WALL_ACTIVATING :
6551                             SND_DC_MAGIC_WALL_ACTIVATING));
6552     }
6553
6554     if (IS_PLAYER(x, y + 1))
6555     {
6556       if (CAN_SMASH_PLAYER(element))
6557       {
6558         KillPlayerUnlessEnemyProtected(x, y + 1);
6559         return;
6560       }
6561     }
6562     else if (smashed == EL_PENGUIN)
6563     {
6564       if (CAN_SMASH_PLAYER(element))
6565       {
6566         Bang(x, y + 1);
6567         return;
6568       }
6569     }
6570     else if (element == EL_BD_DIAMOND)
6571     {
6572       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6573       {
6574         Bang(x, y + 1);
6575         return;
6576       }
6577     }
6578     else if (((element == EL_SP_INFOTRON ||
6579                element == EL_SP_ZONK) &&
6580               (smashed == EL_SP_SNIKSNAK ||
6581                smashed == EL_SP_ELECTRON ||
6582                smashed == EL_SP_DISK_ORANGE)) ||
6583              (element == EL_SP_INFOTRON &&
6584               smashed == EL_SP_DISK_YELLOW))
6585     {
6586       Bang(x, y + 1);
6587       return;
6588     }
6589     else if (CAN_SMASH_EVERYTHING(element))
6590     {
6591       if (IS_CLASSIC_ENEMY(smashed) ||
6592           CAN_EXPLODE_SMASHED(smashed))
6593       {
6594         Bang(x, y + 1);
6595         return;
6596       }
6597       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6598       {
6599         if (smashed == EL_LAMP ||
6600             smashed == EL_LAMP_ACTIVE)
6601         {
6602           Bang(x, y + 1);
6603           return;
6604         }
6605         else if (smashed == EL_NUT)
6606         {
6607           Feld[x][y + 1] = EL_NUT_BREAKING;
6608           PlayLevelSound(x, y, SND_NUT_BREAKING);
6609           RaiseScoreElement(EL_NUT);
6610           return;
6611         }
6612         else if (smashed == EL_PEARL)
6613         {
6614           ResetGfxAnimation(x, y);
6615
6616           Feld[x][y + 1] = EL_PEARL_BREAKING;
6617           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6618           return;
6619         }
6620         else if (smashed == EL_DIAMOND)
6621         {
6622           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6623           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6624           return;
6625         }
6626         else if (IS_BELT_SWITCH(smashed))
6627         {
6628           ToggleBeltSwitch(x, y + 1);
6629         }
6630         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6631                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6632                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6633                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6634         {
6635           ToggleSwitchgateSwitch(x, y + 1);
6636         }
6637         else if (smashed == EL_LIGHT_SWITCH ||
6638                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6639         {
6640           ToggleLightSwitch(x, y + 1);
6641         }
6642         else
6643         {
6644           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6645
6646           CheckElementChangeBySide(x, y + 1, smashed, element,
6647                                    CE_SWITCHED, CH_SIDE_TOP);
6648           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6649                                             CH_SIDE_TOP);
6650         }
6651       }
6652       else
6653       {
6654         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6655       }
6656     }
6657   }
6658
6659   // play sound of magic wall / mill
6660   if (!last_line &&
6661       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6662        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6663        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6664   {
6665     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6666       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6667     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6668       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6669     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6670       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6671
6672     return;
6673   }
6674
6675   // play sound of object that hits the ground
6676   if (last_line || object_hit)
6677     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6678 }
6679
6680 static void TurnRoundExt(int x, int y)
6681 {
6682   static struct
6683   {
6684     int dx, dy;
6685   } move_xy[] =
6686   {
6687     {  0,  0 },
6688     { -1,  0 },
6689     { +1,  0 },
6690     {  0,  0 },
6691     {  0, -1 },
6692     {  0,  0 }, { 0, 0 }, { 0, 0 },
6693     {  0, +1 }
6694   };
6695   static struct
6696   {
6697     int left, right, back;
6698   } turn[] =
6699   {
6700     { 0,        0,              0        },
6701     { MV_DOWN,  MV_UP,          MV_RIGHT },
6702     { MV_UP,    MV_DOWN,        MV_LEFT  },
6703     { 0,        0,              0        },
6704     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6705     { 0,        0,              0        },
6706     { 0,        0,              0        },
6707     { 0,        0,              0        },
6708     { MV_RIGHT, MV_LEFT,        MV_UP    }
6709   };
6710
6711   int element = Feld[x][y];
6712   int move_pattern = element_info[element].move_pattern;
6713
6714   int old_move_dir = MovDir[x][y];
6715   int left_dir  = turn[old_move_dir].left;
6716   int right_dir = turn[old_move_dir].right;
6717   int back_dir  = turn[old_move_dir].back;
6718
6719   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6720   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6721   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6722   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6723
6724   int left_x  = x + left_dx,  left_y  = y + left_dy;
6725   int right_x = x + right_dx, right_y = y + right_dy;
6726   int move_x  = x + move_dx,  move_y  = y + move_dy;
6727
6728   int xx, yy;
6729
6730   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6731   {
6732     TestIfBadThingTouchesOtherBadThing(x, y);
6733
6734     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6735       MovDir[x][y] = right_dir;
6736     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6737       MovDir[x][y] = left_dir;
6738
6739     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6740       MovDelay[x][y] = 9;
6741     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6742       MovDelay[x][y] = 1;
6743   }
6744   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6745   {
6746     TestIfBadThingTouchesOtherBadThing(x, y);
6747
6748     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6749       MovDir[x][y] = left_dir;
6750     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6751       MovDir[x][y] = right_dir;
6752
6753     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6754       MovDelay[x][y] = 9;
6755     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6756       MovDelay[x][y] = 1;
6757   }
6758   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6759   {
6760     TestIfBadThingTouchesOtherBadThing(x, y);
6761
6762     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6763       MovDir[x][y] = left_dir;
6764     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6765       MovDir[x][y] = right_dir;
6766
6767     if (MovDir[x][y] != old_move_dir)
6768       MovDelay[x][y] = 9;
6769   }
6770   else if (element == EL_YAMYAM)
6771   {
6772     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6773     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6774
6775     if (can_turn_left && can_turn_right)
6776       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6777     else if (can_turn_left)
6778       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6779     else if (can_turn_right)
6780       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6781     else
6782       MovDir[x][y] = back_dir;
6783
6784     MovDelay[x][y] = 16 + 16 * RND(3);
6785   }
6786   else if (element == EL_DARK_YAMYAM)
6787   {
6788     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6789                                                          left_x, left_y);
6790     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6791                                                          right_x, right_y);
6792
6793     if (can_turn_left && can_turn_right)
6794       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6795     else if (can_turn_left)
6796       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6797     else if (can_turn_right)
6798       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6799     else
6800       MovDir[x][y] = back_dir;
6801
6802     MovDelay[x][y] = 16 + 16 * RND(3);
6803   }
6804   else if (element == EL_PACMAN)
6805   {
6806     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6807     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6808
6809     if (can_turn_left && can_turn_right)
6810       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6811     else if (can_turn_left)
6812       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6813     else if (can_turn_right)
6814       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6815     else
6816       MovDir[x][y] = back_dir;
6817
6818     MovDelay[x][y] = 6 + RND(40);
6819   }
6820   else if (element == EL_PIG)
6821   {
6822     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6823     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6824     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6825     boolean should_turn_left, should_turn_right, should_move_on;
6826     int rnd_value = 24;
6827     int rnd = RND(rnd_value);
6828
6829     should_turn_left = (can_turn_left &&
6830                         (!can_move_on ||
6831                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6832                                                    y + back_dy + left_dy)));
6833     should_turn_right = (can_turn_right &&
6834                          (!can_move_on ||
6835                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6836                                                     y + back_dy + right_dy)));
6837     should_move_on = (can_move_on &&
6838                       (!can_turn_left ||
6839                        !can_turn_right ||
6840                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6841                                                  y + move_dy + left_dy) ||
6842                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6843                                                  y + move_dy + right_dy)));
6844
6845     if (should_turn_left || should_turn_right || should_move_on)
6846     {
6847       if (should_turn_left && should_turn_right && should_move_on)
6848         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6849                         rnd < 2 * rnd_value / 3 ? right_dir :
6850                         old_move_dir);
6851       else if (should_turn_left && should_turn_right)
6852         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6853       else if (should_turn_left && should_move_on)
6854         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6855       else if (should_turn_right && should_move_on)
6856         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6857       else if (should_turn_left)
6858         MovDir[x][y] = left_dir;
6859       else if (should_turn_right)
6860         MovDir[x][y] = right_dir;
6861       else if (should_move_on)
6862         MovDir[x][y] = old_move_dir;
6863     }
6864     else if (can_move_on && rnd > rnd_value / 8)
6865       MovDir[x][y] = old_move_dir;
6866     else if (can_turn_left && can_turn_right)
6867       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6868     else if (can_turn_left && rnd > rnd_value / 8)
6869       MovDir[x][y] = left_dir;
6870     else if (can_turn_right && rnd > rnd_value/8)
6871       MovDir[x][y] = right_dir;
6872     else
6873       MovDir[x][y] = back_dir;
6874
6875     xx = x + move_xy[MovDir[x][y]].dx;
6876     yy = y + move_xy[MovDir[x][y]].dy;
6877
6878     if (!IN_LEV_FIELD(xx, yy) ||
6879         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6880       MovDir[x][y] = old_move_dir;
6881
6882     MovDelay[x][y] = 0;
6883   }
6884   else if (element == EL_DRAGON)
6885   {
6886     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6887     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6888     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6889     int rnd_value = 24;
6890     int rnd = RND(rnd_value);
6891
6892     if (can_move_on && rnd > rnd_value / 8)
6893       MovDir[x][y] = old_move_dir;
6894     else if (can_turn_left && can_turn_right)
6895       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6896     else if (can_turn_left && rnd > rnd_value / 8)
6897       MovDir[x][y] = left_dir;
6898     else if (can_turn_right && rnd > rnd_value / 8)
6899       MovDir[x][y] = right_dir;
6900     else
6901       MovDir[x][y] = back_dir;
6902
6903     xx = x + move_xy[MovDir[x][y]].dx;
6904     yy = y + move_xy[MovDir[x][y]].dy;
6905
6906     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6907       MovDir[x][y] = old_move_dir;
6908
6909     MovDelay[x][y] = 0;
6910   }
6911   else if (element == EL_MOLE)
6912   {
6913     boolean can_move_on =
6914       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6915                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6916                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6917     if (!can_move_on)
6918     {
6919       boolean can_turn_left =
6920         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6921                               IS_AMOEBOID(Feld[left_x][left_y])));
6922
6923       boolean can_turn_right =
6924         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6925                               IS_AMOEBOID(Feld[right_x][right_y])));
6926
6927       if (can_turn_left && can_turn_right)
6928         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6929       else if (can_turn_left)
6930         MovDir[x][y] = left_dir;
6931       else
6932         MovDir[x][y] = right_dir;
6933     }
6934
6935     if (MovDir[x][y] != old_move_dir)
6936       MovDelay[x][y] = 9;
6937   }
6938   else if (element == EL_BALLOON)
6939   {
6940     MovDir[x][y] = game.wind_direction;
6941     MovDelay[x][y] = 0;
6942   }
6943   else if (element == EL_SPRING)
6944   {
6945     if (MovDir[x][y] & MV_HORIZONTAL)
6946     {
6947       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6948           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6949       {
6950         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6951         ResetGfxAnimation(move_x, move_y);
6952         TEST_DrawLevelField(move_x, move_y);
6953
6954         MovDir[x][y] = back_dir;
6955       }
6956       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6957                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6958         MovDir[x][y] = MV_NONE;
6959     }
6960
6961     MovDelay[x][y] = 0;
6962   }
6963   else if (element == EL_ROBOT ||
6964            element == EL_SATELLITE ||
6965            element == EL_PENGUIN ||
6966            element == EL_EMC_ANDROID)
6967   {
6968     int attr_x = -1, attr_y = -1;
6969
6970     if (game.all_players_gone)
6971     {
6972       attr_x = game.exit_x;
6973       attr_y = game.exit_y;
6974     }
6975     else
6976     {
6977       int i;
6978
6979       for (i = 0; i < MAX_PLAYERS; i++)
6980       {
6981         struct PlayerInfo *player = &stored_player[i];
6982         int jx = player->jx, jy = player->jy;
6983
6984         if (!player->active)
6985           continue;
6986
6987         if (attr_x == -1 ||
6988             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6989         {
6990           attr_x = jx;
6991           attr_y = jy;
6992         }
6993       }
6994     }
6995
6996     if (element == EL_ROBOT &&
6997         game.robot_wheel_x >= 0 &&
6998         game.robot_wheel_y >= 0 &&
6999         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7000          game.engine_version < VERSION_IDENT(3,1,0,0)))
7001     {
7002       attr_x = game.robot_wheel_x;
7003       attr_y = game.robot_wheel_y;
7004     }
7005
7006     if (element == EL_PENGUIN)
7007     {
7008       int i;
7009       static int xy[4][2] =
7010       {
7011         { 0, -1 },
7012         { -1, 0 },
7013         { +1, 0 },
7014         { 0, +1 }
7015       };
7016
7017       for (i = 0; i < NUM_DIRECTIONS; i++)
7018       {
7019         int ex = x + xy[i][0];
7020         int ey = y + xy[i][1];
7021
7022         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7023                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7024                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7025                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7026         {
7027           attr_x = ex;
7028           attr_y = ey;
7029           break;
7030         }
7031       }
7032     }
7033
7034     MovDir[x][y] = MV_NONE;
7035     if (attr_x < x)
7036       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7037     else if (attr_x > x)
7038       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7039     if (attr_y < y)
7040       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7041     else if (attr_y > y)
7042       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7043
7044     if (element == EL_ROBOT)
7045     {
7046       int newx, newy;
7047
7048       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7049         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7050       Moving2Blocked(x, y, &newx, &newy);
7051
7052       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7053         MovDelay[x][y] = 8 + 8 * !RND(3);
7054       else
7055         MovDelay[x][y] = 16;
7056     }
7057     else if (element == EL_PENGUIN)
7058     {
7059       int newx, newy;
7060
7061       MovDelay[x][y] = 1;
7062
7063       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7064       {
7065         boolean first_horiz = RND(2);
7066         int new_move_dir = MovDir[x][y];
7067
7068         MovDir[x][y] =
7069           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7070         Moving2Blocked(x, y, &newx, &newy);
7071
7072         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7073           return;
7074
7075         MovDir[x][y] =
7076           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7077         Moving2Blocked(x, y, &newx, &newy);
7078
7079         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7080           return;
7081
7082         MovDir[x][y] = old_move_dir;
7083         return;
7084       }
7085     }
7086     else if (element == EL_SATELLITE)
7087     {
7088       int newx, newy;
7089
7090       MovDelay[x][y] = 1;
7091
7092       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7093       {
7094         boolean first_horiz = RND(2);
7095         int new_move_dir = MovDir[x][y];
7096
7097         MovDir[x][y] =
7098           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7099         Moving2Blocked(x, y, &newx, &newy);
7100
7101         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7102           return;
7103
7104         MovDir[x][y] =
7105           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7106         Moving2Blocked(x, y, &newx, &newy);
7107
7108         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7109           return;
7110
7111         MovDir[x][y] = old_move_dir;
7112         return;
7113       }
7114     }
7115     else if (element == EL_EMC_ANDROID)
7116     {
7117       static int check_pos[16] =
7118       {
7119         -1,             //  0 => (invalid)
7120         7,              //  1 => MV_LEFT
7121         3,              //  2 => MV_RIGHT
7122         -1,             //  3 => (invalid)
7123         1,              //  4 =>            MV_UP
7124         0,              //  5 => MV_LEFT  | MV_UP
7125         2,              //  6 => MV_RIGHT | MV_UP
7126         -1,             //  7 => (invalid)
7127         5,              //  8 =>            MV_DOWN
7128         6,              //  9 => MV_LEFT  | MV_DOWN
7129         4,              // 10 => MV_RIGHT | MV_DOWN
7130         -1,             // 11 => (invalid)
7131         -1,             // 12 => (invalid)
7132         -1,             // 13 => (invalid)
7133         -1,             // 14 => (invalid)
7134         -1,             // 15 => (invalid)
7135       };
7136       static struct
7137       {
7138         int dx, dy;
7139         int dir;
7140       } check_xy[8] =
7141       {
7142         { -1, -1,       MV_LEFT  | MV_UP   },
7143         {  0, -1,                  MV_UP   },
7144         { +1, -1,       MV_RIGHT | MV_UP   },
7145         { +1,  0,       MV_RIGHT           },
7146         { +1, +1,       MV_RIGHT | MV_DOWN },
7147         {  0, +1,                  MV_DOWN },
7148         { -1, +1,       MV_LEFT  | MV_DOWN },
7149         { -1,  0,       MV_LEFT            },
7150       };
7151       int start_pos, check_order;
7152       boolean can_clone = FALSE;
7153       int i;
7154
7155       // check if there is any free field around current position
7156       for (i = 0; i < 8; i++)
7157       {
7158         int newx = x + check_xy[i].dx;
7159         int newy = y + check_xy[i].dy;
7160
7161         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7162         {
7163           can_clone = TRUE;
7164
7165           break;
7166         }
7167       }
7168
7169       if (can_clone)            // randomly find an element to clone
7170       {
7171         can_clone = FALSE;
7172
7173         start_pos = check_pos[RND(8)];
7174         check_order = (RND(2) ? -1 : +1);
7175
7176         for (i = 0; i < 8; i++)
7177         {
7178           int pos_raw = start_pos + i * check_order;
7179           int pos = (pos_raw + 8) % 8;
7180           int newx = x + check_xy[pos].dx;
7181           int newy = y + check_xy[pos].dy;
7182
7183           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7184           {
7185             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7186             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7187
7188             Store[x][y] = Feld[newx][newy];
7189
7190             can_clone = TRUE;
7191
7192             break;
7193           }
7194         }
7195       }
7196
7197       if (can_clone)            // randomly find a direction to move
7198       {
7199         can_clone = FALSE;
7200
7201         start_pos = check_pos[RND(8)];
7202         check_order = (RND(2) ? -1 : +1);
7203
7204         for (i = 0; i < 8; i++)
7205         {
7206           int pos_raw = start_pos + i * check_order;
7207           int pos = (pos_raw + 8) % 8;
7208           int newx = x + check_xy[pos].dx;
7209           int newy = y + check_xy[pos].dy;
7210           int new_move_dir = check_xy[pos].dir;
7211
7212           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7213           {
7214             MovDir[x][y] = new_move_dir;
7215             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7216
7217             can_clone = TRUE;
7218
7219             break;
7220           }
7221         }
7222       }
7223
7224       if (can_clone)            // cloning and moving successful
7225         return;
7226
7227       // cannot clone -- try to move towards player
7228
7229       start_pos = check_pos[MovDir[x][y] & 0x0f];
7230       check_order = (RND(2) ? -1 : +1);
7231
7232       for (i = 0; i < 3; i++)
7233       {
7234         // first check start_pos, then previous/next or (next/previous) pos
7235         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7236         int pos = (pos_raw + 8) % 8;
7237         int newx = x + check_xy[pos].dx;
7238         int newy = y + check_xy[pos].dy;
7239         int new_move_dir = check_xy[pos].dir;
7240
7241         if (IS_PLAYER(newx, newy))
7242           break;
7243
7244         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7245         {
7246           MovDir[x][y] = new_move_dir;
7247           MovDelay[x][y] = level.android_move_time * 8 + 1;
7248
7249           break;
7250         }
7251       }
7252     }
7253   }
7254   else if (move_pattern == MV_TURNING_LEFT ||
7255            move_pattern == MV_TURNING_RIGHT ||
7256            move_pattern == MV_TURNING_LEFT_RIGHT ||
7257            move_pattern == MV_TURNING_RIGHT_LEFT ||
7258            move_pattern == MV_TURNING_RANDOM ||
7259            move_pattern == MV_ALL_DIRECTIONS)
7260   {
7261     boolean can_turn_left =
7262       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7263     boolean can_turn_right =
7264       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7265
7266     if (element_info[element].move_stepsize == 0)       // "not moving"
7267       return;
7268
7269     if (move_pattern == MV_TURNING_LEFT)
7270       MovDir[x][y] = left_dir;
7271     else if (move_pattern == MV_TURNING_RIGHT)
7272       MovDir[x][y] = right_dir;
7273     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7274       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7275     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7276       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7277     else if (move_pattern == MV_TURNING_RANDOM)
7278       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7279                       can_turn_right && !can_turn_left ? right_dir :
7280                       RND(2) ? left_dir : right_dir);
7281     else if (can_turn_left && can_turn_right)
7282       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7283     else if (can_turn_left)
7284       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7285     else if (can_turn_right)
7286       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7287     else
7288       MovDir[x][y] = back_dir;
7289
7290     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7291   }
7292   else if (move_pattern == MV_HORIZONTAL ||
7293            move_pattern == MV_VERTICAL)
7294   {
7295     if (move_pattern & old_move_dir)
7296       MovDir[x][y] = back_dir;
7297     else if (move_pattern == MV_HORIZONTAL)
7298       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7299     else if (move_pattern == MV_VERTICAL)
7300       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7301
7302     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7303   }
7304   else if (move_pattern & MV_ANY_DIRECTION)
7305   {
7306     MovDir[x][y] = move_pattern;
7307     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7308   }
7309   else if (move_pattern & MV_WIND_DIRECTION)
7310   {
7311     MovDir[x][y] = game.wind_direction;
7312     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7313   }
7314   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7315   {
7316     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7317       MovDir[x][y] = left_dir;
7318     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7319       MovDir[x][y] = right_dir;
7320
7321     if (MovDir[x][y] != old_move_dir)
7322       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7323   }
7324   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7325   {
7326     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7327       MovDir[x][y] = right_dir;
7328     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7329       MovDir[x][y] = left_dir;
7330
7331     if (MovDir[x][y] != old_move_dir)
7332       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7333   }
7334   else if (move_pattern == MV_TOWARDS_PLAYER ||
7335            move_pattern == MV_AWAY_FROM_PLAYER)
7336   {
7337     int attr_x = -1, attr_y = -1;
7338     int newx, newy;
7339     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7340
7341     if (game.all_players_gone)
7342     {
7343       attr_x = game.exit_x;
7344       attr_y = game.exit_y;
7345     }
7346     else
7347     {
7348       int i;
7349
7350       for (i = 0; i < MAX_PLAYERS; i++)
7351       {
7352         struct PlayerInfo *player = &stored_player[i];
7353         int jx = player->jx, jy = player->jy;
7354
7355         if (!player->active)
7356           continue;
7357
7358         if (attr_x == -1 ||
7359             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7360         {
7361           attr_x = jx;
7362           attr_y = jy;
7363         }
7364       }
7365     }
7366
7367     MovDir[x][y] = MV_NONE;
7368     if (attr_x < x)
7369       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7370     else if (attr_x > x)
7371       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7372     if (attr_y < y)
7373       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7374     else if (attr_y > y)
7375       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7376
7377     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7378
7379     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7380     {
7381       boolean first_horiz = RND(2);
7382       int new_move_dir = MovDir[x][y];
7383
7384       if (element_info[element].move_stepsize == 0)     // "not moving"
7385       {
7386         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7387         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7388
7389         return;
7390       }
7391
7392       MovDir[x][y] =
7393         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7394       Moving2Blocked(x, y, &newx, &newy);
7395
7396       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7397         return;
7398
7399       MovDir[x][y] =
7400         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7401       Moving2Blocked(x, y, &newx, &newy);
7402
7403       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7404         return;
7405
7406       MovDir[x][y] = old_move_dir;
7407     }
7408   }
7409   else if (move_pattern == MV_WHEN_PUSHED ||
7410            move_pattern == MV_WHEN_DROPPED)
7411   {
7412     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7413       MovDir[x][y] = MV_NONE;
7414
7415     MovDelay[x][y] = 0;
7416   }
7417   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7418   {
7419     static int test_xy[7][2] =
7420     {
7421       { 0, -1 },
7422       { -1, 0 },
7423       { +1, 0 },
7424       { 0, +1 },
7425       { 0, -1 },
7426       { -1, 0 },
7427       { +1, 0 },
7428     };
7429     static int test_dir[7] =
7430     {
7431       MV_UP,
7432       MV_LEFT,
7433       MV_RIGHT,
7434       MV_DOWN,
7435       MV_UP,
7436       MV_LEFT,
7437       MV_RIGHT,
7438     };
7439     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7440     int move_preference = -1000000;     // start with very low preference
7441     int new_move_dir = MV_NONE;
7442     int start_test = RND(4);
7443     int i;
7444
7445     for (i = 0; i < NUM_DIRECTIONS; i++)
7446     {
7447       int move_dir = test_dir[start_test + i];
7448       int move_dir_preference;
7449
7450       xx = x + test_xy[start_test + i][0];
7451       yy = y + test_xy[start_test + i][1];
7452
7453       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7454           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7455       {
7456         new_move_dir = move_dir;
7457
7458         break;
7459       }
7460
7461       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7462         continue;
7463
7464       move_dir_preference = -1 * RunnerVisit[xx][yy];
7465       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7466         move_dir_preference = PlayerVisit[xx][yy];
7467
7468       if (move_dir_preference > move_preference)
7469       {
7470         // prefer field that has not been visited for the longest time
7471         move_preference = move_dir_preference;
7472         new_move_dir = move_dir;
7473       }
7474       else if (move_dir_preference == move_preference &&
7475                move_dir == old_move_dir)
7476       {
7477         // prefer last direction when all directions are preferred equally
7478         move_preference = move_dir_preference;
7479         new_move_dir = move_dir;
7480       }
7481     }
7482
7483     MovDir[x][y] = new_move_dir;
7484     if (old_move_dir != new_move_dir)
7485       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7486   }
7487 }
7488
7489 static void TurnRound(int x, int y)
7490 {
7491   int direction = MovDir[x][y];
7492
7493   TurnRoundExt(x, y);
7494
7495   GfxDir[x][y] = MovDir[x][y];
7496
7497   if (direction != MovDir[x][y])
7498     GfxFrame[x][y] = 0;
7499
7500   if (MovDelay[x][y])
7501     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7502
7503   ResetGfxFrame(x, y);
7504 }
7505
7506 static boolean JustBeingPushed(int x, int y)
7507 {
7508   int i;
7509
7510   for (i = 0; i < MAX_PLAYERS; i++)
7511   {
7512     struct PlayerInfo *player = &stored_player[i];
7513
7514     if (player->active && player->is_pushing && player->MovPos)
7515     {
7516       int next_jx = player->jx + (player->jx - player->last_jx);
7517       int next_jy = player->jy + (player->jy - player->last_jy);
7518
7519       if (x == next_jx && y == next_jy)
7520         return TRUE;
7521     }
7522   }
7523
7524   return FALSE;
7525 }
7526
7527 static void StartMoving(int x, int y)
7528 {
7529   boolean started_moving = FALSE;       // some elements can fall _and_ move
7530   int element = Feld[x][y];
7531
7532   if (Stop[x][y])
7533     return;
7534
7535   if (MovDelay[x][y] == 0)
7536     GfxAction[x][y] = ACTION_DEFAULT;
7537
7538   if (CAN_FALL(element) && y < lev_fieldy - 1)
7539   {
7540     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7541         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7542       if (JustBeingPushed(x, y))
7543         return;
7544
7545     if (element == EL_QUICKSAND_FULL)
7546     {
7547       if (IS_FREE(x, y + 1))
7548       {
7549         InitMovingField(x, y, MV_DOWN);
7550         started_moving = TRUE;
7551
7552         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7553 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7554         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7555           Store[x][y] = EL_ROCK;
7556 #else
7557         Store[x][y] = EL_ROCK;
7558 #endif
7559
7560         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7561       }
7562       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7563       {
7564         if (!MovDelay[x][y])
7565         {
7566           MovDelay[x][y] = TILEY + 1;
7567
7568           ResetGfxAnimation(x, y);
7569           ResetGfxAnimation(x, y + 1);
7570         }
7571
7572         if (MovDelay[x][y])
7573         {
7574           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7575           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7576
7577           MovDelay[x][y]--;
7578           if (MovDelay[x][y])
7579             return;
7580         }
7581
7582         Feld[x][y] = EL_QUICKSAND_EMPTY;
7583         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7584         Store[x][y + 1] = Store[x][y];
7585         Store[x][y] = 0;
7586
7587         PlayLevelSoundAction(x, y, ACTION_FILLING);
7588       }
7589       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7590       {
7591         if (!MovDelay[x][y])
7592         {
7593           MovDelay[x][y] = TILEY + 1;
7594
7595           ResetGfxAnimation(x, y);
7596           ResetGfxAnimation(x, y + 1);
7597         }
7598
7599         if (MovDelay[x][y])
7600         {
7601           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7602           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7603
7604           MovDelay[x][y]--;
7605           if (MovDelay[x][y])
7606             return;
7607         }
7608
7609         Feld[x][y] = EL_QUICKSAND_EMPTY;
7610         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7611         Store[x][y + 1] = Store[x][y];
7612         Store[x][y] = 0;
7613
7614         PlayLevelSoundAction(x, y, ACTION_FILLING);
7615       }
7616     }
7617     else if (element == EL_QUICKSAND_FAST_FULL)
7618     {
7619       if (IS_FREE(x, y + 1))
7620       {
7621         InitMovingField(x, y, MV_DOWN);
7622         started_moving = TRUE;
7623
7624         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7625 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7626         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7627           Store[x][y] = EL_ROCK;
7628 #else
7629         Store[x][y] = EL_ROCK;
7630 #endif
7631
7632         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7633       }
7634       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7635       {
7636         if (!MovDelay[x][y])
7637         {
7638           MovDelay[x][y] = TILEY + 1;
7639
7640           ResetGfxAnimation(x, y);
7641           ResetGfxAnimation(x, y + 1);
7642         }
7643
7644         if (MovDelay[x][y])
7645         {
7646           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7647           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7648
7649           MovDelay[x][y]--;
7650           if (MovDelay[x][y])
7651             return;
7652         }
7653
7654         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7655         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7656         Store[x][y + 1] = Store[x][y];
7657         Store[x][y] = 0;
7658
7659         PlayLevelSoundAction(x, y, ACTION_FILLING);
7660       }
7661       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7662       {
7663         if (!MovDelay[x][y])
7664         {
7665           MovDelay[x][y] = TILEY + 1;
7666
7667           ResetGfxAnimation(x, y);
7668           ResetGfxAnimation(x, y + 1);
7669         }
7670
7671         if (MovDelay[x][y])
7672         {
7673           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7674           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7675
7676           MovDelay[x][y]--;
7677           if (MovDelay[x][y])
7678             return;
7679         }
7680
7681         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7682         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7683         Store[x][y + 1] = Store[x][y];
7684         Store[x][y] = 0;
7685
7686         PlayLevelSoundAction(x, y, ACTION_FILLING);
7687       }
7688     }
7689     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7690              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7691     {
7692       InitMovingField(x, y, MV_DOWN);
7693       started_moving = TRUE;
7694
7695       Feld[x][y] = EL_QUICKSAND_FILLING;
7696       Store[x][y] = element;
7697
7698       PlayLevelSoundAction(x, y, ACTION_FILLING);
7699     }
7700     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7701              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7702     {
7703       InitMovingField(x, y, MV_DOWN);
7704       started_moving = TRUE;
7705
7706       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7707       Store[x][y] = element;
7708
7709       PlayLevelSoundAction(x, y, ACTION_FILLING);
7710     }
7711     else if (element == EL_MAGIC_WALL_FULL)
7712     {
7713       if (IS_FREE(x, y + 1))
7714       {
7715         InitMovingField(x, y, MV_DOWN);
7716         started_moving = TRUE;
7717
7718         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7719         Store[x][y] = EL_CHANGED(Store[x][y]);
7720       }
7721       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7722       {
7723         if (!MovDelay[x][y])
7724           MovDelay[x][y] = TILEY / 4 + 1;
7725
7726         if (MovDelay[x][y])
7727         {
7728           MovDelay[x][y]--;
7729           if (MovDelay[x][y])
7730             return;
7731         }
7732
7733         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7734         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7735         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7736         Store[x][y] = 0;
7737       }
7738     }
7739     else if (element == EL_BD_MAGIC_WALL_FULL)
7740     {
7741       if (IS_FREE(x, y + 1))
7742       {
7743         InitMovingField(x, y, MV_DOWN);
7744         started_moving = TRUE;
7745
7746         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7747         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7748       }
7749       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7750       {
7751         if (!MovDelay[x][y])
7752           MovDelay[x][y] = TILEY / 4 + 1;
7753
7754         if (MovDelay[x][y])
7755         {
7756           MovDelay[x][y]--;
7757           if (MovDelay[x][y])
7758             return;
7759         }
7760
7761         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7762         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7763         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7764         Store[x][y] = 0;
7765       }
7766     }
7767     else if (element == EL_DC_MAGIC_WALL_FULL)
7768     {
7769       if (IS_FREE(x, y + 1))
7770       {
7771         InitMovingField(x, y, MV_DOWN);
7772         started_moving = TRUE;
7773
7774         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7775         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7776       }
7777       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7778       {
7779         if (!MovDelay[x][y])
7780           MovDelay[x][y] = TILEY / 4 + 1;
7781
7782         if (MovDelay[x][y])
7783         {
7784           MovDelay[x][y]--;
7785           if (MovDelay[x][y])
7786             return;
7787         }
7788
7789         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7790         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7791         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7792         Store[x][y] = 0;
7793       }
7794     }
7795     else if ((CAN_PASS_MAGIC_WALL(element) &&
7796               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7797                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7798              (CAN_PASS_DC_MAGIC_WALL(element) &&
7799               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7800
7801     {
7802       InitMovingField(x, y, MV_DOWN);
7803       started_moving = TRUE;
7804
7805       Feld[x][y] =
7806         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7807          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7808          EL_DC_MAGIC_WALL_FILLING);
7809       Store[x][y] = element;
7810     }
7811     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7812     {
7813       SplashAcid(x, y + 1);
7814
7815       InitMovingField(x, y, MV_DOWN);
7816       started_moving = TRUE;
7817
7818       Store[x][y] = EL_ACID;
7819     }
7820     else if (
7821              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7822               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7823              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7824               CAN_FALL(element) && WasJustFalling[x][y] &&
7825               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7826
7827              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7828               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7829               (Feld[x][y + 1] == EL_BLOCKED)))
7830     {
7831       /* this is needed for a special case not covered by calling "Impact()"
7832          from "ContinueMoving()": if an element moves to a tile directly below
7833          another element which was just falling on that tile (which was empty
7834          in the previous frame), the falling element above would just stop
7835          instead of smashing the element below (in previous version, the above
7836          element was just checked for "moving" instead of "falling", resulting
7837          in incorrect smashes caused by horizontal movement of the above
7838          element; also, the case of the player being the element to smash was
7839          simply not covered here... :-/ ) */
7840
7841       CheckCollision[x][y] = 0;
7842       CheckImpact[x][y] = 0;
7843
7844       Impact(x, y);
7845     }
7846     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7847     {
7848       if (MovDir[x][y] == MV_NONE)
7849       {
7850         InitMovingField(x, y, MV_DOWN);
7851         started_moving = TRUE;
7852       }
7853     }
7854     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7855     {
7856       if (WasJustFalling[x][y]) // prevent animation from being restarted
7857         MovDir[x][y] = MV_DOWN;
7858
7859       InitMovingField(x, y, MV_DOWN);
7860       started_moving = TRUE;
7861     }
7862     else if (element == EL_AMOEBA_DROP)
7863     {
7864       Feld[x][y] = EL_AMOEBA_GROWING;
7865       Store[x][y] = EL_AMOEBA_WET;
7866     }
7867     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7868               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7869              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7870              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7871     {
7872       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7873                                 (IS_FREE(x - 1, y + 1) ||
7874                                  Feld[x - 1][y + 1] == EL_ACID));
7875       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7876                                 (IS_FREE(x + 1, y + 1) ||
7877                                  Feld[x + 1][y + 1] == EL_ACID));
7878       boolean can_fall_any  = (can_fall_left || can_fall_right);
7879       boolean can_fall_both = (can_fall_left && can_fall_right);
7880       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7881
7882       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7883       {
7884         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7885           can_fall_right = FALSE;
7886         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7887           can_fall_left = FALSE;
7888         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7889           can_fall_right = FALSE;
7890         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7891           can_fall_left = FALSE;
7892
7893         can_fall_any  = (can_fall_left || can_fall_right);
7894         can_fall_both = FALSE;
7895       }
7896
7897       if (can_fall_both)
7898       {
7899         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7900           can_fall_right = FALSE;       // slip down on left side
7901         else
7902           can_fall_left = !(can_fall_right = RND(2));
7903
7904         can_fall_both = FALSE;
7905       }
7906
7907       if (can_fall_any)
7908       {
7909         // if not determined otherwise, prefer left side for slipping down
7910         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7911         started_moving = TRUE;
7912       }
7913     }
7914     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7915     {
7916       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7917       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7918       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7919       int belt_dir = game.belt_dir[belt_nr];
7920
7921       if ((belt_dir == MV_LEFT  && left_is_free) ||
7922           (belt_dir == MV_RIGHT && right_is_free))
7923       {
7924         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7925
7926         InitMovingField(x, y, belt_dir);
7927         started_moving = TRUE;
7928
7929         Pushed[x][y] = TRUE;
7930         Pushed[nextx][y] = TRUE;
7931
7932         GfxAction[x][y] = ACTION_DEFAULT;
7933       }
7934       else
7935       {
7936         MovDir[x][y] = 0;       // if element was moving, stop it
7937       }
7938     }
7939   }
7940
7941   // not "else if" because of elements that can fall and move (EL_SPRING)
7942   if (CAN_MOVE(element) && !started_moving)
7943   {
7944     int move_pattern = element_info[element].move_pattern;
7945     int newx, newy;
7946
7947     Moving2Blocked(x, y, &newx, &newy);
7948
7949     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7950       return;
7951
7952     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7953         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7954     {
7955       WasJustMoving[x][y] = 0;
7956       CheckCollision[x][y] = 0;
7957
7958       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7959
7960       if (Feld[x][y] != element)        // element has changed
7961         return;
7962     }
7963
7964     if (!MovDelay[x][y])        // start new movement phase
7965     {
7966       // all objects that can change their move direction after each step
7967       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7968
7969       if (element != EL_YAMYAM &&
7970           element != EL_DARK_YAMYAM &&
7971           element != EL_PACMAN &&
7972           !(move_pattern & MV_ANY_DIRECTION) &&
7973           move_pattern != MV_TURNING_LEFT &&
7974           move_pattern != MV_TURNING_RIGHT &&
7975           move_pattern != MV_TURNING_LEFT_RIGHT &&
7976           move_pattern != MV_TURNING_RIGHT_LEFT &&
7977           move_pattern != MV_TURNING_RANDOM)
7978       {
7979         TurnRound(x, y);
7980
7981         if (MovDelay[x][y] && (element == EL_BUG ||
7982                                element == EL_SPACESHIP ||
7983                                element == EL_SP_SNIKSNAK ||
7984                                element == EL_SP_ELECTRON ||
7985                                element == EL_MOLE))
7986           TEST_DrawLevelField(x, y);
7987       }
7988     }
7989
7990     if (MovDelay[x][y])         // wait some time before next movement
7991     {
7992       MovDelay[x][y]--;
7993
7994       if (element == EL_ROBOT ||
7995           element == EL_YAMYAM ||
7996           element == EL_DARK_YAMYAM)
7997       {
7998         DrawLevelElementAnimationIfNeeded(x, y, element);
7999         PlayLevelSoundAction(x, y, ACTION_WAITING);
8000       }
8001       else if (element == EL_SP_ELECTRON)
8002         DrawLevelElementAnimationIfNeeded(x, y, element);
8003       else if (element == EL_DRAGON)
8004       {
8005         int i;
8006         int dir = MovDir[x][y];
8007         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8008         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8009         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8010                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8011                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8012                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8013         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8014
8015         GfxAction[x][y] = ACTION_ATTACKING;
8016
8017         if (IS_PLAYER(x, y))
8018           DrawPlayerField(x, y);
8019         else
8020           TEST_DrawLevelField(x, y);
8021
8022         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8023
8024         for (i = 1; i <= 3; i++)
8025         {
8026           int xx = x + i * dx;
8027           int yy = y + i * dy;
8028           int sx = SCREENX(xx);
8029           int sy = SCREENY(yy);
8030           int flame_graphic = graphic + (i - 1);
8031
8032           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8033             break;
8034
8035           if (MovDelay[x][y])
8036           {
8037             int flamed = MovingOrBlocked2Element(xx, yy);
8038
8039             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8040               Bang(xx, yy);
8041             else
8042               RemoveMovingField(xx, yy);
8043
8044             ChangeDelay[xx][yy] = 0;
8045
8046             Feld[xx][yy] = EL_FLAMES;
8047
8048             if (IN_SCR_FIELD(sx, sy))
8049             {
8050               TEST_DrawLevelFieldCrumbled(xx, yy);
8051               DrawGraphic(sx, sy, flame_graphic, frame);
8052             }
8053           }
8054           else
8055           {
8056             if (Feld[xx][yy] == EL_FLAMES)
8057               Feld[xx][yy] = EL_EMPTY;
8058             TEST_DrawLevelField(xx, yy);
8059           }
8060         }
8061       }
8062
8063       if (MovDelay[x][y])       // element still has to wait some time
8064       {
8065         PlayLevelSoundAction(x, y, ACTION_WAITING);
8066
8067         return;
8068       }
8069     }
8070
8071     // now make next step
8072
8073     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8074
8075     if (DONT_COLLIDE_WITH(element) &&
8076         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8077         !PLAYER_ENEMY_PROTECTED(newx, newy))
8078     {
8079       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8080
8081       return;
8082     }
8083
8084     else if (CAN_MOVE_INTO_ACID(element) &&
8085              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8086              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8087              (MovDir[x][y] == MV_DOWN ||
8088               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8089     {
8090       SplashAcid(newx, newy);
8091       Store[x][y] = EL_ACID;
8092     }
8093     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8094     {
8095       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8096           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8097           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8098           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8099       {
8100         RemoveField(x, y);
8101         TEST_DrawLevelField(x, y);
8102
8103         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8104         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8105           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8106
8107         game.friends_still_needed--;
8108         if (!game.friends_still_needed &&
8109             !game.GameOver &&
8110             game.all_players_gone)
8111           LevelSolved();
8112
8113         return;
8114       }
8115       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8116       {
8117         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8118           TEST_DrawLevelField(newx, newy);
8119         else
8120           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8121       }
8122       else if (!IS_FREE(newx, newy))
8123       {
8124         GfxAction[x][y] = ACTION_WAITING;
8125
8126         if (IS_PLAYER(x, y))
8127           DrawPlayerField(x, y);
8128         else
8129           TEST_DrawLevelField(x, y);
8130
8131         return;
8132       }
8133     }
8134     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8135     {
8136       if (IS_FOOD_PIG(Feld[newx][newy]))
8137       {
8138         if (IS_MOVING(newx, newy))
8139           RemoveMovingField(newx, newy);
8140         else
8141         {
8142           Feld[newx][newy] = EL_EMPTY;
8143           TEST_DrawLevelField(newx, newy);
8144         }
8145
8146         PlayLevelSound(x, y, SND_PIG_DIGGING);
8147       }
8148       else if (!IS_FREE(newx, newy))
8149       {
8150         if (IS_PLAYER(x, y))
8151           DrawPlayerField(x, y);
8152         else
8153           TEST_DrawLevelField(x, y);
8154
8155         return;
8156       }
8157     }
8158     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8159     {
8160       if (Store[x][y] != EL_EMPTY)
8161       {
8162         boolean can_clone = FALSE;
8163         int xx, yy;
8164
8165         // check if element to clone is still there
8166         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8167         {
8168           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8169           {
8170             can_clone = TRUE;
8171
8172             break;
8173           }
8174         }
8175
8176         // cannot clone or target field not free anymore -- do not clone
8177         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8178           Store[x][y] = EL_EMPTY;
8179       }
8180
8181       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8182       {
8183         if (IS_MV_DIAGONAL(MovDir[x][y]))
8184         {
8185           int diagonal_move_dir = MovDir[x][y];
8186           int stored = Store[x][y];
8187           int change_delay = 8;
8188           int graphic;
8189
8190           // android is moving diagonally
8191
8192           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8193
8194           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8195           GfxElement[x][y] = EL_EMC_ANDROID;
8196           GfxAction[x][y] = ACTION_SHRINKING;
8197           GfxDir[x][y] = diagonal_move_dir;
8198           ChangeDelay[x][y] = change_delay;
8199
8200           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8201                                    GfxDir[x][y]);
8202
8203           DrawLevelGraphicAnimation(x, y, graphic);
8204           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8205
8206           if (Feld[newx][newy] == EL_ACID)
8207           {
8208             SplashAcid(newx, newy);
8209
8210             return;
8211           }
8212
8213           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8214
8215           Store[newx][newy] = EL_EMC_ANDROID;
8216           GfxElement[newx][newy] = EL_EMC_ANDROID;
8217           GfxAction[newx][newy] = ACTION_GROWING;
8218           GfxDir[newx][newy] = diagonal_move_dir;
8219           ChangeDelay[newx][newy] = change_delay;
8220
8221           graphic = el_act_dir2img(GfxElement[newx][newy],
8222                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8223
8224           DrawLevelGraphicAnimation(newx, newy, graphic);
8225           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8226
8227           return;
8228         }
8229         else
8230         {
8231           Feld[newx][newy] = EL_EMPTY;
8232           TEST_DrawLevelField(newx, newy);
8233
8234           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8235         }
8236       }
8237       else if (!IS_FREE(newx, newy))
8238       {
8239         return;
8240       }
8241     }
8242     else if (IS_CUSTOM_ELEMENT(element) &&
8243              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8244     {
8245       if (!DigFieldByCE(newx, newy, element))
8246         return;
8247
8248       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8249       {
8250         RunnerVisit[x][y] = FrameCounter;
8251         PlayerVisit[x][y] /= 8;         // expire player visit path
8252       }
8253     }
8254     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8255     {
8256       if (!IS_FREE(newx, newy))
8257       {
8258         if (IS_PLAYER(x, y))
8259           DrawPlayerField(x, y);
8260         else
8261           TEST_DrawLevelField(x, y);
8262
8263         return;
8264       }
8265       else
8266       {
8267         boolean wanna_flame = !RND(10);
8268         int dx = newx - x, dy = newy - y;
8269         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8270         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8271         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8272                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8273         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8274                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8275
8276         if ((wanna_flame ||
8277              IS_CLASSIC_ENEMY(element1) ||
8278              IS_CLASSIC_ENEMY(element2)) &&
8279             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8280             element1 != EL_FLAMES && element2 != EL_FLAMES)
8281         {
8282           ResetGfxAnimation(x, y);
8283           GfxAction[x][y] = ACTION_ATTACKING;
8284
8285           if (IS_PLAYER(x, y))
8286             DrawPlayerField(x, y);
8287           else
8288             TEST_DrawLevelField(x, y);
8289
8290           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8291
8292           MovDelay[x][y] = 50;
8293
8294           Feld[newx][newy] = EL_FLAMES;
8295           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8296             Feld[newx1][newy1] = EL_FLAMES;
8297           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8298             Feld[newx2][newy2] = EL_FLAMES;
8299
8300           return;
8301         }
8302       }
8303     }
8304     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8305              Feld[newx][newy] == EL_DIAMOND)
8306     {
8307       if (IS_MOVING(newx, newy))
8308         RemoveMovingField(newx, newy);
8309       else
8310       {
8311         Feld[newx][newy] = EL_EMPTY;
8312         TEST_DrawLevelField(newx, newy);
8313       }
8314
8315       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8316     }
8317     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8318              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8319     {
8320       if (AmoebaNr[newx][newy])
8321       {
8322         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8323         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8324             Feld[newx][newy] == EL_BD_AMOEBA)
8325           AmoebaCnt[AmoebaNr[newx][newy]]--;
8326       }
8327
8328       if (IS_MOVING(newx, newy))
8329       {
8330         RemoveMovingField(newx, newy);
8331       }
8332       else
8333       {
8334         Feld[newx][newy] = EL_EMPTY;
8335         TEST_DrawLevelField(newx, newy);
8336       }
8337
8338       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8339     }
8340     else if ((element == EL_PACMAN || element == EL_MOLE)
8341              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8342     {
8343       if (AmoebaNr[newx][newy])
8344       {
8345         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8346         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8347             Feld[newx][newy] == EL_BD_AMOEBA)
8348           AmoebaCnt[AmoebaNr[newx][newy]]--;
8349       }
8350
8351       if (element == EL_MOLE)
8352       {
8353         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8354         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8355
8356         ResetGfxAnimation(x, y);
8357         GfxAction[x][y] = ACTION_DIGGING;
8358         TEST_DrawLevelField(x, y);
8359
8360         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8361
8362         return;                         // wait for shrinking amoeba
8363       }
8364       else      // element == EL_PACMAN
8365       {
8366         Feld[newx][newy] = EL_EMPTY;
8367         TEST_DrawLevelField(newx, newy);
8368         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8369       }
8370     }
8371     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8372              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8373               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8374     {
8375       // wait for shrinking amoeba to completely disappear
8376       return;
8377     }
8378     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8379     {
8380       // object was running against a wall
8381
8382       TurnRound(x, y);
8383
8384       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8385         DrawLevelElementAnimation(x, y, element);
8386
8387       if (DONT_TOUCH(element))
8388         TestIfBadThingTouchesPlayer(x, y);
8389
8390       return;
8391     }
8392
8393     InitMovingField(x, y, MovDir[x][y]);
8394
8395     PlayLevelSoundAction(x, y, ACTION_MOVING);
8396   }
8397
8398   if (MovDir[x][y])
8399     ContinueMoving(x, y);
8400 }
8401
8402 void ContinueMoving(int x, int y)
8403 {
8404   int element = Feld[x][y];
8405   struct ElementInfo *ei = &element_info[element];
8406   int direction = MovDir[x][y];
8407   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8408   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8409   int newx = x + dx, newy = y + dy;
8410   int stored = Store[x][y];
8411   int stored_new = Store[newx][newy];
8412   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8413   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8414   boolean last_line = (newy == lev_fieldy - 1);
8415
8416   MovPos[x][y] += getElementMoveStepsize(x, y);
8417
8418   if (pushed_by_player) // special case: moving object pushed by player
8419     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8420
8421   if (ABS(MovPos[x][y]) < TILEX)
8422   {
8423     TEST_DrawLevelField(x, y);
8424
8425     return;     // element is still moving
8426   }
8427
8428   // element reached destination field
8429
8430   Feld[x][y] = EL_EMPTY;
8431   Feld[newx][newy] = element;
8432   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8433
8434   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8435   {
8436     element = Feld[newx][newy] = EL_ACID;
8437   }
8438   else if (element == EL_MOLE)
8439   {
8440     Feld[x][y] = EL_SAND;
8441
8442     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8443   }
8444   else if (element == EL_QUICKSAND_FILLING)
8445   {
8446     element = Feld[newx][newy] = get_next_element(element);
8447     Store[newx][newy] = Store[x][y];
8448   }
8449   else if (element == EL_QUICKSAND_EMPTYING)
8450   {
8451     Feld[x][y] = get_next_element(element);
8452     element = Feld[newx][newy] = Store[x][y];
8453   }
8454   else if (element == EL_QUICKSAND_FAST_FILLING)
8455   {
8456     element = Feld[newx][newy] = get_next_element(element);
8457     Store[newx][newy] = Store[x][y];
8458   }
8459   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8460   {
8461     Feld[x][y] = get_next_element(element);
8462     element = Feld[newx][newy] = Store[x][y];
8463   }
8464   else if (element == EL_MAGIC_WALL_FILLING)
8465   {
8466     element = Feld[newx][newy] = get_next_element(element);
8467     if (!game.magic_wall_active)
8468       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8469     Store[newx][newy] = Store[x][y];
8470   }
8471   else if (element == EL_MAGIC_WALL_EMPTYING)
8472   {
8473     Feld[x][y] = get_next_element(element);
8474     if (!game.magic_wall_active)
8475       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8476     element = Feld[newx][newy] = Store[x][y];
8477
8478     InitField(newx, newy, FALSE);
8479   }
8480   else if (element == EL_BD_MAGIC_WALL_FILLING)
8481   {
8482     element = Feld[newx][newy] = get_next_element(element);
8483     if (!game.magic_wall_active)
8484       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8485     Store[newx][newy] = Store[x][y];
8486   }
8487   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8488   {
8489     Feld[x][y] = get_next_element(element);
8490     if (!game.magic_wall_active)
8491       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8492     element = Feld[newx][newy] = Store[x][y];
8493
8494     InitField(newx, newy, FALSE);
8495   }
8496   else if (element == EL_DC_MAGIC_WALL_FILLING)
8497   {
8498     element = Feld[newx][newy] = get_next_element(element);
8499     if (!game.magic_wall_active)
8500       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8501     Store[newx][newy] = Store[x][y];
8502   }
8503   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8504   {
8505     Feld[x][y] = get_next_element(element);
8506     if (!game.magic_wall_active)
8507       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8508     element = Feld[newx][newy] = Store[x][y];
8509
8510     InitField(newx, newy, FALSE);
8511   }
8512   else if (element == EL_AMOEBA_DROPPING)
8513   {
8514     Feld[x][y] = get_next_element(element);
8515     element = Feld[newx][newy] = Store[x][y];
8516   }
8517   else if (element == EL_SOKOBAN_OBJECT)
8518   {
8519     if (Back[x][y])
8520       Feld[x][y] = Back[x][y];
8521
8522     if (Back[newx][newy])
8523       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8524
8525     Back[x][y] = Back[newx][newy] = 0;
8526   }
8527
8528   Store[x][y] = EL_EMPTY;
8529   MovPos[x][y] = 0;
8530   MovDir[x][y] = 0;
8531   MovDelay[x][y] = 0;
8532
8533   MovDelay[newx][newy] = 0;
8534
8535   if (CAN_CHANGE_OR_HAS_ACTION(element))
8536   {
8537     // copy element change control values to new field
8538     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8539     ChangePage[newx][newy]  = ChangePage[x][y];
8540     ChangeCount[newx][newy] = ChangeCount[x][y];
8541     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8542   }
8543
8544   CustomValue[newx][newy] = CustomValue[x][y];
8545
8546   ChangeDelay[x][y] = 0;
8547   ChangePage[x][y] = -1;
8548   ChangeCount[x][y] = 0;
8549   ChangeEvent[x][y] = -1;
8550
8551   CustomValue[x][y] = 0;
8552
8553   // copy animation control values to new field
8554   GfxFrame[newx][newy]  = GfxFrame[x][y];
8555   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8556   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8557   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8558
8559   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8560
8561   // some elements can leave other elements behind after moving
8562   if (ei->move_leave_element != EL_EMPTY &&
8563       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8564       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8565   {
8566     int move_leave_element = ei->move_leave_element;
8567
8568     // this makes it possible to leave the removed element again
8569     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8570       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8571
8572     Feld[x][y] = move_leave_element;
8573
8574     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8575       MovDir[x][y] = direction;
8576
8577     InitField(x, y, FALSE);
8578
8579     if (GFX_CRUMBLED(Feld[x][y]))
8580       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8581
8582     if (ELEM_IS_PLAYER(move_leave_element))
8583       RelocatePlayer(x, y, move_leave_element);
8584   }
8585
8586   // do this after checking for left-behind element
8587   ResetGfxAnimation(x, y);      // reset animation values for old field
8588
8589   if (!CAN_MOVE(element) ||
8590       (CAN_FALL(element) && direction == MV_DOWN &&
8591        (element == EL_SPRING ||
8592         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8593         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8594     GfxDir[x][y] = MovDir[newx][newy] = 0;
8595
8596   TEST_DrawLevelField(x, y);
8597   TEST_DrawLevelField(newx, newy);
8598
8599   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8600
8601   // prevent pushed element from moving on in pushed direction
8602   if (pushed_by_player && CAN_MOVE(element) &&
8603       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8604       !(element_info[element].move_pattern & direction))
8605     TurnRound(newx, newy);
8606
8607   // prevent elements on conveyor belt from moving on in last direction
8608   if (pushed_by_conveyor && CAN_FALL(element) &&
8609       direction & MV_HORIZONTAL)
8610     MovDir[newx][newy] = 0;
8611
8612   if (!pushed_by_player)
8613   {
8614     int nextx = newx + dx, nexty = newy + dy;
8615     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8616
8617     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8618
8619     if (CAN_FALL(element) && direction == MV_DOWN)
8620       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8621
8622     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8623       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8624
8625     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8626       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8627   }
8628
8629   if (DONT_TOUCH(element))      // object may be nasty to player or others
8630   {
8631     TestIfBadThingTouchesPlayer(newx, newy);
8632     TestIfBadThingTouchesFriend(newx, newy);
8633
8634     if (!IS_CUSTOM_ELEMENT(element))
8635       TestIfBadThingTouchesOtherBadThing(newx, newy);
8636   }
8637   else if (element == EL_PENGUIN)
8638     TestIfFriendTouchesBadThing(newx, newy);
8639
8640   if (DONT_GET_HIT_BY(element))
8641   {
8642     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8643   }
8644
8645   // give the player one last chance (one more frame) to move away
8646   if (CAN_FALL(element) && direction == MV_DOWN &&
8647       (last_line || (!IS_FREE(x, newy + 1) &&
8648                      (!IS_PLAYER(x, newy + 1) ||
8649                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8650     Impact(x, newy);
8651
8652   if (pushed_by_player && !game.use_change_when_pushing_bug)
8653   {
8654     int push_side = MV_DIR_OPPOSITE(direction);
8655     struct PlayerInfo *player = PLAYERINFO(x, y);
8656
8657     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8658                                player->index_bit, push_side);
8659     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8660                                         player->index_bit, push_side);
8661   }
8662
8663   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8664     MovDelay[newx][newy] = 1;
8665
8666   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8667
8668   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8669   TestIfElementHitsCustomElement(newx, newy, direction);
8670   TestIfPlayerTouchesCustomElement(newx, newy);
8671   TestIfElementTouchesCustomElement(newx, newy);
8672
8673   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8674       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8675     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8676                              MV_DIR_OPPOSITE(direction));
8677 }
8678
8679 int AmoebeNachbarNr(int ax, int ay)
8680 {
8681   int i;
8682   int element = Feld[ax][ay];
8683   int group_nr = 0;
8684   static int xy[4][2] =
8685   {
8686     { 0, -1 },
8687     { -1, 0 },
8688     { +1, 0 },
8689     { 0, +1 }
8690   };
8691
8692   for (i = 0; i < NUM_DIRECTIONS; i++)
8693   {
8694     int x = ax + xy[i][0];
8695     int y = ay + xy[i][1];
8696
8697     if (!IN_LEV_FIELD(x, y))
8698       continue;
8699
8700     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8701       group_nr = AmoebaNr[x][y];
8702   }
8703
8704   return group_nr;
8705 }
8706
8707 static void AmoebenVereinigen(int ax, int ay)
8708 {
8709   int i, x, y, xx, yy;
8710   int new_group_nr = AmoebaNr[ax][ay];
8711   static int xy[4][2] =
8712   {
8713     { 0, -1 },
8714     { -1, 0 },
8715     { +1, 0 },
8716     { 0, +1 }
8717   };
8718
8719   if (new_group_nr == 0)
8720     return;
8721
8722   for (i = 0; i < NUM_DIRECTIONS; i++)
8723   {
8724     x = ax + xy[i][0];
8725     y = ay + xy[i][1];
8726
8727     if (!IN_LEV_FIELD(x, y))
8728       continue;
8729
8730     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8731          Feld[x][y] == EL_BD_AMOEBA ||
8732          Feld[x][y] == EL_AMOEBA_DEAD) &&
8733         AmoebaNr[x][y] != new_group_nr)
8734     {
8735       int old_group_nr = AmoebaNr[x][y];
8736
8737       if (old_group_nr == 0)
8738         return;
8739
8740       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8741       AmoebaCnt[old_group_nr] = 0;
8742       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8743       AmoebaCnt2[old_group_nr] = 0;
8744
8745       SCAN_PLAYFIELD(xx, yy)
8746       {
8747         if (AmoebaNr[xx][yy] == old_group_nr)
8748           AmoebaNr[xx][yy] = new_group_nr;
8749       }
8750     }
8751   }
8752 }
8753
8754 void AmoebeUmwandeln(int ax, int ay)
8755 {
8756   int i, x, y;
8757
8758   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8759   {
8760     int group_nr = AmoebaNr[ax][ay];
8761
8762 #ifdef DEBUG
8763     if (group_nr == 0)
8764     {
8765       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8766       printf("AmoebeUmwandeln(): This should never happen!\n");
8767       return;
8768     }
8769 #endif
8770
8771     SCAN_PLAYFIELD(x, y)
8772     {
8773       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8774       {
8775         AmoebaNr[x][y] = 0;
8776         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8777       }
8778     }
8779
8780     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8781                             SND_AMOEBA_TURNING_TO_GEM :
8782                             SND_AMOEBA_TURNING_TO_ROCK));
8783     Bang(ax, ay);
8784   }
8785   else
8786   {
8787     static int xy[4][2] =
8788     {
8789       { 0, -1 },
8790       { -1, 0 },
8791       { +1, 0 },
8792       { 0, +1 }
8793     };
8794
8795     for (i = 0; i < NUM_DIRECTIONS; i++)
8796     {
8797       x = ax + xy[i][0];
8798       y = ay + xy[i][1];
8799
8800       if (!IN_LEV_FIELD(x, y))
8801         continue;
8802
8803       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8804       {
8805         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8806                               SND_AMOEBA_TURNING_TO_GEM :
8807                               SND_AMOEBA_TURNING_TO_ROCK));
8808         Bang(x, y);
8809       }
8810     }
8811   }
8812 }
8813
8814 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8815 {
8816   int x, y;
8817   int group_nr = AmoebaNr[ax][ay];
8818   boolean done = FALSE;
8819
8820 #ifdef DEBUG
8821   if (group_nr == 0)
8822   {
8823     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8824     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8825     return;
8826   }
8827 #endif
8828
8829   SCAN_PLAYFIELD(x, y)
8830   {
8831     if (AmoebaNr[x][y] == group_nr &&
8832         (Feld[x][y] == EL_AMOEBA_DEAD ||
8833          Feld[x][y] == EL_BD_AMOEBA ||
8834          Feld[x][y] == EL_AMOEBA_GROWING))
8835     {
8836       AmoebaNr[x][y] = 0;
8837       Feld[x][y] = new_element;
8838       InitField(x, y, FALSE);
8839       TEST_DrawLevelField(x, y);
8840       done = TRUE;
8841     }
8842   }
8843
8844   if (done)
8845     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8846                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8847                             SND_BD_AMOEBA_TURNING_TO_GEM));
8848 }
8849
8850 static void AmoebeWaechst(int x, int y)
8851 {
8852   static unsigned int sound_delay = 0;
8853   static unsigned int sound_delay_value = 0;
8854
8855   if (!MovDelay[x][y])          // start new growing cycle
8856   {
8857     MovDelay[x][y] = 7;
8858
8859     if (DelayReached(&sound_delay, sound_delay_value))
8860     {
8861       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8862       sound_delay_value = 30;
8863     }
8864   }
8865
8866   if (MovDelay[x][y])           // wait some time before growing bigger
8867   {
8868     MovDelay[x][y]--;
8869     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8870     {
8871       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8872                                            6 - MovDelay[x][y]);
8873
8874       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8875     }
8876
8877     if (!MovDelay[x][y])
8878     {
8879       Feld[x][y] = Store[x][y];
8880       Store[x][y] = 0;
8881       TEST_DrawLevelField(x, y);
8882     }
8883   }
8884 }
8885
8886 static void AmoebaDisappearing(int x, int y)
8887 {
8888   static unsigned int sound_delay = 0;
8889   static unsigned int sound_delay_value = 0;
8890
8891   if (!MovDelay[x][y])          // start new shrinking cycle
8892   {
8893     MovDelay[x][y] = 7;
8894
8895     if (DelayReached(&sound_delay, sound_delay_value))
8896       sound_delay_value = 30;
8897   }
8898
8899   if (MovDelay[x][y])           // wait some time before shrinking
8900   {
8901     MovDelay[x][y]--;
8902     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8903     {
8904       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8905                                            6 - MovDelay[x][y]);
8906
8907       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8908     }
8909
8910     if (!MovDelay[x][y])
8911     {
8912       Feld[x][y] = EL_EMPTY;
8913       TEST_DrawLevelField(x, y);
8914
8915       // don't let mole enter this field in this cycle;
8916       // (give priority to objects falling to this field from above)
8917       Stop[x][y] = TRUE;
8918     }
8919   }
8920 }
8921
8922 static void AmoebeAbleger(int ax, int ay)
8923 {
8924   int i;
8925   int element = Feld[ax][ay];
8926   int graphic = el2img(element);
8927   int newax = ax, neway = ay;
8928   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8929   static int xy[4][2] =
8930   {
8931     { 0, -1 },
8932     { -1, 0 },
8933     { +1, 0 },
8934     { 0, +1 }
8935   };
8936
8937   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8938   {
8939     Feld[ax][ay] = EL_AMOEBA_DEAD;
8940     TEST_DrawLevelField(ax, ay);
8941     return;
8942   }
8943
8944   if (IS_ANIMATED(graphic))
8945     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8946
8947   if (!MovDelay[ax][ay])        // start making new amoeba field
8948     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8949
8950   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8951   {
8952     MovDelay[ax][ay]--;
8953     if (MovDelay[ax][ay])
8954       return;
8955   }
8956
8957   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8958   {
8959     int start = RND(4);
8960     int x = ax + xy[start][0];
8961     int y = ay + xy[start][1];
8962
8963     if (!IN_LEV_FIELD(x, y))
8964       return;
8965
8966     if (IS_FREE(x, y) ||
8967         CAN_GROW_INTO(Feld[x][y]) ||
8968         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8969         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8970     {
8971       newax = x;
8972       neway = y;
8973     }
8974
8975     if (newax == ax && neway == ay)
8976       return;
8977   }
8978   else                          // normal or "filled" (BD style) amoeba
8979   {
8980     int start = RND(4);
8981     boolean waiting_for_player = FALSE;
8982
8983     for (i = 0; i < NUM_DIRECTIONS; i++)
8984     {
8985       int j = (start + i) % 4;
8986       int x = ax + xy[j][0];
8987       int y = ay + xy[j][1];
8988
8989       if (!IN_LEV_FIELD(x, y))
8990         continue;
8991
8992       if (IS_FREE(x, y) ||
8993           CAN_GROW_INTO(Feld[x][y]) ||
8994           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8995           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8996       {
8997         newax = x;
8998         neway = y;
8999         break;
9000       }
9001       else if (IS_PLAYER(x, y))
9002         waiting_for_player = TRUE;
9003     }
9004
9005     if (newax == ax && neway == ay)             // amoeba cannot grow
9006     {
9007       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9008       {
9009         Feld[ax][ay] = EL_AMOEBA_DEAD;
9010         TEST_DrawLevelField(ax, ay);
9011         AmoebaCnt[AmoebaNr[ax][ay]]--;
9012
9013         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9014         {
9015           if (element == EL_AMOEBA_FULL)
9016             AmoebeUmwandeln(ax, ay);
9017           else if (element == EL_BD_AMOEBA)
9018             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9019         }
9020       }
9021       return;
9022     }
9023     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9024     {
9025       // amoeba gets larger by growing in some direction
9026
9027       int new_group_nr = AmoebaNr[ax][ay];
9028
9029 #ifdef DEBUG
9030   if (new_group_nr == 0)
9031   {
9032     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9033     printf("AmoebeAbleger(): This should never happen!\n");
9034     return;
9035   }
9036 #endif
9037
9038       AmoebaNr[newax][neway] = new_group_nr;
9039       AmoebaCnt[new_group_nr]++;
9040       AmoebaCnt2[new_group_nr]++;
9041
9042       // if amoeba touches other amoeba(s) after growing, unify them
9043       AmoebenVereinigen(newax, neway);
9044
9045       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9046       {
9047         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9048         return;
9049       }
9050     }
9051   }
9052
9053   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9054       (neway == lev_fieldy - 1 && newax != ax))
9055   {
9056     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9057     Store[newax][neway] = element;
9058   }
9059   else if (neway == ay || element == EL_EMC_DRIPPER)
9060   {
9061     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9062
9063     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9064   }
9065   else
9066   {
9067     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9068     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9069     Store[ax][ay] = EL_AMOEBA_DROP;
9070     ContinueMoving(ax, ay);
9071     return;
9072   }
9073
9074   TEST_DrawLevelField(newax, neway);
9075 }
9076
9077 static void Life(int ax, int ay)
9078 {
9079   int x1, y1, x2, y2;
9080   int life_time = 40;
9081   int element = Feld[ax][ay];
9082   int graphic = el2img(element);
9083   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9084                          level.biomaze);
9085   boolean changed = FALSE;
9086
9087   if (IS_ANIMATED(graphic))
9088     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9089
9090   if (Stop[ax][ay])
9091     return;
9092
9093   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9094     MovDelay[ax][ay] = life_time;
9095
9096   if (MovDelay[ax][ay])         // wait some time before next cycle
9097   {
9098     MovDelay[ax][ay]--;
9099     if (MovDelay[ax][ay])
9100       return;
9101   }
9102
9103   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9104   {
9105     int xx = ax+x1, yy = ay+y1;
9106     int old_element = Feld[xx][yy];
9107     int num_neighbours = 0;
9108
9109     if (!IN_LEV_FIELD(xx, yy))
9110       continue;
9111
9112     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9113     {
9114       int x = xx+x2, y = yy+y2;
9115
9116       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9117         continue;
9118
9119       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9120       boolean is_neighbour = FALSE;
9121
9122       if (level.use_life_bugs)
9123         is_neighbour =
9124           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9125            (IS_FREE(x, y)                             &&  Stop[x][y]));
9126       else
9127         is_neighbour =
9128           (Last[x][y] == element || is_player_cell);
9129
9130       if (is_neighbour)
9131         num_neighbours++;
9132     }
9133
9134     boolean is_free = FALSE;
9135
9136     if (level.use_life_bugs)
9137       is_free = (IS_FREE(xx, yy));
9138     else
9139       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9140
9141     if (xx == ax && yy == ay)           // field in the middle
9142     {
9143       if (num_neighbours < life_parameter[0] ||
9144           num_neighbours > life_parameter[1])
9145       {
9146         Feld[xx][yy] = EL_EMPTY;
9147         if (Feld[xx][yy] != old_element)
9148           TEST_DrawLevelField(xx, yy);
9149         Stop[xx][yy] = TRUE;
9150         changed = TRUE;
9151       }
9152     }
9153     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9154     {                                   // free border field
9155       if (num_neighbours >= life_parameter[2] &&
9156           num_neighbours <= life_parameter[3])
9157       {
9158         Feld[xx][yy] = element;
9159         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9160         if (Feld[xx][yy] != old_element)
9161           TEST_DrawLevelField(xx, yy);
9162         Stop[xx][yy] = TRUE;
9163         changed = TRUE;
9164       }
9165     }
9166   }
9167
9168   if (changed)
9169     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9170                    SND_GAME_OF_LIFE_GROWING);
9171 }
9172
9173 static void InitRobotWheel(int x, int y)
9174 {
9175   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9176 }
9177
9178 static void RunRobotWheel(int x, int y)
9179 {
9180   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9181 }
9182
9183 static void StopRobotWheel(int x, int y)
9184 {
9185   if (game.robot_wheel_x == x &&
9186       game.robot_wheel_y == y)
9187   {
9188     game.robot_wheel_x = -1;
9189     game.robot_wheel_y = -1;
9190     game.robot_wheel_active = FALSE;
9191   }
9192 }
9193
9194 static void InitTimegateWheel(int x, int y)
9195 {
9196   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9197 }
9198
9199 static void RunTimegateWheel(int x, int y)
9200 {
9201   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9202 }
9203
9204 static void InitMagicBallDelay(int x, int y)
9205 {
9206   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9207 }
9208
9209 static void ActivateMagicBall(int bx, int by)
9210 {
9211   int x, y;
9212
9213   if (level.ball_random)
9214   {
9215     int pos_border = RND(8);    // select one of the eight border elements
9216     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9217     int xx = pos_content % 3;
9218     int yy = pos_content / 3;
9219
9220     x = bx - 1 + xx;
9221     y = by - 1 + yy;
9222
9223     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9224       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9225   }
9226   else
9227   {
9228     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9229     {
9230       int xx = x - bx + 1;
9231       int yy = y - by + 1;
9232
9233       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9234         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9235     }
9236   }
9237
9238   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9239 }
9240
9241 static void CheckExit(int x, int y)
9242 {
9243   if (game.gems_still_needed > 0 ||
9244       game.sokoban_fields_still_needed > 0 ||
9245       game.sokoban_objects_still_needed > 0 ||
9246       game.lights_still_needed > 0)
9247   {
9248     int element = Feld[x][y];
9249     int graphic = el2img(element);
9250
9251     if (IS_ANIMATED(graphic))
9252       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9253
9254     return;
9255   }
9256
9257   // do not re-open exit door closed after last player
9258   if (game.all_players_gone)
9259     return;
9260
9261   Feld[x][y] = EL_EXIT_OPENING;
9262
9263   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9264 }
9265
9266 static void CheckExitEM(int x, int y)
9267 {
9268   if (game.gems_still_needed > 0 ||
9269       game.sokoban_fields_still_needed > 0 ||
9270       game.sokoban_objects_still_needed > 0 ||
9271       game.lights_still_needed > 0)
9272   {
9273     int element = Feld[x][y];
9274     int graphic = el2img(element);
9275
9276     if (IS_ANIMATED(graphic))
9277       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9278
9279     return;
9280   }
9281
9282   // do not re-open exit door closed after last player
9283   if (game.all_players_gone)
9284     return;
9285
9286   Feld[x][y] = EL_EM_EXIT_OPENING;
9287
9288   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9289 }
9290
9291 static void CheckExitSteel(int x, int y)
9292 {
9293   if (game.gems_still_needed > 0 ||
9294       game.sokoban_fields_still_needed > 0 ||
9295       game.sokoban_objects_still_needed > 0 ||
9296       game.lights_still_needed > 0)
9297   {
9298     int element = Feld[x][y];
9299     int graphic = el2img(element);
9300
9301     if (IS_ANIMATED(graphic))
9302       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9303
9304     return;
9305   }
9306
9307   // do not re-open exit door closed after last player
9308   if (game.all_players_gone)
9309     return;
9310
9311   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9312
9313   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9314 }
9315
9316 static void CheckExitSteelEM(int x, int y)
9317 {
9318   if (game.gems_still_needed > 0 ||
9319       game.sokoban_fields_still_needed > 0 ||
9320       game.sokoban_objects_still_needed > 0 ||
9321       game.lights_still_needed > 0)
9322   {
9323     int element = Feld[x][y];
9324     int graphic = el2img(element);
9325
9326     if (IS_ANIMATED(graphic))
9327       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9328
9329     return;
9330   }
9331
9332   // do not re-open exit door closed after last player
9333   if (game.all_players_gone)
9334     return;
9335
9336   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9337
9338   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9339 }
9340
9341 static void CheckExitSP(int x, int y)
9342 {
9343   if (game.gems_still_needed > 0)
9344   {
9345     int element = Feld[x][y];
9346     int graphic = el2img(element);
9347
9348     if (IS_ANIMATED(graphic))
9349       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9350
9351     return;
9352   }
9353
9354   // do not re-open exit door closed after last player
9355   if (game.all_players_gone)
9356     return;
9357
9358   Feld[x][y] = EL_SP_EXIT_OPENING;
9359
9360   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9361 }
9362
9363 static void CloseAllOpenTimegates(void)
9364 {
9365   int x, y;
9366
9367   SCAN_PLAYFIELD(x, y)
9368   {
9369     int element = Feld[x][y];
9370
9371     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9372     {
9373       Feld[x][y] = EL_TIMEGATE_CLOSING;
9374
9375       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9376     }
9377   }
9378 }
9379
9380 static void DrawTwinkleOnField(int x, int y)
9381 {
9382   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9383     return;
9384
9385   if (Feld[x][y] == EL_BD_DIAMOND)
9386     return;
9387
9388   if (MovDelay[x][y] == 0)      // next animation frame
9389     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9390
9391   if (MovDelay[x][y] != 0)      // wait some time before next frame
9392   {
9393     MovDelay[x][y]--;
9394
9395     DrawLevelElementAnimation(x, y, Feld[x][y]);
9396
9397     if (MovDelay[x][y] != 0)
9398     {
9399       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9400                                            10 - MovDelay[x][y]);
9401
9402       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9403     }
9404   }
9405 }
9406
9407 static void MauerWaechst(int x, int y)
9408 {
9409   int delay = 6;
9410
9411   if (!MovDelay[x][y])          // next animation frame
9412     MovDelay[x][y] = 3 * delay;
9413
9414   if (MovDelay[x][y])           // wait some time before next frame
9415   {
9416     MovDelay[x][y]--;
9417
9418     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9419     {
9420       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9421       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9422
9423       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9424     }
9425
9426     if (!MovDelay[x][y])
9427     {
9428       if (MovDir[x][y] == MV_LEFT)
9429       {
9430         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9431           TEST_DrawLevelField(x - 1, y);
9432       }
9433       else if (MovDir[x][y] == MV_RIGHT)
9434       {
9435         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9436           TEST_DrawLevelField(x + 1, y);
9437       }
9438       else if (MovDir[x][y] == MV_UP)
9439       {
9440         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9441           TEST_DrawLevelField(x, y - 1);
9442       }
9443       else
9444       {
9445         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9446           TEST_DrawLevelField(x, y + 1);
9447       }
9448
9449       Feld[x][y] = Store[x][y];
9450       Store[x][y] = 0;
9451       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9452       TEST_DrawLevelField(x, y);
9453     }
9454   }
9455 }
9456
9457 static void MauerAbleger(int ax, int ay)
9458 {
9459   int element = Feld[ax][ay];
9460   int graphic = el2img(element);
9461   boolean oben_frei = FALSE, unten_frei = FALSE;
9462   boolean links_frei = FALSE, rechts_frei = FALSE;
9463   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9464   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9465   boolean new_wall = FALSE;
9466
9467   if (IS_ANIMATED(graphic))
9468     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9469
9470   if (!MovDelay[ax][ay])        // start building new wall
9471     MovDelay[ax][ay] = 6;
9472
9473   if (MovDelay[ax][ay])         // wait some time before building new wall
9474   {
9475     MovDelay[ax][ay]--;
9476     if (MovDelay[ax][ay])
9477       return;
9478   }
9479
9480   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9481     oben_frei = TRUE;
9482   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9483     unten_frei = TRUE;
9484   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9485     links_frei = TRUE;
9486   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9487     rechts_frei = TRUE;
9488
9489   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9490       element == EL_EXPANDABLE_WALL_ANY)
9491   {
9492     if (oben_frei)
9493     {
9494       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9495       Store[ax][ay-1] = element;
9496       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9497       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9498         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9499                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9500       new_wall = TRUE;
9501     }
9502     if (unten_frei)
9503     {
9504       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9505       Store[ax][ay+1] = element;
9506       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9507       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9508         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9509                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9510       new_wall = TRUE;
9511     }
9512   }
9513
9514   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9515       element == EL_EXPANDABLE_WALL_ANY ||
9516       element == EL_EXPANDABLE_WALL ||
9517       element == EL_BD_EXPANDABLE_WALL)
9518   {
9519     if (links_frei)
9520     {
9521       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9522       Store[ax-1][ay] = element;
9523       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9524       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9525         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9526                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9527       new_wall = TRUE;
9528     }
9529
9530     if (rechts_frei)
9531     {
9532       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9533       Store[ax+1][ay] = element;
9534       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9535       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9536         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9537                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9538       new_wall = TRUE;
9539     }
9540   }
9541
9542   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9543     TEST_DrawLevelField(ax, ay);
9544
9545   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9546     oben_massiv = TRUE;
9547   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9548     unten_massiv = TRUE;
9549   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9550     links_massiv = TRUE;
9551   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9552     rechts_massiv = TRUE;
9553
9554   if (((oben_massiv && unten_massiv) ||
9555        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9556        element == EL_EXPANDABLE_WALL) &&
9557       ((links_massiv && rechts_massiv) ||
9558        element == EL_EXPANDABLE_WALL_VERTICAL))
9559     Feld[ax][ay] = EL_WALL;
9560
9561   if (new_wall)
9562     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9563 }
9564
9565 static void MauerAblegerStahl(int ax, int ay)
9566 {
9567   int element = Feld[ax][ay];
9568   int graphic = el2img(element);
9569   boolean oben_frei = FALSE, unten_frei = FALSE;
9570   boolean links_frei = FALSE, rechts_frei = FALSE;
9571   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9572   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9573   boolean new_wall = FALSE;
9574
9575   if (IS_ANIMATED(graphic))
9576     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9577
9578   if (!MovDelay[ax][ay])        // start building new wall
9579     MovDelay[ax][ay] = 6;
9580
9581   if (MovDelay[ax][ay])         // wait some time before building new wall
9582   {
9583     MovDelay[ax][ay]--;
9584     if (MovDelay[ax][ay])
9585       return;
9586   }
9587
9588   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9589     oben_frei = TRUE;
9590   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9591     unten_frei = TRUE;
9592   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9593     links_frei = TRUE;
9594   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9595     rechts_frei = TRUE;
9596
9597   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9598       element == EL_EXPANDABLE_STEELWALL_ANY)
9599   {
9600     if (oben_frei)
9601     {
9602       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9603       Store[ax][ay-1] = element;
9604       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9605       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9606         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9607                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9608       new_wall = TRUE;
9609     }
9610     if (unten_frei)
9611     {
9612       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9613       Store[ax][ay+1] = element;
9614       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9615       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9616         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9617                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9618       new_wall = TRUE;
9619     }
9620   }
9621
9622   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9623       element == EL_EXPANDABLE_STEELWALL_ANY)
9624   {
9625     if (links_frei)
9626     {
9627       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9628       Store[ax-1][ay] = element;
9629       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9630       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9631         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9632                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9633       new_wall = TRUE;
9634     }
9635
9636     if (rechts_frei)
9637     {
9638       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9639       Store[ax+1][ay] = element;
9640       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9641       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9642         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9643                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9644       new_wall = TRUE;
9645     }
9646   }
9647
9648   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9649     oben_massiv = TRUE;
9650   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9651     unten_massiv = TRUE;
9652   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9653     links_massiv = TRUE;
9654   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9655     rechts_massiv = TRUE;
9656
9657   if (((oben_massiv && unten_massiv) ||
9658        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9659       ((links_massiv && rechts_massiv) ||
9660        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9661     Feld[ax][ay] = EL_STEELWALL;
9662
9663   if (new_wall)
9664     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9665 }
9666
9667 static void CheckForDragon(int x, int y)
9668 {
9669   int i, j;
9670   boolean dragon_found = FALSE;
9671   static int xy[4][2] =
9672   {
9673     { 0, -1 },
9674     { -1, 0 },
9675     { +1, 0 },
9676     { 0, +1 }
9677   };
9678
9679   for (i = 0; i < NUM_DIRECTIONS; i++)
9680   {
9681     for (j = 0; j < 4; j++)
9682     {
9683       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9684
9685       if (IN_LEV_FIELD(xx, yy) &&
9686           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9687       {
9688         if (Feld[xx][yy] == EL_DRAGON)
9689           dragon_found = TRUE;
9690       }
9691       else
9692         break;
9693     }
9694   }
9695
9696   if (!dragon_found)
9697   {
9698     for (i = 0; i < NUM_DIRECTIONS; i++)
9699     {
9700       for (j = 0; j < 3; j++)
9701       {
9702         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9703   
9704         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9705         {
9706           Feld[xx][yy] = EL_EMPTY;
9707           TEST_DrawLevelField(xx, yy);
9708         }
9709         else
9710           break;
9711       }
9712     }
9713   }
9714 }
9715
9716 static void InitBuggyBase(int x, int y)
9717 {
9718   int element = Feld[x][y];
9719   int activating_delay = FRAMES_PER_SECOND / 4;
9720
9721   ChangeDelay[x][y] =
9722     (element == EL_SP_BUGGY_BASE ?
9723      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9724      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9725      activating_delay :
9726      element == EL_SP_BUGGY_BASE_ACTIVE ?
9727      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9728 }
9729
9730 static void WarnBuggyBase(int x, int y)
9731 {
9732   int i;
9733   static int xy[4][2] =
9734   {
9735     { 0, -1 },
9736     { -1, 0 },
9737     { +1, 0 },
9738     { 0, +1 }
9739   };
9740
9741   for (i = 0; i < NUM_DIRECTIONS; i++)
9742   {
9743     int xx = x + xy[i][0];
9744     int yy = y + xy[i][1];
9745
9746     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9747     {
9748       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9749
9750       break;
9751     }
9752   }
9753 }
9754
9755 static void InitTrap(int x, int y)
9756 {
9757   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9758 }
9759
9760 static void ActivateTrap(int x, int y)
9761 {
9762   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9763 }
9764
9765 static void ChangeActiveTrap(int x, int y)
9766 {
9767   int graphic = IMG_TRAP_ACTIVE;
9768
9769   // if new animation frame was drawn, correct crumbled sand border
9770   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9771     TEST_DrawLevelFieldCrumbled(x, y);
9772 }
9773
9774 static int getSpecialActionElement(int element, int number, int base_element)
9775 {
9776   return (element != EL_EMPTY ? element :
9777           number != -1 ? base_element + number - 1 :
9778           EL_EMPTY);
9779 }
9780
9781 static int getModifiedActionNumber(int value_old, int operator, int operand,
9782                                    int value_min, int value_max)
9783 {
9784   int value_new = (operator == CA_MODE_SET      ? operand :
9785                    operator == CA_MODE_ADD      ? value_old + operand :
9786                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9787                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9788                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9789                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9790                    value_old);
9791
9792   return (value_new < value_min ? value_min :
9793           value_new > value_max ? value_max :
9794           value_new);
9795 }
9796
9797 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9798 {
9799   struct ElementInfo *ei = &element_info[element];
9800   struct ElementChangeInfo *change = &ei->change_page[page];
9801   int target_element = change->target_element;
9802   int action_type = change->action_type;
9803   int action_mode = change->action_mode;
9804   int action_arg = change->action_arg;
9805   int action_element = change->action_element;
9806   int i;
9807
9808   if (!change->has_action)
9809     return;
9810
9811   // ---------- determine action paramater values -----------------------------
9812
9813   int level_time_value =
9814     (level.time > 0 ? TimeLeft :
9815      TimePlayed);
9816
9817   int action_arg_element_raw =
9818     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9819      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9820      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9821      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9822      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9823      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9824      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9825      EL_EMPTY);
9826   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9827
9828   int action_arg_direction =
9829     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9830      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9831      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9832      change->actual_trigger_side :
9833      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9834      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9835      MV_NONE);
9836
9837   int action_arg_number_min =
9838     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9839      CA_ARG_MIN);
9840
9841   int action_arg_number_max =
9842     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9843      action_type == CA_SET_LEVEL_GEMS ? 999 :
9844      action_type == CA_SET_LEVEL_TIME ? 9999 :
9845      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9846      action_type == CA_SET_CE_VALUE ? 9999 :
9847      action_type == CA_SET_CE_SCORE ? 9999 :
9848      CA_ARG_MAX);
9849
9850   int action_arg_number_reset =
9851     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9852      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9853      action_type == CA_SET_LEVEL_TIME ? level.time :
9854      action_type == CA_SET_LEVEL_SCORE ? 0 :
9855      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9856      action_type == CA_SET_CE_SCORE ? 0 :
9857      0);
9858
9859   int action_arg_number =
9860     (action_arg <= CA_ARG_MAX ? action_arg :
9861      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9862      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9863      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9864      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9865      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9866      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9867      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9868      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9869      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9870      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9871      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9872      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9873      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9874      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9875      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9876      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9877      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9878      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9879      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9880      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9881      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9882      -1);
9883
9884   int action_arg_number_old =
9885     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9886      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9887      action_type == CA_SET_LEVEL_SCORE ? game.score :
9888      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9889      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9890      0);
9891
9892   int action_arg_number_new =
9893     getModifiedActionNumber(action_arg_number_old,
9894                             action_mode, action_arg_number,
9895                             action_arg_number_min, action_arg_number_max);
9896
9897   int trigger_player_bits =
9898     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9899      change->actual_trigger_player_bits : change->trigger_player);
9900
9901   int action_arg_player_bits =
9902     (action_arg >= CA_ARG_PLAYER_1 &&
9903      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9904      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9905      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9906      PLAYER_BITS_ANY);
9907
9908   // ---------- execute action  -----------------------------------------------
9909
9910   switch (action_type)
9911   {
9912     case CA_NO_ACTION:
9913     {
9914       return;
9915     }
9916
9917     // ---------- level actions  ----------------------------------------------
9918
9919     case CA_RESTART_LEVEL:
9920     {
9921       game.restart_level = TRUE;
9922
9923       break;
9924     }
9925
9926     case CA_SHOW_ENVELOPE:
9927     {
9928       int element = getSpecialActionElement(action_arg_element,
9929                                             action_arg_number, EL_ENVELOPE_1);
9930
9931       if (IS_ENVELOPE(element))
9932         local_player->show_envelope = element;
9933
9934       break;
9935     }
9936
9937     case CA_SET_LEVEL_TIME:
9938     {
9939       if (level.time > 0)       // only modify limited time value
9940       {
9941         TimeLeft = action_arg_number_new;
9942
9943         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9944
9945         DisplayGameControlValues();
9946
9947         if (!TimeLeft && setup.time_limit)
9948           for (i = 0; i < MAX_PLAYERS; i++)
9949             KillPlayer(&stored_player[i]);
9950       }
9951
9952       break;
9953     }
9954
9955     case CA_SET_LEVEL_SCORE:
9956     {
9957       game.score = action_arg_number_new;
9958
9959       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9960
9961       DisplayGameControlValues();
9962
9963       break;
9964     }
9965
9966     case CA_SET_LEVEL_GEMS:
9967     {
9968       game.gems_still_needed = action_arg_number_new;
9969
9970       game.snapshot.collected_item = TRUE;
9971
9972       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9973
9974       DisplayGameControlValues();
9975
9976       break;
9977     }
9978
9979     case CA_SET_LEVEL_WIND:
9980     {
9981       game.wind_direction = action_arg_direction;
9982
9983       break;
9984     }
9985
9986     case CA_SET_LEVEL_RANDOM_SEED:
9987     {
9988       // ensure that setting a new random seed while playing is predictable
9989       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9990
9991       break;
9992     }
9993
9994     // ---------- player actions  ---------------------------------------------
9995
9996     case CA_MOVE_PLAYER:
9997     case CA_MOVE_PLAYER_NEW:
9998     {
9999       // automatically move to the next field in specified direction
10000       for (i = 0; i < MAX_PLAYERS; i++)
10001         if (trigger_player_bits & (1 << i))
10002           if (action_type == CA_MOVE_PLAYER ||
10003               stored_player[i].MovPos == 0)
10004             stored_player[i].programmed_action = action_arg_direction;
10005
10006       break;
10007     }
10008
10009     case CA_EXIT_PLAYER:
10010     {
10011       for (i = 0; i < MAX_PLAYERS; i++)
10012         if (action_arg_player_bits & (1 << i))
10013           ExitPlayer(&stored_player[i]);
10014
10015       if (game.players_still_needed == 0)
10016         LevelSolved();
10017
10018       break;
10019     }
10020
10021     case CA_KILL_PLAYER:
10022     {
10023       for (i = 0; i < MAX_PLAYERS; i++)
10024         if (action_arg_player_bits & (1 << i))
10025           KillPlayer(&stored_player[i]);
10026
10027       break;
10028     }
10029
10030     case CA_SET_PLAYER_KEYS:
10031     {
10032       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10033       int element = getSpecialActionElement(action_arg_element,
10034                                             action_arg_number, EL_KEY_1);
10035
10036       if (IS_KEY(element))
10037       {
10038         for (i = 0; i < MAX_PLAYERS; i++)
10039         {
10040           if (trigger_player_bits & (1 << i))
10041           {
10042             stored_player[i].key[KEY_NR(element)] = key_state;
10043
10044             DrawGameDoorValues();
10045           }
10046         }
10047       }
10048
10049       break;
10050     }
10051
10052     case CA_SET_PLAYER_SPEED:
10053     {
10054       for (i = 0; i < MAX_PLAYERS; i++)
10055       {
10056         if (trigger_player_bits & (1 << i))
10057         {
10058           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10059
10060           if (action_arg == CA_ARG_SPEED_FASTER &&
10061               stored_player[i].cannot_move)
10062           {
10063             action_arg_number = STEPSIZE_VERY_SLOW;
10064           }
10065           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10066                    action_arg == CA_ARG_SPEED_FASTER)
10067           {
10068             action_arg_number = 2;
10069             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10070                            CA_MODE_MULTIPLY);
10071           }
10072           else if (action_arg == CA_ARG_NUMBER_RESET)
10073           {
10074             action_arg_number = level.initial_player_stepsize[i];
10075           }
10076
10077           move_stepsize =
10078             getModifiedActionNumber(move_stepsize,
10079                                     action_mode,
10080                                     action_arg_number,
10081                                     action_arg_number_min,
10082                                     action_arg_number_max);
10083
10084           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10085         }
10086       }
10087
10088       break;
10089     }
10090
10091     case CA_SET_PLAYER_SHIELD:
10092     {
10093       for (i = 0; i < MAX_PLAYERS; i++)
10094       {
10095         if (trigger_player_bits & (1 << i))
10096         {
10097           if (action_arg == CA_ARG_SHIELD_OFF)
10098           {
10099             stored_player[i].shield_normal_time_left = 0;
10100             stored_player[i].shield_deadly_time_left = 0;
10101           }
10102           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10103           {
10104             stored_player[i].shield_normal_time_left = 999999;
10105           }
10106           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10107           {
10108             stored_player[i].shield_normal_time_left = 999999;
10109             stored_player[i].shield_deadly_time_left = 999999;
10110           }
10111         }
10112       }
10113
10114       break;
10115     }
10116
10117     case CA_SET_PLAYER_GRAVITY:
10118     {
10119       for (i = 0; i < MAX_PLAYERS; i++)
10120       {
10121         if (trigger_player_bits & (1 << i))
10122         {
10123           stored_player[i].gravity =
10124             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10125              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10126              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10127              stored_player[i].gravity);
10128         }
10129       }
10130
10131       break;
10132     }
10133
10134     case CA_SET_PLAYER_ARTWORK:
10135     {
10136       for (i = 0; i < MAX_PLAYERS; i++)
10137       {
10138         if (trigger_player_bits & (1 << i))
10139         {
10140           int artwork_element = action_arg_element;
10141
10142           if (action_arg == CA_ARG_ELEMENT_RESET)
10143             artwork_element =
10144               (level.use_artwork_element[i] ? level.artwork_element[i] :
10145                stored_player[i].element_nr);
10146
10147           if (stored_player[i].artwork_element != artwork_element)
10148             stored_player[i].Frame = 0;
10149
10150           stored_player[i].artwork_element = artwork_element;
10151
10152           SetPlayerWaiting(&stored_player[i], FALSE);
10153
10154           // set number of special actions for bored and sleeping animation
10155           stored_player[i].num_special_action_bored =
10156             get_num_special_action(artwork_element,
10157                                    ACTION_BORING_1, ACTION_BORING_LAST);
10158           stored_player[i].num_special_action_sleeping =
10159             get_num_special_action(artwork_element,
10160                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10161         }
10162       }
10163
10164       break;
10165     }
10166
10167     case CA_SET_PLAYER_INVENTORY:
10168     {
10169       for (i = 0; i < MAX_PLAYERS; i++)
10170       {
10171         struct PlayerInfo *player = &stored_player[i];
10172         int j, k;
10173
10174         if (trigger_player_bits & (1 << i))
10175         {
10176           int inventory_element = action_arg_element;
10177
10178           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10179               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10180               action_arg == CA_ARG_ELEMENT_ACTION)
10181           {
10182             int element = inventory_element;
10183             int collect_count = element_info[element].collect_count_initial;
10184
10185             if (!IS_CUSTOM_ELEMENT(element))
10186               collect_count = 1;
10187
10188             if (collect_count == 0)
10189               player->inventory_infinite_element = element;
10190             else
10191               for (k = 0; k < collect_count; k++)
10192                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10193                   player->inventory_element[player->inventory_size++] =
10194                     element;
10195           }
10196           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10197                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10198                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10199           {
10200             if (player->inventory_infinite_element != EL_UNDEFINED &&
10201                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10202                                      action_arg_element_raw))
10203               player->inventory_infinite_element = EL_UNDEFINED;
10204
10205             for (k = 0, j = 0; j < player->inventory_size; j++)
10206             {
10207               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10208                                         action_arg_element_raw))
10209                 player->inventory_element[k++] = player->inventory_element[j];
10210             }
10211
10212             player->inventory_size = k;
10213           }
10214           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10215           {
10216             if (player->inventory_size > 0)
10217             {
10218               for (j = 0; j < player->inventory_size - 1; j++)
10219                 player->inventory_element[j] = player->inventory_element[j + 1];
10220
10221               player->inventory_size--;
10222             }
10223           }
10224           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10225           {
10226             if (player->inventory_size > 0)
10227               player->inventory_size--;
10228           }
10229           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10230           {
10231             player->inventory_infinite_element = EL_UNDEFINED;
10232             player->inventory_size = 0;
10233           }
10234           else if (action_arg == CA_ARG_INVENTORY_RESET)
10235           {
10236             player->inventory_infinite_element = EL_UNDEFINED;
10237             player->inventory_size = 0;
10238
10239             if (level.use_initial_inventory[i])
10240             {
10241               for (j = 0; j < level.initial_inventory_size[i]; j++)
10242               {
10243                 int element = level.initial_inventory_content[i][j];
10244                 int collect_count = element_info[element].collect_count_initial;
10245
10246                 if (!IS_CUSTOM_ELEMENT(element))
10247                   collect_count = 1;
10248
10249                 if (collect_count == 0)
10250                   player->inventory_infinite_element = element;
10251                 else
10252                   for (k = 0; k < collect_count; k++)
10253                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10254                       player->inventory_element[player->inventory_size++] =
10255                         element;
10256               }
10257             }
10258           }
10259         }
10260       }
10261
10262       break;
10263     }
10264
10265     // ---------- CE actions  -------------------------------------------------
10266
10267     case CA_SET_CE_VALUE:
10268     {
10269       int last_ce_value = CustomValue[x][y];
10270
10271       CustomValue[x][y] = action_arg_number_new;
10272
10273       if (CustomValue[x][y] != last_ce_value)
10274       {
10275         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10276         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10277
10278         if (CustomValue[x][y] == 0)
10279         {
10280           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10281           ChangeCount[x][y] = 0;        // allow at least one more change
10282
10283           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10284           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10285         }
10286       }
10287
10288       break;
10289     }
10290
10291     case CA_SET_CE_SCORE:
10292     {
10293       int last_ce_score = ei->collect_score;
10294
10295       ei->collect_score = action_arg_number_new;
10296
10297       if (ei->collect_score != last_ce_score)
10298       {
10299         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10300         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10301
10302         if (ei->collect_score == 0)
10303         {
10304           int xx, yy;
10305
10306           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10307           ChangeCount[x][y] = 0;        // allow at least one more change
10308
10309           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10310           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10311
10312           /*
10313             This is a very special case that seems to be a mixture between
10314             CheckElementChange() and CheckTriggeredElementChange(): while
10315             the first one only affects single elements that are triggered
10316             directly, the second one affects multiple elements in the playfield
10317             that are triggered indirectly by another element. This is a third
10318             case: Changing the CE score always affects multiple identical CEs,
10319             so every affected CE must be checked, not only the single CE for
10320             which the CE score was changed in the first place (as every instance
10321             of that CE shares the same CE score, and therefore also can change)!
10322           */
10323           SCAN_PLAYFIELD(xx, yy)
10324           {
10325             if (Feld[xx][yy] == element)
10326               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10327                                  CE_SCORE_GETS_ZERO);
10328           }
10329         }
10330       }
10331
10332       break;
10333     }
10334
10335     case CA_SET_CE_ARTWORK:
10336     {
10337       int artwork_element = action_arg_element;
10338       boolean reset_frame = FALSE;
10339       int xx, yy;
10340
10341       if (action_arg == CA_ARG_ELEMENT_RESET)
10342         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10343                            element);
10344
10345       if (ei->gfx_element != artwork_element)
10346         reset_frame = TRUE;
10347
10348       ei->gfx_element = artwork_element;
10349
10350       SCAN_PLAYFIELD(xx, yy)
10351       {
10352         if (Feld[xx][yy] == element)
10353         {
10354           if (reset_frame)
10355           {
10356             ResetGfxAnimation(xx, yy);
10357             ResetRandomAnimationValue(xx, yy);
10358           }
10359
10360           TEST_DrawLevelField(xx, yy);
10361         }
10362       }
10363
10364       break;
10365     }
10366
10367     // ---------- engine actions  ---------------------------------------------
10368
10369     case CA_SET_ENGINE_SCAN_MODE:
10370     {
10371       InitPlayfieldScanMode(action_arg);
10372
10373       break;
10374     }
10375
10376     default:
10377       break;
10378   }
10379 }
10380
10381 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10382 {
10383   int old_element = Feld[x][y];
10384   int new_element = GetElementFromGroupElement(element);
10385   int previous_move_direction = MovDir[x][y];
10386   int last_ce_value = CustomValue[x][y];
10387   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10388   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10389   boolean add_player_onto_element = (new_element_is_player &&
10390                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10391                                      IS_WALKABLE(old_element));
10392
10393   if (!add_player_onto_element)
10394   {
10395     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10396       RemoveMovingField(x, y);
10397     else
10398       RemoveField(x, y);
10399
10400     Feld[x][y] = new_element;
10401
10402     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10403       MovDir[x][y] = previous_move_direction;
10404
10405     if (element_info[new_element].use_last_ce_value)
10406       CustomValue[x][y] = last_ce_value;
10407
10408     InitField_WithBug1(x, y, FALSE);
10409
10410     new_element = Feld[x][y];   // element may have changed
10411
10412     ResetGfxAnimation(x, y);
10413     ResetRandomAnimationValue(x, y);
10414
10415     TEST_DrawLevelField(x, y);
10416
10417     if (GFX_CRUMBLED(new_element))
10418       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10419   }
10420
10421   // check if element under the player changes from accessible to unaccessible
10422   // (needed for special case of dropping element which then changes)
10423   // (must be checked after creating new element for walkable group elements)
10424   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10425       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10426   {
10427     Bang(x, y);
10428
10429     return;
10430   }
10431
10432   // "ChangeCount" not set yet to allow "entered by player" change one time
10433   if (new_element_is_player)
10434     RelocatePlayer(x, y, new_element);
10435
10436   if (is_change)
10437     ChangeCount[x][y]++;        // count number of changes in the same frame
10438
10439   TestIfBadThingTouchesPlayer(x, y);
10440   TestIfPlayerTouchesCustomElement(x, y);
10441   TestIfElementTouchesCustomElement(x, y);
10442 }
10443
10444 static void CreateField(int x, int y, int element)
10445 {
10446   CreateFieldExt(x, y, element, FALSE);
10447 }
10448
10449 static void CreateElementFromChange(int x, int y, int element)
10450 {
10451   element = GET_VALID_RUNTIME_ELEMENT(element);
10452
10453   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10454   {
10455     int old_element = Feld[x][y];
10456
10457     // prevent changed element from moving in same engine frame
10458     // unless both old and new element can either fall or move
10459     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10460         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10461       Stop[x][y] = TRUE;
10462   }
10463
10464   CreateFieldExt(x, y, element, TRUE);
10465 }
10466
10467 static boolean ChangeElement(int x, int y, int element, int page)
10468 {
10469   struct ElementInfo *ei = &element_info[element];
10470   struct ElementChangeInfo *change = &ei->change_page[page];
10471   int ce_value = CustomValue[x][y];
10472   int ce_score = ei->collect_score;
10473   int target_element;
10474   int old_element = Feld[x][y];
10475
10476   // always use default change event to prevent running into a loop
10477   if (ChangeEvent[x][y] == -1)
10478     ChangeEvent[x][y] = CE_DELAY;
10479
10480   if (ChangeEvent[x][y] == CE_DELAY)
10481   {
10482     // reset actual trigger element, trigger player and action element
10483     change->actual_trigger_element = EL_EMPTY;
10484     change->actual_trigger_player = EL_EMPTY;
10485     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10486     change->actual_trigger_side = CH_SIDE_NONE;
10487     change->actual_trigger_ce_value = 0;
10488     change->actual_trigger_ce_score = 0;
10489   }
10490
10491   // do not change elements more than a specified maximum number of changes
10492   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10493     return FALSE;
10494
10495   ChangeCount[x][y]++;          // count number of changes in the same frame
10496
10497   if (change->explode)
10498   {
10499     Bang(x, y);
10500
10501     return TRUE;
10502   }
10503
10504   if (change->use_target_content)
10505   {
10506     boolean complete_replace = TRUE;
10507     boolean can_replace[3][3];
10508     int xx, yy;
10509
10510     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10511     {
10512       boolean is_empty;
10513       boolean is_walkable;
10514       boolean is_diggable;
10515       boolean is_collectible;
10516       boolean is_removable;
10517       boolean is_destructible;
10518       int ex = x + xx - 1;
10519       int ey = y + yy - 1;
10520       int content_element = change->target_content.e[xx][yy];
10521       int e;
10522
10523       can_replace[xx][yy] = TRUE;
10524
10525       if (ex == x && ey == y)   // do not check changing element itself
10526         continue;
10527
10528       if (content_element == EL_EMPTY_SPACE)
10529       {
10530         can_replace[xx][yy] = FALSE;    // do not replace border with space
10531
10532         continue;
10533       }
10534
10535       if (!IN_LEV_FIELD(ex, ey))
10536       {
10537         can_replace[xx][yy] = FALSE;
10538         complete_replace = FALSE;
10539
10540         continue;
10541       }
10542
10543       e = Feld[ex][ey];
10544
10545       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10546         e = MovingOrBlocked2Element(ex, ey);
10547
10548       is_empty = (IS_FREE(ex, ey) ||
10549                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10550
10551       is_walkable     = (is_empty || IS_WALKABLE(e));
10552       is_diggable     = (is_empty || IS_DIGGABLE(e));
10553       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10554       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10555       is_removable    = (is_diggable || is_collectible);
10556
10557       can_replace[xx][yy] =
10558         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10559           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10560           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10561           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10562           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10563           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10564          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10565
10566       if (!can_replace[xx][yy])
10567         complete_replace = FALSE;
10568     }
10569
10570     if (!change->only_if_complete || complete_replace)
10571     {
10572       boolean something_has_changed = FALSE;
10573
10574       if (change->only_if_complete && change->use_random_replace &&
10575           RND(100) < change->random_percentage)
10576         return FALSE;
10577
10578       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10579       {
10580         int ex = x + xx - 1;
10581         int ey = y + yy - 1;
10582         int content_element;
10583
10584         if (can_replace[xx][yy] && (!change->use_random_replace ||
10585                                     RND(100) < change->random_percentage))
10586         {
10587           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10588             RemoveMovingField(ex, ey);
10589
10590           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10591
10592           content_element = change->target_content.e[xx][yy];
10593           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10594                                               ce_value, ce_score);
10595
10596           CreateElementFromChange(ex, ey, target_element);
10597
10598           something_has_changed = TRUE;
10599
10600           // for symmetry reasons, freeze newly created border elements
10601           if (ex != x || ey != y)
10602             Stop[ex][ey] = TRUE;        // no more moving in this frame
10603         }
10604       }
10605
10606       if (something_has_changed)
10607       {
10608         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10609         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10610       }
10611     }
10612   }
10613   else
10614   {
10615     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10616                                         ce_value, ce_score);
10617
10618     if (element == EL_DIAGONAL_GROWING ||
10619         element == EL_DIAGONAL_SHRINKING)
10620     {
10621       target_element = Store[x][y];
10622
10623       Store[x][y] = EL_EMPTY;
10624     }
10625
10626     CreateElementFromChange(x, y, target_element);
10627
10628     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10629     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10630   }
10631
10632   // this uses direct change before indirect change
10633   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10634
10635   return TRUE;
10636 }
10637
10638 static void HandleElementChange(int x, int y, int page)
10639 {
10640   int element = MovingOrBlocked2Element(x, y);
10641   struct ElementInfo *ei = &element_info[element];
10642   struct ElementChangeInfo *change = &ei->change_page[page];
10643   boolean handle_action_before_change = FALSE;
10644
10645 #ifdef DEBUG
10646   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10647       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10648   {
10649     printf("\n\n");
10650     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10651            x, y, element, element_info[element].token_name);
10652     printf("HandleElementChange(): This should never happen!\n");
10653     printf("\n\n");
10654   }
10655 #endif
10656
10657   // this can happen with classic bombs on walkable, changing elements
10658   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10659   {
10660     return;
10661   }
10662
10663   if (ChangeDelay[x][y] == 0)           // initialize element change
10664   {
10665     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10666
10667     if (change->can_change)
10668     {
10669       // !!! not clear why graphic animation should be reset at all here !!!
10670       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10671       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10672
10673       /*
10674         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10675
10676         When using an animation frame delay of 1 (this only happens with
10677         "sp_zonk.moving.left/right" in the classic graphics), the default
10678         (non-moving) animation shows wrong animation frames (while the
10679         moving animation, like "sp_zonk.moving.left/right", is correct,
10680         so this graphical bug never shows up with the classic graphics).
10681         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10682         be drawn instead of the correct frames 0,1,2,3. This is caused by
10683         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10684         an element change: First when the change delay ("ChangeDelay[][]")
10685         counter has reached zero after decrementing, then a second time in
10686         the next frame (after "GfxFrame[][]" was already incremented) when
10687         "ChangeDelay[][]" is reset to the initial delay value again.
10688
10689         This causes frame 0 to be drawn twice, while the last frame won't
10690         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10691
10692         As some animations may already be cleverly designed around this bug
10693         (at least the "Snake Bite" snake tail animation does this), it cannot
10694         simply be fixed here without breaking such existing animations.
10695         Unfortunately, it cannot easily be detected if a graphics set was
10696         designed "before" or "after" the bug was fixed. As a workaround,
10697         a new graphics set option "game.graphics_engine_version" was added
10698         to be able to specify the game's major release version for which the
10699         graphics set was designed, which can then be used to decide if the
10700         bugfix should be used (version 4 and above) or not (version 3 or
10701         below, or if no version was specified at all, as with old sets).
10702
10703         (The wrong/fixed animation frames can be tested with the test level set
10704         "test_gfxframe" and level "000", which contains a specially prepared
10705         custom element at level position (x/y) == (11/9) which uses the zonk
10706         animation mentioned above. Using "game.graphics_engine_version: 4"
10707         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10708         This can also be seen from the debug output for this test element.)
10709       */
10710
10711       // when a custom element is about to change (for example by change delay),
10712       // do not reset graphic animation when the custom element is moving
10713       if (game.graphics_engine_version < 4 &&
10714           !IS_MOVING(x, y))
10715       {
10716         ResetGfxAnimation(x, y);
10717         ResetRandomAnimationValue(x, y);
10718       }
10719
10720       if (change->pre_change_function)
10721         change->pre_change_function(x, y);
10722     }
10723   }
10724
10725   ChangeDelay[x][y]--;
10726
10727   if (ChangeDelay[x][y] != 0)           // continue element change
10728   {
10729     if (change->can_change)
10730     {
10731       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10732
10733       if (IS_ANIMATED(graphic))
10734         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10735
10736       if (change->change_function)
10737         change->change_function(x, y);
10738     }
10739   }
10740   else                                  // finish element change
10741   {
10742     if (ChangePage[x][y] != -1)         // remember page from delayed change
10743     {
10744       page = ChangePage[x][y];
10745       ChangePage[x][y] = -1;
10746
10747       change = &ei->change_page[page];
10748     }
10749
10750     if (IS_MOVING(x, y))                // never change a running system ;-)
10751     {
10752       ChangeDelay[x][y] = 1;            // try change after next move step
10753       ChangePage[x][y] = page;          // remember page to use for change
10754
10755       return;
10756     }
10757
10758     // special case: set new level random seed before changing element
10759     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10760       handle_action_before_change = TRUE;
10761
10762     if (change->has_action && handle_action_before_change)
10763       ExecuteCustomElementAction(x, y, element, page);
10764
10765     if (change->can_change)
10766     {
10767       if (ChangeElement(x, y, element, page))
10768       {
10769         if (change->post_change_function)
10770           change->post_change_function(x, y);
10771       }
10772     }
10773
10774     if (change->has_action && !handle_action_before_change)
10775       ExecuteCustomElementAction(x, y, element, page);
10776   }
10777 }
10778
10779 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10780                                               int trigger_element,
10781                                               int trigger_event,
10782                                               int trigger_player,
10783                                               int trigger_side,
10784                                               int trigger_page)
10785 {
10786   boolean change_done_any = FALSE;
10787   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10788   int i;
10789
10790   if (!(trigger_events[trigger_element][trigger_event]))
10791     return FALSE;
10792
10793   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10794
10795   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10796   {
10797     int element = EL_CUSTOM_START + i;
10798     boolean change_done = FALSE;
10799     int p;
10800
10801     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10802         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10803       continue;
10804
10805     for (p = 0; p < element_info[element].num_change_pages; p++)
10806     {
10807       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10808
10809       if (change->can_change_or_has_action &&
10810           change->has_event[trigger_event] &&
10811           change->trigger_side & trigger_side &&
10812           change->trigger_player & trigger_player &&
10813           change->trigger_page & trigger_page_bits &&
10814           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10815       {
10816         change->actual_trigger_element = trigger_element;
10817         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10818         change->actual_trigger_player_bits = trigger_player;
10819         change->actual_trigger_side = trigger_side;
10820         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10821         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10822
10823         if ((change->can_change && !change_done) || change->has_action)
10824         {
10825           int x, y;
10826
10827           SCAN_PLAYFIELD(x, y)
10828           {
10829             if (Feld[x][y] == element)
10830             {
10831               if (change->can_change && !change_done)
10832               {
10833                 // if element already changed in this frame, not only prevent
10834                 // another element change (checked in ChangeElement()), but
10835                 // also prevent additional element actions for this element
10836
10837                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10838                     !level.use_action_after_change_bug)
10839                   continue;
10840
10841                 ChangeDelay[x][y] = 1;
10842                 ChangeEvent[x][y] = trigger_event;
10843
10844                 HandleElementChange(x, y, p);
10845               }
10846               else if (change->has_action)
10847               {
10848                 // if element already changed in this frame, not only prevent
10849                 // another element change (checked in ChangeElement()), but
10850                 // also prevent additional element actions for this element
10851
10852                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10853                     !level.use_action_after_change_bug)
10854                   continue;
10855
10856                 ExecuteCustomElementAction(x, y, element, p);
10857                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10858               }
10859             }
10860           }
10861
10862           if (change->can_change)
10863           {
10864             change_done = TRUE;
10865             change_done_any = TRUE;
10866           }
10867         }
10868       }
10869     }
10870   }
10871
10872   RECURSION_LOOP_DETECTION_END();
10873
10874   return change_done_any;
10875 }
10876
10877 static boolean CheckElementChangeExt(int x, int y,
10878                                      int element,
10879                                      int trigger_element,
10880                                      int trigger_event,
10881                                      int trigger_player,
10882                                      int trigger_side)
10883 {
10884   boolean change_done = FALSE;
10885   int p;
10886
10887   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10888       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10889     return FALSE;
10890
10891   if (Feld[x][y] == EL_BLOCKED)
10892   {
10893     Blocked2Moving(x, y, &x, &y);
10894     element = Feld[x][y];
10895   }
10896
10897   // check if element has already changed or is about to change after moving
10898   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10899        Feld[x][y] != element) ||
10900
10901       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10902        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10903         ChangePage[x][y] != -1)))
10904     return FALSE;
10905
10906   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10907
10908   for (p = 0; p < element_info[element].num_change_pages; p++)
10909   {
10910     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10911
10912     /* check trigger element for all events where the element that is checked
10913        for changing interacts with a directly adjacent element -- this is
10914        different to element changes that affect other elements to change on the
10915        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10916     boolean check_trigger_element =
10917       (trigger_event == CE_TOUCHING_X ||
10918        trigger_event == CE_HITTING_X ||
10919        trigger_event == CE_HIT_BY_X ||
10920        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10921
10922     if (change->can_change_or_has_action &&
10923         change->has_event[trigger_event] &&
10924         change->trigger_side & trigger_side &&
10925         change->trigger_player & trigger_player &&
10926         (!check_trigger_element ||
10927          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10928     {
10929       change->actual_trigger_element = trigger_element;
10930       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10931       change->actual_trigger_player_bits = trigger_player;
10932       change->actual_trigger_side = trigger_side;
10933       change->actual_trigger_ce_value = CustomValue[x][y];
10934       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10935
10936       // special case: trigger element not at (x,y) position for some events
10937       if (check_trigger_element)
10938       {
10939         static struct
10940         {
10941           int dx, dy;
10942         } move_xy[] =
10943           {
10944             {  0,  0 },
10945             { -1,  0 },
10946             { +1,  0 },
10947             {  0,  0 },
10948             {  0, -1 },
10949             {  0,  0 }, { 0, 0 }, { 0, 0 },
10950             {  0, +1 }
10951           };
10952
10953         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10954         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10955
10956         change->actual_trigger_ce_value = CustomValue[xx][yy];
10957         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10958       }
10959
10960       if (change->can_change && !change_done)
10961       {
10962         ChangeDelay[x][y] = 1;
10963         ChangeEvent[x][y] = trigger_event;
10964
10965         HandleElementChange(x, y, p);
10966
10967         change_done = TRUE;
10968       }
10969       else if (change->has_action)
10970       {
10971         ExecuteCustomElementAction(x, y, element, p);
10972         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10973       }
10974     }
10975   }
10976
10977   RECURSION_LOOP_DETECTION_END();
10978
10979   return change_done;
10980 }
10981
10982 static void PlayPlayerSound(struct PlayerInfo *player)
10983 {
10984   int jx = player->jx, jy = player->jy;
10985   int sound_element = player->artwork_element;
10986   int last_action = player->last_action_waiting;
10987   int action = player->action_waiting;
10988
10989   if (player->is_waiting)
10990   {
10991     if (action != last_action)
10992       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10993     else
10994       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10995   }
10996   else
10997   {
10998     if (action != last_action)
10999       StopSound(element_info[sound_element].sound[last_action]);
11000
11001     if (last_action == ACTION_SLEEPING)
11002       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11003   }
11004 }
11005
11006 static void PlayAllPlayersSound(void)
11007 {
11008   int i;
11009
11010   for (i = 0; i < MAX_PLAYERS; i++)
11011     if (stored_player[i].active)
11012       PlayPlayerSound(&stored_player[i]);
11013 }
11014
11015 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11016 {
11017   boolean last_waiting = player->is_waiting;
11018   int move_dir = player->MovDir;
11019
11020   player->dir_waiting = move_dir;
11021   player->last_action_waiting = player->action_waiting;
11022
11023   if (is_waiting)
11024   {
11025     if (!last_waiting)          // not waiting -> waiting
11026     {
11027       player->is_waiting = TRUE;
11028
11029       player->frame_counter_bored =
11030         FrameCounter +
11031         game.player_boring_delay_fixed +
11032         GetSimpleRandom(game.player_boring_delay_random);
11033       player->frame_counter_sleeping =
11034         FrameCounter +
11035         game.player_sleeping_delay_fixed +
11036         GetSimpleRandom(game.player_sleeping_delay_random);
11037
11038       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11039     }
11040
11041     if (game.player_sleeping_delay_fixed +
11042         game.player_sleeping_delay_random > 0 &&
11043         player->anim_delay_counter == 0 &&
11044         player->post_delay_counter == 0 &&
11045         FrameCounter >= player->frame_counter_sleeping)
11046       player->is_sleeping = TRUE;
11047     else if (game.player_boring_delay_fixed +
11048              game.player_boring_delay_random > 0 &&
11049              FrameCounter >= player->frame_counter_bored)
11050       player->is_bored = TRUE;
11051
11052     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11053                               player->is_bored ? ACTION_BORING :
11054                               ACTION_WAITING);
11055
11056     if (player->is_sleeping && player->use_murphy)
11057     {
11058       // special case for sleeping Murphy when leaning against non-free tile
11059
11060       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11061           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11062            !IS_MOVING(player->jx - 1, player->jy)))
11063         move_dir = MV_LEFT;
11064       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11065                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11066                 !IS_MOVING(player->jx + 1, player->jy)))
11067         move_dir = MV_RIGHT;
11068       else
11069         player->is_sleeping = FALSE;
11070
11071       player->dir_waiting = move_dir;
11072     }
11073
11074     if (player->is_sleeping)
11075     {
11076       if (player->num_special_action_sleeping > 0)
11077       {
11078         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11079         {
11080           int last_special_action = player->special_action_sleeping;
11081           int num_special_action = player->num_special_action_sleeping;
11082           int special_action =
11083             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11084              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11085              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11086              last_special_action + 1 : ACTION_SLEEPING);
11087           int special_graphic =
11088             el_act_dir2img(player->artwork_element, special_action, move_dir);
11089
11090           player->anim_delay_counter =
11091             graphic_info[special_graphic].anim_delay_fixed +
11092             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11093           player->post_delay_counter =
11094             graphic_info[special_graphic].post_delay_fixed +
11095             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11096
11097           player->special_action_sleeping = special_action;
11098         }
11099
11100         if (player->anim_delay_counter > 0)
11101         {
11102           player->action_waiting = player->special_action_sleeping;
11103           player->anim_delay_counter--;
11104         }
11105         else if (player->post_delay_counter > 0)
11106         {
11107           player->post_delay_counter--;
11108         }
11109       }
11110     }
11111     else if (player->is_bored)
11112     {
11113       if (player->num_special_action_bored > 0)
11114       {
11115         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11116         {
11117           int special_action =
11118             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11119           int special_graphic =
11120             el_act_dir2img(player->artwork_element, special_action, move_dir);
11121
11122           player->anim_delay_counter =
11123             graphic_info[special_graphic].anim_delay_fixed +
11124             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11125           player->post_delay_counter =
11126             graphic_info[special_graphic].post_delay_fixed +
11127             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11128
11129           player->special_action_bored = special_action;
11130         }
11131
11132         if (player->anim_delay_counter > 0)
11133         {
11134           player->action_waiting = player->special_action_bored;
11135           player->anim_delay_counter--;
11136         }
11137         else if (player->post_delay_counter > 0)
11138         {
11139           player->post_delay_counter--;
11140         }
11141       }
11142     }
11143   }
11144   else if (last_waiting)        // waiting -> not waiting
11145   {
11146     player->is_waiting = FALSE;
11147     player->is_bored = FALSE;
11148     player->is_sleeping = FALSE;
11149
11150     player->frame_counter_bored = -1;
11151     player->frame_counter_sleeping = -1;
11152
11153     player->anim_delay_counter = 0;
11154     player->post_delay_counter = 0;
11155
11156     player->dir_waiting = player->MovDir;
11157     player->action_waiting = ACTION_DEFAULT;
11158
11159     player->special_action_bored = ACTION_DEFAULT;
11160     player->special_action_sleeping = ACTION_DEFAULT;
11161   }
11162 }
11163
11164 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11165 {
11166   if ((!player->is_moving  && player->was_moving) ||
11167       (player->MovPos == 0 && player->was_moving) ||
11168       (player->is_snapping && !player->was_snapping) ||
11169       (player->is_dropping && !player->was_dropping))
11170   {
11171     if (!CheckSaveEngineSnapshotToList())
11172       return;
11173
11174     player->was_moving = FALSE;
11175     player->was_snapping = TRUE;
11176     player->was_dropping = TRUE;
11177   }
11178   else
11179   {
11180     if (player->is_moving)
11181       player->was_moving = TRUE;
11182
11183     if (!player->is_snapping)
11184       player->was_snapping = FALSE;
11185
11186     if (!player->is_dropping)
11187       player->was_dropping = FALSE;
11188   }
11189 }
11190
11191 static void CheckSingleStepMode(struct PlayerInfo *player)
11192 {
11193   if (tape.single_step && tape.recording && !tape.pausing)
11194   {
11195     /* as it is called "single step mode", just return to pause mode when the
11196        player stopped moving after one tile (or never starts moving at all) */
11197     if (!player->is_moving &&
11198         !player->is_pushing &&
11199         !player->is_dropping_pressed)
11200       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11201   }
11202
11203   CheckSaveEngineSnapshot(player);
11204 }
11205
11206 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11207 {
11208   int left      = player_action & JOY_LEFT;
11209   int right     = player_action & JOY_RIGHT;
11210   int up        = player_action & JOY_UP;
11211   int down      = player_action & JOY_DOWN;
11212   int button1   = player_action & JOY_BUTTON_1;
11213   int button2   = player_action & JOY_BUTTON_2;
11214   int dx        = (left ? -1 : right ? 1 : 0);
11215   int dy        = (up   ? -1 : down  ? 1 : 0);
11216
11217   if (!player->active || tape.pausing)
11218     return 0;
11219
11220   if (player_action)
11221   {
11222     if (button1)
11223       SnapField(player, dx, dy);
11224     else
11225     {
11226       if (button2)
11227         DropElement(player);
11228
11229       MovePlayer(player, dx, dy);
11230     }
11231
11232     CheckSingleStepMode(player);
11233
11234     SetPlayerWaiting(player, FALSE);
11235
11236     return player_action;
11237   }
11238   else
11239   {
11240     // no actions for this player (no input at player's configured device)
11241
11242     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11243     SnapField(player, 0, 0);
11244     CheckGravityMovementWhenNotMoving(player);
11245
11246     if (player->MovPos == 0)
11247       SetPlayerWaiting(player, TRUE);
11248
11249     if (player->MovPos == 0)    // needed for tape.playing
11250       player->is_moving = FALSE;
11251
11252     player->is_dropping = FALSE;
11253     player->is_dropping_pressed = FALSE;
11254     player->drop_pressed_delay = 0;
11255
11256     CheckSingleStepMode(player);
11257
11258     return 0;
11259   }
11260 }
11261
11262 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11263                                          byte *tape_action)
11264 {
11265   if (!tape.use_mouse_actions)
11266     return;
11267
11268   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11269   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11270   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11271 }
11272
11273 static void SetTapeActionFromMouseAction(byte *tape_action,
11274                                          struct MouseActionInfo *mouse_action)
11275 {
11276   if (!tape.use_mouse_actions)
11277     return;
11278
11279   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11280   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11281   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11282 }
11283
11284 static void CheckLevelSolved(void)
11285 {
11286   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11287   {
11288     if (game_em.level_solved &&
11289         !game_em.game_over)                             // game won
11290     {
11291       LevelSolved();
11292
11293       game_em.game_over = TRUE;
11294
11295       game.all_players_gone = TRUE;
11296     }
11297
11298     if (game_em.game_over)                              // game lost
11299       game.all_players_gone = TRUE;
11300   }
11301   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11302   {
11303     if (game_sp.level_solved &&
11304         !game_sp.game_over)                             // game won
11305     {
11306       LevelSolved();
11307
11308       game_sp.game_over = TRUE;
11309
11310       game.all_players_gone = TRUE;
11311     }
11312
11313     if (game_sp.game_over)                              // game lost
11314       game.all_players_gone = TRUE;
11315   }
11316   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11317   {
11318     if (game_mm.level_solved &&
11319         !game_mm.game_over)                             // game won
11320     {
11321       LevelSolved();
11322
11323       game_mm.game_over = TRUE;
11324
11325       game.all_players_gone = TRUE;
11326     }
11327
11328     if (game_mm.game_over)                              // game lost
11329       game.all_players_gone = TRUE;
11330   }
11331 }
11332
11333 static void CheckLevelTime(void)
11334 {
11335   int i;
11336
11337   if (TimeFrames >= FRAMES_PER_SECOND)
11338   {
11339     TimeFrames = 0;
11340     TapeTime++;
11341
11342     for (i = 0; i < MAX_PLAYERS; i++)
11343     {
11344       struct PlayerInfo *player = &stored_player[i];
11345
11346       if (SHIELD_ON(player))
11347       {
11348         player->shield_normal_time_left--;
11349
11350         if (player->shield_deadly_time_left > 0)
11351           player->shield_deadly_time_left--;
11352       }
11353     }
11354
11355     if (!game.LevelSolved && !level.use_step_counter)
11356     {
11357       TimePlayed++;
11358
11359       if (TimeLeft > 0)
11360       {
11361         TimeLeft--;
11362
11363         if (TimeLeft <= 10 && setup.time_limit)
11364           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11365
11366         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11367            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11368
11369         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11370
11371         if (!TimeLeft && setup.time_limit)
11372         {
11373           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11374             game_em.lev->killed_out_of_time = TRUE;
11375           else
11376             for (i = 0; i < MAX_PLAYERS; i++)
11377               KillPlayer(&stored_player[i]);
11378         }
11379       }
11380       else if (game.no_time_limit && !game.all_players_gone)
11381       {
11382         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11383       }
11384
11385       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11386     }
11387
11388     if (tape.recording || tape.playing)
11389       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11390   }
11391
11392   if (tape.recording || tape.playing)
11393     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11394
11395   UpdateAndDisplayGameControlValues();
11396 }
11397
11398 void AdvanceFrameAndPlayerCounters(int player_nr)
11399 {
11400   int i;
11401
11402   // advance frame counters (global frame counter and time frame counter)
11403   FrameCounter++;
11404   TimeFrames++;
11405
11406   // advance player counters (counters for move delay, move animation etc.)
11407   for (i = 0; i < MAX_PLAYERS; i++)
11408   {
11409     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11410     int move_delay_value = stored_player[i].move_delay_value;
11411     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11412
11413     if (!advance_player_counters)       // not all players may be affected
11414       continue;
11415
11416     if (move_frames == 0)       // less than one move per game frame
11417     {
11418       int stepsize = TILEX / move_delay_value;
11419       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11420       int count = (stored_player[i].is_moving ?
11421                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11422
11423       if (count % delay == 0)
11424         move_frames = 1;
11425     }
11426
11427     stored_player[i].Frame += move_frames;
11428
11429     if (stored_player[i].MovPos != 0)
11430       stored_player[i].StepFrame += move_frames;
11431
11432     if (stored_player[i].move_delay > 0)
11433       stored_player[i].move_delay--;
11434
11435     // due to bugs in previous versions, counter must count up, not down
11436     if (stored_player[i].push_delay != -1)
11437       stored_player[i].push_delay++;
11438
11439     if (stored_player[i].drop_delay > 0)
11440       stored_player[i].drop_delay--;
11441
11442     if (stored_player[i].is_dropping_pressed)
11443       stored_player[i].drop_pressed_delay++;
11444   }
11445 }
11446
11447 void StartGameActions(boolean init_network_game, boolean record_tape,
11448                       int random_seed)
11449 {
11450   unsigned int new_random_seed = InitRND(random_seed);
11451
11452   if (record_tape)
11453     TapeStartRecording(new_random_seed);
11454
11455   if (init_network_game)
11456   {
11457     SendToServer_LevelFile();
11458     SendToServer_StartPlaying();
11459
11460     return;
11461   }
11462
11463   InitGame();
11464 }
11465
11466 static void GameActionsExt(void)
11467 {
11468 #if 0
11469   static unsigned int game_frame_delay = 0;
11470 #endif
11471   unsigned int game_frame_delay_value;
11472   byte *recorded_player_action;
11473   byte summarized_player_action = 0;
11474   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11475   int i;
11476
11477   // detect endless loops, caused by custom element programming
11478   if (recursion_loop_detected && recursion_loop_depth == 0)
11479   {
11480     char *message = getStringCat3("Internal Error! Element ",
11481                                   EL_NAME(recursion_loop_element),
11482                                   " caused endless loop! Quit the game?");
11483
11484     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11485           EL_NAME(recursion_loop_element));
11486
11487     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11488
11489     recursion_loop_detected = FALSE;    // if game should be continued
11490
11491     free(message);
11492
11493     return;
11494   }
11495
11496   if (game.restart_level)
11497     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11498
11499   CheckLevelSolved();
11500
11501   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11502     GameWon();
11503
11504   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11505     TapeStop();
11506
11507   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11508     return;
11509
11510   game_frame_delay_value =
11511     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11512
11513   if (tape.playing && tape.warp_forward && !tape.pausing)
11514     game_frame_delay_value = 0;
11515
11516   SetVideoFrameDelay(game_frame_delay_value);
11517
11518   // (de)activate virtual buttons depending on current game status
11519   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11520   {
11521     if (game.all_players_gone)  // if no players there to be controlled anymore
11522       SetOverlayActive(FALSE);
11523     else if (!tape.playing)     // if game continues after tape stopped playing
11524       SetOverlayActive(TRUE);
11525   }
11526
11527 #if 0
11528 #if 0
11529   // ---------- main game synchronization point ----------
11530
11531   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11532
11533   printf("::: skip == %d\n", skip);
11534
11535 #else
11536   // ---------- main game synchronization point ----------
11537
11538   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11539 #endif
11540 #endif
11541
11542   if (network_playing && !network_player_action_received)
11543   {
11544     // try to get network player actions in time
11545
11546     // last chance to get network player actions without main loop delay
11547     HandleNetworking();
11548
11549     // game was quit by network peer
11550     if (game_status != GAME_MODE_PLAYING)
11551       return;
11552
11553     // check if network player actions still missing and game still running
11554     if (!network_player_action_received && !checkGameEnded())
11555       return;           // failed to get network player actions in time
11556
11557     // do not yet reset "network_player_action_received" (for tape.pausing)
11558   }
11559
11560   if (tape.pausing)
11561     return;
11562
11563   // at this point we know that we really continue executing the game
11564
11565   network_player_action_received = FALSE;
11566
11567   // when playing tape, read previously recorded player input from tape data
11568   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11569
11570   local_player->effective_mouse_action = local_player->mouse_action;
11571
11572   if (recorded_player_action != NULL)
11573     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11574                                  recorded_player_action);
11575
11576   // TapePlayAction() may return NULL when toggling to "pause before death"
11577   if (tape.pausing)
11578     return;
11579
11580   if (tape.set_centered_player)
11581   {
11582     game.centered_player_nr_next = tape.centered_player_nr_next;
11583     game.set_centered_player = TRUE;
11584   }
11585
11586   for (i = 0; i < MAX_PLAYERS; i++)
11587   {
11588     summarized_player_action |= stored_player[i].action;
11589
11590     if (!network_playing && (game.team_mode || tape.playing))
11591       stored_player[i].effective_action = stored_player[i].action;
11592   }
11593
11594   if (network_playing && !checkGameEnded())
11595     SendToServer_MovePlayer(summarized_player_action);
11596
11597   // summarize all actions at local players mapped input device position
11598   // (this allows using different input devices in single player mode)
11599   if (!network.enabled && !game.team_mode)
11600     stored_player[map_player_action[local_player->index_nr]].effective_action =
11601       summarized_player_action;
11602
11603   // summarize all actions at centered player in local team mode
11604   if (tape.recording &&
11605       setup.team_mode && !network.enabled &&
11606       setup.input_on_focus &&
11607       game.centered_player_nr != -1)
11608   {
11609     for (i = 0; i < MAX_PLAYERS; i++)
11610       stored_player[map_player_action[i]].effective_action =
11611         (i == game.centered_player_nr ? summarized_player_action : 0);
11612   }
11613
11614   if (recorded_player_action != NULL)
11615     for (i = 0; i < MAX_PLAYERS; i++)
11616       stored_player[i].effective_action = recorded_player_action[i];
11617
11618   for (i = 0; i < MAX_PLAYERS; i++)
11619   {
11620     tape_action[i] = stored_player[i].effective_action;
11621
11622     /* (this may happen in the RND game engine if a player was not present on
11623        the playfield on level start, but appeared later from a custom element */
11624     if (setup.team_mode &&
11625         tape.recording &&
11626         tape_action[i] &&
11627         !tape.player_participates[i])
11628       tape.player_participates[i] = TRUE;
11629   }
11630
11631   SetTapeActionFromMouseAction(tape_action,
11632                                &local_player->effective_mouse_action);
11633
11634   // only record actions from input devices, but not programmed actions
11635   if (tape.recording)
11636     TapeRecordAction(tape_action);
11637
11638   // remember if game was played (especially after tape stopped playing)
11639   if (!tape.playing && summarized_player_action)
11640     game.GamePlayed = TRUE;
11641
11642 #if USE_NEW_PLAYER_ASSIGNMENTS
11643   // !!! also map player actions in single player mode !!!
11644   // if (game.team_mode)
11645   if (1)
11646   {
11647     byte mapped_action[MAX_PLAYERS];
11648
11649 #if DEBUG_PLAYER_ACTIONS
11650     printf(":::");
11651     for (i = 0; i < MAX_PLAYERS; i++)
11652       printf(" %d, ", stored_player[i].effective_action);
11653 #endif
11654
11655     for (i = 0; i < MAX_PLAYERS; i++)
11656       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11657
11658     for (i = 0; i < MAX_PLAYERS; i++)
11659       stored_player[i].effective_action = mapped_action[i];
11660
11661 #if DEBUG_PLAYER_ACTIONS
11662     printf(" =>");
11663     for (i = 0; i < MAX_PLAYERS; i++)
11664       printf(" %d, ", stored_player[i].effective_action);
11665     printf("\n");
11666 #endif
11667   }
11668 #if DEBUG_PLAYER_ACTIONS
11669   else
11670   {
11671     printf(":::");
11672     for (i = 0; i < MAX_PLAYERS; i++)
11673       printf(" %d, ", stored_player[i].effective_action);
11674     printf("\n");
11675   }
11676 #endif
11677 #endif
11678
11679   for (i = 0; i < MAX_PLAYERS; i++)
11680   {
11681     // allow engine snapshot in case of changed movement attempt
11682     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11683         (stored_player[i].effective_action & KEY_MOTION))
11684       game.snapshot.changed_action = TRUE;
11685
11686     // allow engine snapshot in case of snapping/dropping attempt
11687     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11688         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11689       game.snapshot.changed_action = TRUE;
11690
11691     game.snapshot.last_action[i] = stored_player[i].effective_action;
11692   }
11693
11694   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11695   {
11696     GameActions_EM_Main();
11697   }
11698   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11699   {
11700     GameActions_SP_Main();
11701   }
11702   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11703   {
11704     GameActions_MM_Main();
11705   }
11706   else
11707   {
11708     GameActions_RND_Main();
11709   }
11710
11711   BlitScreenToBitmap(backbuffer);
11712
11713   CheckLevelSolved();
11714   CheckLevelTime();
11715
11716   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11717
11718   if (global.show_frames_per_second)
11719   {
11720     static unsigned int fps_counter = 0;
11721     static int fps_frames = 0;
11722     unsigned int fps_delay_ms = Counter() - fps_counter;
11723
11724     fps_frames++;
11725
11726     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11727     {
11728       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11729
11730       fps_frames = 0;
11731       fps_counter = Counter();
11732
11733       // always draw FPS to screen after FPS value was updated
11734       redraw_mask |= REDRAW_FPS;
11735     }
11736
11737     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11738     if (GetDrawDeactivationMask() == REDRAW_NONE)
11739       redraw_mask |= REDRAW_FPS;
11740   }
11741 }
11742
11743 static void GameActions_CheckSaveEngineSnapshot(void)
11744 {
11745   if (!game.snapshot.save_snapshot)
11746     return;
11747
11748   // clear flag for saving snapshot _before_ saving snapshot
11749   game.snapshot.save_snapshot = FALSE;
11750
11751   SaveEngineSnapshotToList();
11752 }
11753
11754 void GameActions(void)
11755 {
11756   GameActionsExt();
11757
11758   GameActions_CheckSaveEngineSnapshot();
11759 }
11760
11761 void GameActions_EM_Main(void)
11762 {
11763   byte effective_action[MAX_PLAYERS];
11764   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11765   int i;
11766
11767   for (i = 0; i < MAX_PLAYERS; i++)
11768     effective_action[i] = stored_player[i].effective_action;
11769
11770   GameActions_EM(effective_action, warp_mode);
11771 }
11772
11773 void GameActions_SP_Main(void)
11774 {
11775   byte effective_action[MAX_PLAYERS];
11776   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11777   int i;
11778
11779   for (i = 0; i < MAX_PLAYERS; i++)
11780     effective_action[i] = stored_player[i].effective_action;
11781
11782   GameActions_SP(effective_action, warp_mode);
11783
11784   for (i = 0; i < MAX_PLAYERS; i++)
11785   {
11786     if (stored_player[i].force_dropping)
11787       stored_player[i].action |= KEY_BUTTON_DROP;
11788
11789     stored_player[i].force_dropping = FALSE;
11790   }
11791 }
11792
11793 void GameActions_MM_Main(void)
11794 {
11795   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11796
11797   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11798 }
11799
11800 void GameActions_RND_Main(void)
11801 {
11802   GameActions_RND();
11803 }
11804
11805 void GameActions_RND(void)
11806 {
11807   static struct MouseActionInfo mouse_action_last = { 0 };
11808   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11809   int magic_wall_x = 0, magic_wall_y = 0;
11810   int i, x, y, element, graphic, last_gfx_frame;
11811
11812   InitPlayfieldScanModeVars();
11813
11814   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11815   {
11816     SCAN_PLAYFIELD(x, y)
11817     {
11818       ChangeCount[x][y] = 0;
11819       ChangeEvent[x][y] = -1;
11820     }
11821   }
11822
11823   if (game.set_centered_player)
11824   {
11825     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11826
11827     // switching to "all players" only possible if all players fit to screen
11828     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11829     {
11830       game.centered_player_nr_next = game.centered_player_nr;
11831       game.set_centered_player = FALSE;
11832     }
11833
11834     // do not switch focus to non-existing (or non-active) player
11835     if (game.centered_player_nr_next >= 0 &&
11836         !stored_player[game.centered_player_nr_next].active)
11837     {
11838       game.centered_player_nr_next = game.centered_player_nr;
11839       game.set_centered_player = FALSE;
11840     }
11841   }
11842
11843   if (game.set_centered_player &&
11844       ScreenMovPos == 0)        // screen currently aligned at tile position
11845   {
11846     int sx, sy;
11847
11848     if (game.centered_player_nr_next == -1)
11849     {
11850       setScreenCenteredToAllPlayers(&sx, &sy);
11851     }
11852     else
11853     {
11854       sx = stored_player[game.centered_player_nr_next].jx;
11855       sy = stored_player[game.centered_player_nr_next].jy;
11856     }
11857
11858     game.centered_player_nr = game.centered_player_nr_next;
11859     game.set_centered_player = FALSE;
11860
11861     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11862     DrawGameDoorValues();
11863   }
11864
11865   for (i = 0; i < MAX_PLAYERS; i++)
11866   {
11867     int actual_player_action = stored_player[i].effective_action;
11868
11869 #if 1
11870     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11871        - rnd_equinox_tetrachloride 048
11872        - rnd_equinox_tetrachloride_ii 096
11873        - rnd_emanuel_schmieg 002
11874        - doctor_sloan_ww 001, 020
11875     */
11876     if (stored_player[i].MovPos == 0)
11877       CheckGravityMovement(&stored_player[i]);
11878 #endif
11879
11880     // overwrite programmed action with tape action
11881     if (stored_player[i].programmed_action)
11882       actual_player_action = stored_player[i].programmed_action;
11883
11884     PlayerActions(&stored_player[i], actual_player_action);
11885
11886     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11887   }
11888
11889   ScrollScreen(NULL, SCROLL_GO_ON);
11890
11891   /* for backwards compatibility, the following code emulates a fixed bug that
11892      occured when pushing elements (causing elements that just made their last
11893      pushing step to already (if possible) make their first falling step in the
11894      same game frame, which is bad); this code is also needed to use the famous
11895      "spring push bug" which is used in older levels and might be wanted to be
11896      used also in newer levels, but in this case the buggy pushing code is only
11897      affecting the "spring" element and no other elements */
11898
11899   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11900   {
11901     for (i = 0; i < MAX_PLAYERS; i++)
11902     {
11903       struct PlayerInfo *player = &stored_player[i];
11904       int x = player->jx;
11905       int y = player->jy;
11906
11907       if (player->active && player->is_pushing && player->is_moving &&
11908           IS_MOVING(x, y) &&
11909           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11910            Feld[x][y] == EL_SPRING))
11911       {
11912         ContinueMoving(x, y);
11913
11914         // continue moving after pushing (this is actually a bug)
11915         if (!IS_MOVING(x, y))
11916           Stop[x][y] = FALSE;
11917       }
11918     }
11919   }
11920
11921   SCAN_PLAYFIELD(x, y)
11922   {
11923     Last[x][y] = Feld[x][y];
11924
11925     ChangeCount[x][y] = 0;
11926     ChangeEvent[x][y] = -1;
11927
11928     // this must be handled before main playfield loop
11929     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11930     {
11931       MovDelay[x][y]--;
11932       if (MovDelay[x][y] <= 0)
11933         RemoveField(x, y);
11934     }
11935
11936     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11937     {
11938       MovDelay[x][y]--;
11939       if (MovDelay[x][y] <= 0)
11940       {
11941         RemoveField(x, y);
11942         TEST_DrawLevelField(x, y);
11943
11944         TestIfElementTouchesCustomElement(x, y);        // for empty space
11945       }
11946     }
11947
11948 #if DEBUG
11949     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11950     {
11951       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11952       printf("GameActions(): This should never happen!\n");
11953
11954       ChangePage[x][y] = -1;
11955     }
11956 #endif
11957
11958     Stop[x][y] = FALSE;
11959     if (WasJustMoving[x][y] > 0)
11960       WasJustMoving[x][y]--;
11961     if (WasJustFalling[x][y] > 0)
11962       WasJustFalling[x][y]--;
11963     if (CheckCollision[x][y] > 0)
11964       CheckCollision[x][y]--;
11965     if (CheckImpact[x][y] > 0)
11966       CheckImpact[x][y]--;
11967
11968     GfxFrame[x][y]++;
11969
11970     /* reset finished pushing action (not done in ContinueMoving() to allow
11971        continuous pushing animation for elements with zero push delay) */
11972     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11973     {
11974       ResetGfxAnimation(x, y);
11975       TEST_DrawLevelField(x, y);
11976     }
11977
11978 #if DEBUG
11979     if (IS_BLOCKED(x, y))
11980     {
11981       int oldx, oldy;
11982
11983       Blocked2Moving(x, y, &oldx, &oldy);
11984       if (!IS_MOVING(oldx, oldy))
11985       {
11986         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11987         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11988         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11989         printf("GameActions(): This should never happen!\n");
11990       }
11991     }
11992 #endif
11993   }
11994
11995   if (mouse_action.button)
11996   {
11997     int new_button = (mouse_action.button && mouse_action_last.button == 0);
11998
11999     x = mouse_action.lx;
12000     y = mouse_action.ly;
12001     element = Feld[x][y];
12002
12003     if (new_button)
12004     {
12005       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12006       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12007     }
12008
12009     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12010     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12011   }
12012
12013   SCAN_PLAYFIELD(x, y)
12014   {
12015     element = Feld[x][y];
12016     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12017     last_gfx_frame = GfxFrame[x][y];
12018
12019     ResetGfxFrame(x, y);
12020
12021     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12022       DrawLevelGraphicAnimation(x, y, graphic);
12023
12024     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12025         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12026       ResetRandomAnimationValue(x, y);
12027
12028     SetRandomAnimationValue(x, y);
12029
12030     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12031
12032     if (IS_INACTIVE(element))
12033     {
12034       if (IS_ANIMATED(graphic))
12035         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12036
12037       continue;
12038     }
12039
12040     // this may take place after moving, so 'element' may have changed
12041     if (IS_CHANGING(x, y) &&
12042         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12043     {
12044       int page = element_info[element].event_page_nr[CE_DELAY];
12045
12046       HandleElementChange(x, y, page);
12047
12048       element = Feld[x][y];
12049       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12050     }
12051
12052     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12053     {
12054       StartMoving(x, y);
12055
12056       element = Feld[x][y];
12057       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12058
12059       if (IS_ANIMATED(graphic) &&
12060           !IS_MOVING(x, y) &&
12061           !Stop[x][y])
12062         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12063
12064       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12065         TEST_DrawTwinkleOnField(x, y);
12066     }
12067     else if (element == EL_ACID)
12068     {
12069       if (!Stop[x][y])
12070         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12071     }
12072     else if ((element == EL_EXIT_OPEN ||
12073               element == EL_EM_EXIT_OPEN ||
12074               element == EL_SP_EXIT_OPEN ||
12075               element == EL_STEEL_EXIT_OPEN ||
12076               element == EL_EM_STEEL_EXIT_OPEN ||
12077               element == EL_SP_TERMINAL ||
12078               element == EL_SP_TERMINAL_ACTIVE ||
12079               element == EL_EXTRA_TIME ||
12080               element == EL_SHIELD_NORMAL ||
12081               element == EL_SHIELD_DEADLY) &&
12082              IS_ANIMATED(graphic))
12083       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12084     else if (IS_MOVING(x, y))
12085       ContinueMoving(x, y);
12086     else if (IS_ACTIVE_BOMB(element))
12087       CheckDynamite(x, y);
12088     else if (element == EL_AMOEBA_GROWING)
12089       AmoebeWaechst(x, y);
12090     else if (element == EL_AMOEBA_SHRINKING)
12091       AmoebaDisappearing(x, y);
12092
12093 #if !USE_NEW_AMOEBA_CODE
12094     else if (IS_AMOEBALIVE(element))
12095       AmoebeAbleger(x, y);
12096 #endif
12097
12098     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12099       Life(x, y);
12100     else if (element == EL_EXIT_CLOSED)
12101       CheckExit(x, y);
12102     else if (element == EL_EM_EXIT_CLOSED)
12103       CheckExitEM(x, y);
12104     else if (element == EL_STEEL_EXIT_CLOSED)
12105       CheckExitSteel(x, y);
12106     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12107       CheckExitSteelEM(x, y);
12108     else if (element == EL_SP_EXIT_CLOSED)
12109       CheckExitSP(x, y);
12110     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12111              element == EL_EXPANDABLE_STEELWALL_GROWING)
12112       MauerWaechst(x, y);
12113     else if (element == EL_EXPANDABLE_WALL ||
12114              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12115              element == EL_EXPANDABLE_WALL_VERTICAL ||
12116              element == EL_EXPANDABLE_WALL_ANY ||
12117              element == EL_BD_EXPANDABLE_WALL)
12118       MauerAbleger(x, y);
12119     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12120              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12121              element == EL_EXPANDABLE_STEELWALL_ANY)
12122       MauerAblegerStahl(x, y);
12123     else if (element == EL_FLAMES)
12124       CheckForDragon(x, y);
12125     else if (element == EL_EXPLOSION)
12126       ; // drawing of correct explosion animation is handled separately
12127     else if (element == EL_ELEMENT_SNAPPING ||
12128              element == EL_DIAGONAL_SHRINKING ||
12129              element == EL_DIAGONAL_GROWING)
12130     {
12131       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12132
12133       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12134     }
12135     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12136       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12137
12138     if (IS_BELT_ACTIVE(element))
12139       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12140
12141     if (game.magic_wall_active)
12142     {
12143       int jx = local_player->jx, jy = local_player->jy;
12144
12145       // play the element sound at the position nearest to the player
12146       if ((element == EL_MAGIC_WALL_FULL ||
12147            element == EL_MAGIC_WALL_ACTIVE ||
12148            element == EL_MAGIC_WALL_EMPTYING ||
12149            element == EL_BD_MAGIC_WALL_FULL ||
12150            element == EL_BD_MAGIC_WALL_ACTIVE ||
12151            element == EL_BD_MAGIC_WALL_EMPTYING ||
12152            element == EL_DC_MAGIC_WALL_FULL ||
12153            element == EL_DC_MAGIC_WALL_ACTIVE ||
12154            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12155           ABS(x - jx) + ABS(y - jy) <
12156           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12157       {
12158         magic_wall_x = x;
12159         magic_wall_y = y;
12160       }
12161     }
12162   }
12163
12164 #if USE_NEW_AMOEBA_CODE
12165   // new experimental amoeba growth stuff
12166   if (!(FrameCounter % 8))
12167   {
12168     static unsigned int random = 1684108901;
12169
12170     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12171     {
12172       x = RND(lev_fieldx);
12173       y = RND(lev_fieldy);
12174       element = Feld[x][y];
12175
12176       if (!IS_PLAYER(x,y) &&
12177           (element == EL_EMPTY ||
12178            CAN_GROW_INTO(element) ||
12179            element == EL_QUICKSAND_EMPTY ||
12180            element == EL_QUICKSAND_FAST_EMPTY ||
12181            element == EL_ACID_SPLASH_LEFT ||
12182            element == EL_ACID_SPLASH_RIGHT))
12183       {
12184         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12185             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12186             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12187             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12188           Feld[x][y] = EL_AMOEBA_DROP;
12189       }
12190
12191       random = random * 129 + 1;
12192     }
12193   }
12194 #endif
12195
12196   game.explosions_delayed = FALSE;
12197
12198   SCAN_PLAYFIELD(x, y)
12199   {
12200     element = Feld[x][y];
12201
12202     if (ExplodeField[x][y])
12203       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12204     else if (element == EL_EXPLOSION)
12205       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12206
12207     ExplodeField[x][y] = EX_TYPE_NONE;
12208   }
12209
12210   game.explosions_delayed = TRUE;
12211
12212   if (game.magic_wall_active)
12213   {
12214     if (!(game.magic_wall_time_left % 4))
12215     {
12216       int element = Feld[magic_wall_x][magic_wall_y];
12217
12218       if (element == EL_BD_MAGIC_WALL_FULL ||
12219           element == EL_BD_MAGIC_WALL_ACTIVE ||
12220           element == EL_BD_MAGIC_WALL_EMPTYING)
12221         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12222       else if (element == EL_DC_MAGIC_WALL_FULL ||
12223                element == EL_DC_MAGIC_WALL_ACTIVE ||
12224                element == EL_DC_MAGIC_WALL_EMPTYING)
12225         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12226       else
12227         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12228     }
12229
12230     if (game.magic_wall_time_left > 0)
12231     {
12232       game.magic_wall_time_left--;
12233
12234       if (!game.magic_wall_time_left)
12235       {
12236         SCAN_PLAYFIELD(x, y)
12237         {
12238           element = Feld[x][y];
12239
12240           if (element == EL_MAGIC_WALL_ACTIVE ||
12241               element == EL_MAGIC_WALL_FULL)
12242           {
12243             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12244             TEST_DrawLevelField(x, y);
12245           }
12246           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12247                    element == EL_BD_MAGIC_WALL_FULL)
12248           {
12249             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12250             TEST_DrawLevelField(x, y);
12251           }
12252           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12253                    element == EL_DC_MAGIC_WALL_FULL)
12254           {
12255             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12256             TEST_DrawLevelField(x, y);
12257           }
12258         }
12259
12260         game.magic_wall_active = FALSE;
12261       }
12262     }
12263   }
12264
12265   if (game.light_time_left > 0)
12266   {
12267     game.light_time_left--;
12268
12269     if (game.light_time_left == 0)
12270       RedrawAllLightSwitchesAndInvisibleElements();
12271   }
12272
12273   if (game.timegate_time_left > 0)
12274   {
12275     game.timegate_time_left--;
12276
12277     if (game.timegate_time_left == 0)
12278       CloseAllOpenTimegates();
12279   }
12280
12281   if (game.lenses_time_left > 0)
12282   {
12283     game.lenses_time_left--;
12284
12285     if (game.lenses_time_left == 0)
12286       RedrawAllInvisibleElementsForLenses();
12287   }
12288
12289   if (game.magnify_time_left > 0)
12290   {
12291     game.magnify_time_left--;
12292
12293     if (game.magnify_time_left == 0)
12294       RedrawAllInvisibleElementsForMagnifier();
12295   }
12296
12297   for (i = 0; i < MAX_PLAYERS; i++)
12298   {
12299     struct PlayerInfo *player = &stored_player[i];
12300
12301     if (SHIELD_ON(player))
12302     {
12303       if (player->shield_deadly_time_left)
12304         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12305       else if (player->shield_normal_time_left)
12306         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12307     }
12308   }
12309
12310 #if USE_DELAYED_GFX_REDRAW
12311   SCAN_PLAYFIELD(x, y)
12312   {
12313     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12314     {
12315       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12316          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12317
12318       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12319         DrawLevelField(x, y);
12320
12321       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12322         DrawLevelFieldCrumbled(x, y);
12323
12324       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12325         DrawLevelFieldCrumbledNeighbours(x, y);
12326
12327       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12328         DrawTwinkleOnField(x, y);
12329     }
12330
12331     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12332   }
12333 #endif
12334
12335   DrawAllPlayers();
12336   PlayAllPlayersSound();
12337
12338   for (i = 0; i < MAX_PLAYERS; i++)
12339   {
12340     struct PlayerInfo *player = &stored_player[i];
12341
12342     if (player->show_envelope != 0 && (!player->active ||
12343                                        player->MovPos == 0))
12344     {
12345       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12346
12347       player->show_envelope = 0;
12348     }
12349   }
12350
12351   // use random number generator in every frame to make it less predictable
12352   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12353     RND(1);
12354
12355   mouse_action_last = mouse_action;
12356 }
12357
12358 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12359 {
12360   int min_x = x, min_y = y, max_x = x, max_y = y;
12361   int i;
12362
12363   for (i = 0; i < MAX_PLAYERS; i++)
12364   {
12365     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12366
12367     if (!stored_player[i].active || &stored_player[i] == player)
12368       continue;
12369
12370     min_x = MIN(min_x, jx);
12371     min_y = MIN(min_y, jy);
12372     max_x = MAX(max_x, jx);
12373     max_y = MAX(max_y, jy);
12374   }
12375
12376   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12377 }
12378
12379 static boolean AllPlayersInVisibleScreen(void)
12380 {
12381   int i;
12382
12383   for (i = 0; i < MAX_PLAYERS; i++)
12384   {
12385     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12386
12387     if (!stored_player[i].active)
12388       continue;
12389
12390     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12391       return FALSE;
12392   }
12393
12394   return TRUE;
12395 }
12396
12397 void ScrollLevel(int dx, int dy)
12398 {
12399   int scroll_offset = 2 * TILEX_VAR;
12400   int x, y;
12401
12402   BlitBitmap(drawto_field, drawto_field,
12403              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12404              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12405              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12406              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12407              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12408              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12409
12410   if (dx != 0)
12411   {
12412     x = (dx == 1 ? BX1 : BX2);
12413     for (y = BY1; y <= BY2; y++)
12414       DrawScreenField(x, y);
12415   }
12416
12417   if (dy != 0)
12418   {
12419     y = (dy == 1 ? BY1 : BY2);
12420     for (x = BX1; x <= BX2; x++)
12421       DrawScreenField(x, y);
12422   }
12423
12424   redraw_mask |= REDRAW_FIELD;
12425 }
12426
12427 static boolean canFallDown(struct PlayerInfo *player)
12428 {
12429   int jx = player->jx, jy = player->jy;
12430
12431   return (IN_LEV_FIELD(jx, jy + 1) &&
12432           (IS_FREE(jx, jy + 1) ||
12433            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12434           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12435           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12436 }
12437
12438 static boolean canPassField(int x, int y, int move_dir)
12439 {
12440   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12441   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12442   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12443   int nextx = x + dx;
12444   int nexty = y + dy;
12445   int element = Feld[x][y];
12446
12447   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12448           !CAN_MOVE(element) &&
12449           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12450           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12451           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12452 }
12453
12454 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12455 {
12456   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12457   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12458   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12459   int newx = x + dx;
12460   int newy = y + dy;
12461
12462   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12463           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12464           (IS_DIGGABLE(Feld[newx][newy]) ||
12465            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12466            canPassField(newx, newy, move_dir)));
12467 }
12468
12469 static void CheckGravityMovement(struct PlayerInfo *player)
12470 {
12471   if (player->gravity && !player->programmed_action)
12472   {
12473     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12474     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12475     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12476     int jx = player->jx, jy = player->jy;
12477     boolean player_is_moving_to_valid_field =
12478       (!player_is_snapping &&
12479        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12480         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12481     boolean player_can_fall_down = canFallDown(player);
12482
12483     if (player_can_fall_down &&
12484         !player_is_moving_to_valid_field)
12485       player->programmed_action = MV_DOWN;
12486   }
12487 }
12488
12489 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12490 {
12491   return CheckGravityMovement(player);
12492
12493   if (player->gravity && !player->programmed_action)
12494   {
12495     int jx = player->jx, jy = player->jy;
12496     boolean field_under_player_is_free =
12497       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12498     boolean player_is_standing_on_valid_field =
12499       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12500        (IS_WALKABLE(Feld[jx][jy]) &&
12501         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12502
12503     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12504       player->programmed_action = MV_DOWN;
12505   }
12506 }
12507
12508 /*
12509   MovePlayerOneStep()
12510   -----------------------------------------------------------------------------
12511   dx, dy:               direction (non-diagonal) to try to move the player to
12512   real_dx, real_dy:     direction as read from input device (can be diagonal)
12513 */
12514
12515 boolean MovePlayerOneStep(struct PlayerInfo *player,
12516                           int dx, int dy, int real_dx, int real_dy)
12517 {
12518   int jx = player->jx, jy = player->jy;
12519   int new_jx = jx + dx, new_jy = jy + dy;
12520   int can_move;
12521   boolean player_can_move = !player->cannot_move;
12522
12523   if (!player->active || (!dx && !dy))
12524     return MP_NO_ACTION;
12525
12526   player->MovDir = (dx < 0 ? MV_LEFT :
12527                     dx > 0 ? MV_RIGHT :
12528                     dy < 0 ? MV_UP :
12529                     dy > 0 ? MV_DOWN :  MV_NONE);
12530
12531   if (!IN_LEV_FIELD(new_jx, new_jy))
12532     return MP_NO_ACTION;
12533
12534   if (!player_can_move)
12535   {
12536     if (player->MovPos == 0)
12537     {
12538       player->is_moving = FALSE;
12539       player->is_digging = FALSE;
12540       player->is_collecting = FALSE;
12541       player->is_snapping = FALSE;
12542       player->is_pushing = FALSE;
12543     }
12544   }
12545
12546   if (!network.enabled && game.centered_player_nr == -1 &&
12547       !AllPlayersInSight(player, new_jx, new_jy))
12548     return MP_NO_ACTION;
12549
12550   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12551   if (can_move != MP_MOVING)
12552     return can_move;
12553
12554   // check if DigField() has caused relocation of the player
12555   if (player->jx != jx || player->jy != jy)
12556     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12557
12558   StorePlayer[jx][jy] = 0;
12559   player->last_jx = jx;
12560   player->last_jy = jy;
12561   player->jx = new_jx;
12562   player->jy = new_jy;
12563   StorePlayer[new_jx][new_jy] = player->element_nr;
12564
12565   if (player->move_delay_value_next != -1)
12566   {
12567     player->move_delay_value = player->move_delay_value_next;
12568     player->move_delay_value_next = -1;
12569   }
12570
12571   player->MovPos =
12572     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12573
12574   player->step_counter++;
12575
12576   PlayerVisit[jx][jy] = FrameCounter;
12577
12578   player->is_moving = TRUE;
12579
12580 #if 1
12581   // should better be called in MovePlayer(), but this breaks some tapes
12582   ScrollPlayer(player, SCROLL_INIT);
12583 #endif
12584
12585   return MP_MOVING;
12586 }
12587
12588 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12589 {
12590   int jx = player->jx, jy = player->jy;
12591   int old_jx = jx, old_jy = jy;
12592   int moved = MP_NO_ACTION;
12593
12594   if (!player->active)
12595     return FALSE;
12596
12597   if (!dx && !dy)
12598   {
12599     if (player->MovPos == 0)
12600     {
12601       player->is_moving = FALSE;
12602       player->is_digging = FALSE;
12603       player->is_collecting = FALSE;
12604       player->is_snapping = FALSE;
12605       player->is_pushing = FALSE;
12606     }
12607
12608     return FALSE;
12609   }
12610
12611   if (player->move_delay > 0)
12612     return FALSE;
12613
12614   player->move_delay = -1;              // set to "uninitialized" value
12615
12616   // store if player is automatically moved to next field
12617   player->is_auto_moving = (player->programmed_action != MV_NONE);
12618
12619   // remove the last programmed player action
12620   player->programmed_action = 0;
12621
12622   if (player->MovPos)
12623   {
12624     // should only happen if pre-1.2 tape recordings are played
12625     // this is only for backward compatibility
12626
12627     int original_move_delay_value = player->move_delay_value;
12628
12629 #if DEBUG
12630     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12631            tape.counter);
12632 #endif
12633
12634     // scroll remaining steps with finest movement resolution
12635     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12636
12637     while (player->MovPos)
12638     {
12639       ScrollPlayer(player, SCROLL_GO_ON);
12640       ScrollScreen(NULL, SCROLL_GO_ON);
12641
12642       AdvanceFrameAndPlayerCounters(player->index_nr);
12643
12644       DrawAllPlayers();
12645       BackToFront_WithFrameDelay(0);
12646     }
12647
12648     player->move_delay_value = original_move_delay_value;
12649   }
12650
12651   player->is_active = FALSE;
12652
12653   if (player->last_move_dir & MV_HORIZONTAL)
12654   {
12655     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12656       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12657   }
12658   else
12659   {
12660     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12661       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12662   }
12663
12664   if (!moved && !player->is_active)
12665   {
12666     player->is_moving = FALSE;
12667     player->is_digging = FALSE;
12668     player->is_collecting = FALSE;
12669     player->is_snapping = FALSE;
12670     player->is_pushing = FALSE;
12671   }
12672
12673   jx = player->jx;
12674   jy = player->jy;
12675
12676   if (moved & MP_MOVING && !ScreenMovPos &&
12677       (player->index_nr == game.centered_player_nr ||
12678        game.centered_player_nr == -1))
12679   {
12680     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12681
12682     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12683     {
12684       // actual player has left the screen -- scroll in that direction
12685       if (jx != old_jx)         // player has moved horizontally
12686         scroll_x += (jx - old_jx);
12687       else                      // player has moved vertically
12688         scroll_y += (jy - old_jy);
12689     }
12690     else
12691     {
12692       int offset_raw = game.scroll_delay_value;
12693
12694       if (jx != old_jx)         // player has moved horizontally
12695       {
12696         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12697         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12698         int new_scroll_x = jx - MIDPOSX + offset_x;
12699
12700         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12701             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12702           scroll_x = new_scroll_x;
12703
12704         // don't scroll over playfield boundaries
12705         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12706
12707         // don't scroll more than one field at a time
12708         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12709
12710         // don't scroll against the player's moving direction
12711         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12712             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12713           scroll_x = old_scroll_x;
12714       }
12715       else                      // player has moved vertically
12716       {
12717         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12718         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12719         int new_scroll_y = jy - MIDPOSY + offset_y;
12720
12721         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12722             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12723           scroll_y = new_scroll_y;
12724
12725         // don't scroll over playfield boundaries
12726         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12727
12728         // don't scroll more than one field at a time
12729         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12730
12731         // don't scroll against the player's moving direction
12732         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12733             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12734           scroll_y = old_scroll_y;
12735       }
12736     }
12737
12738     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12739     {
12740       if (!network.enabled && game.centered_player_nr == -1 &&
12741           !AllPlayersInVisibleScreen())
12742       {
12743         scroll_x = old_scroll_x;
12744         scroll_y = old_scroll_y;
12745       }
12746       else
12747       {
12748         ScrollScreen(player, SCROLL_INIT);
12749         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12750       }
12751     }
12752   }
12753
12754   player->StepFrame = 0;
12755
12756   if (moved & MP_MOVING)
12757   {
12758     if (old_jx != jx && old_jy == jy)
12759       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12760     else if (old_jx == jx && old_jy != jy)
12761       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12762
12763     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12764
12765     player->last_move_dir = player->MovDir;
12766     player->is_moving = TRUE;
12767     player->is_snapping = FALSE;
12768     player->is_switching = FALSE;
12769     player->is_dropping = FALSE;
12770     player->is_dropping_pressed = FALSE;
12771     player->drop_pressed_delay = 0;
12772
12773 #if 0
12774     // should better be called here than above, but this breaks some tapes
12775     ScrollPlayer(player, SCROLL_INIT);
12776 #endif
12777   }
12778   else
12779   {
12780     CheckGravityMovementWhenNotMoving(player);
12781
12782     player->is_moving = FALSE;
12783
12784     /* at this point, the player is allowed to move, but cannot move right now
12785        (e.g. because of something blocking the way) -- ensure that the player
12786        is also allowed to move in the next frame (in old versions before 3.1.1,
12787        the player was forced to wait again for eight frames before next try) */
12788
12789     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12790       player->move_delay = 0;   // allow direct movement in the next frame
12791   }
12792
12793   if (player->move_delay == -1)         // not yet initialized by DigField()
12794     player->move_delay = player->move_delay_value;
12795
12796   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12797   {
12798     TestIfPlayerTouchesBadThing(jx, jy);
12799     TestIfPlayerTouchesCustomElement(jx, jy);
12800   }
12801
12802   if (!player->active)
12803     RemovePlayer(player);
12804
12805   return moved;
12806 }
12807
12808 void ScrollPlayer(struct PlayerInfo *player, int mode)
12809 {
12810   int jx = player->jx, jy = player->jy;
12811   int last_jx = player->last_jx, last_jy = player->last_jy;
12812   int move_stepsize = TILEX / player->move_delay_value;
12813
12814   if (!player->active)
12815     return;
12816
12817   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12818     return;
12819
12820   if (mode == SCROLL_INIT)
12821   {
12822     player->actual_frame_counter = FrameCounter;
12823     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12824
12825     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12826         Feld[last_jx][last_jy] == EL_EMPTY)
12827     {
12828       int last_field_block_delay = 0;   // start with no blocking at all
12829       int block_delay_adjustment = player->block_delay_adjustment;
12830
12831       // if player blocks last field, add delay for exactly one move
12832       if (player->block_last_field)
12833       {
12834         last_field_block_delay += player->move_delay_value;
12835
12836         // when blocking enabled, prevent moving up despite gravity
12837         if (player->gravity && player->MovDir == MV_UP)
12838           block_delay_adjustment = -1;
12839       }
12840
12841       // add block delay adjustment (also possible when not blocking)
12842       last_field_block_delay += block_delay_adjustment;
12843
12844       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12845       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12846     }
12847
12848     if (player->MovPos != 0)    // player has not yet reached destination
12849       return;
12850   }
12851   else if (!FrameReached(&player->actual_frame_counter, 1))
12852     return;
12853
12854   if (player->MovPos != 0)
12855   {
12856     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12857     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12858
12859     // before DrawPlayer() to draw correct player graphic for this case
12860     if (player->MovPos == 0)
12861       CheckGravityMovement(player);
12862   }
12863
12864   if (player->MovPos == 0)      // player reached destination field
12865   {
12866     if (player->move_delay_reset_counter > 0)
12867     {
12868       player->move_delay_reset_counter--;
12869
12870       if (player->move_delay_reset_counter == 0)
12871       {
12872         // continue with normal speed after quickly moving through gate
12873         HALVE_PLAYER_SPEED(player);
12874
12875         // be able to make the next move without delay
12876         player->move_delay = 0;
12877       }
12878     }
12879
12880     player->last_jx = jx;
12881     player->last_jy = jy;
12882
12883     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12884         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12885         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12886         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12887         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12888         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12889         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12890         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12891     {
12892       ExitPlayer(player);
12893
12894       if (game.players_still_needed == 0 &&
12895           (game.friends_still_needed == 0 ||
12896            IS_SP_ELEMENT(Feld[jx][jy])))
12897         LevelSolved();
12898     }
12899
12900     // this breaks one level: "machine", level 000
12901     {
12902       int move_direction = player->MovDir;
12903       int enter_side = MV_DIR_OPPOSITE(move_direction);
12904       int leave_side = move_direction;
12905       int old_jx = last_jx;
12906       int old_jy = last_jy;
12907       int old_element = Feld[old_jx][old_jy];
12908       int new_element = Feld[jx][jy];
12909
12910       if (IS_CUSTOM_ELEMENT(old_element))
12911         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12912                                    CE_LEFT_BY_PLAYER,
12913                                    player->index_bit, leave_side);
12914
12915       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12916                                           CE_PLAYER_LEAVES_X,
12917                                           player->index_bit, leave_side);
12918
12919       if (IS_CUSTOM_ELEMENT(new_element))
12920         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12921                                    player->index_bit, enter_side);
12922
12923       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12924                                           CE_PLAYER_ENTERS_X,
12925                                           player->index_bit, enter_side);
12926
12927       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12928                                         CE_MOVE_OF_X, move_direction);
12929     }
12930
12931     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12932     {
12933       TestIfPlayerTouchesBadThing(jx, jy);
12934       TestIfPlayerTouchesCustomElement(jx, jy);
12935
12936       /* needed because pushed element has not yet reached its destination,
12937          so it would trigger a change event at its previous field location */
12938       if (!player->is_pushing)
12939         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12940
12941       if (!player->active)
12942         RemovePlayer(player);
12943     }
12944
12945     if (!game.LevelSolved && level.use_step_counter)
12946     {
12947       int i;
12948
12949       TimePlayed++;
12950
12951       if (TimeLeft > 0)
12952       {
12953         TimeLeft--;
12954
12955         if (TimeLeft <= 10 && setup.time_limit)
12956           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12957
12958         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12959
12960         DisplayGameControlValues();
12961
12962         if (!TimeLeft && setup.time_limit)
12963           for (i = 0; i < MAX_PLAYERS; i++)
12964             KillPlayer(&stored_player[i]);
12965       }
12966       else if (game.no_time_limit && !game.all_players_gone)
12967       {
12968         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12969
12970         DisplayGameControlValues();
12971       }
12972     }
12973
12974     if (tape.single_step && tape.recording && !tape.pausing &&
12975         !player->programmed_action)
12976       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12977
12978     if (!player->programmed_action)
12979       CheckSaveEngineSnapshot(player);
12980   }
12981 }
12982
12983 void ScrollScreen(struct PlayerInfo *player, int mode)
12984 {
12985   static unsigned int screen_frame_counter = 0;
12986
12987   if (mode == SCROLL_INIT)
12988   {
12989     // set scrolling step size according to actual player's moving speed
12990     ScrollStepSize = TILEX / player->move_delay_value;
12991
12992     screen_frame_counter = FrameCounter;
12993     ScreenMovDir = player->MovDir;
12994     ScreenMovPos = player->MovPos;
12995     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12996     return;
12997   }
12998   else if (!FrameReached(&screen_frame_counter, 1))
12999     return;
13000
13001   if (ScreenMovPos)
13002   {
13003     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13004     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13005     redraw_mask |= REDRAW_FIELD;
13006   }
13007   else
13008     ScreenMovDir = MV_NONE;
13009 }
13010
13011 void TestIfPlayerTouchesCustomElement(int x, int y)
13012 {
13013   static int xy[4][2] =
13014   {
13015     { 0, -1 },
13016     { -1, 0 },
13017     { +1, 0 },
13018     { 0, +1 }
13019   };
13020   static int trigger_sides[4][2] =
13021   {
13022     // center side       border side
13023     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13024     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13025     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13026     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13027   };
13028   static int touch_dir[4] =
13029   {
13030     MV_LEFT | MV_RIGHT,
13031     MV_UP   | MV_DOWN,
13032     MV_UP   | MV_DOWN,
13033     MV_LEFT | MV_RIGHT
13034   };
13035   int center_element = Feld[x][y];      // should always be non-moving!
13036   int i;
13037
13038   for (i = 0; i < NUM_DIRECTIONS; i++)
13039   {
13040     int xx = x + xy[i][0];
13041     int yy = y + xy[i][1];
13042     int center_side = trigger_sides[i][0];
13043     int border_side = trigger_sides[i][1];
13044     int border_element;
13045
13046     if (!IN_LEV_FIELD(xx, yy))
13047       continue;
13048
13049     if (IS_PLAYER(x, y))                // player found at center element
13050     {
13051       struct PlayerInfo *player = PLAYERINFO(x, y);
13052
13053       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13054         border_element = Feld[xx][yy];          // may be moving!
13055       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13056         border_element = Feld[xx][yy];
13057       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13058         border_element = MovingOrBlocked2Element(xx, yy);
13059       else
13060         continue;               // center and border element do not touch
13061
13062       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13063                                  player->index_bit, border_side);
13064       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13065                                           CE_PLAYER_TOUCHES_X,
13066                                           player->index_bit, border_side);
13067
13068       {
13069         /* use player element that is initially defined in the level playfield,
13070            not the player element that corresponds to the runtime player number
13071            (example: a level that contains EL_PLAYER_3 as the only player would
13072            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13073         int player_element = PLAYERINFO(x, y)->initial_element;
13074
13075         CheckElementChangeBySide(xx, yy, border_element, player_element,
13076                                  CE_TOUCHING_X, border_side);
13077       }
13078     }
13079     else if (IS_PLAYER(xx, yy))         // player found at border element
13080     {
13081       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13082
13083       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13084       {
13085         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13086           continue;             // center and border element do not touch
13087       }
13088
13089       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13090                                  player->index_bit, center_side);
13091       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13092                                           CE_PLAYER_TOUCHES_X,
13093                                           player->index_bit, center_side);
13094
13095       {
13096         /* use player element that is initially defined in the level playfield,
13097            not the player element that corresponds to the runtime player number
13098            (example: a level that contains EL_PLAYER_3 as the only player would
13099            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13100         int player_element = PLAYERINFO(xx, yy)->initial_element;
13101
13102         CheckElementChangeBySide(x, y, center_element, player_element,
13103                                  CE_TOUCHING_X, center_side);
13104       }
13105
13106       break;
13107     }
13108   }
13109 }
13110
13111 void TestIfElementTouchesCustomElement(int x, int y)
13112 {
13113   static int xy[4][2] =
13114   {
13115     { 0, -1 },
13116     { -1, 0 },
13117     { +1, 0 },
13118     { 0, +1 }
13119   };
13120   static int trigger_sides[4][2] =
13121   {
13122     // center side      border side
13123     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13124     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13125     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13126     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13127   };
13128   static int touch_dir[4] =
13129   {
13130     MV_LEFT | MV_RIGHT,
13131     MV_UP   | MV_DOWN,
13132     MV_UP   | MV_DOWN,
13133     MV_LEFT | MV_RIGHT
13134   };
13135   boolean change_center_element = FALSE;
13136   int center_element = Feld[x][y];      // should always be non-moving!
13137   int border_element_old[NUM_DIRECTIONS];
13138   int i;
13139
13140   for (i = 0; i < NUM_DIRECTIONS; i++)
13141   {
13142     int xx = x + xy[i][0];
13143     int yy = y + xy[i][1];
13144     int border_element;
13145
13146     border_element_old[i] = -1;
13147
13148     if (!IN_LEV_FIELD(xx, yy))
13149       continue;
13150
13151     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13152       border_element = Feld[xx][yy];    // may be moving!
13153     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13154       border_element = Feld[xx][yy];
13155     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13156       border_element = MovingOrBlocked2Element(xx, yy);
13157     else
13158       continue;                 // center and border element do not touch
13159
13160     border_element_old[i] = border_element;
13161   }
13162
13163   for (i = 0; i < NUM_DIRECTIONS; i++)
13164   {
13165     int xx = x + xy[i][0];
13166     int yy = y + xy[i][1];
13167     int center_side = trigger_sides[i][0];
13168     int border_element = border_element_old[i];
13169
13170     if (border_element == -1)
13171       continue;
13172
13173     // check for change of border element
13174     CheckElementChangeBySide(xx, yy, border_element, center_element,
13175                              CE_TOUCHING_X, center_side);
13176
13177     // (center element cannot be player, so we dont have to check this here)
13178   }
13179
13180   for (i = 0; i < NUM_DIRECTIONS; i++)
13181   {
13182     int xx = x + xy[i][0];
13183     int yy = y + xy[i][1];
13184     int border_side = trigger_sides[i][1];
13185     int border_element = border_element_old[i];
13186
13187     if (border_element == -1)
13188       continue;
13189
13190     // check for change of center element (but change it only once)
13191     if (!change_center_element)
13192       change_center_element =
13193         CheckElementChangeBySide(x, y, center_element, border_element,
13194                                  CE_TOUCHING_X, border_side);
13195
13196     if (IS_PLAYER(xx, yy))
13197     {
13198       /* use player element that is initially defined in the level playfield,
13199          not the player element that corresponds to the runtime player number
13200          (example: a level that contains EL_PLAYER_3 as the only player would
13201          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13202       int player_element = PLAYERINFO(xx, yy)->initial_element;
13203
13204       CheckElementChangeBySide(x, y, center_element, player_element,
13205                                CE_TOUCHING_X, border_side);
13206     }
13207   }
13208 }
13209
13210 void TestIfElementHitsCustomElement(int x, int y, int direction)
13211 {
13212   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13213   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13214   int hitx = x + dx, hity = y + dy;
13215   int hitting_element = Feld[x][y];
13216   int touched_element;
13217
13218   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13219     return;
13220
13221   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13222                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13223
13224   if (IN_LEV_FIELD(hitx, hity))
13225   {
13226     int opposite_direction = MV_DIR_OPPOSITE(direction);
13227     int hitting_side = direction;
13228     int touched_side = opposite_direction;
13229     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13230                           MovDir[hitx][hity] != direction ||
13231                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13232
13233     object_hit = TRUE;
13234
13235     if (object_hit)
13236     {
13237       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13238                                CE_HITTING_X, touched_side);
13239
13240       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13241                                CE_HIT_BY_X, hitting_side);
13242
13243       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13244                                CE_HIT_BY_SOMETHING, opposite_direction);
13245
13246       if (IS_PLAYER(hitx, hity))
13247       {
13248         /* use player element that is initially defined in the level playfield,
13249            not the player element that corresponds to the runtime player number
13250            (example: a level that contains EL_PLAYER_3 as the only player would
13251            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13252         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13253
13254         CheckElementChangeBySide(x, y, hitting_element, player_element,
13255                                  CE_HITTING_X, touched_side);
13256       }
13257     }
13258   }
13259
13260   // "hitting something" is also true when hitting the playfield border
13261   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13262                            CE_HITTING_SOMETHING, direction);
13263 }
13264
13265 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13266 {
13267   int i, kill_x = -1, kill_y = -1;
13268
13269   int bad_element = -1;
13270   static int test_xy[4][2] =
13271   {
13272     { 0, -1 },
13273     { -1, 0 },
13274     { +1, 0 },
13275     { 0, +1 }
13276   };
13277   static int test_dir[4] =
13278   {
13279     MV_UP,
13280     MV_LEFT,
13281     MV_RIGHT,
13282     MV_DOWN
13283   };
13284
13285   for (i = 0; i < NUM_DIRECTIONS; i++)
13286   {
13287     int test_x, test_y, test_move_dir, test_element;
13288
13289     test_x = good_x + test_xy[i][0];
13290     test_y = good_y + test_xy[i][1];
13291
13292     if (!IN_LEV_FIELD(test_x, test_y))
13293       continue;
13294
13295     test_move_dir =
13296       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13297
13298     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13299
13300     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13301        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13302     */
13303     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13304         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13305     {
13306       kill_x = test_x;
13307       kill_y = test_y;
13308       bad_element = test_element;
13309
13310       break;
13311     }
13312   }
13313
13314   if (kill_x != -1 || kill_y != -1)
13315   {
13316     if (IS_PLAYER(good_x, good_y))
13317     {
13318       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13319
13320       if (player->shield_deadly_time_left > 0 &&
13321           !IS_INDESTRUCTIBLE(bad_element))
13322         Bang(kill_x, kill_y);
13323       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13324         KillPlayer(player);
13325     }
13326     else
13327       Bang(good_x, good_y);
13328   }
13329 }
13330
13331 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13332 {
13333   int i, kill_x = -1, kill_y = -1;
13334   int bad_element = Feld[bad_x][bad_y];
13335   static int test_xy[4][2] =
13336   {
13337     { 0, -1 },
13338     { -1, 0 },
13339     { +1, 0 },
13340     { 0, +1 }
13341   };
13342   static int touch_dir[4] =
13343   {
13344     MV_LEFT | MV_RIGHT,
13345     MV_UP   | MV_DOWN,
13346     MV_UP   | MV_DOWN,
13347     MV_LEFT | MV_RIGHT
13348   };
13349   static int test_dir[4] =
13350   {
13351     MV_UP,
13352     MV_LEFT,
13353     MV_RIGHT,
13354     MV_DOWN
13355   };
13356
13357   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13358     return;
13359
13360   for (i = 0; i < NUM_DIRECTIONS; i++)
13361   {
13362     int test_x, test_y, test_move_dir, test_element;
13363
13364     test_x = bad_x + test_xy[i][0];
13365     test_y = bad_y + test_xy[i][1];
13366
13367     if (!IN_LEV_FIELD(test_x, test_y))
13368       continue;
13369
13370     test_move_dir =
13371       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13372
13373     test_element = Feld[test_x][test_y];
13374
13375     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13376        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13377     */
13378     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13379         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13380     {
13381       // good thing is player or penguin that does not move away
13382       if (IS_PLAYER(test_x, test_y))
13383       {
13384         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13385
13386         if (bad_element == EL_ROBOT && player->is_moving)
13387           continue;     // robot does not kill player if he is moving
13388
13389         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13390         {
13391           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13392             continue;           // center and border element do not touch
13393         }
13394
13395         kill_x = test_x;
13396         kill_y = test_y;
13397
13398         break;
13399       }
13400       else if (test_element == EL_PENGUIN)
13401       {
13402         kill_x = test_x;
13403         kill_y = test_y;
13404
13405         break;
13406       }
13407     }
13408   }
13409
13410   if (kill_x != -1 || kill_y != -1)
13411   {
13412     if (IS_PLAYER(kill_x, kill_y))
13413     {
13414       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13415
13416       if (player->shield_deadly_time_left > 0 &&
13417           !IS_INDESTRUCTIBLE(bad_element))
13418         Bang(bad_x, bad_y);
13419       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13420         KillPlayer(player);
13421     }
13422     else
13423       Bang(kill_x, kill_y);
13424   }
13425 }
13426
13427 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13428 {
13429   int bad_element = Feld[bad_x][bad_y];
13430   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13431   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13432   int test_x = bad_x + dx, test_y = bad_y + dy;
13433   int test_move_dir, test_element;
13434   int kill_x = -1, kill_y = -1;
13435
13436   if (!IN_LEV_FIELD(test_x, test_y))
13437     return;
13438
13439   test_move_dir =
13440     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13441
13442   test_element = Feld[test_x][test_y];
13443
13444   if (test_move_dir != bad_move_dir)
13445   {
13446     // good thing can be player or penguin that does not move away
13447     if (IS_PLAYER(test_x, test_y))
13448     {
13449       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13450
13451       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13452          player as being hit when he is moving towards the bad thing, because
13453          the "get hit by" condition would be lost after the player stops) */
13454       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13455         return;         // player moves away from bad thing
13456
13457       kill_x = test_x;
13458       kill_y = test_y;
13459     }
13460     else if (test_element == EL_PENGUIN)
13461     {
13462       kill_x = test_x;
13463       kill_y = test_y;
13464     }
13465   }
13466
13467   if (kill_x != -1 || kill_y != -1)
13468   {
13469     if (IS_PLAYER(kill_x, kill_y))
13470     {
13471       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13472
13473       if (player->shield_deadly_time_left > 0 &&
13474           !IS_INDESTRUCTIBLE(bad_element))
13475         Bang(bad_x, bad_y);
13476       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13477         KillPlayer(player);
13478     }
13479     else
13480       Bang(kill_x, kill_y);
13481   }
13482 }
13483
13484 void TestIfPlayerTouchesBadThing(int x, int y)
13485 {
13486   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13487 }
13488
13489 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13490 {
13491   TestIfGoodThingHitsBadThing(x, y, move_dir);
13492 }
13493
13494 void TestIfBadThingTouchesPlayer(int x, int y)
13495 {
13496   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13497 }
13498
13499 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13500 {
13501   TestIfBadThingHitsGoodThing(x, y, move_dir);
13502 }
13503
13504 void TestIfFriendTouchesBadThing(int x, int y)
13505 {
13506   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13507 }
13508
13509 void TestIfBadThingTouchesFriend(int x, int y)
13510 {
13511   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13512 }
13513
13514 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13515 {
13516   int i, kill_x = bad_x, kill_y = bad_y;
13517   static int xy[4][2] =
13518   {
13519     { 0, -1 },
13520     { -1, 0 },
13521     { +1, 0 },
13522     { 0, +1 }
13523   };
13524
13525   for (i = 0; i < NUM_DIRECTIONS; i++)
13526   {
13527     int x, y, element;
13528
13529     x = bad_x + xy[i][0];
13530     y = bad_y + xy[i][1];
13531     if (!IN_LEV_FIELD(x, y))
13532       continue;
13533
13534     element = Feld[x][y];
13535     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13536         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13537     {
13538       kill_x = x;
13539       kill_y = y;
13540       break;
13541     }
13542   }
13543
13544   if (kill_x != bad_x || kill_y != bad_y)
13545     Bang(bad_x, bad_y);
13546 }
13547
13548 void KillPlayer(struct PlayerInfo *player)
13549 {
13550   int jx = player->jx, jy = player->jy;
13551
13552   if (!player->active)
13553     return;
13554
13555 #if 0
13556   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13557          player->killed, player->active, player->reanimated);
13558 #endif
13559
13560   /* the following code was introduced to prevent an infinite loop when calling
13561      -> Bang()
13562      -> CheckTriggeredElementChangeExt()
13563      -> ExecuteCustomElementAction()
13564      -> KillPlayer()
13565      -> (infinitely repeating the above sequence of function calls)
13566      which occurs when killing the player while having a CE with the setting
13567      "kill player X when explosion of <player X>"; the solution using a new
13568      field "player->killed" was chosen for backwards compatibility, although
13569      clever use of the fields "player->active" etc. would probably also work */
13570 #if 1
13571   if (player->killed)
13572     return;
13573 #endif
13574
13575   player->killed = TRUE;
13576
13577   // remove accessible field at the player's position
13578   Feld[jx][jy] = EL_EMPTY;
13579
13580   // deactivate shield (else Bang()/Explode() would not work right)
13581   player->shield_normal_time_left = 0;
13582   player->shield_deadly_time_left = 0;
13583
13584 #if 0
13585   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13586          player->killed, player->active, player->reanimated);
13587 #endif
13588
13589   Bang(jx, jy);
13590
13591 #if 0
13592   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13593          player->killed, player->active, player->reanimated);
13594 #endif
13595
13596   if (player->reanimated)       // killed player may have been reanimated
13597     player->killed = player->reanimated = FALSE;
13598   else
13599     BuryPlayer(player);
13600 }
13601
13602 static void KillPlayerUnlessEnemyProtected(int x, int y)
13603 {
13604   if (!PLAYER_ENEMY_PROTECTED(x, y))
13605     KillPlayer(PLAYERINFO(x, y));
13606 }
13607
13608 static void KillPlayerUnlessExplosionProtected(int x, int y)
13609 {
13610   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13611     KillPlayer(PLAYERINFO(x, y));
13612 }
13613
13614 void BuryPlayer(struct PlayerInfo *player)
13615 {
13616   int jx = player->jx, jy = player->jy;
13617
13618   if (!player->active)
13619     return;
13620
13621   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13622   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13623
13624   RemovePlayer(player);
13625
13626   player->buried = TRUE;
13627
13628   if (game.all_players_gone)
13629     game.GameOver = TRUE;
13630 }
13631
13632 void RemovePlayer(struct PlayerInfo *player)
13633 {
13634   int jx = player->jx, jy = player->jy;
13635   int i, found = FALSE;
13636
13637   player->present = FALSE;
13638   player->active = FALSE;
13639
13640   // required for some CE actions (even if the player is not active anymore)
13641   player->MovPos = 0;
13642
13643   if (!ExplodeField[jx][jy])
13644     StorePlayer[jx][jy] = 0;
13645
13646   if (player->is_moving)
13647     TEST_DrawLevelField(player->last_jx, player->last_jy);
13648
13649   for (i = 0; i < MAX_PLAYERS; i++)
13650     if (stored_player[i].active)
13651       found = TRUE;
13652
13653   if (!found)
13654   {
13655     game.all_players_gone = TRUE;
13656     game.GameOver = TRUE;
13657   }
13658
13659   game.exit_x = game.robot_wheel_x = jx;
13660   game.exit_y = game.robot_wheel_y = jy;
13661 }
13662
13663 void ExitPlayer(struct PlayerInfo *player)
13664 {
13665   DrawPlayer(player);   // needed here only to cleanup last field
13666   RemovePlayer(player);
13667
13668   if (game.players_still_needed > 0)
13669     game.players_still_needed--;
13670 }
13671
13672 static void setFieldForSnapping(int x, int y, int element, int direction)
13673 {
13674   struct ElementInfo *ei = &element_info[element];
13675   int direction_bit = MV_DIR_TO_BIT(direction);
13676   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13677   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13678                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13679
13680   Feld[x][y] = EL_ELEMENT_SNAPPING;
13681   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13682
13683   ResetGfxAnimation(x, y);
13684
13685   GfxElement[x][y] = element;
13686   GfxAction[x][y] = action;
13687   GfxDir[x][y] = direction;
13688   GfxFrame[x][y] = -1;
13689 }
13690
13691 /*
13692   =============================================================================
13693   checkDiagonalPushing()
13694   -----------------------------------------------------------------------------
13695   check if diagonal input device direction results in pushing of object
13696   (by checking if the alternative direction is walkable, diggable, ...)
13697   =============================================================================
13698 */
13699
13700 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13701                                     int x, int y, int real_dx, int real_dy)
13702 {
13703   int jx, jy, dx, dy, xx, yy;
13704
13705   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13706     return TRUE;
13707
13708   // diagonal direction: check alternative direction
13709   jx = player->jx;
13710   jy = player->jy;
13711   dx = x - jx;
13712   dy = y - jy;
13713   xx = jx + (dx == 0 ? real_dx : 0);
13714   yy = jy + (dy == 0 ? real_dy : 0);
13715
13716   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13717 }
13718
13719 /*
13720   =============================================================================
13721   DigField()
13722   -----------------------------------------------------------------------------
13723   x, y:                 field next to player (non-diagonal) to try to dig to
13724   real_dx, real_dy:     direction as read from input device (can be diagonal)
13725   =============================================================================
13726 */
13727
13728 static int DigField(struct PlayerInfo *player,
13729                     int oldx, int oldy, int x, int y,
13730                     int real_dx, int real_dy, int mode)
13731 {
13732   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13733   boolean player_was_pushing = player->is_pushing;
13734   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13735   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13736   int jx = oldx, jy = oldy;
13737   int dx = x - jx, dy = y - jy;
13738   int nextx = x + dx, nexty = y + dy;
13739   int move_direction = (dx == -1 ? MV_LEFT  :
13740                         dx == +1 ? MV_RIGHT :
13741                         dy == -1 ? MV_UP    :
13742                         dy == +1 ? MV_DOWN  : MV_NONE);
13743   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13744   int dig_side = MV_DIR_OPPOSITE(move_direction);
13745   int old_element = Feld[jx][jy];
13746   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13747   int collect_count;
13748
13749   if (is_player)                // function can also be called by EL_PENGUIN
13750   {
13751     if (player->MovPos == 0)
13752     {
13753       player->is_digging = FALSE;
13754       player->is_collecting = FALSE;
13755     }
13756
13757     if (player->MovPos == 0)    // last pushing move finished
13758       player->is_pushing = FALSE;
13759
13760     if (mode == DF_NO_PUSH)     // player just stopped pushing
13761     {
13762       player->is_switching = FALSE;
13763       player->push_delay = -1;
13764
13765       return MP_NO_ACTION;
13766     }
13767   }
13768
13769   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13770     old_element = Back[jx][jy];
13771
13772   // in case of element dropped at player position, check background
13773   else if (Back[jx][jy] != EL_EMPTY &&
13774            game.engine_version >= VERSION_IDENT(2,2,0,0))
13775     old_element = Back[jx][jy];
13776
13777   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13778     return MP_NO_ACTION;        // field has no opening in this direction
13779
13780   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13781     return MP_NO_ACTION;        // field has no opening in this direction
13782
13783   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13784   {
13785     SplashAcid(x, y);
13786
13787     Feld[jx][jy] = player->artwork_element;
13788     InitMovingField(jx, jy, MV_DOWN);
13789     Store[jx][jy] = EL_ACID;
13790     ContinueMoving(jx, jy);
13791     BuryPlayer(player);
13792
13793     return MP_DONT_RUN_INTO;
13794   }
13795
13796   if (player_can_move && DONT_RUN_INTO(element))
13797   {
13798     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13799
13800     return MP_DONT_RUN_INTO;
13801   }
13802
13803   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13804     return MP_NO_ACTION;
13805
13806   collect_count = element_info[element].collect_count_initial;
13807
13808   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13809     return MP_NO_ACTION;
13810
13811   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13812     player_can_move = player_can_move_or_snap;
13813
13814   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13815       game.engine_version >= VERSION_IDENT(2,2,0,0))
13816   {
13817     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13818                                player->index_bit, dig_side);
13819     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13820                                         player->index_bit, dig_side);
13821
13822     if (element == EL_DC_LANDMINE)
13823       Bang(x, y);
13824
13825     if (Feld[x][y] != element)          // field changed by snapping
13826       return MP_ACTION;
13827
13828     return MP_NO_ACTION;
13829   }
13830
13831   if (player->gravity && is_player && !player->is_auto_moving &&
13832       canFallDown(player) && move_direction != MV_DOWN &&
13833       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13834     return MP_NO_ACTION;        // player cannot walk here due to gravity
13835
13836   if (player_can_move &&
13837       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13838   {
13839     int sound_element = SND_ELEMENT(element);
13840     int sound_action = ACTION_WALKING;
13841
13842     if (IS_RND_GATE(element))
13843     {
13844       if (!player->key[RND_GATE_NR(element)])
13845         return MP_NO_ACTION;
13846     }
13847     else if (IS_RND_GATE_GRAY(element))
13848     {
13849       if (!player->key[RND_GATE_GRAY_NR(element)])
13850         return MP_NO_ACTION;
13851     }
13852     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13853     {
13854       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13855         return MP_NO_ACTION;
13856     }
13857     else if (element == EL_EXIT_OPEN ||
13858              element == EL_EM_EXIT_OPEN ||
13859              element == EL_EM_EXIT_OPENING ||
13860              element == EL_STEEL_EXIT_OPEN ||
13861              element == EL_EM_STEEL_EXIT_OPEN ||
13862              element == EL_EM_STEEL_EXIT_OPENING ||
13863              element == EL_SP_EXIT_OPEN ||
13864              element == EL_SP_EXIT_OPENING)
13865     {
13866       sound_action = ACTION_PASSING;    // player is passing exit
13867     }
13868     else if (element == EL_EMPTY)
13869     {
13870       sound_action = ACTION_MOVING;             // nothing to walk on
13871     }
13872
13873     // play sound from background or player, whatever is available
13874     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13875       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13876     else
13877       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13878   }
13879   else if (player_can_move &&
13880            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13881   {
13882     if (!ACCESS_FROM(element, opposite_direction))
13883       return MP_NO_ACTION;      // field not accessible from this direction
13884
13885     if (CAN_MOVE(element))      // only fixed elements can be passed!
13886       return MP_NO_ACTION;
13887
13888     if (IS_EM_GATE(element))
13889     {
13890       if (!player->key[EM_GATE_NR(element)])
13891         return MP_NO_ACTION;
13892     }
13893     else if (IS_EM_GATE_GRAY(element))
13894     {
13895       if (!player->key[EM_GATE_GRAY_NR(element)])
13896         return MP_NO_ACTION;
13897     }
13898     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13899     {
13900       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13901         return MP_NO_ACTION;
13902     }
13903     else if (IS_EMC_GATE(element))
13904     {
13905       if (!player->key[EMC_GATE_NR(element)])
13906         return MP_NO_ACTION;
13907     }
13908     else if (IS_EMC_GATE_GRAY(element))
13909     {
13910       if (!player->key[EMC_GATE_GRAY_NR(element)])
13911         return MP_NO_ACTION;
13912     }
13913     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13914     {
13915       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13916         return MP_NO_ACTION;
13917     }
13918     else if (element == EL_DC_GATE_WHITE ||
13919              element == EL_DC_GATE_WHITE_GRAY ||
13920              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13921     {
13922       if (player->num_white_keys == 0)
13923         return MP_NO_ACTION;
13924
13925       player->num_white_keys--;
13926     }
13927     else if (IS_SP_PORT(element))
13928     {
13929       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13930           element == EL_SP_GRAVITY_PORT_RIGHT ||
13931           element == EL_SP_GRAVITY_PORT_UP ||
13932           element == EL_SP_GRAVITY_PORT_DOWN)
13933         player->gravity = !player->gravity;
13934       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13935                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13936                element == EL_SP_GRAVITY_ON_PORT_UP ||
13937                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13938         player->gravity = TRUE;
13939       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13940                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13941                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13942                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13943         player->gravity = FALSE;
13944     }
13945
13946     // automatically move to the next field with double speed
13947     player->programmed_action = move_direction;
13948
13949     if (player->move_delay_reset_counter == 0)
13950     {
13951       player->move_delay_reset_counter = 2;     // two double speed steps
13952
13953       DOUBLE_PLAYER_SPEED(player);
13954     }
13955
13956     PlayLevelSoundAction(x, y, ACTION_PASSING);
13957   }
13958   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13959   {
13960     RemoveField(x, y);
13961
13962     if (mode != DF_SNAP)
13963     {
13964       GfxElement[x][y] = GFX_ELEMENT(element);
13965       player->is_digging = TRUE;
13966     }
13967
13968     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13969
13970     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13971                                         player->index_bit, dig_side);
13972
13973     if (mode == DF_SNAP)
13974     {
13975       if (level.block_snap_field)
13976         setFieldForSnapping(x, y, element, move_direction);
13977       else
13978         TestIfElementTouchesCustomElement(x, y);        // for empty space
13979
13980       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13981                                           player->index_bit, dig_side);
13982     }
13983   }
13984   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13985   {
13986     RemoveField(x, y);
13987
13988     if (is_player && mode != DF_SNAP)
13989     {
13990       GfxElement[x][y] = element;
13991       player->is_collecting = TRUE;
13992     }
13993
13994     if (element == EL_SPEED_PILL)
13995     {
13996       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13997     }
13998     else if (element == EL_EXTRA_TIME && level.time > 0)
13999     {
14000       TimeLeft += level.extra_time;
14001
14002       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14003
14004       DisplayGameControlValues();
14005     }
14006     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14007     {
14008       player->shield_normal_time_left += level.shield_normal_time;
14009       if (element == EL_SHIELD_DEADLY)
14010         player->shield_deadly_time_left += level.shield_deadly_time;
14011     }
14012     else if (element == EL_DYNAMITE ||
14013              element == EL_EM_DYNAMITE ||
14014              element == EL_SP_DISK_RED)
14015     {
14016       if (player->inventory_size < MAX_INVENTORY_SIZE)
14017         player->inventory_element[player->inventory_size++] = element;
14018
14019       DrawGameDoorValues();
14020     }
14021     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14022     {
14023       player->dynabomb_count++;
14024       player->dynabombs_left++;
14025     }
14026     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14027     {
14028       player->dynabomb_size++;
14029     }
14030     else if (element == EL_DYNABOMB_INCREASE_POWER)
14031     {
14032       player->dynabomb_xl = TRUE;
14033     }
14034     else if (IS_KEY(element))
14035     {
14036       player->key[KEY_NR(element)] = TRUE;
14037
14038       DrawGameDoorValues();
14039     }
14040     else if (element == EL_DC_KEY_WHITE)
14041     {
14042       player->num_white_keys++;
14043
14044       // display white keys?
14045       // DrawGameDoorValues();
14046     }
14047     else if (IS_ENVELOPE(element))
14048     {
14049       player->show_envelope = element;
14050     }
14051     else if (element == EL_EMC_LENSES)
14052     {
14053       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14054
14055       RedrawAllInvisibleElementsForLenses();
14056     }
14057     else if (element == EL_EMC_MAGNIFIER)
14058     {
14059       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14060
14061       RedrawAllInvisibleElementsForMagnifier();
14062     }
14063     else if (IS_DROPPABLE(element) ||
14064              IS_THROWABLE(element))     // can be collected and dropped
14065     {
14066       int i;
14067
14068       if (collect_count == 0)
14069         player->inventory_infinite_element = element;
14070       else
14071         for (i = 0; i < collect_count; i++)
14072           if (player->inventory_size < MAX_INVENTORY_SIZE)
14073             player->inventory_element[player->inventory_size++] = element;
14074
14075       DrawGameDoorValues();
14076     }
14077     else if (collect_count > 0)
14078     {
14079       game.gems_still_needed -= collect_count;
14080       if (game.gems_still_needed < 0)
14081         game.gems_still_needed = 0;
14082
14083       game.snapshot.collected_item = TRUE;
14084
14085       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14086
14087       DisplayGameControlValues();
14088     }
14089
14090     RaiseScoreElement(element);
14091     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14092
14093     if (is_player)
14094       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14095                                           player->index_bit, dig_side);
14096
14097     if (mode == DF_SNAP)
14098     {
14099       if (level.block_snap_field)
14100         setFieldForSnapping(x, y, element, move_direction);
14101       else
14102         TestIfElementTouchesCustomElement(x, y);        // for empty space
14103
14104       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14105                                           player->index_bit, dig_side);
14106     }
14107   }
14108   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14109   {
14110     if (mode == DF_SNAP && element != EL_BD_ROCK)
14111       return MP_NO_ACTION;
14112
14113     if (CAN_FALL(element) && dy)
14114       return MP_NO_ACTION;
14115
14116     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14117         !(element == EL_SPRING && level.use_spring_bug))
14118       return MP_NO_ACTION;
14119
14120     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14121         ((move_direction & MV_VERTICAL &&
14122           ((element_info[element].move_pattern & MV_LEFT &&
14123             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14124            (element_info[element].move_pattern & MV_RIGHT &&
14125             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14126          (move_direction & MV_HORIZONTAL &&
14127           ((element_info[element].move_pattern & MV_UP &&
14128             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14129            (element_info[element].move_pattern & MV_DOWN &&
14130             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14131       return MP_NO_ACTION;
14132
14133     // do not push elements already moving away faster than player
14134     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14135         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14136       return MP_NO_ACTION;
14137
14138     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14139     {
14140       if (player->push_delay_value == -1 || !player_was_pushing)
14141         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14142     }
14143     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14144     {
14145       if (player->push_delay_value == -1)
14146         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14147     }
14148     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14149     {
14150       if (!player->is_pushing)
14151         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14152     }
14153
14154     player->is_pushing = TRUE;
14155     player->is_active = TRUE;
14156
14157     if (!(IN_LEV_FIELD(nextx, nexty) &&
14158           (IS_FREE(nextx, nexty) ||
14159            (IS_SB_ELEMENT(element) &&
14160             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14161            (IS_CUSTOM_ELEMENT(element) &&
14162             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14163       return MP_NO_ACTION;
14164
14165     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14166       return MP_NO_ACTION;
14167
14168     if (player->push_delay == -1)       // new pushing; restart delay
14169       player->push_delay = 0;
14170
14171     if (player->push_delay < player->push_delay_value &&
14172         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14173         element != EL_SPRING && element != EL_BALLOON)
14174     {
14175       // make sure that there is no move delay before next try to push
14176       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14177         player->move_delay = 0;
14178
14179       return MP_NO_ACTION;
14180     }
14181
14182     if (IS_CUSTOM_ELEMENT(element) &&
14183         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14184     {
14185       if (!DigFieldByCE(nextx, nexty, element))
14186         return MP_NO_ACTION;
14187     }
14188
14189     if (IS_SB_ELEMENT(element))
14190     {
14191       boolean sokoban_task_solved = FALSE;
14192
14193       if (element == EL_SOKOBAN_FIELD_FULL)
14194       {
14195         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14196
14197         IncrementSokobanFieldsNeeded();
14198         IncrementSokobanObjectsNeeded();
14199       }
14200
14201       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14202       {
14203         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14204
14205         DecrementSokobanFieldsNeeded();
14206         DecrementSokobanObjectsNeeded();
14207
14208         // sokoban object was pushed from empty field to sokoban field
14209         if (Back[x][y] == EL_EMPTY)
14210           sokoban_task_solved = TRUE;
14211       }
14212
14213       Feld[x][y] = EL_SOKOBAN_OBJECT;
14214
14215       if (Back[x][y] == Back[nextx][nexty])
14216         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14217       else if (Back[x][y] != 0)
14218         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14219                                     ACTION_EMPTYING);
14220       else
14221         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14222                                     ACTION_FILLING);
14223
14224       if (sokoban_task_solved &&
14225           game.sokoban_fields_still_needed == 0 &&
14226           game.sokoban_objects_still_needed == 0 &&
14227           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14228       {
14229         game.players_still_needed = 0;
14230
14231         LevelSolved();
14232
14233         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14234       }
14235     }
14236     else
14237       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14238
14239     InitMovingField(x, y, move_direction);
14240     GfxAction[x][y] = ACTION_PUSHING;
14241
14242     if (mode == DF_SNAP)
14243       ContinueMoving(x, y);
14244     else
14245       MovPos[x][y] = (dx != 0 ? dx : dy);
14246
14247     Pushed[x][y] = TRUE;
14248     Pushed[nextx][nexty] = TRUE;
14249
14250     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14251       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14252     else
14253       player->push_delay_value = -1;    // get new value later
14254
14255     // check for element change _after_ element has been pushed
14256     if (game.use_change_when_pushing_bug)
14257     {
14258       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14259                                  player->index_bit, dig_side);
14260       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14261                                           player->index_bit, dig_side);
14262     }
14263   }
14264   else if (IS_SWITCHABLE(element))
14265   {
14266     if (PLAYER_SWITCHING(player, x, y))
14267     {
14268       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14269                                           player->index_bit, dig_side);
14270
14271       return MP_ACTION;
14272     }
14273
14274     player->is_switching = TRUE;
14275     player->switch_x = x;
14276     player->switch_y = y;
14277
14278     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14279
14280     if (element == EL_ROBOT_WHEEL)
14281     {
14282       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14283
14284       game.robot_wheel_x = x;
14285       game.robot_wheel_y = y;
14286       game.robot_wheel_active = TRUE;
14287
14288       TEST_DrawLevelField(x, y);
14289     }
14290     else if (element == EL_SP_TERMINAL)
14291     {
14292       int xx, yy;
14293
14294       SCAN_PLAYFIELD(xx, yy)
14295       {
14296         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14297         {
14298           Bang(xx, yy);
14299         }
14300         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14301         {
14302           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14303
14304           ResetGfxAnimation(xx, yy);
14305           TEST_DrawLevelField(xx, yy);
14306         }
14307       }
14308     }
14309     else if (IS_BELT_SWITCH(element))
14310     {
14311       ToggleBeltSwitch(x, y);
14312     }
14313     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14314              element == EL_SWITCHGATE_SWITCH_DOWN ||
14315              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14316              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14317     {
14318       ToggleSwitchgateSwitch(x, y);
14319     }
14320     else if (element == EL_LIGHT_SWITCH ||
14321              element == EL_LIGHT_SWITCH_ACTIVE)
14322     {
14323       ToggleLightSwitch(x, y);
14324     }
14325     else if (element == EL_TIMEGATE_SWITCH ||
14326              element == EL_DC_TIMEGATE_SWITCH)
14327     {
14328       ActivateTimegateSwitch(x, y);
14329     }
14330     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14331              element == EL_BALLOON_SWITCH_RIGHT ||
14332              element == EL_BALLOON_SWITCH_UP    ||
14333              element == EL_BALLOON_SWITCH_DOWN  ||
14334              element == EL_BALLOON_SWITCH_NONE  ||
14335              element == EL_BALLOON_SWITCH_ANY)
14336     {
14337       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14338                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14339                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14340                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14341                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14342                              move_direction);
14343     }
14344     else if (element == EL_LAMP)
14345     {
14346       Feld[x][y] = EL_LAMP_ACTIVE;
14347       game.lights_still_needed--;
14348
14349       ResetGfxAnimation(x, y);
14350       TEST_DrawLevelField(x, y);
14351     }
14352     else if (element == EL_TIME_ORB_FULL)
14353     {
14354       Feld[x][y] = EL_TIME_ORB_EMPTY;
14355
14356       if (level.time > 0 || level.use_time_orb_bug)
14357       {
14358         TimeLeft += level.time_orb_time;
14359         game.no_time_limit = FALSE;
14360
14361         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14362
14363         DisplayGameControlValues();
14364       }
14365
14366       ResetGfxAnimation(x, y);
14367       TEST_DrawLevelField(x, y);
14368     }
14369     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14370              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14371     {
14372       int xx, yy;
14373
14374       game.ball_active = !game.ball_active;
14375
14376       SCAN_PLAYFIELD(xx, yy)
14377       {
14378         int e = Feld[xx][yy];
14379
14380         if (game.ball_active)
14381         {
14382           if (e == EL_EMC_MAGIC_BALL)
14383             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14384           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14385             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14386         }
14387         else
14388         {
14389           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14390             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14391           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14392             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14393         }
14394       }
14395     }
14396
14397     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14398                                         player->index_bit, dig_side);
14399
14400     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14401                                         player->index_bit, dig_side);
14402
14403     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14404                                         player->index_bit, dig_side);
14405
14406     return MP_ACTION;
14407   }
14408   else
14409   {
14410     if (!PLAYER_SWITCHING(player, x, y))
14411     {
14412       player->is_switching = TRUE;
14413       player->switch_x = x;
14414       player->switch_y = y;
14415
14416       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14417                                  player->index_bit, dig_side);
14418       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14419                                           player->index_bit, dig_side);
14420
14421       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14422                                  player->index_bit, dig_side);
14423       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14424                                           player->index_bit, dig_side);
14425     }
14426
14427     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14428                                player->index_bit, dig_side);
14429     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14430                                         player->index_bit, dig_side);
14431
14432     return MP_NO_ACTION;
14433   }
14434
14435   player->push_delay = -1;
14436
14437   if (is_player)                // function can also be called by EL_PENGUIN
14438   {
14439     if (Feld[x][y] != element)          // really digged/collected something
14440     {
14441       player->is_collecting = !player->is_digging;
14442       player->is_active = TRUE;
14443     }
14444   }
14445
14446   return MP_MOVING;
14447 }
14448
14449 static boolean DigFieldByCE(int x, int y, int digging_element)
14450 {
14451   int element = Feld[x][y];
14452
14453   if (!IS_FREE(x, y))
14454   {
14455     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14456                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14457                   ACTION_BREAKING);
14458
14459     // no element can dig solid indestructible elements
14460     if (IS_INDESTRUCTIBLE(element) &&
14461         !IS_DIGGABLE(element) &&
14462         !IS_COLLECTIBLE(element))
14463       return FALSE;
14464
14465     if (AmoebaNr[x][y] &&
14466         (element == EL_AMOEBA_FULL ||
14467          element == EL_BD_AMOEBA ||
14468          element == EL_AMOEBA_GROWING))
14469     {
14470       AmoebaCnt[AmoebaNr[x][y]]--;
14471       AmoebaCnt2[AmoebaNr[x][y]]--;
14472     }
14473
14474     if (IS_MOVING(x, y))
14475       RemoveMovingField(x, y);
14476     else
14477     {
14478       RemoveField(x, y);
14479       TEST_DrawLevelField(x, y);
14480     }
14481
14482     // if digged element was about to explode, prevent the explosion
14483     ExplodeField[x][y] = EX_TYPE_NONE;
14484
14485     PlayLevelSoundAction(x, y, action);
14486   }
14487
14488   Store[x][y] = EL_EMPTY;
14489
14490   // this makes it possible to leave the removed element again
14491   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14492     Store[x][y] = element;
14493
14494   return TRUE;
14495 }
14496
14497 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14498 {
14499   int jx = player->jx, jy = player->jy;
14500   int x = jx + dx, y = jy + dy;
14501   int snap_direction = (dx == -1 ? MV_LEFT  :
14502                         dx == +1 ? MV_RIGHT :
14503                         dy == -1 ? MV_UP    :
14504                         dy == +1 ? MV_DOWN  : MV_NONE);
14505   boolean can_continue_snapping = (level.continuous_snapping &&
14506                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14507
14508   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14509     return FALSE;
14510
14511   if (!player->active || !IN_LEV_FIELD(x, y))
14512     return FALSE;
14513
14514   if (dx && dy)
14515     return FALSE;
14516
14517   if (!dx && !dy)
14518   {
14519     if (player->MovPos == 0)
14520       player->is_pushing = FALSE;
14521
14522     player->is_snapping = FALSE;
14523
14524     if (player->MovPos == 0)
14525     {
14526       player->is_moving = FALSE;
14527       player->is_digging = FALSE;
14528       player->is_collecting = FALSE;
14529     }
14530
14531     return FALSE;
14532   }
14533
14534   // prevent snapping with already pressed snap key when not allowed
14535   if (player->is_snapping && !can_continue_snapping)
14536     return FALSE;
14537
14538   player->MovDir = snap_direction;
14539
14540   if (player->MovPos == 0)
14541   {
14542     player->is_moving = FALSE;
14543     player->is_digging = FALSE;
14544     player->is_collecting = FALSE;
14545   }
14546
14547   player->is_dropping = FALSE;
14548   player->is_dropping_pressed = FALSE;
14549   player->drop_pressed_delay = 0;
14550
14551   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14552     return FALSE;
14553
14554   player->is_snapping = TRUE;
14555   player->is_active = TRUE;
14556
14557   if (player->MovPos == 0)
14558   {
14559     player->is_moving = FALSE;
14560     player->is_digging = FALSE;
14561     player->is_collecting = FALSE;
14562   }
14563
14564   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14565     TEST_DrawLevelField(player->last_jx, player->last_jy);
14566
14567   TEST_DrawLevelField(x, y);
14568
14569   return TRUE;
14570 }
14571
14572 static boolean DropElement(struct PlayerInfo *player)
14573 {
14574   int old_element, new_element;
14575   int dropx = player->jx, dropy = player->jy;
14576   int drop_direction = player->MovDir;
14577   int drop_side = drop_direction;
14578   int drop_element = get_next_dropped_element(player);
14579
14580   /* do not drop an element on top of another element; when holding drop key
14581      pressed without moving, dropped element must move away before the next
14582      element can be dropped (this is especially important if the next element
14583      is dynamite, which can be placed on background for historical reasons) */
14584   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14585     return MP_ACTION;
14586
14587   if (IS_THROWABLE(drop_element))
14588   {
14589     dropx += GET_DX_FROM_DIR(drop_direction);
14590     dropy += GET_DY_FROM_DIR(drop_direction);
14591
14592     if (!IN_LEV_FIELD(dropx, dropy))
14593       return FALSE;
14594   }
14595
14596   old_element = Feld[dropx][dropy];     // old element at dropping position
14597   new_element = drop_element;           // default: no change when dropping
14598
14599   // check if player is active, not moving and ready to drop
14600   if (!player->active || player->MovPos || player->drop_delay > 0)
14601     return FALSE;
14602
14603   // check if player has anything that can be dropped
14604   if (new_element == EL_UNDEFINED)
14605     return FALSE;
14606
14607   // only set if player has anything that can be dropped
14608   player->is_dropping_pressed = TRUE;
14609
14610   // check if drop key was pressed long enough for EM style dynamite
14611   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14612     return FALSE;
14613
14614   // check if anything can be dropped at the current position
14615   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14616     return FALSE;
14617
14618   // collected custom elements can only be dropped on empty fields
14619   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14620     return FALSE;
14621
14622   if (old_element != EL_EMPTY)
14623     Back[dropx][dropy] = old_element;   // store old element on this field
14624
14625   ResetGfxAnimation(dropx, dropy);
14626   ResetRandomAnimationValue(dropx, dropy);
14627
14628   if (player->inventory_size > 0 ||
14629       player->inventory_infinite_element != EL_UNDEFINED)
14630   {
14631     if (player->inventory_size > 0)
14632     {
14633       player->inventory_size--;
14634
14635       DrawGameDoorValues();
14636
14637       if (new_element == EL_DYNAMITE)
14638         new_element = EL_DYNAMITE_ACTIVE;
14639       else if (new_element == EL_EM_DYNAMITE)
14640         new_element = EL_EM_DYNAMITE_ACTIVE;
14641       else if (new_element == EL_SP_DISK_RED)
14642         new_element = EL_SP_DISK_RED_ACTIVE;
14643     }
14644
14645     Feld[dropx][dropy] = new_element;
14646
14647     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14648       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14649                           el2img(Feld[dropx][dropy]), 0);
14650
14651     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14652
14653     // needed if previous element just changed to "empty" in the last frame
14654     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14655
14656     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14657                                player->index_bit, drop_side);
14658     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14659                                         CE_PLAYER_DROPS_X,
14660                                         player->index_bit, drop_side);
14661
14662     TestIfElementTouchesCustomElement(dropx, dropy);
14663   }
14664   else          // player is dropping a dyna bomb
14665   {
14666     player->dynabombs_left--;
14667
14668     Feld[dropx][dropy] = new_element;
14669
14670     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14671       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14672                           el2img(Feld[dropx][dropy]), 0);
14673
14674     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14675   }
14676
14677   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14678     InitField_WithBug1(dropx, dropy, FALSE);
14679
14680   new_element = Feld[dropx][dropy];     // element might have changed
14681
14682   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14683       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14684   {
14685     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14686       MovDir[dropx][dropy] = drop_direction;
14687
14688     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14689
14690     // do not cause impact style collision by dropping elements that can fall
14691     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14692   }
14693
14694   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14695   player->is_dropping = TRUE;
14696
14697   player->drop_pressed_delay = 0;
14698   player->is_dropping_pressed = FALSE;
14699
14700   player->drop_x = dropx;
14701   player->drop_y = dropy;
14702
14703   return TRUE;
14704 }
14705
14706 // ----------------------------------------------------------------------------
14707 // game sound playing functions
14708 // ----------------------------------------------------------------------------
14709
14710 static int *loop_sound_frame = NULL;
14711 static int *loop_sound_volume = NULL;
14712
14713 void InitPlayLevelSound(void)
14714 {
14715   int num_sounds = getSoundListSize();
14716
14717   checked_free(loop_sound_frame);
14718   checked_free(loop_sound_volume);
14719
14720   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14721   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14722 }
14723
14724 static void PlayLevelSound(int x, int y, int nr)
14725 {
14726   int sx = SCREENX(x), sy = SCREENY(y);
14727   int volume, stereo_position;
14728   int max_distance = 8;
14729   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14730
14731   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14732       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14733     return;
14734
14735   if (!IN_LEV_FIELD(x, y) ||
14736       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14737       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14738     return;
14739
14740   volume = SOUND_MAX_VOLUME;
14741
14742   if (!IN_SCR_FIELD(sx, sy))
14743   {
14744     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14745     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14746
14747     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14748   }
14749
14750   stereo_position = (SOUND_MAX_LEFT +
14751                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14752                      (SCR_FIELDX + 2 * max_distance));
14753
14754   if (IS_LOOP_SOUND(nr))
14755   {
14756     /* This assures that quieter loop sounds do not overwrite louder ones,
14757        while restarting sound volume comparison with each new game frame. */
14758
14759     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14760       return;
14761
14762     loop_sound_volume[nr] = volume;
14763     loop_sound_frame[nr] = FrameCounter;
14764   }
14765
14766   PlaySoundExt(nr, volume, stereo_position, type);
14767 }
14768
14769 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14770 {
14771   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14772                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14773                  y < LEVELY(BY1) ? LEVELY(BY1) :
14774                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14775                  sound_action);
14776 }
14777
14778 static void PlayLevelSoundAction(int x, int y, int action)
14779 {
14780   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14781 }
14782
14783 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14784 {
14785   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14786
14787   if (sound_effect != SND_UNDEFINED)
14788     PlayLevelSound(x, y, sound_effect);
14789 }
14790
14791 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14792                                               int action)
14793 {
14794   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14795
14796   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14797     PlayLevelSound(x, y, sound_effect);
14798 }
14799
14800 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14801 {
14802   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14803
14804   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14805     PlayLevelSound(x, y, sound_effect);
14806 }
14807
14808 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14809 {
14810   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14811
14812   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14813     StopSound(sound_effect);
14814 }
14815
14816 static int getLevelMusicNr(void)
14817 {
14818   if (levelset.music[level_nr] != MUS_UNDEFINED)
14819     return levelset.music[level_nr];            // from config file
14820   else
14821     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14822 }
14823
14824 static void FadeLevelSounds(void)
14825 {
14826   FadeSounds();
14827 }
14828
14829 static void FadeLevelMusic(void)
14830 {
14831   int music_nr = getLevelMusicNr();
14832   char *curr_music = getCurrentlyPlayingMusicFilename();
14833   char *next_music = getMusicInfoEntryFilename(music_nr);
14834
14835   if (!strEqual(curr_music, next_music))
14836     FadeMusic();
14837 }
14838
14839 void FadeLevelSoundsAndMusic(void)
14840 {
14841   FadeLevelSounds();
14842   FadeLevelMusic();
14843 }
14844
14845 static void PlayLevelMusic(void)
14846 {
14847   int music_nr = getLevelMusicNr();
14848   char *curr_music = getCurrentlyPlayingMusicFilename();
14849   char *next_music = getMusicInfoEntryFilename(music_nr);
14850
14851   if (!strEqual(curr_music, next_music))
14852     PlayMusicLoop(music_nr);
14853 }
14854
14855 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14856 {
14857   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14858   int offset = 0;
14859   int x = xx - offset;
14860   int y = yy - offset;
14861
14862   switch (sample)
14863   {
14864     case SOUND_blank:
14865       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14866       break;
14867
14868     case SOUND_roll:
14869       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14870       break;
14871
14872     case SOUND_stone:
14873       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14874       break;
14875
14876     case SOUND_nut:
14877       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14878       break;
14879
14880     case SOUND_crack:
14881       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14882       break;
14883
14884     case SOUND_bug:
14885       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14886       break;
14887
14888     case SOUND_tank:
14889       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14890       break;
14891
14892     case SOUND_android_clone:
14893       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14894       break;
14895
14896     case SOUND_android_move:
14897       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14898       break;
14899
14900     case SOUND_spring:
14901       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14902       break;
14903
14904     case SOUND_slurp:
14905       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14906       break;
14907
14908     case SOUND_eater:
14909       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14910       break;
14911
14912     case SOUND_eater_eat:
14913       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14914       break;
14915
14916     case SOUND_alien:
14917       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14918       break;
14919
14920     case SOUND_collect:
14921       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14922       break;
14923
14924     case SOUND_diamond:
14925       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14926       break;
14927
14928     case SOUND_squash:
14929       // !!! CHECK THIS !!!
14930 #if 1
14931       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14932 #else
14933       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14934 #endif
14935       break;
14936
14937     case SOUND_wonderfall:
14938       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14939       break;
14940
14941     case SOUND_drip:
14942       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14943       break;
14944
14945     case SOUND_push:
14946       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14947       break;
14948
14949     case SOUND_dirt:
14950       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14951       break;
14952
14953     case SOUND_acid:
14954       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14955       break;
14956
14957     case SOUND_ball:
14958       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14959       break;
14960
14961     case SOUND_slide:
14962       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14963       break;
14964
14965     case SOUND_wonder:
14966       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14967       break;
14968
14969     case SOUND_door:
14970       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14971       break;
14972
14973     case SOUND_exit_open:
14974       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14975       break;
14976
14977     case SOUND_exit_leave:
14978       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14979       break;
14980
14981     case SOUND_dynamite:
14982       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14983       break;
14984
14985     case SOUND_tick:
14986       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14987       break;
14988
14989     case SOUND_press:
14990       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14991       break;
14992
14993     case SOUND_wheel:
14994       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14995       break;
14996
14997     case SOUND_boom:
14998       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14999       break;
15000
15001     case SOUND_die:
15002       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15003       break;
15004
15005     case SOUND_time:
15006       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15007       break;
15008
15009     default:
15010       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15011       break;
15012   }
15013 }
15014
15015 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15016 {
15017   int element = map_element_SP_to_RND(element_sp);
15018   int action = map_action_SP_to_RND(action_sp);
15019   int offset = (setup.sp_show_border_elements ? 0 : 1);
15020   int x = xx - offset;
15021   int y = yy - offset;
15022
15023   PlayLevelSoundElementAction(x, y, element, action);
15024 }
15025
15026 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15027 {
15028   int element = map_element_MM_to_RND(element_mm);
15029   int action = map_action_MM_to_RND(action_mm);
15030   int offset = 0;
15031   int x = xx - offset;
15032   int y = yy - offset;
15033
15034   if (!IS_MM_ELEMENT(element))
15035     element = EL_MM_DEFAULT;
15036
15037   PlayLevelSoundElementAction(x, y, element, action);
15038 }
15039
15040 void PlaySound_MM(int sound_mm)
15041 {
15042   int sound = map_sound_MM_to_RND(sound_mm);
15043
15044   if (sound == SND_UNDEFINED)
15045     return;
15046
15047   PlaySound(sound);
15048 }
15049
15050 void PlaySoundLoop_MM(int sound_mm)
15051 {
15052   int sound = map_sound_MM_to_RND(sound_mm);
15053
15054   if (sound == SND_UNDEFINED)
15055     return;
15056
15057   PlaySoundLoop(sound);
15058 }
15059
15060 void StopSound_MM(int sound_mm)
15061 {
15062   int sound = map_sound_MM_to_RND(sound_mm);
15063
15064   if (sound == SND_UNDEFINED)
15065     return;
15066
15067   StopSound(sound);
15068 }
15069
15070 void RaiseScore(int value)
15071 {
15072   game.score += value;
15073
15074   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15075
15076   DisplayGameControlValues();
15077 }
15078
15079 void RaiseScoreElement(int element)
15080 {
15081   switch (element)
15082   {
15083     case EL_EMERALD:
15084     case EL_BD_DIAMOND:
15085     case EL_EMERALD_YELLOW:
15086     case EL_EMERALD_RED:
15087     case EL_EMERALD_PURPLE:
15088     case EL_SP_INFOTRON:
15089       RaiseScore(level.score[SC_EMERALD]);
15090       break;
15091     case EL_DIAMOND:
15092       RaiseScore(level.score[SC_DIAMOND]);
15093       break;
15094     case EL_CRYSTAL:
15095       RaiseScore(level.score[SC_CRYSTAL]);
15096       break;
15097     case EL_PEARL:
15098       RaiseScore(level.score[SC_PEARL]);
15099       break;
15100     case EL_BUG:
15101     case EL_BD_BUTTERFLY:
15102     case EL_SP_ELECTRON:
15103       RaiseScore(level.score[SC_BUG]);
15104       break;
15105     case EL_SPACESHIP:
15106     case EL_BD_FIREFLY:
15107     case EL_SP_SNIKSNAK:
15108       RaiseScore(level.score[SC_SPACESHIP]);
15109       break;
15110     case EL_YAMYAM:
15111     case EL_DARK_YAMYAM:
15112       RaiseScore(level.score[SC_YAMYAM]);
15113       break;
15114     case EL_ROBOT:
15115       RaiseScore(level.score[SC_ROBOT]);
15116       break;
15117     case EL_PACMAN:
15118       RaiseScore(level.score[SC_PACMAN]);
15119       break;
15120     case EL_NUT:
15121       RaiseScore(level.score[SC_NUT]);
15122       break;
15123     case EL_DYNAMITE:
15124     case EL_EM_DYNAMITE:
15125     case EL_SP_DISK_RED:
15126     case EL_DYNABOMB_INCREASE_NUMBER:
15127     case EL_DYNABOMB_INCREASE_SIZE:
15128     case EL_DYNABOMB_INCREASE_POWER:
15129       RaiseScore(level.score[SC_DYNAMITE]);
15130       break;
15131     case EL_SHIELD_NORMAL:
15132     case EL_SHIELD_DEADLY:
15133       RaiseScore(level.score[SC_SHIELD]);
15134       break;
15135     case EL_EXTRA_TIME:
15136       RaiseScore(level.extra_time_score);
15137       break;
15138     case EL_KEY_1:
15139     case EL_KEY_2:
15140     case EL_KEY_3:
15141     case EL_KEY_4:
15142     case EL_EM_KEY_1:
15143     case EL_EM_KEY_2:
15144     case EL_EM_KEY_3:
15145     case EL_EM_KEY_4:
15146     case EL_EMC_KEY_5:
15147     case EL_EMC_KEY_6:
15148     case EL_EMC_KEY_7:
15149     case EL_EMC_KEY_8:
15150     case EL_DC_KEY_WHITE:
15151       RaiseScore(level.score[SC_KEY]);
15152       break;
15153     default:
15154       RaiseScore(element_info[element].collect_score);
15155       break;
15156   }
15157 }
15158
15159 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15160 {
15161   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15162   {
15163     // closing door required in case of envelope style request dialogs
15164     if (!skip_request)
15165     {
15166       // prevent short reactivation of overlay buttons while closing door
15167       SetOverlayActive(FALSE);
15168
15169       CloseDoor(DOOR_CLOSE_1);
15170     }
15171
15172     if (network.enabled)
15173       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15174     else
15175     {
15176       if (quick_quit)
15177         FadeSkipNextFadeIn();
15178
15179       SetGameStatus(GAME_MODE_MAIN);
15180
15181       DrawMainMenu();
15182     }
15183   }
15184   else          // continue playing the game
15185   {
15186     if (tape.playing && tape.deactivate_display)
15187       TapeDeactivateDisplayOff(TRUE);
15188
15189     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15190
15191     if (tape.playing && tape.deactivate_display)
15192       TapeDeactivateDisplayOn();
15193   }
15194 }
15195
15196 void RequestQuitGame(boolean ask_if_really_quit)
15197 {
15198   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15199   boolean skip_request = game.all_players_gone || quick_quit;
15200
15201   RequestQuitGameExt(skip_request, quick_quit,
15202                      "Do you really want to quit the game?");
15203 }
15204
15205 void RequestRestartGame(char *message)
15206 {
15207   game.restart_game_message = NULL;
15208
15209   boolean has_started_game = hasStartedNetworkGame();
15210   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15211
15212   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15213   {
15214     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15215   }
15216   else
15217   {
15218     SetGameStatus(GAME_MODE_MAIN);
15219
15220     DrawMainMenu();
15221   }
15222 }
15223
15224 void CheckGameOver(void)
15225 {
15226   static boolean last_game_over = FALSE;
15227   static int game_over_delay = 0;
15228   int game_over_delay_value = 50;
15229   boolean game_over = checkGameFailed();
15230
15231   // do not handle game over if request dialog is already active
15232   if (game.request_active)
15233     return;
15234
15235   // do not ask to play again if game was never actually played
15236   if (!game.GamePlayed)
15237     return;
15238
15239   if (!game_over)
15240   {
15241     last_game_over = FALSE;
15242     game_over_delay = game_over_delay_value;
15243
15244     return;
15245   }
15246
15247   if (game_over_delay > 0)
15248   {
15249     game_over_delay--;
15250
15251     return;
15252   }
15253
15254   if (last_game_over != game_over)
15255     game.restart_game_message = (hasStartedNetworkGame() ?
15256                                  "Game over! Play it again?" :
15257                                  "Game over!");
15258
15259   last_game_over = game_over;
15260 }
15261
15262 boolean checkGameSolved(void)
15263 {
15264   // set for all game engines if level was solved
15265   return game.LevelSolved_GameEnd;
15266 }
15267
15268 boolean checkGameFailed(void)
15269 {
15270   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15271     return (game_em.game_over && !game_em.level_solved);
15272   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15273     return (game_sp.game_over && !game_sp.level_solved);
15274   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15275     return (game_mm.game_over && !game_mm.level_solved);
15276   else                          // GAME_ENGINE_TYPE_RND
15277     return (game.GameOver && !game.LevelSolved);
15278 }
15279
15280 boolean checkGameEnded(void)
15281 {
15282   return (checkGameSolved() || checkGameFailed());
15283 }
15284
15285
15286 // ----------------------------------------------------------------------------
15287 // random generator functions
15288 // ----------------------------------------------------------------------------
15289
15290 unsigned int InitEngineRandom_RND(int seed)
15291 {
15292   game.num_random_calls = 0;
15293
15294   return InitEngineRandom(seed);
15295 }
15296
15297 unsigned int RND(int max)
15298 {
15299   if (max > 0)
15300   {
15301     game.num_random_calls++;
15302
15303     return GetEngineRandom(max);
15304   }
15305
15306   return 0;
15307 }
15308
15309
15310 // ----------------------------------------------------------------------------
15311 // game engine snapshot handling functions
15312 // ----------------------------------------------------------------------------
15313
15314 struct EngineSnapshotInfo
15315 {
15316   // runtime values for custom element collect score
15317   int collect_score[NUM_CUSTOM_ELEMENTS];
15318
15319   // runtime values for group element choice position
15320   int choice_pos[NUM_GROUP_ELEMENTS];
15321
15322   // runtime values for belt position animations
15323   int belt_graphic[4][NUM_BELT_PARTS];
15324   int belt_anim_mode[4][NUM_BELT_PARTS];
15325 };
15326
15327 static struct EngineSnapshotInfo engine_snapshot_rnd;
15328 static char *snapshot_level_identifier = NULL;
15329 static int snapshot_level_nr = -1;
15330
15331 static void SaveEngineSnapshotValues_RND(void)
15332 {
15333   static int belt_base_active_element[4] =
15334   {
15335     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15336     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15337     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15338     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15339   };
15340   int i, j;
15341
15342   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15343   {
15344     int element = EL_CUSTOM_START + i;
15345
15346     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15347   }
15348
15349   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15350   {
15351     int element = EL_GROUP_START + i;
15352
15353     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15354   }
15355
15356   for (i = 0; i < 4; i++)
15357   {
15358     for (j = 0; j < NUM_BELT_PARTS; j++)
15359     {
15360       int element = belt_base_active_element[i] + j;
15361       int graphic = el2img(element);
15362       int anim_mode = graphic_info[graphic].anim_mode;
15363
15364       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15365       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15366     }
15367   }
15368 }
15369
15370 static void LoadEngineSnapshotValues_RND(void)
15371 {
15372   unsigned int num_random_calls = game.num_random_calls;
15373   int i, j;
15374
15375   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15376   {
15377     int element = EL_CUSTOM_START + i;
15378
15379     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15380   }
15381
15382   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15383   {
15384     int element = EL_GROUP_START + i;
15385
15386     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15387   }
15388
15389   for (i = 0; i < 4; i++)
15390   {
15391     for (j = 0; j < NUM_BELT_PARTS; j++)
15392     {
15393       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15394       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15395
15396       graphic_info[graphic].anim_mode = anim_mode;
15397     }
15398   }
15399
15400   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15401   {
15402     InitRND(tape.random_seed);
15403     for (i = 0; i < num_random_calls; i++)
15404       RND(1);
15405   }
15406
15407   if (game.num_random_calls != num_random_calls)
15408   {
15409     Error(ERR_INFO, "number of random calls out of sync");
15410     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15411     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15412     Error(ERR_EXIT, "this should not happen -- please debug");
15413   }
15414 }
15415
15416 void FreeEngineSnapshotSingle(void)
15417 {
15418   FreeSnapshotSingle();
15419
15420   setString(&snapshot_level_identifier, NULL);
15421   snapshot_level_nr = -1;
15422 }
15423
15424 void FreeEngineSnapshotList(void)
15425 {
15426   FreeSnapshotList();
15427 }
15428
15429 static ListNode *SaveEngineSnapshotBuffers(void)
15430 {
15431   ListNode *buffers = NULL;
15432
15433   // copy some special values to a structure better suited for the snapshot
15434
15435   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15436     SaveEngineSnapshotValues_RND();
15437   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15438     SaveEngineSnapshotValues_EM();
15439   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15440     SaveEngineSnapshotValues_SP(&buffers);
15441   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15442     SaveEngineSnapshotValues_MM(&buffers);
15443
15444   // save values stored in special snapshot structure
15445
15446   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15447     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15448   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15449     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15450   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15451     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15452   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15453     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15454
15455   // save further RND engine values
15456
15457   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15458   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15459   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15460
15461   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15462   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15463   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15464   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15465   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15466
15467   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15468   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15469   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15470
15471   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15472
15473   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15474   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15475
15476   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15477   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15478   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15479   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15480   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15481   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15482   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15483   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15484   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15485   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15486   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15487   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15488   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15489   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15490   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15491   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15492   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15493   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15494
15495   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15496   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15497
15498   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15499   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15500   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15501
15502   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15503   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15504
15505   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15506   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15507   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15508   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15509   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15510
15511   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15512   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15513
15514 #if 0
15515   ListNode *node = engine_snapshot_list_rnd;
15516   int num_bytes = 0;
15517
15518   while (node != NULL)
15519   {
15520     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15521
15522     node = node->next;
15523   }
15524
15525   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15526 #endif
15527
15528   return buffers;
15529 }
15530
15531 void SaveEngineSnapshotSingle(void)
15532 {
15533   ListNode *buffers = SaveEngineSnapshotBuffers();
15534
15535   // finally save all snapshot buffers to single snapshot
15536   SaveSnapshotSingle(buffers);
15537
15538   // save level identification information
15539   setString(&snapshot_level_identifier, leveldir_current->identifier);
15540   snapshot_level_nr = level_nr;
15541 }
15542
15543 boolean CheckSaveEngineSnapshotToList(void)
15544 {
15545   boolean save_snapshot =
15546     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15547      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15548       game.snapshot.changed_action) ||
15549      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15550       game.snapshot.collected_item));
15551
15552   game.snapshot.changed_action = FALSE;
15553   game.snapshot.collected_item = FALSE;
15554   game.snapshot.save_snapshot = save_snapshot;
15555
15556   return save_snapshot;
15557 }
15558
15559 void SaveEngineSnapshotToList(void)
15560 {
15561   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15562       tape.quick_resume)
15563     return;
15564
15565   ListNode *buffers = SaveEngineSnapshotBuffers();
15566
15567   // finally save all snapshot buffers to snapshot list
15568   SaveSnapshotToList(buffers);
15569 }
15570
15571 void SaveEngineSnapshotToListInitial(void)
15572 {
15573   FreeEngineSnapshotList();
15574
15575   SaveEngineSnapshotToList();
15576 }
15577
15578 static void LoadEngineSnapshotValues(void)
15579 {
15580   // restore special values from snapshot structure
15581
15582   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15583     LoadEngineSnapshotValues_RND();
15584   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15585     LoadEngineSnapshotValues_EM();
15586   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15587     LoadEngineSnapshotValues_SP();
15588   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15589     LoadEngineSnapshotValues_MM();
15590 }
15591
15592 void LoadEngineSnapshotSingle(void)
15593 {
15594   LoadSnapshotSingle();
15595
15596   LoadEngineSnapshotValues();
15597 }
15598
15599 static void LoadEngineSnapshot_Undo(int steps)
15600 {
15601   LoadSnapshotFromList_Older(steps);
15602
15603   LoadEngineSnapshotValues();
15604 }
15605
15606 static void LoadEngineSnapshot_Redo(int steps)
15607 {
15608   LoadSnapshotFromList_Newer(steps);
15609
15610   LoadEngineSnapshotValues();
15611 }
15612
15613 boolean CheckEngineSnapshotSingle(void)
15614 {
15615   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15616           snapshot_level_nr == level_nr);
15617 }
15618
15619 boolean CheckEngineSnapshotList(void)
15620 {
15621   return CheckSnapshotList();
15622 }
15623
15624
15625 // ---------- new game button stuff -------------------------------------------
15626
15627 static struct
15628 {
15629   int graphic;
15630   struct XY *pos;
15631   int gadget_id;
15632   boolean *setup_value;
15633   boolean allowed_on_tape;
15634   boolean is_touch_button;
15635   char *infotext;
15636 } gamebutton_info[NUM_GAME_BUTTONS] =
15637 {
15638   {
15639     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15640     GAME_CTRL_ID_STOP,                          NULL,
15641     TRUE, FALSE,                                "stop game"
15642   },
15643   {
15644     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15645     GAME_CTRL_ID_PAUSE,                         NULL,
15646     TRUE, FALSE,                                "pause game"
15647   },
15648   {
15649     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15650     GAME_CTRL_ID_PLAY,                          NULL,
15651     TRUE, FALSE,                                "play game"
15652   },
15653   {
15654     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15655     GAME_CTRL_ID_UNDO,                          NULL,
15656     TRUE, FALSE,                                "undo step"
15657   },
15658   {
15659     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15660     GAME_CTRL_ID_REDO,                          NULL,
15661     TRUE, FALSE,                                "redo step"
15662   },
15663   {
15664     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15665     GAME_CTRL_ID_SAVE,                          NULL,
15666     TRUE, FALSE,                                "save game"
15667   },
15668   {
15669     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15670     GAME_CTRL_ID_PAUSE2,                        NULL,
15671     TRUE, FALSE,                                "pause game"
15672   },
15673   {
15674     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15675     GAME_CTRL_ID_LOAD,                          NULL,
15676     TRUE, FALSE,                                "load game"
15677   },
15678   {
15679     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15680     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15681     FALSE, FALSE,                               "stop game"
15682   },
15683   {
15684     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15685     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15686     FALSE, FALSE,                               "pause game"
15687   },
15688   {
15689     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15690     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15691     FALSE, FALSE,                               "play game"
15692   },
15693   {
15694     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15695     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15696     FALSE, TRUE,                                "stop game"
15697   },
15698   {
15699     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15700     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15701     FALSE, TRUE,                                "pause game"
15702   },
15703   {
15704     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15705     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15706     TRUE, FALSE,                                "background music on/off"
15707   },
15708   {
15709     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15710     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15711     TRUE, FALSE,                                "sound loops on/off"
15712   },
15713   {
15714     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15715     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15716     TRUE, FALSE,                                "normal sounds on/off"
15717   },
15718   {
15719     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15720     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15721     FALSE, FALSE,                               "background music on/off"
15722   },
15723   {
15724     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15725     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15726     FALSE, FALSE,                               "sound loops on/off"
15727   },
15728   {
15729     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15730     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15731     FALSE, FALSE,                               "normal sounds on/off"
15732   }
15733 };
15734
15735 void CreateGameButtons(void)
15736 {
15737   int i;
15738
15739   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15740   {
15741     int graphic = gamebutton_info[i].graphic;
15742     struct GraphicInfo *gfx = &graphic_info[graphic];
15743     struct XY *pos = gamebutton_info[i].pos;
15744     struct GadgetInfo *gi;
15745     int button_type;
15746     boolean checked;
15747     unsigned int event_mask;
15748     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15749     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15750     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15751     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15752     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15753     int gd_x   = gfx->src_x;
15754     int gd_y   = gfx->src_y;
15755     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15756     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15757     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15758     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15759     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15760     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15761     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15762     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15763     int id = i;
15764
15765     if (gfx->bitmap == NULL)
15766     {
15767       game_gadget[id] = NULL;
15768
15769       continue;
15770     }
15771
15772     if (id == GAME_CTRL_ID_STOP ||
15773         id == GAME_CTRL_ID_PANEL_STOP ||
15774         id == GAME_CTRL_ID_TOUCH_STOP ||
15775         id == GAME_CTRL_ID_PLAY ||
15776         id == GAME_CTRL_ID_PANEL_PLAY ||
15777         id == GAME_CTRL_ID_SAVE ||
15778         id == GAME_CTRL_ID_LOAD)
15779     {
15780       button_type = GD_TYPE_NORMAL_BUTTON;
15781       checked = FALSE;
15782       event_mask = GD_EVENT_RELEASED;
15783     }
15784     else if (id == GAME_CTRL_ID_UNDO ||
15785              id == GAME_CTRL_ID_REDO)
15786     {
15787       button_type = GD_TYPE_NORMAL_BUTTON;
15788       checked = FALSE;
15789       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15790     }
15791     else
15792     {
15793       button_type = GD_TYPE_CHECK_BUTTON;
15794       checked = (gamebutton_info[i].setup_value != NULL ?
15795                  *gamebutton_info[i].setup_value : FALSE);
15796       event_mask = GD_EVENT_PRESSED;
15797     }
15798
15799     gi = CreateGadget(GDI_CUSTOM_ID, id,
15800                       GDI_IMAGE_ID, graphic,
15801                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15802                       GDI_X, base_x + x,
15803                       GDI_Y, base_y + y,
15804                       GDI_WIDTH, gfx->width,
15805                       GDI_HEIGHT, gfx->height,
15806                       GDI_TYPE, button_type,
15807                       GDI_STATE, GD_BUTTON_UNPRESSED,
15808                       GDI_CHECKED, checked,
15809                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15810                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15811                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15812                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15813                       GDI_DIRECT_DRAW, FALSE,
15814                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15815                       GDI_EVENT_MASK, event_mask,
15816                       GDI_CALLBACK_ACTION, HandleGameButtons,
15817                       GDI_END);
15818
15819     if (gi == NULL)
15820       Error(ERR_EXIT, "cannot create gadget");
15821
15822     game_gadget[id] = gi;
15823   }
15824 }
15825
15826 void FreeGameButtons(void)
15827 {
15828   int i;
15829
15830   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15831     FreeGadget(game_gadget[i]);
15832 }
15833
15834 static void UnmapGameButtonsAtSamePosition(int id)
15835 {
15836   int i;
15837
15838   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15839     if (i != id &&
15840         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15841         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15842       UnmapGadget(game_gadget[i]);
15843 }
15844
15845 static void UnmapGameButtonsAtSamePosition_All(void)
15846 {
15847   if (setup.show_snapshot_buttons)
15848   {
15849     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15850     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15851     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15852   }
15853   else
15854   {
15855     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15856     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15857     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15858
15859     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15860     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15861     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15862   }
15863 }
15864
15865 static void MapGameButtonsAtSamePosition(int id)
15866 {
15867   int i;
15868
15869   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15870     if (i != id &&
15871         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15872         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15873       MapGadget(game_gadget[i]);
15874
15875   UnmapGameButtonsAtSamePosition_All();
15876 }
15877
15878 void MapUndoRedoButtons(void)
15879 {
15880   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15881   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15882
15883   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15884   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15885 }
15886
15887 void UnmapUndoRedoButtons(void)
15888 {
15889   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15890   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15891
15892   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15893   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15894 }
15895
15896 void ModifyPauseButtons(void)
15897 {
15898   static int ids[] =
15899   {
15900     GAME_CTRL_ID_PAUSE,
15901     GAME_CTRL_ID_PAUSE2,
15902     GAME_CTRL_ID_PANEL_PAUSE,
15903     GAME_CTRL_ID_TOUCH_PAUSE,
15904     -1
15905   };
15906   int i;
15907
15908   for (i = 0; ids[i] > -1; i++)
15909     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15910 }
15911
15912 static void MapGameButtonsExt(boolean on_tape)
15913 {
15914   int i;
15915
15916   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15917     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15918         i != GAME_CTRL_ID_UNDO &&
15919         i != GAME_CTRL_ID_REDO)
15920       MapGadget(game_gadget[i]);
15921
15922   UnmapGameButtonsAtSamePosition_All();
15923
15924   RedrawGameButtons();
15925 }
15926
15927 static void UnmapGameButtonsExt(boolean on_tape)
15928 {
15929   int i;
15930
15931   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15932     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15933       UnmapGadget(game_gadget[i]);
15934 }
15935
15936 static void RedrawGameButtonsExt(boolean on_tape)
15937 {
15938   int i;
15939
15940   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15941     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15942       RedrawGadget(game_gadget[i]);
15943 }
15944
15945 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15946 {
15947   if (gi == NULL)
15948     return;
15949
15950   gi->checked = state;
15951 }
15952
15953 static void RedrawSoundButtonGadget(int id)
15954 {
15955   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15956              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15957              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15958              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15959              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15960              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15961              id);
15962
15963   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15964   RedrawGadget(game_gadget[id2]);
15965 }
15966
15967 void MapGameButtons(void)
15968 {
15969   MapGameButtonsExt(FALSE);
15970 }
15971
15972 void UnmapGameButtons(void)
15973 {
15974   UnmapGameButtonsExt(FALSE);
15975 }
15976
15977 void RedrawGameButtons(void)
15978 {
15979   RedrawGameButtonsExt(FALSE);
15980 }
15981
15982 void MapGameButtonsOnTape(void)
15983 {
15984   MapGameButtonsExt(TRUE);
15985 }
15986
15987 void UnmapGameButtonsOnTape(void)
15988 {
15989   UnmapGameButtonsExt(TRUE);
15990 }
15991
15992 void RedrawGameButtonsOnTape(void)
15993 {
15994   RedrawGameButtonsExt(TRUE);
15995 }
15996
15997 static void GameUndoRedoExt(void)
15998 {
15999   ClearPlayerAction();
16000
16001   tape.pausing = TRUE;
16002
16003   RedrawPlayfield();
16004   UpdateAndDisplayGameControlValues();
16005
16006   DrawCompleteVideoDisplay();
16007   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16008   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16009   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16010
16011   BackToFront();
16012 }
16013
16014 static void GameUndo(int steps)
16015 {
16016   if (!CheckEngineSnapshotList())
16017     return;
16018
16019   LoadEngineSnapshot_Undo(steps);
16020
16021   GameUndoRedoExt();
16022 }
16023
16024 static void GameRedo(int steps)
16025 {
16026   if (!CheckEngineSnapshotList())
16027     return;
16028
16029   LoadEngineSnapshot_Redo(steps);
16030
16031   GameUndoRedoExt();
16032 }
16033
16034 static void HandleGameButtonsExt(int id, int button)
16035 {
16036   static boolean game_undo_executed = FALSE;
16037   int steps = BUTTON_STEPSIZE(button);
16038   boolean handle_game_buttons =
16039     (game_status == GAME_MODE_PLAYING ||
16040      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16041
16042   if (!handle_game_buttons)
16043     return;
16044
16045   switch (id)
16046   {
16047     case GAME_CTRL_ID_STOP:
16048     case GAME_CTRL_ID_PANEL_STOP:
16049     case GAME_CTRL_ID_TOUCH_STOP:
16050       if (game_status == GAME_MODE_MAIN)
16051         break;
16052
16053       if (tape.playing)
16054         TapeStop();
16055       else
16056         RequestQuitGame(TRUE);
16057
16058       break;
16059
16060     case GAME_CTRL_ID_PAUSE:
16061     case GAME_CTRL_ID_PAUSE2:
16062     case GAME_CTRL_ID_PANEL_PAUSE:
16063     case GAME_CTRL_ID_TOUCH_PAUSE:
16064       if (network.enabled && game_status == GAME_MODE_PLAYING)
16065       {
16066         if (tape.pausing)
16067           SendToServer_ContinuePlaying();
16068         else
16069           SendToServer_PausePlaying();
16070       }
16071       else
16072         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16073
16074       game_undo_executed = FALSE;
16075
16076       break;
16077
16078     case GAME_CTRL_ID_PLAY:
16079     case GAME_CTRL_ID_PANEL_PLAY:
16080       if (game_status == GAME_MODE_MAIN)
16081       {
16082         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16083       }
16084       else if (tape.pausing)
16085       {
16086         if (network.enabled)
16087           SendToServer_ContinuePlaying();
16088         else
16089           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16090       }
16091       break;
16092
16093     case GAME_CTRL_ID_UNDO:
16094       // Important: When using "save snapshot when collecting an item" mode,
16095       // load last (current) snapshot for first "undo" after pressing "pause"
16096       // (else the last-but-one snapshot would be loaded, because the snapshot
16097       // pointer already points to the last snapshot when pressing "pause",
16098       // which is fine for "every step/move" mode, but not for "every collect")
16099       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16100           !game_undo_executed)
16101         steps--;
16102
16103       game_undo_executed = TRUE;
16104
16105       GameUndo(steps);
16106       break;
16107
16108     case GAME_CTRL_ID_REDO:
16109       GameRedo(steps);
16110       break;
16111
16112     case GAME_CTRL_ID_SAVE:
16113       TapeQuickSave();
16114       break;
16115
16116     case GAME_CTRL_ID_LOAD:
16117       TapeQuickLoad();
16118       break;
16119
16120     case SOUND_CTRL_ID_MUSIC:
16121     case SOUND_CTRL_ID_PANEL_MUSIC:
16122       if (setup.sound_music)
16123       { 
16124         setup.sound_music = FALSE;
16125
16126         FadeMusic();
16127       }
16128       else if (audio.music_available)
16129       { 
16130         setup.sound = setup.sound_music = TRUE;
16131
16132         SetAudioMode(setup.sound);
16133
16134         if (game_status == GAME_MODE_PLAYING)
16135           PlayLevelMusic();
16136       }
16137
16138       RedrawSoundButtonGadget(id);
16139
16140       break;
16141
16142     case SOUND_CTRL_ID_LOOPS:
16143     case SOUND_CTRL_ID_PANEL_LOOPS:
16144       if (setup.sound_loops)
16145         setup.sound_loops = FALSE;
16146       else if (audio.loops_available)
16147       {
16148         setup.sound = setup.sound_loops = TRUE;
16149
16150         SetAudioMode(setup.sound);
16151       }
16152
16153       RedrawSoundButtonGadget(id);
16154
16155       break;
16156
16157     case SOUND_CTRL_ID_SIMPLE:
16158     case SOUND_CTRL_ID_PANEL_SIMPLE:
16159       if (setup.sound_simple)
16160         setup.sound_simple = FALSE;
16161       else if (audio.sound_available)
16162       {
16163         setup.sound = setup.sound_simple = TRUE;
16164
16165         SetAudioMode(setup.sound);
16166       }
16167
16168       RedrawSoundButtonGadget(id);
16169
16170       break;
16171
16172     default:
16173       break;
16174   }
16175 }
16176
16177 static void HandleGameButtons(struct GadgetInfo *gi)
16178 {
16179   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16180 }
16181
16182 void HandleSoundButtonKeys(Key key)
16183 {
16184   if (key == setup.shortcut.sound_simple)
16185     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16186   else if (key == setup.shortcut.sound_loops)
16187     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16188   else if (key == setup.shortcut.sound_music)
16189     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16190 }