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