moved code for incrementing level time step counter to separate function
[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 //                  https://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_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1558
1559 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1560 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1561 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1562                                  IS_JUST_CHANGING(x, y))
1563
1564 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1565
1566 // static variables for playfield scan mode (scanning forward or backward)
1567 static int playfield_scan_start_x = 0;
1568 static int playfield_scan_start_y = 0;
1569 static int playfield_scan_delta_x = 1;
1570 static int playfield_scan_delta_y = 1;
1571
1572 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1573                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1574                                      (y) += playfield_scan_delta_y)     \
1575                                 for ((x) = playfield_scan_start_x;      \
1576                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1577                                      (x) += playfield_scan_delta_x)
1578
1579 #ifdef DEBUG
1580 void DEBUG_SetMaximumDynamite(void)
1581 {
1582   int i;
1583
1584   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1585     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1586       local_player->inventory_element[local_player->inventory_size++] =
1587         EL_DYNAMITE;
1588 }
1589 #endif
1590
1591 static void InitPlayfieldScanModeVars(void)
1592 {
1593   if (game.use_reverse_scan_direction)
1594   {
1595     playfield_scan_start_x = lev_fieldx - 1;
1596     playfield_scan_start_y = lev_fieldy - 1;
1597
1598     playfield_scan_delta_x = -1;
1599     playfield_scan_delta_y = -1;
1600   }
1601   else
1602   {
1603     playfield_scan_start_x = 0;
1604     playfield_scan_start_y = 0;
1605
1606     playfield_scan_delta_x = 1;
1607     playfield_scan_delta_y = 1;
1608   }
1609 }
1610
1611 static void InitPlayfieldScanMode(int mode)
1612 {
1613   game.use_reverse_scan_direction =
1614     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1615
1616   InitPlayfieldScanModeVars();
1617 }
1618
1619 static int get_move_delay_from_stepsize(int move_stepsize)
1620 {
1621   move_stepsize =
1622     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1623
1624   // make sure that stepsize value is always a power of 2
1625   move_stepsize = (1 << log_2(move_stepsize));
1626
1627   return TILEX / move_stepsize;
1628 }
1629
1630 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1631                                boolean init_game)
1632 {
1633   int player_nr = player->index_nr;
1634   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1635   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1636
1637   // do no immediately change move delay -- the player might just be moving
1638   player->move_delay_value_next = move_delay;
1639
1640   // information if player can move must be set separately
1641   player->cannot_move = cannot_move;
1642
1643   if (init_game)
1644   {
1645     player->move_delay       = game.initial_move_delay[player_nr];
1646     player->move_delay_value = game.initial_move_delay_value[player_nr];
1647
1648     player->move_delay_value_next = -1;
1649
1650     player->move_delay_reset_counter = 0;
1651   }
1652 }
1653
1654 void GetPlayerConfig(void)
1655 {
1656   GameFrameDelay = setup.game_frame_delay;
1657
1658   if (!audio.sound_available)
1659     setup.sound_simple = FALSE;
1660
1661   if (!audio.loops_available)
1662     setup.sound_loops = FALSE;
1663
1664   if (!audio.music_available)
1665     setup.sound_music = FALSE;
1666
1667   if (!video.fullscreen_available)
1668     setup.fullscreen = FALSE;
1669
1670   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1671
1672   SetAudioMode(setup.sound);
1673 }
1674
1675 int GetElementFromGroupElement(int element)
1676 {
1677   if (IS_GROUP_ELEMENT(element))
1678   {
1679     struct ElementGroupInfo *group = element_info[element].group;
1680     int last_anim_random_frame = gfx.anim_random_frame;
1681     int element_pos;
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = RND(group->num_elements_resolved);
1685
1686     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1687                                     group->choice_mode, 0,
1688                                     group->choice_pos);
1689
1690     if (group->choice_mode == ANIM_RANDOM)
1691       gfx.anim_random_frame = last_anim_random_frame;
1692
1693     group->choice_pos++;
1694
1695     element = group->element_resolved[element_pos];
1696   }
1697
1698   return element;
1699 }
1700
1701 static void IncrementSokobanFieldsNeeded(void)
1702 {
1703   if (level.sb_fields_needed)
1704     game.sokoban_fields_still_needed++;
1705 }
1706
1707 static void IncrementSokobanObjectsNeeded(void)
1708 {
1709   if (level.sb_objects_needed)
1710     game.sokoban_objects_still_needed++;
1711 }
1712
1713 static void DecrementSokobanFieldsNeeded(void)
1714 {
1715   if (game.sokoban_fields_still_needed > 0)
1716     game.sokoban_fields_still_needed--;
1717 }
1718
1719 static void DecrementSokobanObjectsNeeded(void)
1720 {
1721   if (game.sokoban_objects_still_needed > 0)
1722     game.sokoban_objects_still_needed--;
1723 }
1724
1725 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 {
1727   if (element == EL_SP_MURPHY)
1728   {
1729     if (init_game)
1730     {
1731       if (stored_player[0].present)
1732       {
1733         Tile[x][y] = EL_SP_MURPHY_CLONE;
1734
1735         return;
1736       }
1737       else
1738       {
1739         stored_player[0].initial_element = element;
1740         stored_player[0].use_murphy = TRUE;
1741
1742         if (!level.use_artwork_element[0])
1743           stored_player[0].artwork_element = EL_SP_MURPHY;
1744       }
1745
1746       Tile[x][y] = EL_PLAYER_1;
1747     }
1748   }
1749
1750   if (init_game)
1751   {
1752     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1753     int jx = player->jx, jy = player->jy;
1754
1755     player->present = TRUE;
1756
1757     player->block_last_field = (element == EL_SP_MURPHY ?
1758                                 level.sp_block_last_field :
1759                                 level.block_last_field);
1760
1761     // ---------- initialize player's last field block delay ------------------
1762
1763     // always start with reliable default value (no adjustment needed)
1764     player->block_delay_adjustment = 0;
1765
1766     // special case 1: in Supaplex, Murphy blocks last field one more frame
1767     if (player->block_last_field && element == EL_SP_MURPHY)
1768       player->block_delay_adjustment = 1;
1769
1770     // special case 2: in game engines before 3.1.1, blocking was different
1771     if (game.use_block_last_field_bug)
1772       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773
1774     if (!network.enabled || player->connected_network)
1775     {
1776       player->active = TRUE;
1777
1778       // remove potentially duplicate players
1779       if (StorePlayer[jx][jy] == Tile[x][y])
1780         StorePlayer[jx][jy] = 0;
1781
1782       StorePlayer[x][y] = Tile[x][y];
1783
1784 #if DEBUG_INIT_PLAYER
1785       Debug("game:init:player", "- player element %d activated",
1786             player->element_nr);
1787       Debug("game:init:player", "  (local player is %d and currently %s)",
1788             local_player->element_nr,
1789             local_player->active ? "active" : "not active");
1790     }
1791 #endif
1792
1793     Tile[x][y] = EL_EMPTY;
1794
1795     player->jx = player->last_jx = x;
1796     player->jy = player->last_jy = y;
1797   }
1798
1799   // always check if player was just killed and should be reanimated
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; // if player was just killed, reanimate him
1806   }
1807 }
1808
1809 static void InitField(int x, int y, boolean init_game)
1810 {
1811   int element = Tile[x][y];
1812
1813   switch (element)
1814   {
1815     case EL_SP_MURPHY:
1816     case EL_PLAYER_1:
1817     case EL_PLAYER_2:
1818     case EL_PLAYER_3:
1819     case EL_PLAYER_4:
1820       InitPlayerField(x, y, element, init_game);
1821       break;
1822
1823     case EL_SOKOBAN_FIELD_PLAYER:
1824       element = Tile[x][y] = EL_PLAYER_1;
1825       InitField(x, y, init_game);
1826
1827       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1828       InitField(x, y, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_EMPTY:
1832       IncrementSokobanFieldsNeeded();
1833       break;
1834
1835     case EL_SOKOBAN_OBJECT:
1836       IncrementSokobanObjectsNeeded();
1837       break;
1838
1839     case EL_STONEBLOCK:
1840       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1841         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1842       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1843         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1844       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1845         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1846       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1847         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1848       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1849         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1850       break;
1851
1852     case EL_BUG:
1853     case EL_BUG_RIGHT:
1854     case EL_BUG_UP:
1855     case EL_BUG_LEFT:
1856     case EL_BUG_DOWN:
1857     case EL_SPACESHIP:
1858     case EL_SPACESHIP_RIGHT:
1859     case EL_SPACESHIP_UP:
1860     case EL_SPACESHIP_LEFT:
1861     case EL_SPACESHIP_DOWN:
1862     case EL_BD_BUTTERFLY:
1863     case EL_BD_BUTTERFLY_RIGHT:
1864     case EL_BD_BUTTERFLY_UP:
1865     case EL_BD_BUTTERFLY_LEFT:
1866     case EL_BD_BUTTERFLY_DOWN:
1867     case EL_BD_FIREFLY:
1868     case EL_BD_FIREFLY_RIGHT:
1869     case EL_BD_FIREFLY_UP:
1870     case EL_BD_FIREFLY_LEFT:
1871     case EL_BD_FIREFLY_DOWN:
1872     case EL_PACMAN_RIGHT:
1873     case EL_PACMAN_UP:
1874     case EL_PACMAN_LEFT:
1875     case EL_PACMAN_DOWN:
1876     case EL_YAMYAM:
1877     case EL_YAMYAM_LEFT:
1878     case EL_YAMYAM_RIGHT:
1879     case EL_YAMYAM_UP:
1880     case EL_YAMYAM_DOWN:
1881     case EL_DARK_YAMYAM:
1882     case EL_ROBOT:
1883     case EL_PACMAN:
1884     case EL_SP_SNIKSNAK:
1885     case EL_SP_ELECTRON:
1886     case EL_MOLE:
1887     case EL_MOLE_LEFT:
1888     case EL_MOLE_RIGHT:
1889     case EL_MOLE_UP:
1890     case EL_MOLE_DOWN:
1891     case EL_SPRING_LEFT:
1892     case EL_SPRING_RIGHT:
1893       InitMovDir(x, y);
1894       break;
1895
1896     case EL_AMOEBA_FULL:
1897     case EL_BD_AMOEBA:
1898       InitAmoebaNr(x, y);
1899       break;
1900
1901     case EL_AMOEBA_DROP:
1902       if (y == lev_fieldy - 1)
1903       {
1904         Tile[x][y] = EL_AMOEBA_GROWING;
1905         Store[x][y] = EL_AMOEBA_WET;
1906       }
1907       break;
1908
1909     case EL_DYNAMITE_ACTIVE:
1910     case EL_SP_DISK_RED_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1912     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1913     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1914     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1915       MovDelay[x][y] = 96;
1916       break;
1917
1918     case EL_EM_DYNAMITE_ACTIVE:
1919       MovDelay[x][y] = 32;
1920       break;
1921
1922     case EL_LAMP:
1923       game.lights_still_needed++;
1924       break;
1925
1926     case EL_PENGUIN:
1927       game.friends_still_needed++;
1928       break;
1929
1930     case EL_PIG:
1931     case EL_DRAGON:
1932       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1933       break;
1934
1935     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1944     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1945     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1946     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1947       if (init_game)
1948       {
1949         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1950         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1951         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1952
1953         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1954         {
1955           game.belt_dir[belt_nr] = belt_dir;
1956           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1957         }
1958         else    // more than one switch -- set it like the first switch
1959         {
1960           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1961         }
1962       }
1963       break;
1964
1965     case EL_LIGHT_SWITCH_ACTIVE:
1966       if (init_game)
1967         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1968       break;
1969
1970     case EL_INVISIBLE_STEELWALL:
1971     case EL_INVISIBLE_WALL:
1972     case EL_INVISIBLE_SAND:
1973       if (game.light_time_left > 0 ||
1974           game.lenses_time_left > 0)
1975         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1976       break;
1977
1978     case EL_EMC_MAGIC_BALL:
1979       if (game.ball_active)
1980         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1981       break;
1982
1983     case EL_EMC_MAGIC_BALL_SWITCH:
1984       if (game.ball_active)
1985         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1986       break;
1987
1988     case EL_TRIGGER_PLAYER:
1989     case EL_TRIGGER_ELEMENT:
1990     case EL_TRIGGER_CE_VALUE:
1991     case EL_TRIGGER_CE_SCORE:
1992     case EL_SELF:
1993     case EL_ANY_ELEMENT:
1994     case EL_CURRENT_CE_VALUE:
1995     case EL_CURRENT_CE_SCORE:
1996     case EL_PREV_CE_1:
1997     case EL_PREV_CE_2:
1998     case EL_PREV_CE_3:
1999     case EL_PREV_CE_4:
2000     case EL_PREV_CE_5:
2001     case EL_PREV_CE_6:
2002     case EL_PREV_CE_7:
2003     case EL_PREV_CE_8:
2004     case EL_NEXT_CE_1:
2005     case EL_NEXT_CE_2:
2006     case EL_NEXT_CE_3:
2007     case EL_NEXT_CE_4:
2008     case EL_NEXT_CE_5:
2009     case EL_NEXT_CE_6:
2010     case EL_NEXT_CE_7:
2011     case EL_NEXT_CE_8:
2012       // reference elements should not be used on the playfield
2013       Tile[x][y] = EL_EMPTY;
2014       break;
2015
2016     default:
2017       if (IS_CUSTOM_ELEMENT(element))
2018       {
2019         if (CAN_MOVE(element))
2020           InitMovDir(x, y);
2021
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2024       }
2025       else if (IS_GROUP_ELEMENT(element))
2026       {
2027         Tile[x][y] = GetElementFromGroupElement(element);
2028
2029         InitField(x, y, init_game);
2030       }
2031       else if (IS_EMPTY_ELEMENT(element))
2032       {
2033         GfxElementEmpty[x][y] = element;
2034         Tile[x][y] = EL_EMPTY;
2035
2036         if (element_info[element].use_gfx_element)
2037           game.use_masked_elements = TRUE;
2038       }
2039
2040       break;
2041   }
2042
2043   if (!init_game)
2044     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2045 }
2046
2047 static void InitField_WithBug1(int x, int y, boolean init_game)
2048 {
2049   InitField(x, y, init_game);
2050
2051   // not needed to call InitMovDir() -- already done by InitField()!
2052   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2053       CAN_MOVE(Tile[x][y]))
2054     InitMovDir(x, y);
2055 }
2056
2057 static void InitField_WithBug2(int x, int y, boolean init_game)
2058 {
2059   int old_element = Tile[x][y];
2060
2061   InitField(x, y, init_game);
2062
2063   // not needed to call InitMovDir() -- already done by InitField()!
2064   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2065       CAN_MOVE(old_element) &&
2066       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2067     InitMovDir(x, y);
2068
2069   /* this case is in fact a combination of not less than three bugs:
2070      first, it calls InitMovDir() for elements that can move, although this is
2071      already done by InitField(); then, it checks the element that was at this
2072      field _before_ the call to InitField() (which can change it); lastly, it
2073      was not called for "mole with direction" elements, which were treated as
2074      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075   */
2076 }
2077
2078 static int get_key_element_from_nr(int key_nr)
2079 {
2080   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2081                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2082                           EL_EM_KEY_1 : EL_KEY_1);
2083
2084   return key_base_element + key_nr;
2085 }
2086
2087 static int get_next_dropped_element(struct PlayerInfo *player)
2088 {
2089   return (player->inventory_size > 0 ?
2090           player->inventory_element[player->inventory_size - 1] :
2091           player->inventory_infinite_element != EL_UNDEFINED ?
2092           player->inventory_infinite_element :
2093           player->dynabombs_left > 0 ?
2094           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095           EL_UNDEFINED);
2096 }
2097
2098 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2099 {
2100   // pos >= 0: get element from bottom of the stack;
2101   // pos <  0: get element from top of the stack
2102
2103   if (pos < 0)
2104   {
2105     int min_inventory_size = -pos;
2106     int inventory_pos = player->inventory_size - min_inventory_size;
2107     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2108
2109     return (player->inventory_size >= min_inventory_size ?
2110             player->inventory_element[inventory_pos] :
2111             player->inventory_infinite_element != EL_UNDEFINED ?
2112             player->inventory_infinite_element :
2113             player->dynabombs_left >= min_dynabombs_left ?
2114             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2115             EL_UNDEFINED);
2116   }
2117   else
2118   {
2119     int min_dynabombs_left = pos + 1;
2120     int min_inventory_size = pos + 1 - player->dynabombs_left;
2121     int inventory_pos = pos - player->dynabombs_left;
2122
2123     return (player->inventory_infinite_element != EL_UNDEFINED ?
2124             player->inventory_infinite_element :
2125             player->dynabombs_left >= min_dynabombs_left ?
2126             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2127             player->inventory_size >= min_inventory_size ?
2128             player->inventory_element[inventory_pos] :
2129             EL_UNDEFINED);
2130   }
2131 }
2132
2133 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2134 {
2135   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2136   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2137   int compare_result;
2138
2139   if (gpo1->sort_priority != gpo2->sort_priority)
2140     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2141   else
2142     compare_result = gpo1->nr - gpo2->nr;
2143
2144   return compare_result;
2145 }
2146
2147 int getPlayerInventorySize(int player_nr)
2148 {
2149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2150     return game_em.ply[player_nr]->dynamite;
2151   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2152     return game_sp.red_disk_count;
2153   else
2154     return stored_player[player_nr].inventory_size;
2155 }
2156
2157 static void InitGameControlValues(void)
2158 {
2159   int i;
2160
2161   for (i = 0; game_panel_controls[i].nr != -1; i++)
2162   {
2163     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2164     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2165     struct TextPosInfo *pos = gpc->pos;
2166     int nr = gpc->nr;
2167     int type = gpc->type;
2168
2169     if (nr != i)
2170     {
2171       Error("'game_panel_controls' structure corrupted at %d", i);
2172
2173       Fail("this should not happen -- please debug");
2174     }
2175
2176     // force update of game controls after initialization
2177     gpc->value = gpc->last_value = -1;
2178     gpc->frame = gpc->last_frame = -1;
2179     gpc->gfx_frame = -1;
2180
2181     // determine panel value width for later calculation of alignment
2182     if (type == TYPE_INTEGER || type == TYPE_STRING)
2183     {
2184       pos->width = pos->size * getFontWidth(pos->font);
2185       pos->height = getFontHeight(pos->font);
2186     }
2187     else if (type == TYPE_ELEMENT)
2188     {
2189       pos->width = pos->size;
2190       pos->height = pos->size;
2191     }
2192
2193     // fill structure for game panel draw order
2194     gpo->nr = gpc->nr;
2195     gpo->sort_priority = pos->sort_priority;
2196   }
2197
2198   // sort game panel controls according to sort_priority and control number
2199   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2200         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2201 }
2202
2203 static void UpdatePlayfieldElementCount(void)
2204 {
2205   boolean use_element_count = FALSE;
2206   int i, j, x, y;
2207
2208   // first check if it is needed at all to calculate playfield element count
2209   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2210     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2211       use_element_count = TRUE;
2212
2213   if (!use_element_count)
2214     return;
2215
2216   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2217     element_info[i].element_count = 0;
2218
2219   SCAN_PLAYFIELD(x, y)
2220   {
2221     element_info[Tile[x][y]].element_count++;
2222   }
2223
2224   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2225     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2226       if (IS_IN_GROUP(j, i))
2227         element_info[EL_GROUP_START + i].element_count +=
2228           element_info[j].element_count;
2229 }
2230
2231 static void UpdateGameControlValues(void)
2232 {
2233   int i, k;
2234   int time = (game.LevelSolved ?
2235               game.LevelSolved_CountingTime :
2236               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2237               game_em.lev->time :
2238               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2239               game_sp.time_played :
2240               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2241               game_mm.energy_left :
2242               game.no_time_limit ? TimePlayed : TimeLeft);
2243   int score = (game.LevelSolved ?
2244                game.LevelSolved_CountingScore :
2245                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                game_em.lev->score :
2247                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                game_sp.score :
2249                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2250                game_mm.score :
2251                game.score);
2252   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2253               game_em.lev->gems_needed :
2254               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2255               game_sp.infotrons_still_needed :
2256               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2257               game_mm.kettles_still_needed :
2258               game.gems_still_needed);
2259   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2260                      game_em.lev->gems_needed > 0 :
2261                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2262                      game_sp.infotrons_still_needed > 0 :
2263                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2264                      game_mm.kettles_still_needed > 0 ||
2265                      game_mm.lights_still_needed > 0 :
2266                      game.gems_still_needed > 0 ||
2267                      game.sokoban_fields_still_needed > 0 ||
2268                      game.sokoban_objects_still_needed > 0 ||
2269                      game.lights_still_needed > 0);
2270   int health = (game.LevelSolved ?
2271                 game.LevelSolved_CountingHealth :
2272                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2273                 MM_HEALTH(game_mm.laser_overload_value) :
2274                 game.health);
2275   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2276
2277   UpdatePlayfieldElementCount();
2278
2279   // update game panel control values
2280
2281   // used instead of "level_nr" (for network games)
2282   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2283   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2284
2285   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2286   for (i = 0; i < MAX_NUM_KEYS; i++)
2287     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2288   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2289   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2290
2291   if (game.centered_player_nr == -1)
2292   {
2293     for (i = 0; i < MAX_PLAYERS; i++)
2294     {
2295       // only one player in Supaplex game engine
2296       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2297         break;
2298
2299       for (k = 0; k < MAX_NUM_KEYS; k++)
2300       {
2301         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302         {
2303           if (game_em.ply[i]->keys & (1 << k))
2304             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2305               get_key_element_from_nr(k);
2306         }
2307         else if (stored_player[i].key[k])
2308           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2309             get_key_element_from_nr(k);
2310       }
2311
2312       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2313         getPlayerInventorySize(i);
2314
2315       if (stored_player[i].num_white_keys > 0)
2316         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2317           EL_DC_KEY_WHITE;
2318
2319       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2320         stored_player[i].num_white_keys;
2321     }
2322   }
2323   else
2324   {
2325     int player_nr = game.centered_player_nr;
2326
2327     for (k = 0; k < MAX_NUM_KEYS; k++)
2328     {
2329       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2330       {
2331         if (game_em.ply[player_nr]->keys & (1 << k))
2332           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2333             get_key_element_from_nr(k);
2334       }
2335       else if (stored_player[player_nr].key[k])
2336         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2337           get_key_element_from_nr(k);
2338     }
2339
2340     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2341       getPlayerInventorySize(player_nr);
2342
2343     if (stored_player[player_nr].num_white_keys > 0)
2344       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2345
2346     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2347       stored_player[player_nr].num_white_keys;
2348   }
2349
2350   // re-arrange keys on game panel, if needed or if defined by style settings
2351   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2352   {
2353     int nr = GAME_PANEL_KEY_1 + i;
2354     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2355     struct TextPosInfo *pos = gpc->pos;
2356
2357     // skip check if key is not in the player's inventory
2358     if (gpc->value == EL_EMPTY)
2359       continue;
2360
2361     // check if keys should be arranged on panel from left to right
2362     if (pos->style == STYLE_LEFTMOST_POSITION)
2363     {
2364       // check previous key positions (left from current key)
2365       for (k = 0; k < i; k++)
2366       {
2367         int nr_new = GAME_PANEL_KEY_1 + k;
2368
2369         if (game_panel_controls[nr_new].value == EL_EMPTY)
2370         {
2371           game_panel_controls[nr_new].value = gpc->value;
2372           gpc->value = EL_EMPTY;
2373
2374           break;
2375         }
2376       }
2377     }
2378
2379     // check if "undefined" keys can be placed at some other position
2380     if (pos->x == -1 && pos->y == -1)
2381     {
2382       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2383
2384       // 1st try: display key at the same position as normal or EM keys
2385       if (game_panel_controls[nr_new].value == EL_EMPTY)
2386       {
2387         game_panel_controls[nr_new].value = gpc->value;
2388       }
2389       else
2390       {
2391         // 2nd try: display key at the next free position in the key panel
2392         for (k = 0; k < STD_NUM_KEYS; k++)
2393         {
2394           nr_new = GAME_PANEL_KEY_1 + k;
2395
2396           if (game_panel_controls[nr_new].value == EL_EMPTY)
2397           {
2398             game_panel_controls[nr_new].value = gpc->value;
2399
2400             break;
2401           }
2402         }
2403       }
2404     }
2405   }
2406
2407   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2408   {
2409     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2410       get_inventory_element_from_pos(local_player, i);
2411     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2412       get_inventory_element_from_pos(local_player, -i - 1);
2413   }
2414
2415   game_panel_controls[GAME_PANEL_SCORE].value = score;
2416   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2417
2418   game_panel_controls[GAME_PANEL_TIME].value = time;
2419
2420   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2421   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2422   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2423
2424   if (level.time == 0)
2425     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2426   else
2427     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2428
2429   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2430   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2431
2432   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2433
2434   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2435     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2436      EL_EMPTY);
2437   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2438     local_player->shield_normal_time_left;
2439   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2440     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2441      EL_EMPTY);
2442   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2443     local_player->shield_deadly_time_left;
2444
2445   game_panel_controls[GAME_PANEL_EXIT].value =
2446     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2447
2448   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2449     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2450   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2451     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2452      EL_EMC_MAGIC_BALL_SWITCH);
2453
2454   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2455     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2456   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2457     game.light_time_left;
2458
2459   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2460     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2461   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2462     game.timegate_time_left;
2463
2464   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2465     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2466
2467   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2468     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2469   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2470     game.lenses_time_left;
2471
2472   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2473     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2474   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2475     game.magnify_time_left;
2476
2477   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2478     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2479      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2480      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2481      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2482      EL_BALLOON_SWITCH_NONE);
2483
2484   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2485     local_player->dynabomb_count;
2486   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2487     local_player->dynabomb_size;
2488   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2489     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2490
2491   game_panel_controls[GAME_PANEL_PENGUINS].value =
2492     game.friends_still_needed;
2493
2494   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2495     game.sokoban_objects_still_needed;
2496   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2497     game.sokoban_fields_still_needed;
2498
2499   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2500     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2501
2502   for (i = 0; i < NUM_BELTS; i++)
2503   {
2504     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2505       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2506        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2507     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2508       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2509   }
2510
2511   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2512     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2513   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2514     game.magic_wall_time_left;
2515
2516   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2517     local_player->gravity;
2518
2519   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2520     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2521
2522   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2523     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2524       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2525        game.panel.element[i].id : EL_UNDEFINED);
2526
2527   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2528     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2529       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2530        element_info[game.panel.element_count[i].id].element_count : 0);
2531
2532   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2533     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2534       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2535        element_info[game.panel.ce_score[i].id].collect_score : 0);
2536
2537   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2538     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2539       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2540        element_info[game.panel.ce_score_element[i].id].collect_score :
2541        EL_UNDEFINED);
2542
2543   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2544   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2545   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2546
2547   // update game panel control frames
2548
2549   for (i = 0; game_panel_controls[i].nr != -1; i++)
2550   {
2551     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2552
2553     if (gpc->type == TYPE_ELEMENT)
2554     {
2555       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2556       {
2557         int last_anim_random_frame = gfx.anim_random_frame;
2558         int element = gpc->value;
2559         int graphic = el2panelimg(element);
2560         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2561                                sync_random_frame : INIT_GFX_RANDOM());
2562
2563         if (gpc->value != gpc->last_value)
2564         {
2565           gpc->gfx_frame = 0;
2566           gpc->gfx_random = init_gfx_random;
2567         }
2568         else
2569         {
2570           gpc->gfx_frame++;
2571
2572           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2573               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2574             gpc->gfx_random = init_gfx_random;
2575         }
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = gpc->gfx_random;
2579
2580         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2581           gpc->gfx_frame = element_info[element].collect_score;
2582
2583         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = last_anim_random_frame;
2587       }
2588     }
2589     else if (gpc->type == TYPE_GRAPHIC)
2590     {
2591       if (gpc->graphic != IMG_UNDEFINED)
2592       {
2593         int last_anim_random_frame = gfx.anim_random_frame;
2594         int graphic = gpc->graphic;
2595         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2596                                sync_random_frame : INIT_GFX_RANDOM());
2597
2598         if (gpc->value != gpc->last_value)
2599         {
2600           gpc->gfx_frame = 0;
2601           gpc->gfx_random = init_gfx_random;
2602         }
2603         else
2604         {
2605           gpc->gfx_frame++;
2606
2607           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2608               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2609             gpc->gfx_random = init_gfx_random;
2610         }
2611
2612         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2613           gfx.anim_random_frame = gpc->gfx_random;
2614
2615         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2616
2617         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2618           gfx.anim_random_frame = last_anim_random_frame;
2619       }
2620     }
2621   }
2622 }
2623
2624 static void DisplayGameControlValues(void)
2625 {
2626   boolean redraw_panel = FALSE;
2627   int i;
2628
2629   for (i = 0; game_panel_controls[i].nr != -1; i++)
2630   {
2631     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2632
2633     if (PANEL_DEACTIVATED(gpc->pos))
2634       continue;
2635
2636     if (gpc->value == gpc->last_value &&
2637         gpc->frame == gpc->last_frame)
2638       continue;
2639
2640     redraw_panel = TRUE;
2641   }
2642
2643   if (!redraw_panel)
2644     return;
2645
2646   // copy default game door content to main double buffer
2647
2648   // !!! CHECK AGAIN !!!
2649   SetPanelBackground();
2650   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2651   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2652
2653   // redraw game control buttons
2654   RedrawGameButtons();
2655
2656   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2657
2658   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2659   {
2660     int nr = game_panel_order[i].nr;
2661     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2662     struct TextPosInfo *pos = gpc->pos;
2663     int type = gpc->type;
2664     int value = gpc->value;
2665     int frame = gpc->frame;
2666     int size = pos->size;
2667     int font = pos->font;
2668     boolean draw_masked = pos->draw_masked;
2669     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2670
2671     if (PANEL_DEACTIVATED(pos))
2672       continue;
2673
2674     if (pos->class == get_hash_from_key("extra_panel_items") &&
2675         !setup.prefer_extra_panel_items)
2676       continue;
2677
2678     gpc->last_value = value;
2679     gpc->last_frame = frame;
2680
2681     if (type == TYPE_INTEGER)
2682     {
2683       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2684           nr == GAME_PANEL_TIME)
2685       {
2686         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2687
2688         if (use_dynamic_size)           // use dynamic number of digits
2689         {
2690           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2691           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2692           int size2 = size1 + 1;
2693           int font1 = pos->font;
2694           int font2 = pos->font_alt;
2695
2696           size = (value < value_change ? size1 : size2);
2697           font = (value < value_change ? font1 : font2);
2698         }
2699       }
2700
2701       // correct text size if "digits" is zero or less
2702       if (size <= 0)
2703         size = strlen(int2str(value, size));
2704
2705       // dynamically correct text alignment
2706       pos->width = size * getFontWidth(font);
2707
2708       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2709                   int2str(value, size), font, mask_mode);
2710     }
2711     else if (type == TYPE_ELEMENT)
2712     {
2713       int element, graphic;
2714       Bitmap *src_bitmap;
2715       int src_x, src_y;
2716       int width, height;
2717       int dst_x = PANEL_XPOS(pos);
2718       int dst_y = PANEL_YPOS(pos);
2719
2720       if (value != EL_UNDEFINED && value != EL_EMPTY)
2721       {
2722         element = value;
2723         graphic = el2panelimg(value);
2724
2725 #if 0
2726         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2727               element, EL_NAME(element), size);
2728 #endif
2729
2730         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2731           size = TILESIZE;
2732
2733         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2734                               &src_x, &src_y);
2735
2736         width  = graphic_info[graphic].width  * size / TILESIZE;
2737         height = graphic_info[graphic].height * size / TILESIZE;
2738
2739         if (draw_masked)
2740           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2741                            dst_x, dst_y);
2742         else
2743           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2744                      dst_x, dst_y);
2745       }
2746     }
2747     else if (type == TYPE_GRAPHIC)
2748     {
2749       int graphic        = gpc->graphic;
2750       int graphic_active = gpc->graphic_active;
2751       Bitmap *src_bitmap;
2752       int src_x, src_y;
2753       int width, height;
2754       int dst_x = PANEL_XPOS(pos);
2755       int dst_y = PANEL_YPOS(pos);
2756       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2757                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2758
2759       if (graphic != IMG_UNDEFINED && !skip)
2760       {
2761         if (pos->style == STYLE_REVERSE)
2762           value = 100 - value;
2763
2764         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2765
2766         if (pos->direction & MV_HORIZONTAL)
2767         {
2768           width  = graphic_info[graphic_active].width * value / 100;
2769           height = graphic_info[graphic_active].height;
2770
2771           if (pos->direction == MV_LEFT)
2772           {
2773             src_x += graphic_info[graphic_active].width - width;
2774             dst_x += graphic_info[graphic_active].width - width;
2775           }
2776         }
2777         else
2778         {
2779           width  = graphic_info[graphic_active].width;
2780           height = graphic_info[graphic_active].height * value / 100;
2781
2782           if (pos->direction == MV_UP)
2783           {
2784             src_y += graphic_info[graphic_active].height - height;
2785             dst_y += graphic_info[graphic_active].height - height;
2786           }
2787         }
2788
2789         if (draw_masked)
2790           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2791                            dst_x, dst_y);
2792         else
2793           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2794                      dst_x, dst_y);
2795
2796         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2797
2798         if (pos->direction & MV_HORIZONTAL)
2799         {
2800           if (pos->direction == MV_RIGHT)
2801           {
2802             src_x += width;
2803             dst_x += width;
2804           }
2805           else
2806           {
2807             dst_x = PANEL_XPOS(pos);
2808           }
2809
2810           width = graphic_info[graphic].width - width;
2811         }
2812         else
2813         {
2814           if (pos->direction == MV_DOWN)
2815           {
2816             src_y += height;
2817             dst_y += height;
2818           }
2819           else
2820           {
2821             dst_y = PANEL_YPOS(pos);
2822           }
2823
2824           height = graphic_info[graphic].height - height;
2825         }
2826
2827         if (draw_masked)
2828           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2829                            dst_x, dst_y);
2830         else
2831           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2832                      dst_x, dst_y);
2833       }
2834     }
2835     else if (type == TYPE_STRING)
2836     {
2837       boolean active = (value != 0);
2838       char *state_normal = "off";
2839       char *state_active = "on";
2840       char *state = (active ? state_active : state_normal);
2841       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2842                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2843                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2844                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2845
2846       if (nr == GAME_PANEL_GRAVITY_STATE)
2847       {
2848         int font1 = pos->font;          // (used for normal state)
2849         int font2 = pos->font_alt;      // (used for active state)
2850
2851         font = (active ? font2 : font1);
2852       }
2853
2854       if (s != NULL)
2855       {
2856         char *s_cut;
2857
2858         if (size <= 0)
2859         {
2860           // don't truncate output if "chars" is zero or less
2861           size = strlen(s);
2862
2863           // dynamically correct text alignment
2864           pos->width = size * getFontWidth(font);
2865         }
2866
2867         s_cut = getStringCopyN(s, size);
2868
2869         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2870                     s_cut, font, mask_mode);
2871
2872         free(s_cut);
2873       }
2874     }
2875
2876     redraw_mask |= REDRAW_DOOR_1;
2877   }
2878
2879   SetGameStatus(GAME_MODE_PLAYING);
2880 }
2881
2882 void UpdateAndDisplayGameControlValues(void)
2883 {
2884   if (tape.deactivate_display)
2885     return;
2886
2887   UpdateGameControlValues();
2888   DisplayGameControlValues();
2889 }
2890
2891 void UpdateGameDoorValues(void)
2892 {
2893   UpdateGameControlValues();
2894 }
2895
2896 void DrawGameDoorValues(void)
2897 {
2898   DisplayGameControlValues();
2899 }
2900
2901
2902 // ============================================================================
2903 // InitGameEngine()
2904 // ----------------------------------------------------------------------------
2905 // initialize game engine due to level / tape version number
2906 // ============================================================================
2907
2908 static void InitGameEngine(void)
2909 {
2910   int i, j, k, l, x, y;
2911
2912   // set game engine from tape file when re-playing, else from level file
2913   game.engine_version = (tape.playing ? tape.engine_version :
2914                          level.game_version);
2915
2916   // set single or multi-player game mode (needed for re-playing tapes)
2917   game.team_mode = setup.team_mode;
2918
2919   if (tape.playing)
2920   {
2921     int num_players = 0;
2922
2923     for (i = 0; i < MAX_PLAYERS; i++)
2924       if (tape.player_participates[i])
2925         num_players++;
2926
2927     // multi-player tapes contain input data for more than one player
2928     game.team_mode = (num_players > 1);
2929   }
2930
2931 #if 0
2932   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2933         level.game_version);
2934   Debug("game:init:level", "          tape.file_version   == %06d",
2935         tape.file_version);
2936   Debug("game:init:level", "          tape.game_version   == %06d",
2937         tape.game_version);
2938   Debug("game:init:level", "          tape.engine_version == %06d",
2939         tape.engine_version);
2940   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2941         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2942 #endif
2943
2944   // --------------------------------------------------------------------------
2945   // set flags for bugs and changes according to active game engine version
2946   // --------------------------------------------------------------------------
2947
2948   /*
2949     Summary of bugfix:
2950     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2951
2952     Bug was introduced in version:
2953     2.0.1
2954
2955     Bug was fixed in version:
2956     4.2.0.0
2957
2958     Description:
2959     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2960     but the property "can fall" was missing, which caused some levels to be
2961     unsolvable. This was fixed in version 4.2.0.0.
2962
2963     Affected levels/tapes:
2964     An example for a tape that was fixed by this bugfix is tape 029 from the
2965     level set "rnd_sam_bateman".
2966     The wrong behaviour will still be used for all levels or tapes that were
2967     created/recorded with it. An example for this is tape 023 from the level
2968     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2969   */
2970
2971   boolean use_amoeba_dropping_cannot_fall_bug =
2972     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2973       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2974      (tape.playing &&
2975       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2976       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2977
2978   /*
2979     Summary of bugfix/change:
2980     Fixed move speed of elements entering or leaving magic wall.
2981
2982     Fixed/changed in version:
2983     2.0.1
2984
2985     Description:
2986     Before 2.0.1, move speed of elements entering or leaving magic wall was
2987     twice as fast as it is now.
2988     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2989
2990     Affected levels/tapes:
2991     The first condition is generally needed for all levels/tapes before version
2992     2.0.1, which might use the old behaviour before it was changed; known tapes
2993     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2994     The second condition is an exception from the above case and is needed for
2995     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2996     above, but before it was known that this change would break tapes like the
2997     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2998     although the engine version while recording maybe was before 2.0.1. There
2999     are a lot of tapes that are affected by this exception, like tape 006 from
3000     the level set "rnd_conor_mancone".
3001   */
3002
3003   boolean use_old_move_stepsize_for_magic_wall =
3004     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3005      !(tape.playing &&
3006        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3007        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3008
3009   /*
3010     Summary of bugfix/change:
3011     Fixed handling for custom elements that change when pushed by the player.
3012
3013     Fixed/changed in version:
3014     3.1.0
3015
3016     Description:
3017     Before 3.1.0, custom elements that "change when pushing" changed directly
3018     after the player started pushing them (until then handled in "DigField()").
3019     Since 3.1.0, these custom elements are not changed until the "pushing"
3020     move of the element is finished (now handled in "ContinueMoving()").
3021
3022     Affected levels/tapes:
3023     The first condition is generally needed for all levels/tapes before version
3024     3.1.0, which might use the old behaviour before it was changed; known tapes
3025     that are affected are some tapes from the level set "Walpurgis Gardens" by
3026     Jamie Cullen.
3027     The second condition is an exception from the above case and is needed for
3028     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3029     above (including some development versions of 3.1.0), but before it was
3030     known that this change would break tapes like the above and was fixed in
3031     3.1.1, so that the changed behaviour was active although the engine version
3032     while recording maybe was before 3.1.0. There is at least one tape that is
3033     affected by this exception, which is the tape for the one-level set "Bug
3034     Machine" by Juergen Bonhagen.
3035   */
3036
3037   game.use_change_when_pushing_bug =
3038     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3039      !(tape.playing &&
3040        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3041        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3042
3043   /*
3044     Summary of bugfix/change:
3045     Fixed handling for blocking the field the player leaves when moving.
3046
3047     Fixed/changed in version:
3048     3.1.1
3049
3050     Description:
3051     Before 3.1.1, when "block last field when moving" was enabled, the field
3052     the player is leaving when moving was blocked for the time of the move,
3053     and was directly unblocked afterwards. This resulted in the last field
3054     being blocked for exactly one less than the number of frames of one player
3055     move. Additionally, even when blocking was disabled, the last field was
3056     blocked for exactly one frame.
3057     Since 3.1.1, due to changes in player movement handling, the last field
3058     is not blocked at all when blocking is disabled. When blocking is enabled,
3059     the last field is blocked for exactly the number of frames of one player
3060     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3061     last field is blocked for exactly one more than the number of frames of
3062     one player move.
3063
3064     Affected levels/tapes:
3065     (!!! yet to be determined -- probably many !!!)
3066   */
3067
3068   game.use_block_last_field_bug =
3069     (game.engine_version < VERSION_IDENT(3,1,1,0));
3070
3071   /* various special flags and settings for native Emerald Mine game engine */
3072
3073   game_em.use_single_button =
3074     (game.engine_version > VERSION_IDENT(4,0,0,2));
3075
3076   game_em.use_snap_key_bug =
3077     (game.engine_version < VERSION_IDENT(4,0,1,0));
3078
3079   game_em.use_random_bug =
3080     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3081
3082   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3083
3084   game_em.use_old_explosions            = use_old_em_engine;
3085   game_em.use_old_android               = use_old_em_engine;
3086   game_em.use_old_push_elements         = use_old_em_engine;
3087   game_em.use_old_push_into_acid        = use_old_em_engine;
3088
3089   game_em.use_wrap_around               = !use_old_em_engine;
3090
3091   // --------------------------------------------------------------------------
3092
3093   // set maximal allowed number of custom element changes per game frame
3094   game.max_num_changes_per_frame = 1;
3095
3096   // default scan direction: scan playfield from top/left to bottom/right
3097   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3098
3099   // dynamically adjust element properties according to game engine version
3100   InitElementPropertiesEngine(game.engine_version);
3101
3102   // ---------- initialize special element properties -------------------------
3103
3104   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3105   if (use_amoeba_dropping_cannot_fall_bug)
3106     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3107
3108   // ---------- initialize player's initial move delay ------------------------
3109
3110   // dynamically adjust player properties according to level information
3111   for (i = 0; i < MAX_PLAYERS; i++)
3112     game.initial_move_delay_value[i] =
3113       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3114
3115   // dynamically adjust player properties according to game engine version
3116   for (i = 0; i < MAX_PLAYERS; i++)
3117     game.initial_move_delay[i] =
3118       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3119        game.initial_move_delay_value[i] : 0);
3120
3121   // ---------- initialize player's initial push delay ------------------------
3122
3123   // dynamically adjust player properties according to game engine version
3124   game.initial_push_delay_value =
3125     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3126
3127   // ---------- initialize changing elements ----------------------------------
3128
3129   // initialize changing elements information
3130   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3131   {
3132     struct ElementInfo *ei = &element_info[i];
3133
3134     // this pointer might have been changed in the level editor
3135     ei->change = &ei->change_page[0];
3136
3137     if (!IS_CUSTOM_ELEMENT(i))
3138     {
3139       ei->change->target_element = EL_EMPTY_SPACE;
3140       ei->change->delay_fixed = 0;
3141       ei->change->delay_random = 0;
3142       ei->change->delay_frames = 1;
3143     }
3144
3145     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3146     {
3147       ei->has_change_event[j] = FALSE;
3148
3149       ei->event_page_nr[j] = 0;
3150       ei->event_page[j] = &ei->change_page[0];
3151     }
3152   }
3153
3154   // add changing elements from pre-defined list
3155   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3156   {
3157     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3158     struct ElementInfo *ei = &element_info[ch_delay->element];
3159
3160     ei->change->target_element       = ch_delay->target_element;
3161     ei->change->delay_fixed          = ch_delay->change_delay;
3162
3163     ei->change->pre_change_function  = ch_delay->pre_change_function;
3164     ei->change->change_function      = ch_delay->change_function;
3165     ei->change->post_change_function = ch_delay->post_change_function;
3166
3167     ei->change->can_change = TRUE;
3168     ei->change->can_change_or_has_action = TRUE;
3169
3170     ei->has_change_event[CE_DELAY] = TRUE;
3171
3172     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3173     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3174   }
3175
3176   // ---------- initialize internal run-time variables ------------------------
3177
3178   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3179   {
3180     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3181
3182     for (j = 0; j < ei->num_change_pages; j++)
3183     {
3184       ei->change_page[j].can_change_or_has_action =
3185         (ei->change_page[j].can_change |
3186          ei->change_page[j].has_action);
3187     }
3188   }
3189
3190   // add change events from custom element configuration
3191   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3192   {
3193     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3194
3195     for (j = 0; j < ei->num_change_pages; j++)
3196     {
3197       if (!ei->change_page[j].can_change_or_has_action)
3198         continue;
3199
3200       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3201       {
3202         // only add event page for the first page found with this event
3203         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3204         {
3205           ei->has_change_event[k] = TRUE;
3206
3207           ei->event_page_nr[k] = j;
3208           ei->event_page[k] = &ei->change_page[j];
3209         }
3210       }
3211     }
3212   }
3213
3214   // ---------- initialize reference elements in change conditions ------------
3215
3216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3217   {
3218     int element = EL_CUSTOM_START + i;
3219     struct ElementInfo *ei = &element_info[element];
3220
3221     for (j = 0; j < ei->num_change_pages; j++)
3222     {
3223       int trigger_element = ei->change_page[j].initial_trigger_element;
3224
3225       if (trigger_element >= EL_PREV_CE_8 &&
3226           trigger_element <= EL_NEXT_CE_8)
3227         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3228
3229       ei->change_page[j].trigger_element = trigger_element;
3230     }
3231   }
3232
3233   // ---------- initialize run-time trigger player and element ----------------
3234
3235   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3236   {
3237     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3238
3239     for (j = 0; j < ei->num_change_pages; j++)
3240     {
3241       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3242       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3243       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3244       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3245       ei->change_page[j].actual_trigger_ce_value = 0;
3246       ei->change_page[j].actual_trigger_ce_score = 0;
3247     }
3248   }
3249
3250   // ---------- initialize trigger events -------------------------------------
3251
3252   // initialize trigger events information
3253   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3254     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3255       trigger_events[i][j] = FALSE;
3256
3257   // add trigger events from element change event properties
3258   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3259   {
3260     struct ElementInfo *ei = &element_info[i];
3261
3262     for (j = 0; j < ei->num_change_pages; j++)
3263     {
3264       if (!ei->change_page[j].can_change_or_has_action)
3265         continue;
3266
3267       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3268       {
3269         int trigger_element = ei->change_page[j].trigger_element;
3270
3271         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3272         {
3273           if (ei->change_page[j].has_event[k])
3274           {
3275             if (IS_GROUP_ELEMENT(trigger_element))
3276             {
3277               struct ElementGroupInfo *group =
3278                 element_info[trigger_element].group;
3279
3280               for (l = 0; l < group->num_elements_resolved; l++)
3281                 trigger_events[group->element_resolved[l]][k] = TRUE;
3282             }
3283             else if (trigger_element == EL_ANY_ELEMENT)
3284               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3285                 trigger_events[l][k] = TRUE;
3286             else
3287               trigger_events[trigger_element][k] = TRUE;
3288           }
3289         }
3290       }
3291     }
3292   }
3293
3294   // ---------- initialize push delay -----------------------------------------
3295
3296   // initialize push delay values to default
3297   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3298   {
3299     if (!IS_CUSTOM_ELEMENT(i))
3300     {
3301       // set default push delay values (corrected since version 3.0.7-1)
3302       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3303       {
3304         element_info[i].push_delay_fixed = 2;
3305         element_info[i].push_delay_random = 8;
3306       }
3307       else
3308       {
3309         element_info[i].push_delay_fixed = 8;
3310         element_info[i].push_delay_random = 8;
3311       }
3312     }
3313   }
3314
3315   // set push delay value for certain elements from pre-defined list
3316   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3317   {
3318     int e = push_delay_list[i].element;
3319
3320     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3321     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3322   }
3323
3324   // set push delay value for Supaplex elements for newer engine versions
3325   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3326   {
3327     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3328     {
3329       if (IS_SP_ELEMENT(i))
3330       {
3331         // set SP push delay to just enough to push under a falling zonk
3332         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3333
3334         element_info[i].push_delay_fixed  = delay;
3335         element_info[i].push_delay_random = 0;
3336       }
3337     }
3338   }
3339
3340   // ---------- initialize move stepsize --------------------------------------
3341
3342   // initialize move stepsize values to default
3343   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3344     if (!IS_CUSTOM_ELEMENT(i))
3345       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3346
3347   // set move stepsize value for certain elements from pre-defined list
3348   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3349   {
3350     int e = move_stepsize_list[i].element;
3351
3352     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3353
3354     // set move stepsize value for certain elements for older engine versions
3355     if (use_old_move_stepsize_for_magic_wall)
3356     {
3357       if (e == EL_MAGIC_WALL_FILLING ||
3358           e == EL_MAGIC_WALL_EMPTYING ||
3359           e == EL_BD_MAGIC_WALL_FILLING ||
3360           e == EL_BD_MAGIC_WALL_EMPTYING)
3361         element_info[e].move_stepsize *= 2;
3362     }
3363   }
3364
3365   // ---------- initialize collect score --------------------------------------
3366
3367   // initialize collect score values for custom elements from initial value
3368   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3369     if (IS_CUSTOM_ELEMENT(i))
3370       element_info[i].collect_score = element_info[i].collect_score_initial;
3371
3372   // ---------- initialize collect count --------------------------------------
3373
3374   // initialize collect count values for non-custom elements
3375   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3376     if (!IS_CUSTOM_ELEMENT(i))
3377       element_info[i].collect_count_initial = 0;
3378
3379   // add collect count values for all elements from pre-defined list
3380   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3381     element_info[collect_count_list[i].element].collect_count_initial =
3382       collect_count_list[i].count;
3383
3384   // ---------- initialize access direction -----------------------------------
3385
3386   // initialize access direction values to default (access from every side)
3387   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3388     if (!IS_CUSTOM_ELEMENT(i))
3389       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3390
3391   // set access direction value for certain elements from pre-defined list
3392   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3393     element_info[access_direction_list[i].element].access_direction =
3394       access_direction_list[i].direction;
3395
3396   // ---------- initialize explosion content ----------------------------------
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398   {
3399     if (IS_CUSTOM_ELEMENT(i))
3400       continue;
3401
3402     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3403     {
3404       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3405
3406       element_info[i].content.e[x][y] =
3407         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3408          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3409          i == EL_PLAYER_3 ? EL_EMERALD :
3410          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3411          i == EL_MOLE ? EL_EMERALD_RED :
3412          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3413          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3414          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3415          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3416          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3417          i == EL_WALL_EMERALD ? EL_EMERALD :
3418          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3419          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3420          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3421          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3422          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3423          i == EL_WALL_PEARL ? EL_PEARL :
3424          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3425          EL_EMPTY);
3426     }
3427   }
3428
3429   // ---------- initialize recursion detection --------------------------------
3430   recursion_loop_depth = 0;
3431   recursion_loop_detected = FALSE;
3432   recursion_loop_element = EL_UNDEFINED;
3433
3434   // ---------- initialize graphics engine ------------------------------------
3435   game.scroll_delay_value =
3436     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3437      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3438      !setup.forced_scroll_delay           ? 0 :
3439      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3440   game.scroll_delay_value =
3441     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3442
3443   // ---------- initialize game engine snapshots ------------------------------
3444   for (i = 0; i < MAX_PLAYERS; i++)
3445     game.snapshot.last_action[i] = 0;
3446   game.snapshot.changed_action = FALSE;
3447   game.snapshot.collected_item = FALSE;
3448   game.snapshot.mode =
3449     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3450      SNAPSHOT_MODE_EVERY_STEP :
3451      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3452      SNAPSHOT_MODE_EVERY_MOVE :
3453      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3454      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3455   game.snapshot.save_snapshot = FALSE;
3456
3457   // ---------- initialize level time for Supaplex engine ---------------------
3458   // Supaplex levels with time limit currently unsupported -- should be added
3459   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3460     level.time = 0;
3461
3462   // ---------- initialize flags for handling game actions --------------------
3463
3464   // set flags for game actions to default values
3465   game.use_key_actions = TRUE;
3466   game.use_mouse_actions = FALSE;
3467
3468   // when using Mirror Magic game engine, handle mouse events only
3469   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3470   {
3471     game.use_key_actions = FALSE;
3472     game.use_mouse_actions = TRUE;
3473   }
3474
3475   // check for custom elements with mouse click events
3476   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3477   {
3478     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3479     {
3480       int element = EL_CUSTOM_START + i;
3481
3482       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3483           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3484           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3485           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3486         game.use_mouse_actions = TRUE;
3487     }
3488   }
3489 }
3490
3491 static int get_num_special_action(int element, int action_first,
3492                                   int action_last)
3493 {
3494   int num_special_action = 0;
3495   int i, j;
3496
3497   for (i = action_first; i <= action_last; i++)
3498   {
3499     boolean found = FALSE;
3500
3501     for (j = 0; j < NUM_DIRECTIONS; j++)
3502       if (el_act_dir2img(element, i, j) !=
3503           el_act_dir2img(element, ACTION_DEFAULT, j))
3504         found = TRUE;
3505
3506     if (found)
3507       num_special_action++;
3508     else
3509       break;
3510   }
3511
3512   return num_special_action;
3513 }
3514
3515
3516 // ============================================================================
3517 // InitGame()
3518 // ----------------------------------------------------------------------------
3519 // initialize and start new game
3520 // ============================================================================
3521
3522 #if DEBUG_INIT_PLAYER
3523 static void DebugPrintPlayerStatus(char *message)
3524 {
3525   int i;
3526
3527   if (!options.debug)
3528     return;
3529
3530   Debug("game:init:player", "%s:", message);
3531
3532   for (i = 0; i < MAX_PLAYERS; i++)
3533   {
3534     struct PlayerInfo *player = &stored_player[i];
3535
3536     Debug("game:init:player",
3537           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3538           i + 1,
3539           player->present,
3540           player->connected,
3541           player->connected_locally,
3542           player->connected_network,
3543           player->active,
3544           (local_player == player ? " (local player)" : ""));
3545   }
3546 }
3547 #endif
3548
3549 void InitGame(void)
3550 {
3551   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3552   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3553   int fade_mask = REDRAW_FIELD;
3554
3555   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3556   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3557   int initial_move_dir = MV_DOWN;
3558   int i, j, x, y;
3559
3560   // required here to update video display before fading (FIX THIS)
3561   DrawMaskedBorder(REDRAW_DOOR_2);
3562
3563   if (!game.restart_level)
3564     CloseDoor(DOOR_CLOSE_1);
3565
3566   SetGameStatus(GAME_MODE_PLAYING);
3567
3568   if (level_editor_test_game)
3569     FadeSkipNextFadeOut();
3570   else
3571     FadeSetEnterScreen();
3572
3573   if (CheckFadeAll())
3574     fade_mask = REDRAW_ALL;
3575
3576   FadeLevelSoundsAndMusic();
3577
3578   ExpireSoundLoops(TRUE);
3579
3580   FadeOut(fade_mask);
3581
3582   if (level_editor_test_game)
3583     FadeSkipNextFadeIn();
3584
3585   // needed if different viewport properties defined for playing
3586   ChangeViewportPropertiesIfNeeded();
3587
3588   ClearField();
3589
3590   DrawCompleteVideoDisplay();
3591
3592   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3593
3594   InitGameEngine();
3595   InitGameControlValues();
3596
3597   if (tape.recording)
3598   {
3599     // initialize tape actions from game when recording tape
3600     tape.use_key_actions   = game.use_key_actions;
3601     tape.use_mouse_actions = game.use_mouse_actions;
3602
3603     // initialize visible playfield size when recording tape (for team mode)
3604     tape.scr_fieldx = SCR_FIELDX;
3605     tape.scr_fieldy = SCR_FIELDY;
3606   }
3607
3608   // don't play tapes over network
3609   network_playing = (network.enabled && !tape.playing);
3610
3611   for (i = 0; i < MAX_PLAYERS; i++)
3612   {
3613     struct PlayerInfo *player = &stored_player[i];
3614
3615     player->index_nr = i;
3616     player->index_bit = (1 << i);
3617     player->element_nr = EL_PLAYER_1 + i;
3618
3619     player->present = FALSE;
3620     player->active = FALSE;
3621     player->mapped = FALSE;
3622
3623     player->killed = FALSE;
3624     player->reanimated = FALSE;
3625     player->buried = FALSE;
3626
3627     player->action = 0;
3628     player->effective_action = 0;
3629     player->programmed_action = 0;
3630     player->snap_action = 0;
3631
3632     player->mouse_action.lx = 0;
3633     player->mouse_action.ly = 0;
3634     player->mouse_action.button = 0;
3635     player->mouse_action.button_hint = 0;
3636
3637     player->effective_mouse_action.lx = 0;
3638     player->effective_mouse_action.ly = 0;
3639     player->effective_mouse_action.button = 0;
3640     player->effective_mouse_action.button_hint = 0;
3641
3642     for (j = 0; j < MAX_NUM_KEYS; j++)
3643       player->key[j] = FALSE;
3644
3645     player->num_white_keys = 0;
3646
3647     player->dynabomb_count = 0;
3648     player->dynabomb_size = 1;
3649     player->dynabombs_left = 0;
3650     player->dynabomb_xl = FALSE;
3651
3652     player->MovDir = initial_move_dir;
3653     player->MovPos = 0;
3654     player->GfxPos = 0;
3655     player->GfxDir = initial_move_dir;
3656     player->GfxAction = ACTION_DEFAULT;
3657     player->Frame = 0;
3658     player->StepFrame = 0;
3659
3660     player->initial_element = player->element_nr;
3661     player->artwork_element =
3662       (level.use_artwork_element[i] ? level.artwork_element[i] :
3663        player->element_nr);
3664     player->use_murphy = FALSE;
3665
3666     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3667     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3668
3669     player->gravity = level.initial_player_gravity[i];
3670
3671     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3672
3673     player->actual_frame_counter = 0;
3674
3675     player->step_counter = 0;
3676
3677     player->last_move_dir = initial_move_dir;
3678
3679     player->is_active = FALSE;
3680
3681     player->is_waiting = FALSE;
3682     player->is_moving = FALSE;
3683     player->is_auto_moving = FALSE;
3684     player->is_digging = FALSE;
3685     player->is_snapping = FALSE;
3686     player->is_collecting = FALSE;
3687     player->is_pushing = FALSE;
3688     player->is_switching = FALSE;
3689     player->is_dropping = FALSE;
3690     player->is_dropping_pressed = FALSE;
3691
3692     player->is_bored = FALSE;
3693     player->is_sleeping = FALSE;
3694
3695     player->was_waiting = TRUE;
3696     player->was_moving = FALSE;
3697     player->was_snapping = FALSE;
3698     player->was_dropping = FALSE;
3699
3700     player->force_dropping = FALSE;
3701
3702     player->frame_counter_bored = -1;
3703     player->frame_counter_sleeping = -1;
3704
3705     player->anim_delay_counter = 0;
3706     player->post_delay_counter = 0;
3707
3708     player->dir_waiting = initial_move_dir;
3709     player->action_waiting = ACTION_DEFAULT;
3710     player->last_action_waiting = ACTION_DEFAULT;
3711     player->special_action_bored = ACTION_DEFAULT;
3712     player->special_action_sleeping = ACTION_DEFAULT;
3713
3714     player->switch_x = -1;
3715     player->switch_y = -1;
3716
3717     player->drop_x = -1;
3718     player->drop_y = -1;
3719
3720     player->show_envelope = 0;
3721
3722     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3723
3724     player->push_delay       = -1;      // initialized when pushing starts
3725     player->push_delay_value = game.initial_push_delay_value;
3726
3727     player->drop_delay = 0;
3728     player->drop_pressed_delay = 0;
3729
3730     player->last_jx = -1;
3731     player->last_jy = -1;
3732     player->jx = -1;
3733     player->jy = -1;
3734
3735     player->shield_normal_time_left = 0;
3736     player->shield_deadly_time_left = 0;
3737
3738     player->last_removed_element = EL_UNDEFINED;
3739
3740     player->inventory_infinite_element = EL_UNDEFINED;
3741     player->inventory_size = 0;
3742
3743     if (level.use_initial_inventory[i])
3744     {
3745       for (j = 0; j < level.initial_inventory_size[i]; j++)
3746       {
3747         int element = level.initial_inventory_content[i][j];
3748         int collect_count = element_info[element].collect_count_initial;
3749         int k;
3750
3751         if (!IS_CUSTOM_ELEMENT(element))
3752           collect_count = 1;
3753
3754         if (collect_count == 0)
3755           player->inventory_infinite_element = element;
3756         else
3757           for (k = 0; k < collect_count; k++)
3758             if (player->inventory_size < MAX_INVENTORY_SIZE)
3759               player->inventory_element[player->inventory_size++] = element;
3760       }
3761     }
3762
3763     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3764     SnapField(player, 0, 0);
3765
3766     map_player_action[i] = i;
3767   }
3768
3769   network_player_action_received = FALSE;
3770
3771   // initial null action
3772   if (network_playing)
3773     SendToServer_MovePlayer(MV_NONE);
3774
3775   FrameCounter = 0;
3776   TimeFrames = 0;
3777   TimePlayed = 0;
3778   TimeLeft = level.time;
3779   TapeTime = 0;
3780
3781   ScreenMovDir = MV_NONE;
3782   ScreenMovPos = 0;
3783   ScreenGfxPos = 0;
3784
3785   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3786
3787   game.robot_wheel_x = -1;
3788   game.robot_wheel_y = -1;
3789
3790   game.exit_x = -1;
3791   game.exit_y = -1;
3792
3793   game.all_players_gone = FALSE;
3794
3795   game.LevelSolved = FALSE;
3796   game.GameOver = FALSE;
3797
3798   game.GamePlayed = !tape.playing;
3799
3800   game.LevelSolved_GameWon = FALSE;
3801   game.LevelSolved_GameEnd = FALSE;
3802   game.LevelSolved_SaveTape = FALSE;
3803   game.LevelSolved_SaveScore = FALSE;
3804
3805   game.LevelSolved_CountingTime = 0;
3806   game.LevelSolved_CountingScore = 0;
3807   game.LevelSolved_CountingHealth = 0;
3808
3809   game.panel.active = TRUE;
3810
3811   game.no_time_limit = (level.time == 0);
3812
3813   game.yamyam_content_nr = 0;
3814   game.robot_wheel_active = FALSE;
3815   game.magic_wall_active = FALSE;
3816   game.magic_wall_time_left = 0;
3817   game.light_time_left = 0;
3818   game.timegate_time_left = 0;
3819   game.switchgate_pos = 0;
3820   game.wind_direction = level.wind_direction_initial;
3821
3822   game.time_final = 0;
3823   game.score_time_final = 0;
3824
3825   game.score = 0;
3826   game.score_final = 0;
3827
3828   game.health = MAX_HEALTH;
3829   game.health_final = MAX_HEALTH;
3830
3831   game.gems_still_needed = level.gems_needed;
3832   game.sokoban_fields_still_needed = 0;
3833   game.sokoban_objects_still_needed = 0;
3834   game.lights_still_needed = 0;
3835   game.players_still_needed = 0;
3836   game.friends_still_needed = 0;
3837
3838   game.lenses_time_left = 0;
3839   game.magnify_time_left = 0;
3840
3841   game.ball_active = level.ball_active_initial;
3842   game.ball_content_nr = 0;
3843
3844   game.explosions_delayed = TRUE;
3845
3846   game.envelope_active = FALSE;
3847
3848   // special case: set custom artwork setting to initial value
3849   game.use_masked_elements = game.use_masked_elements_initial;
3850
3851   for (i = 0; i < NUM_BELTS; i++)
3852   {
3853     game.belt_dir[i] = MV_NONE;
3854     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3855   }
3856
3857   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3858     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3859
3860 #if DEBUG_INIT_PLAYER
3861   DebugPrintPlayerStatus("Player status at level initialization");
3862 #endif
3863
3864   SCAN_PLAYFIELD(x, y)
3865   {
3866     Tile[x][y] = Last[x][y] = level.field[x][y];
3867     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3868     ChangeDelay[x][y] = 0;
3869     ChangePage[x][y] = -1;
3870     CustomValue[x][y] = 0;              // initialized in InitField()
3871     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3872     AmoebaNr[x][y] = 0;
3873     WasJustMoving[x][y] = 0;
3874     WasJustFalling[x][y] = 0;
3875     CheckCollision[x][y] = 0;
3876     CheckImpact[x][y] = 0;
3877     Stop[x][y] = FALSE;
3878     Pushed[x][y] = FALSE;
3879
3880     ChangeCount[x][y] = 0;
3881     ChangeEvent[x][y] = -1;
3882
3883     ExplodePhase[x][y] = 0;
3884     ExplodeDelay[x][y] = 0;
3885     ExplodeField[x][y] = EX_TYPE_NONE;
3886
3887     RunnerVisit[x][y] = 0;
3888     PlayerVisit[x][y] = 0;
3889
3890     GfxFrame[x][y] = 0;
3891     GfxRandom[x][y] = INIT_GFX_RANDOM();
3892     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3893     GfxElement[x][y] = EL_UNDEFINED;
3894     GfxElementEmpty[x][y] = EL_EMPTY;
3895     GfxAction[x][y] = ACTION_DEFAULT;
3896     GfxDir[x][y] = MV_NONE;
3897     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3898   }
3899
3900   SCAN_PLAYFIELD(x, y)
3901   {
3902     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3903       emulate_bd = FALSE;
3904     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3905       emulate_sp = FALSE;
3906
3907     InitField(x, y, TRUE);
3908
3909     ResetGfxAnimation(x, y);
3910   }
3911
3912   InitBeltMovement();
3913
3914   for (i = 0; i < MAX_PLAYERS; i++)
3915   {
3916     struct PlayerInfo *player = &stored_player[i];
3917
3918     // set number of special actions for bored and sleeping animation
3919     player->num_special_action_bored =
3920       get_num_special_action(player->artwork_element,
3921                              ACTION_BORING_1, ACTION_BORING_LAST);
3922     player->num_special_action_sleeping =
3923       get_num_special_action(player->artwork_element,
3924                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3925   }
3926
3927   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3928                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3929
3930   // initialize type of slippery elements
3931   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3932   {
3933     if (!IS_CUSTOM_ELEMENT(i))
3934     {
3935       // default: elements slip down either to the left or right randomly
3936       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3937
3938       // SP style elements prefer to slip down on the left side
3939       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3940         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3941
3942       // BD style elements prefer to slip down on the left side
3943       if (game.emulation == EMU_BOULDERDASH)
3944         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3945     }
3946   }
3947
3948   // initialize explosion and ignition delay
3949   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3950   {
3951     if (!IS_CUSTOM_ELEMENT(i))
3952     {
3953       int num_phase = 8;
3954       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3955                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3956                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3957       int last_phase = (num_phase + 1) * delay;
3958       int half_phase = (num_phase / 2) * delay;
3959
3960       element_info[i].explosion_delay = last_phase - 1;
3961       element_info[i].ignition_delay = half_phase;
3962
3963       if (i == EL_BLACK_ORB)
3964         element_info[i].ignition_delay = 1;
3965     }
3966   }
3967
3968   // correct non-moving belts to start moving left
3969   for (i = 0; i < NUM_BELTS; i++)
3970     if (game.belt_dir[i] == MV_NONE)
3971       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3972
3973 #if USE_NEW_PLAYER_ASSIGNMENTS
3974   // use preferred player also in local single-player mode
3975   if (!network.enabled && !game.team_mode)
3976   {
3977     int new_index_nr = setup.network_player_nr;
3978
3979     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3980     {
3981       for (i = 0; i < MAX_PLAYERS; i++)
3982         stored_player[i].connected_locally = FALSE;
3983
3984       stored_player[new_index_nr].connected_locally = TRUE;
3985     }
3986   }
3987
3988   for (i = 0; i < MAX_PLAYERS; i++)
3989   {
3990     stored_player[i].connected = FALSE;
3991
3992     // in network game mode, the local player might not be the first player
3993     if (stored_player[i].connected_locally)
3994       local_player = &stored_player[i];
3995   }
3996
3997   if (!network.enabled)
3998     local_player->connected = TRUE;
3999
4000   if (tape.playing)
4001   {
4002     for (i = 0; i < MAX_PLAYERS; i++)
4003       stored_player[i].connected = tape.player_participates[i];
4004   }
4005   else if (network.enabled)
4006   {
4007     // add team mode players connected over the network (needed for correct
4008     // assignment of player figures from level to locally playing players)
4009
4010     for (i = 0; i < MAX_PLAYERS; i++)
4011       if (stored_player[i].connected_network)
4012         stored_player[i].connected = TRUE;
4013   }
4014   else if (game.team_mode)
4015   {
4016     // try to guess locally connected team mode players (needed for correct
4017     // assignment of player figures from level to locally playing players)
4018
4019     for (i = 0; i < MAX_PLAYERS; i++)
4020       if (setup.input[i].use_joystick ||
4021           setup.input[i].key.left != KSYM_UNDEFINED)
4022         stored_player[i].connected = TRUE;
4023   }
4024
4025 #if DEBUG_INIT_PLAYER
4026   DebugPrintPlayerStatus("Player status after level initialization");
4027 #endif
4028
4029 #if DEBUG_INIT_PLAYER
4030   Debug("game:init:player", "Reassigning players ...");
4031 #endif
4032
4033   // check if any connected player was not found in playfield
4034   for (i = 0; i < MAX_PLAYERS; i++)
4035   {
4036     struct PlayerInfo *player = &stored_player[i];
4037
4038     if (player->connected && !player->present)
4039     {
4040       struct PlayerInfo *field_player = NULL;
4041
4042 #if DEBUG_INIT_PLAYER
4043       Debug("game:init:player",
4044             "- looking for field player for player %d ...", i + 1);
4045 #endif
4046
4047       // assign first free player found that is present in the playfield
4048
4049       // first try: look for unmapped playfield player that is not connected
4050       for (j = 0; j < MAX_PLAYERS; j++)
4051         if (field_player == NULL &&
4052             stored_player[j].present &&
4053             !stored_player[j].mapped &&
4054             !stored_player[j].connected)
4055           field_player = &stored_player[j];
4056
4057       // second try: look for *any* unmapped playfield player
4058       for (j = 0; j < MAX_PLAYERS; j++)
4059         if (field_player == NULL &&
4060             stored_player[j].present &&
4061             !stored_player[j].mapped)
4062           field_player = &stored_player[j];
4063
4064       if (field_player != NULL)
4065       {
4066         int jx = field_player->jx, jy = field_player->jy;
4067
4068 #if DEBUG_INIT_PLAYER
4069         Debug("game:init:player", "- found player %d",
4070               field_player->index_nr + 1);
4071 #endif
4072
4073         player->present = FALSE;
4074         player->active = FALSE;
4075
4076         field_player->present = TRUE;
4077         field_player->active = TRUE;
4078
4079         /*
4080         player->initial_element = field_player->initial_element;
4081         player->artwork_element = field_player->artwork_element;
4082
4083         player->block_last_field       = field_player->block_last_field;
4084         player->block_delay_adjustment = field_player->block_delay_adjustment;
4085         */
4086
4087         StorePlayer[jx][jy] = field_player->element_nr;
4088
4089         field_player->jx = field_player->last_jx = jx;
4090         field_player->jy = field_player->last_jy = jy;
4091
4092         if (local_player == player)
4093           local_player = field_player;
4094
4095         map_player_action[field_player->index_nr] = i;
4096
4097         field_player->mapped = TRUE;
4098
4099 #if DEBUG_INIT_PLAYER
4100         Debug("game:init:player", "- map_player_action[%d] == %d",
4101               field_player->index_nr + 1, i + 1);
4102 #endif
4103       }
4104     }
4105
4106     if (player->connected && player->present)
4107       player->mapped = TRUE;
4108   }
4109
4110 #if DEBUG_INIT_PLAYER
4111   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4112 #endif
4113
4114 #else
4115
4116   // check if any connected player was not found in playfield
4117   for (i = 0; i < MAX_PLAYERS; i++)
4118   {
4119     struct PlayerInfo *player = &stored_player[i];
4120
4121     if (player->connected && !player->present)
4122     {
4123       for (j = 0; j < MAX_PLAYERS; j++)
4124       {
4125         struct PlayerInfo *field_player = &stored_player[j];
4126         int jx = field_player->jx, jy = field_player->jy;
4127
4128         // assign first free player found that is present in the playfield
4129         if (field_player->present && !field_player->connected)
4130         {
4131           player->present = TRUE;
4132           player->active = TRUE;
4133
4134           field_player->present = FALSE;
4135           field_player->active = FALSE;
4136
4137           player->initial_element = field_player->initial_element;
4138           player->artwork_element = field_player->artwork_element;
4139
4140           player->block_last_field       = field_player->block_last_field;
4141           player->block_delay_adjustment = field_player->block_delay_adjustment;
4142
4143           StorePlayer[jx][jy] = player->element_nr;
4144
4145           player->jx = player->last_jx = jx;
4146           player->jy = player->last_jy = jy;
4147
4148           break;
4149         }
4150       }
4151     }
4152   }
4153 #endif
4154
4155 #if 0
4156   Debug("game:init:player", "local_player->present == %d",
4157         local_player->present);
4158 #endif
4159
4160   // set focus to local player for network games, else to all players
4161   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4162   game.centered_player_nr_next = game.centered_player_nr;
4163   game.set_centered_player = FALSE;
4164   game.set_centered_player_wrap = FALSE;
4165
4166   if (network_playing && tape.recording)
4167   {
4168     // store client dependent player focus when recording network games
4169     tape.centered_player_nr_next = game.centered_player_nr_next;
4170     tape.set_centered_player = TRUE;
4171   }
4172
4173   if (tape.playing)
4174   {
4175     // when playing a tape, eliminate all players who do not participate
4176
4177 #if USE_NEW_PLAYER_ASSIGNMENTS
4178
4179     if (!game.team_mode)
4180     {
4181       for (i = 0; i < MAX_PLAYERS; i++)
4182       {
4183         if (stored_player[i].active &&
4184             !tape.player_participates[map_player_action[i]])
4185         {
4186           struct PlayerInfo *player = &stored_player[i];
4187           int jx = player->jx, jy = player->jy;
4188
4189 #if DEBUG_INIT_PLAYER
4190           Debug("game:init:player", "Removing player %d at (%d, %d)",
4191                 i + 1, jx, jy);
4192 #endif
4193
4194           player->active = FALSE;
4195           StorePlayer[jx][jy] = 0;
4196           Tile[jx][jy] = EL_EMPTY;
4197         }
4198       }
4199     }
4200
4201 #else
4202
4203     for (i = 0; i < MAX_PLAYERS; i++)
4204     {
4205       if (stored_player[i].active &&
4206           !tape.player_participates[i])
4207       {
4208         struct PlayerInfo *player = &stored_player[i];
4209         int jx = player->jx, jy = player->jy;
4210
4211         player->active = FALSE;
4212         StorePlayer[jx][jy] = 0;
4213         Tile[jx][jy] = EL_EMPTY;
4214       }
4215     }
4216 #endif
4217   }
4218   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4219   {
4220     // when in single player mode, eliminate all but the local player
4221
4222     for (i = 0; i < MAX_PLAYERS; i++)
4223     {
4224       struct PlayerInfo *player = &stored_player[i];
4225
4226       if (player->active && player != local_player)
4227       {
4228         int jx = player->jx, jy = player->jy;
4229
4230         player->active = FALSE;
4231         player->present = FALSE;
4232
4233         StorePlayer[jx][jy] = 0;
4234         Tile[jx][jy] = EL_EMPTY;
4235       }
4236     }
4237   }
4238
4239   for (i = 0; i < MAX_PLAYERS; i++)
4240     if (stored_player[i].active)
4241       game.players_still_needed++;
4242
4243   if (level.solved_by_one_player)
4244     game.players_still_needed = 1;
4245
4246   // when recording the game, store which players take part in the game
4247   if (tape.recording)
4248   {
4249 #if USE_NEW_PLAYER_ASSIGNMENTS
4250     for (i = 0; i < MAX_PLAYERS; i++)
4251       if (stored_player[i].connected)
4252         tape.player_participates[i] = TRUE;
4253 #else
4254     for (i = 0; i < MAX_PLAYERS; i++)
4255       if (stored_player[i].active)
4256         tape.player_participates[i] = TRUE;
4257 #endif
4258   }
4259
4260 #if DEBUG_INIT_PLAYER
4261   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4262 #endif
4263
4264   if (BorderElement == EL_EMPTY)
4265   {
4266     SBX_Left = 0;
4267     SBX_Right = lev_fieldx - SCR_FIELDX;
4268     SBY_Upper = 0;
4269     SBY_Lower = lev_fieldy - SCR_FIELDY;
4270   }
4271   else
4272   {
4273     SBX_Left = -1;
4274     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4275     SBY_Upper = -1;
4276     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4277   }
4278
4279   if (full_lev_fieldx <= SCR_FIELDX)
4280     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4281   if (full_lev_fieldy <= SCR_FIELDY)
4282     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4283
4284   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4285     SBX_Left--;
4286   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4287     SBY_Upper--;
4288
4289   // if local player not found, look for custom element that might create
4290   // the player (make some assumptions about the right custom element)
4291   if (!local_player->present)
4292   {
4293     int start_x = 0, start_y = 0;
4294     int found_rating = 0;
4295     int found_element = EL_UNDEFINED;
4296     int player_nr = local_player->index_nr;
4297
4298     SCAN_PLAYFIELD(x, y)
4299     {
4300       int element = Tile[x][y];
4301       int content;
4302       int xx, yy;
4303       boolean is_player;
4304
4305       if (level.use_start_element[player_nr] &&
4306           level.start_element[player_nr] == element &&
4307           found_rating < 4)
4308       {
4309         start_x = x;
4310         start_y = y;
4311
4312         found_rating = 4;
4313         found_element = element;
4314       }
4315
4316       if (!IS_CUSTOM_ELEMENT(element))
4317         continue;
4318
4319       if (CAN_CHANGE(element))
4320       {
4321         for (i = 0; i < element_info[element].num_change_pages; i++)
4322         {
4323           // check for player created from custom element as single target
4324           content = element_info[element].change_page[i].target_element;
4325           is_player = IS_PLAYER_ELEMENT(content);
4326
4327           if (is_player && (found_rating < 3 ||
4328                             (found_rating == 3 && element < found_element)))
4329           {
4330             start_x = x;
4331             start_y = y;
4332
4333             found_rating = 3;
4334             found_element = element;
4335           }
4336         }
4337       }
4338
4339       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4340       {
4341         // check for player created from custom element as explosion content
4342         content = element_info[element].content.e[xx][yy];
4343         is_player = IS_PLAYER_ELEMENT(content);
4344
4345         if (is_player && (found_rating < 2 ||
4346                           (found_rating == 2 && element < found_element)))
4347         {
4348           start_x = x + xx - 1;
4349           start_y = y + yy - 1;
4350
4351           found_rating = 2;
4352           found_element = element;
4353         }
4354
4355         if (!CAN_CHANGE(element))
4356           continue;
4357
4358         for (i = 0; i < element_info[element].num_change_pages; i++)
4359         {
4360           // check for player created from custom element as extended target
4361           content =
4362             element_info[element].change_page[i].target_content.e[xx][yy];
4363
4364           is_player = IS_PLAYER_ELEMENT(content);
4365
4366           if (is_player && (found_rating < 1 ||
4367                             (found_rating == 1 && element < found_element)))
4368           {
4369             start_x = x + xx - 1;
4370             start_y = y + yy - 1;
4371
4372             found_rating = 1;
4373             found_element = element;
4374           }
4375         }
4376       }
4377     }
4378
4379     scroll_x = SCROLL_POSITION_X(start_x);
4380     scroll_y = SCROLL_POSITION_Y(start_y);
4381   }
4382   else
4383   {
4384     scroll_x = SCROLL_POSITION_X(local_player->jx);
4385     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4386   }
4387
4388   // !!! FIX THIS (START) !!!
4389   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4390   {
4391     InitGameEngine_EM();
4392   }
4393   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4394   {
4395     InitGameEngine_SP();
4396   }
4397   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4398   {
4399     InitGameEngine_MM();
4400   }
4401   else
4402   {
4403     DrawLevel(REDRAW_FIELD);
4404     DrawAllPlayers();
4405
4406     // after drawing the level, correct some elements
4407     if (game.timegate_time_left == 0)
4408       CloseAllOpenTimegates();
4409   }
4410
4411   // blit playfield from scroll buffer to normal back buffer for fading in
4412   BlitScreenToBitmap(backbuffer);
4413   // !!! FIX THIS (END) !!!
4414
4415   DrawMaskedBorder(fade_mask);
4416
4417   FadeIn(fade_mask);
4418
4419 #if 1
4420   // full screen redraw is required at this point in the following cases:
4421   // - special editor door undrawn when game was started from level editor
4422   // - drawing area (playfield) was changed and has to be removed completely
4423   redraw_mask = REDRAW_ALL;
4424   BackToFront();
4425 #endif
4426
4427   if (!game.restart_level)
4428   {
4429     // copy default game door content to main double buffer
4430
4431     // !!! CHECK AGAIN !!!
4432     SetPanelBackground();
4433     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4434     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4435   }
4436
4437   SetPanelBackground();
4438   SetDrawBackgroundMask(REDRAW_DOOR_1);
4439
4440   UpdateAndDisplayGameControlValues();
4441
4442   if (!game.restart_level)
4443   {
4444     UnmapGameButtons();
4445     UnmapTapeButtons();
4446
4447     FreeGameButtons();
4448     CreateGameButtons();
4449
4450     MapGameButtons();
4451     MapTapeButtons();
4452
4453     // copy actual game door content to door double buffer for OpenDoor()
4454     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4455
4456     OpenDoor(DOOR_OPEN_ALL);
4457
4458     KeyboardAutoRepeatOffUnlessAutoplay();
4459
4460 #if DEBUG_INIT_PLAYER
4461     DebugPrintPlayerStatus("Player status (final)");
4462 #endif
4463   }
4464
4465   UnmapAllGadgets();
4466
4467   MapGameButtons();
4468   MapTapeButtons();
4469
4470   if (!game.restart_level && !tape.playing)
4471   {
4472     LevelStats_incPlayed(level_nr);
4473
4474     SaveLevelSetup_SeriesInfo();
4475   }
4476
4477   game.restart_level = FALSE;
4478   game.restart_game_message = NULL;
4479
4480   game.request_active = FALSE;
4481   game.request_active_or_moving = FALSE;
4482
4483   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4484     InitGameActions_MM();
4485
4486   SaveEngineSnapshotToListInitial();
4487
4488   if (!game.restart_level)
4489   {
4490     PlaySound(SND_GAME_STARTING);
4491
4492     if (setup.sound_music)
4493       PlayLevelMusic();
4494   }
4495
4496   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4497 }
4498
4499 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4500                         int actual_player_x, int actual_player_y)
4501 {
4502   // this is used for non-R'n'D game engines to update certain engine values
4503
4504   // needed to determine if sounds are played within the visible screen area
4505   scroll_x = actual_scroll_x;
4506   scroll_y = actual_scroll_y;
4507
4508   // needed to get player position for "follow finger" playing input method
4509   local_player->jx = actual_player_x;
4510   local_player->jy = actual_player_y;
4511 }
4512
4513 void InitMovDir(int x, int y)
4514 {
4515   int i, element = Tile[x][y];
4516   static int xy[4][2] =
4517   {
4518     {  0, +1 },
4519     { +1,  0 },
4520     {  0, -1 },
4521     { -1,  0 }
4522   };
4523   static int direction[3][4] =
4524   {
4525     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4526     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4527     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4528   };
4529
4530   switch (element)
4531   {
4532     case EL_BUG_RIGHT:
4533     case EL_BUG_UP:
4534     case EL_BUG_LEFT:
4535     case EL_BUG_DOWN:
4536       Tile[x][y] = EL_BUG;
4537       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4538       break;
4539
4540     case EL_SPACESHIP_RIGHT:
4541     case EL_SPACESHIP_UP:
4542     case EL_SPACESHIP_LEFT:
4543     case EL_SPACESHIP_DOWN:
4544       Tile[x][y] = EL_SPACESHIP;
4545       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4546       break;
4547
4548     case EL_BD_BUTTERFLY_RIGHT:
4549     case EL_BD_BUTTERFLY_UP:
4550     case EL_BD_BUTTERFLY_LEFT:
4551     case EL_BD_BUTTERFLY_DOWN:
4552       Tile[x][y] = EL_BD_BUTTERFLY;
4553       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4554       break;
4555
4556     case EL_BD_FIREFLY_RIGHT:
4557     case EL_BD_FIREFLY_UP:
4558     case EL_BD_FIREFLY_LEFT:
4559     case EL_BD_FIREFLY_DOWN:
4560       Tile[x][y] = EL_BD_FIREFLY;
4561       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4562       break;
4563
4564     case EL_PACMAN_RIGHT:
4565     case EL_PACMAN_UP:
4566     case EL_PACMAN_LEFT:
4567     case EL_PACMAN_DOWN:
4568       Tile[x][y] = EL_PACMAN;
4569       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4570       break;
4571
4572     case EL_YAMYAM_LEFT:
4573     case EL_YAMYAM_RIGHT:
4574     case EL_YAMYAM_UP:
4575     case EL_YAMYAM_DOWN:
4576       Tile[x][y] = EL_YAMYAM;
4577       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4578       break;
4579
4580     case EL_SP_SNIKSNAK:
4581       MovDir[x][y] = MV_UP;
4582       break;
4583
4584     case EL_SP_ELECTRON:
4585       MovDir[x][y] = MV_LEFT;
4586       break;
4587
4588     case EL_MOLE_LEFT:
4589     case EL_MOLE_RIGHT:
4590     case EL_MOLE_UP:
4591     case EL_MOLE_DOWN:
4592       Tile[x][y] = EL_MOLE;
4593       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4594       break;
4595
4596     case EL_SPRING_LEFT:
4597     case EL_SPRING_RIGHT:
4598       Tile[x][y] = EL_SPRING;
4599       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4600       break;
4601
4602     default:
4603       if (IS_CUSTOM_ELEMENT(element))
4604       {
4605         struct ElementInfo *ei = &element_info[element];
4606         int move_direction_initial = ei->move_direction_initial;
4607         int move_pattern = ei->move_pattern;
4608
4609         if (move_direction_initial == MV_START_PREVIOUS)
4610         {
4611           if (MovDir[x][y] != MV_NONE)
4612             return;
4613
4614           move_direction_initial = MV_START_AUTOMATIC;
4615         }
4616
4617         if (move_direction_initial == MV_START_RANDOM)
4618           MovDir[x][y] = 1 << RND(4);
4619         else if (move_direction_initial & MV_ANY_DIRECTION)
4620           MovDir[x][y] = move_direction_initial;
4621         else if (move_pattern == MV_ALL_DIRECTIONS ||
4622                  move_pattern == MV_TURNING_LEFT ||
4623                  move_pattern == MV_TURNING_RIGHT ||
4624                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4625                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4626                  move_pattern == MV_TURNING_RANDOM)
4627           MovDir[x][y] = 1 << RND(4);
4628         else if (move_pattern == MV_HORIZONTAL)
4629           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4630         else if (move_pattern == MV_VERTICAL)
4631           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4632         else if (move_pattern & MV_ANY_DIRECTION)
4633           MovDir[x][y] = element_info[element].move_pattern;
4634         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4635                  move_pattern == MV_ALONG_RIGHT_SIDE)
4636         {
4637           // use random direction as default start direction
4638           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4639             MovDir[x][y] = 1 << RND(4);
4640
4641           for (i = 0; i < NUM_DIRECTIONS; i++)
4642           {
4643             int x1 = x + xy[i][0];
4644             int y1 = y + xy[i][1];
4645
4646             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4647             {
4648               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4649                 MovDir[x][y] = direction[0][i];
4650               else
4651                 MovDir[x][y] = direction[1][i];
4652
4653               break;
4654             }
4655           }
4656         }                
4657       }
4658       else
4659       {
4660         MovDir[x][y] = 1 << RND(4);
4661
4662         if (element != EL_BUG &&
4663             element != EL_SPACESHIP &&
4664             element != EL_BD_BUTTERFLY &&
4665             element != EL_BD_FIREFLY)
4666           break;
4667
4668         for (i = 0; i < NUM_DIRECTIONS; i++)
4669         {
4670           int x1 = x + xy[i][0];
4671           int y1 = y + xy[i][1];
4672
4673           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4674           {
4675             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4676             {
4677               MovDir[x][y] = direction[0][i];
4678               break;
4679             }
4680             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4681                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4682             {
4683               MovDir[x][y] = direction[1][i];
4684               break;
4685             }
4686           }
4687         }
4688       }
4689       break;
4690   }
4691
4692   GfxDir[x][y] = MovDir[x][y];
4693 }
4694
4695 void InitAmoebaNr(int x, int y)
4696 {
4697   int i;
4698   int group_nr = AmoebaNeighbourNr(x, y);
4699
4700   if (group_nr == 0)
4701   {
4702     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4703     {
4704       if (AmoebaCnt[i] == 0)
4705       {
4706         group_nr = i;
4707         break;
4708       }
4709     }
4710   }
4711
4712   AmoebaNr[x][y] = group_nr;
4713   AmoebaCnt[group_nr]++;
4714   AmoebaCnt2[group_nr]++;
4715 }
4716
4717 static void LevelSolved_SetFinalGameValues(void)
4718 {
4719   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4720   game.score_time_final = (level.use_step_counter ? TimePlayed :
4721                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4722
4723   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4724                       game_em.lev->score :
4725                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4726                       game_mm.score :
4727                       game.score);
4728
4729   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4730                        MM_HEALTH(game_mm.laser_overload_value) :
4731                        game.health);
4732
4733   game.LevelSolved_CountingTime = game.time_final;
4734   game.LevelSolved_CountingScore = game.score_final;
4735   game.LevelSolved_CountingHealth = game.health_final;
4736 }
4737
4738 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4739 {
4740   game.LevelSolved_CountingTime = time;
4741   game.LevelSolved_CountingScore = score;
4742   game.LevelSolved_CountingHealth = health;
4743
4744   game_panel_controls[GAME_PANEL_TIME].value = time;
4745   game_panel_controls[GAME_PANEL_SCORE].value = score;
4746   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4747
4748   DisplayGameControlValues();
4749 }
4750
4751 static void LevelSolved(void)
4752 {
4753   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4754       game.players_still_needed > 0)
4755     return;
4756
4757   game.LevelSolved = TRUE;
4758   game.GameOver = TRUE;
4759
4760   // needed here to display correct panel values while player walks into exit
4761   LevelSolved_SetFinalGameValues();
4762 }
4763
4764 void GameWon(void)
4765 {
4766   static int time_count_steps;
4767   static int time, time_final;
4768   static float score, score_final; // needed for time score < 10 for 10 seconds
4769   static int health, health_final;
4770   static int game_over_delay_1 = 0;
4771   static int game_over_delay_2 = 0;
4772   static int game_over_delay_3 = 0;
4773   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4774   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4775
4776   if (!game.LevelSolved_GameWon)
4777   {
4778     int i;
4779
4780     // do not start end game actions before the player stops moving (to exit)
4781     if (local_player->active && local_player->MovPos)
4782       return;
4783
4784     // calculate final game values after player finished walking into exit
4785     LevelSolved_SetFinalGameValues();
4786
4787     game.LevelSolved_GameWon = TRUE;
4788     game.LevelSolved_SaveTape = tape.recording;
4789     game.LevelSolved_SaveScore = !tape.playing;
4790
4791     if (!tape.playing)
4792     {
4793       LevelStats_incSolved(level_nr);
4794
4795       SaveLevelSetup_SeriesInfo();
4796     }
4797
4798     if (tape.auto_play)         // tape might already be stopped here
4799       tape.auto_play_level_solved = TRUE;
4800
4801     TapeStop();
4802
4803     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4804     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4805     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4806
4807     time = time_final = game.time_final;
4808     score = score_final = game.score_final;
4809     health = health_final = game.health_final;
4810
4811     // update game panel values before (delayed) counting of score (if any)
4812     LevelSolved_DisplayFinalGameValues(time, score, health);
4813
4814     // if level has time score defined, calculate new final game values
4815     if (time_score > 0)
4816     {
4817       int time_final_max = 999;
4818       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4819       int time_frames = 0;
4820       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4821       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4822
4823       if (TimeLeft > 0)
4824       {
4825         time_final = 0;
4826         time_frames = time_frames_left;
4827       }
4828       else if (game.no_time_limit && TimePlayed < time_final_max)
4829       {
4830         time_final = time_final_max;
4831         time_frames = time_frames_final_max - time_frames_played;
4832       }
4833
4834       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4835
4836       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4837
4838       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4839       {
4840         health_final = 0;
4841         score_final += health * time_score;
4842       }
4843
4844       game.score_final = score_final;
4845       game.health_final = health_final;
4846     }
4847
4848     // if not counting score after game, immediately update game panel values
4849     if (level_editor_test_game || !setup.count_score_after_game)
4850     {
4851       time = time_final;
4852       score = score_final;
4853
4854       LevelSolved_DisplayFinalGameValues(time, score, health);
4855     }
4856
4857     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4858     {
4859       // check if last player has left the level
4860       if (game.exit_x >= 0 &&
4861           game.exit_y >= 0)
4862       {
4863         int x = game.exit_x;
4864         int y = game.exit_y;
4865         int element = Tile[x][y];
4866
4867         // close exit door after last player
4868         if ((game.all_players_gone &&
4869              (element == EL_EXIT_OPEN ||
4870               element == EL_SP_EXIT_OPEN ||
4871               element == EL_STEEL_EXIT_OPEN)) ||
4872             element == EL_EM_EXIT_OPEN ||
4873             element == EL_EM_STEEL_EXIT_OPEN)
4874         {
4875
4876           Tile[x][y] =
4877             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4878              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4879              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4880              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4881              EL_EM_STEEL_EXIT_CLOSING);
4882
4883           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4884         }
4885
4886         // player disappears
4887         DrawLevelField(x, y);
4888       }
4889
4890       for (i = 0; i < MAX_PLAYERS; i++)
4891       {
4892         struct PlayerInfo *player = &stored_player[i];
4893
4894         if (player->present)
4895         {
4896           RemovePlayer(player);
4897
4898           // player disappears
4899           DrawLevelField(player->jx, player->jy);
4900         }
4901       }
4902     }
4903
4904     PlaySound(SND_GAME_WINNING);
4905   }
4906
4907   if (setup.count_score_after_game)
4908   {
4909     if (time != time_final)
4910     {
4911       if (game_over_delay_1 > 0)
4912       {
4913         game_over_delay_1--;
4914
4915         return;
4916       }
4917
4918       int time_to_go = ABS(time_final - time);
4919       int time_count_dir = (time < time_final ? +1 : -1);
4920
4921       if (time_to_go < time_count_steps)
4922         time_count_steps = 1;
4923
4924       time  += time_count_steps * time_count_dir;
4925       score += time_count_steps * time_score;
4926
4927       // set final score to correct rounding differences after counting score
4928       if (time == time_final)
4929         score = score_final;
4930
4931       LevelSolved_DisplayFinalGameValues(time, score, health);
4932
4933       if (time == time_final)
4934         StopSound(SND_GAME_LEVELTIME_BONUS);
4935       else if (setup.sound_loops)
4936         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4937       else
4938         PlaySound(SND_GAME_LEVELTIME_BONUS);
4939
4940       return;
4941     }
4942
4943     if (health != health_final)
4944     {
4945       if (game_over_delay_2 > 0)
4946       {
4947         game_over_delay_2--;
4948
4949         return;
4950       }
4951
4952       int health_count_dir = (health < health_final ? +1 : -1);
4953
4954       health += health_count_dir;
4955       score  += time_score;
4956
4957       LevelSolved_DisplayFinalGameValues(time, score, health);
4958
4959       if (health == health_final)
4960         StopSound(SND_GAME_LEVELTIME_BONUS);
4961       else if (setup.sound_loops)
4962         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4963       else
4964         PlaySound(SND_GAME_LEVELTIME_BONUS);
4965
4966       return;
4967     }
4968   }
4969
4970   game.panel.active = FALSE;
4971
4972   if (game_over_delay_3 > 0)
4973   {
4974     game_over_delay_3--;
4975
4976     return;
4977   }
4978
4979   GameEnd();
4980 }
4981
4982 void GameEnd(void)
4983 {
4984   // used instead of "level_nr" (needed for network games)
4985   int last_level_nr = levelset.level_nr;
4986   boolean tape_saved = FALSE;
4987
4988   game.LevelSolved_GameEnd = TRUE;
4989
4990   if (game.LevelSolved_SaveTape)
4991   {
4992     // make sure that request dialog to save tape does not open door again
4993     if (!global.use_envelope_request)
4994       CloseDoor(DOOR_CLOSE_1);
4995
4996     // ask to save tape
4997     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
4998
4999     // set unique basename for score tape (also saved in high score table)
5000     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5001   }
5002
5003   // if no tape is to be saved, close both doors simultaneously
5004   CloseDoor(DOOR_CLOSE_ALL);
5005
5006   if (level_editor_test_game)
5007   {
5008     SetGameStatus(GAME_MODE_MAIN);
5009
5010     DrawMainMenu();
5011
5012     return;
5013   }
5014
5015   if (!game.LevelSolved_SaveScore)
5016   {
5017     SetGameStatus(GAME_MODE_MAIN);
5018
5019     DrawMainMenu();
5020
5021     return;
5022   }
5023
5024   if (level_nr == leveldir_current->handicap_level)
5025   {
5026     leveldir_current->handicap_level++;
5027
5028     SaveLevelSetup_SeriesInfo();
5029   }
5030
5031   // save score and score tape before potentially erasing tape below
5032   NewHighScore(last_level_nr, tape_saved);
5033
5034   if (setup.increment_levels &&
5035       level_nr < leveldir_current->last_level &&
5036       !network_playing)
5037   {
5038     level_nr++;         // advance to next level
5039     TapeErase();        // start with empty tape
5040
5041     if (setup.auto_play_next_level)
5042     {
5043       LoadLevel(level_nr);
5044
5045       SaveLevelSetup_SeriesInfo();
5046     }
5047   }
5048
5049   if (scores.last_added >= 0 && setup.show_scores_after_game)
5050   {
5051     SetGameStatus(GAME_MODE_SCORES);
5052
5053     DrawHallOfFame(last_level_nr);
5054   }
5055   else if (setup.auto_play_next_level && setup.increment_levels &&
5056            last_level_nr < leveldir_current->last_level &&
5057            !network_playing)
5058   {
5059     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5060   }
5061   else
5062   {
5063     SetGameStatus(GAME_MODE_MAIN);
5064
5065     DrawMainMenu();
5066   }
5067 }
5068
5069 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5070                          boolean one_score_entry_per_name)
5071 {
5072   int i;
5073
5074   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5075     return -1;
5076
5077   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5078   {
5079     struct ScoreEntry *entry = &list->entry[i];
5080     boolean score_is_better = (new_entry->score >  entry->score);
5081     boolean score_is_equal  = (new_entry->score == entry->score);
5082     boolean time_is_better  = (new_entry->time  <  entry->time);
5083     boolean time_is_equal   = (new_entry->time  == entry->time);
5084     boolean better_by_score = (score_is_better ||
5085                                (score_is_equal && time_is_better));
5086     boolean better_by_time  = (time_is_better ||
5087                                (time_is_equal && score_is_better));
5088     boolean is_better = (level.rate_time_over_score ? better_by_time :
5089                          better_by_score);
5090     boolean entry_is_empty = (entry->score == 0 &&
5091                               entry->time == 0);
5092
5093     // prevent adding server score entries if also existing in local score file
5094     // (special case: historic score entries have an empty tape basename entry)
5095     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5096         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5097       return -1;
5098
5099     if (is_better || entry_is_empty)
5100     {
5101       // player has made it to the hall of fame
5102
5103       if (i < MAX_SCORE_ENTRIES - 1)
5104       {
5105         int m = MAX_SCORE_ENTRIES - 1;
5106         int l;
5107
5108         if (one_score_entry_per_name)
5109         {
5110           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5111             if (strEqual(list->entry[l].name, new_entry->name))
5112               m = l;
5113
5114           if (m == i)   // player's new highscore overwrites his old one
5115             goto put_into_list;
5116         }
5117
5118         for (l = m; l > i; l--)
5119           list->entry[l] = list->entry[l - 1];
5120       }
5121
5122       put_into_list:
5123
5124       *entry = *new_entry;
5125
5126       return i;
5127     }
5128     else if (one_score_entry_per_name &&
5129              strEqual(entry->name, new_entry->name))
5130     {
5131       // player already in high score list with better score or time
5132
5133       return -1;
5134     }
5135   }
5136
5137   return -1;
5138 }
5139
5140 void NewHighScore(int level_nr, boolean tape_saved)
5141 {
5142   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5143   boolean one_per_name = FALSE;
5144
5145   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5146   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5147
5148   new_entry.score = game.score_final;
5149   new_entry.time = game.score_time_final;
5150
5151   LoadScore(level_nr);
5152
5153   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5154
5155   if (scores.last_added < 0)
5156     return;
5157
5158   SaveScore(level_nr);
5159
5160   // store last added local score entry (before merging server scores)
5161   scores.last_added_local = scores.last_added;
5162
5163   if (!game.LevelSolved_SaveTape)
5164     return;
5165
5166   SaveScoreTape(level_nr);
5167
5168   if (setup.ask_for_using_api_server)
5169   {
5170     setup.use_api_server =
5171       Request("Upload your score and tape to the high score server?", REQ_ASK);
5172
5173     if (!setup.use_api_server)
5174       Request("Not using high score server! Use setup menu to enable again!",
5175               REQ_CONFIRM);
5176
5177     runtime.use_api_server = setup.use_api_server;
5178
5179     // after asking for using API server once, do not ask again
5180     setup.ask_for_using_api_server = FALSE;
5181
5182     SaveSetup_ServerSetup();
5183   }
5184
5185   SaveServerScore(level_nr, tape_saved);
5186 }
5187
5188 void MergeServerScore(void)
5189 {
5190   struct ScoreEntry last_added_entry;
5191   boolean one_per_name = FALSE;
5192   int i;
5193
5194   if (scores.last_added >= 0)
5195     last_added_entry = scores.entry[scores.last_added];
5196
5197   for (i = 0; i < server_scores.num_entries; i++)
5198   {
5199     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5200
5201     if (pos >= 0 && pos <= scores.last_added)
5202       scores.last_added++;
5203   }
5204
5205   if (scores.last_added >= MAX_SCORE_ENTRIES)
5206   {
5207     scores.last_added = MAX_SCORE_ENTRIES - 1;
5208     scores.force_last_added = TRUE;
5209
5210     scores.entry[scores.last_added] = last_added_entry;
5211   }
5212 }
5213
5214 static int getElementMoveStepsizeExt(int x, int y, int direction)
5215 {
5216   int element = Tile[x][y];
5217   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5218   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5219   int horiz_move = (dx != 0);
5220   int sign = (horiz_move ? dx : dy);
5221   int step = sign * element_info[element].move_stepsize;
5222
5223   // special values for move stepsize for spring and things on conveyor belt
5224   if (horiz_move)
5225   {
5226     if (CAN_FALL(element) &&
5227         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5228       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5229     else if (element == EL_SPRING)
5230       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5231   }
5232
5233   return step;
5234 }
5235
5236 static int getElementMoveStepsize(int x, int y)
5237 {
5238   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5239 }
5240
5241 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5242 {
5243   if (player->GfxAction != action || player->GfxDir != dir)
5244   {
5245     player->GfxAction = action;
5246     player->GfxDir = dir;
5247     player->Frame = 0;
5248     player->StepFrame = 0;
5249   }
5250 }
5251
5252 static void ResetGfxFrame(int x, int y)
5253 {
5254   // profiling showed that "autotest" spends 10~20% of its time in this function
5255   if (DrawingDeactivatedField())
5256     return;
5257
5258   int element = Tile[x][y];
5259   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5260
5261   if (graphic_info[graphic].anim_global_sync)
5262     GfxFrame[x][y] = FrameCounter;
5263   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5264     GfxFrame[x][y] = CustomValue[x][y];
5265   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5266     GfxFrame[x][y] = element_info[element].collect_score;
5267   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5268     GfxFrame[x][y] = ChangeDelay[x][y];
5269 }
5270
5271 static void ResetGfxAnimation(int x, int y)
5272 {
5273   GfxAction[x][y] = ACTION_DEFAULT;
5274   GfxDir[x][y] = MovDir[x][y];
5275   GfxFrame[x][y] = 0;
5276
5277   ResetGfxFrame(x, y);
5278 }
5279
5280 static void ResetRandomAnimationValue(int x, int y)
5281 {
5282   GfxRandom[x][y] = INIT_GFX_RANDOM();
5283 }
5284
5285 static void InitMovingField(int x, int y, int direction)
5286 {
5287   int element = Tile[x][y];
5288   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5289   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5290   int newx = x + dx;
5291   int newy = y + dy;
5292   boolean is_moving_before, is_moving_after;
5293
5294   // check if element was/is moving or being moved before/after mode change
5295   is_moving_before = (WasJustMoving[x][y] != 0);
5296   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5297
5298   // reset animation only for moving elements which change direction of moving
5299   // or which just started or stopped moving
5300   // (else CEs with property "can move" / "not moving" are reset each frame)
5301   if (is_moving_before != is_moving_after ||
5302       direction != MovDir[x][y])
5303     ResetGfxAnimation(x, y);
5304
5305   MovDir[x][y] = direction;
5306   GfxDir[x][y] = direction;
5307
5308   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5309                      direction == MV_DOWN && CAN_FALL(element) ?
5310                      ACTION_FALLING : ACTION_MOVING);
5311
5312   // this is needed for CEs with property "can move" / "not moving"
5313
5314   if (is_moving_after)
5315   {
5316     if (Tile[newx][newy] == EL_EMPTY)
5317       Tile[newx][newy] = EL_BLOCKED;
5318
5319     MovDir[newx][newy] = MovDir[x][y];
5320
5321     CustomValue[newx][newy] = CustomValue[x][y];
5322
5323     GfxFrame[newx][newy] = GfxFrame[x][y];
5324     GfxRandom[newx][newy] = GfxRandom[x][y];
5325     GfxAction[newx][newy] = GfxAction[x][y];
5326     GfxDir[newx][newy] = GfxDir[x][y];
5327   }
5328 }
5329
5330 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5331 {
5332   int direction = MovDir[x][y];
5333   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5334   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5335
5336   *goes_to_x = newx;
5337   *goes_to_y = newy;
5338 }
5339
5340 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5341 {
5342   int oldx = x, oldy = y;
5343   int direction = MovDir[x][y];
5344
5345   if (direction == MV_LEFT)
5346     oldx++;
5347   else if (direction == MV_RIGHT)
5348     oldx--;
5349   else if (direction == MV_UP)
5350     oldy++;
5351   else if (direction == MV_DOWN)
5352     oldy--;
5353
5354   *comes_from_x = oldx;
5355   *comes_from_y = oldy;
5356 }
5357
5358 static int MovingOrBlocked2Element(int x, int y)
5359 {
5360   int element = Tile[x][y];
5361
5362   if (element == EL_BLOCKED)
5363   {
5364     int oldx, oldy;
5365
5366     Blocked2Moving(x, y, &oldx, &oldy);
5367     return Tile[oldx][oldy];
5368   }
5369   else
5370     return element;
5371 }
5372
5373 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5374 {
5375   // like MovingOrBlocked2Element(), but if element is moving
5376   // and (x,y) is the field the moving element is just leaving,
5377   // return EL_BLOCKED instead of the element value
5378   int element = Tile[x][y];
5379
5380   if (IS_MOVING(x, y))
5381   {
5382     if (element == EL_BLOCKED)
5383     {
5384       int oldx, oldy;
5385
5386       Blocked2Moving(x, y, &oldx, &oldy);
5387       return Tile[oldx][oldy];
5388     }
5389     else
5390       return EL_BLOCKED;
5391   }
5392   else
5393     return element;
5394 }
5395
5396 static void RemoveField(int x, int y)
5397 {
5398   Tile[x][y] = EL_EMPTY;
5399
5400   MovPos[x][y] = 0;
5401   MovDir[x][y] = 0;
5402   MovDelay[x][y] = 0;
5403
5404   CustomValue[x][y] = 0;
5405
5406   AmoebaNr[x][y] = 0;
5407   ChangeDelay[x][y] = 0;
5408   ChangePage[x][y] = -1;
5409   Pushed[x][y] = FALSE;
5410
5411   GfxElement[x][y] = EL_UNDEFINED;
5412   GfxAction[x][y] = ACTION_DEFAULT;
5413   GfxDir[x][y] = MV_NONE;
5414 }
5415
5416 static void RemoveMovingField(int x, int y)
5417 {
5418   int oldx = x, oldy = y, newx = x, newy = y;
5419   int element = Tile[x][y];
5420   int next_element = EL_UNDEFINED;
5421
5422   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5423     return;
5424
5425   if (IS_MOVING(x, y))
5426   {
5427     Moving2Blocked(x, y, &newx, &newy);
5428
5429     if (Tile[newx][newy] != EL_BLOCKED)
5430     {
5431       // element is moving, but target field is not free (blocked), but
5432       // already occupied by something different (example: acid pool);
5433       // in this case, only remove the moving field, but not the target
5434
5435       RemoveField(oldx, oldy);
5436
5437       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5438
5439       TEST_DrawLevelField(oldx, oldy);
5440
5441       return;
5442     }
5443   }
5444   else if (element == EL_BLOCKED)
5445   {
5446     Blocked2Moving(x, y, &oldx, &oldy);
5447     if (!IS_MOVING(oldx, oldy))
5448       return;
5449   }
5450
5451   if (element == EL_BLOCKED &&
5452       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5453        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5454        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5455        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5456        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5457        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5458     next_element = get_next_element(Tile[oldx][oldy]);
5459
5460   RemoveField(oldx, oldy);
5461   RemoveField(newx, newy);
5462
5463   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5464
5465   if (next_element != EL_UNDEFINED)
5466     Tile[oldx][oldy] = next_element;
5467
5468   TEST_DrawLevelField(oldx, oldy);
5469   TEST_DrawLevelField(newx, newy);
5470 }
5471
5472 void DrawDynamite(int x, int y)
5473 {
5474   int sx = SCREENX(x), sy = SCREENY(y);
5475   int graphic = el2img(Tile[x][y]);
5476   int frame;
5477
5478   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5479     return;
5480
5481   if (IS_WALKABLE_INSIDE(Back[x][y]))
5482     return;
5483
5484   if (Back[x][y])
5485     DrawLevelElement(x, y, Back[x][y]);
5486   else if (Store[x][y])
5487     DrawLevelElement(x, y, Store[x][y]);
5488   else if (game.use_masked_elements)
5489     DrawLevelElement(x, y, EL_EMPTY);
5490
5491   frame = getGraphicAnimationFrameXY(graphic, x, y);
5492
5493   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5494     DrawGraphicThruMask(sx, sy, graphic, frame);
5495   else
5496     DrawGraphic(sx, sy, graphic, frame);
5497 }
5498
5499 static void CheckDynamite(int x, int y)
5500 {
5501   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5502   {
5503     MovDelay[x][y]--;
5504
5505     if (MovDelay[x][y] != 0)
5506     {
5507       DrawDynamite(x, y);
5508       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5509
5510       return;
5511     }
5512   }
5513
5514   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5515
5516   Bang(x, y);
5517 }
5518
5519 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5520 {
5521   boolean num_checked_players = 0;
5522   int i;
5523
5524   for (i = 0; i < MAX_PLAYERS; i++)
5525   {
5526     if (stored_player[i].active)
5527     {
5528       int sx = stored_player[i].jx;
5529       int sy = stored_player[i].jy;
5530
5531       if (num_checked_players == 0)
5532       {
5533         *sx1 = *sx2 = sx;
5534         *sy1 = *sy2 = sy;
5535       }
5536       else
5537       {
5538         *sx1 = MIN(*sx1, sx);
5539         *sy1 = MIN(*sy1, sy);
5540         *sx2 = MAX(*sx2, sx);
5541         *sy2 = MAX(*sy2, sy);
5542       }
5543
5544       num_checked_players++;
5545     }
5546   }
5547 }
5548
5549 static boolean checkIfAllPlayersFitToScreen_RND(void)
5550 {
5551   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5552
5553   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5554
5555   return (sx2 - sx1 < SCR_FIELDX &&
5556           sy2 - sy1 < SCR_FIELDY);
5557 }
5558
5559 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5560 {
5561   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5562
5563   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5564
5565   *sx = (sx1 + sx2) / 2;
5566   *sy = (sy1 + sy2) / 2;
5567 }
5568
5569 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5570                                boolean center_screen, boolean quick_relocation)
5571 {
5572   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5573   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5574   boolean no_delay = (tape.warp_forward);
5575   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5576   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5577   int new_scroll_x, new_scroll_y;
5578
5579   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5580   {
5581     // case 1: quick relocation inside visible screen (without scrolling)
5582
5583     RedrawPlayfield();
5584
5585     return;
5586   }
5587
5588   if (!level.shifted_relocation || center_screen)
5589   {
5590     // relocation _with_ centering of screen
5591
5592     new_scroll_x = SCROLL_POSITION_X(x);
5593     new_scroll_y = SCROLL_POSITION_Y(y);
5594   }
5595   else
5596   {
5597     // relocation _without_ centering of screen
5598
5599     int center_scroll_x = SCROLL_POSITION_X(old_x);
5600     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5601     int offset_x = x + (scroll_x - center_scroll_x);
5602     int offset_y = y + (scroll_y - center_scroll_y);
5603
5604     // for new screen position, apply previous offset to center position
5605     new_scroll_x = SCROLL_POSITION_X(offset_x);
5606     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5607   }
5608
5609   if (quick_relocation)
5610   {
5611     // case 2: quick relocation (redraw without visible scrolling)
5612
5613     scroll_x = new_scroll_x;
5614     scroll_y = new_scroll_y;
5615
5616     RedrawPlayfield();
5617
5618     return;
5619   }
5620
5621   // case 3: visible relocation (with scrolling to new position)
5622
5623   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5624
5625   SetVideoFrameDelay(wait_delay_value);
5626
5627   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5628   {
5629     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5630     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5631
5632     if (dx == 0 && dy == 0)             // no scrolling needed at all
5633       break;
5634
5635     scroll_x -= dx;
5636     scroll_y -= dy;
5637
5638     // set values for horizontal/vertical screen scrolling (half tile size)
5639     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5640     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5641     int pos_x = dx * TILEX / 2;
5642     int pos_y = dy * TILEY / 2;
5643     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5644     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5645
5646     ScrollLevel(dx, dy);
5647     DrawAllPlayers();
5648
5649     // scroll in two steps of half tile size to make things smoother
5650     BlitScreenToBitmapExt_RND(window, fx, fy);
5651
5652     // scroll second step to align at full tile size
5653     BlitScreenToBitmap(window);
5654   }
5655
5656   DrawAllPlayers();
5657   BackToFront();
5658
5659   SetVideoFrameDelay(frame_delay_value_old);
5660 }
5661
5662 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5663 {
5664   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5665   int player_nr = GET_PLAYER_NR(el_player);
5666   struct PlayerInfo *player = &stored_player[player_nr];
5667   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5668   boolean no_delay = (tape.warp_forward);
5669   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5670   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5671   int old_jx = player->jx;
5672   int old_jy = player->jy;
5673   int old_element = Tile[old_jx][old_jy];
5674   int element = Tile[jx][jy];
5675   boolean player_relocated = (old_jx != jx || old_jy != jy);
5676
5677   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5678   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5679   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5680   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5681   int leave_side_horiz = move_dir_horiz;
5682   int leave_side_vert  = move_dir_vert;
5683   int enter_side = enter_side_horiz | enter_side_vert;
5684   int leave_side = leave_side_horiz | leave_side_vert;
5685
5686   if (player->buried)           // do not reanimate dead player
5687     return;
5688
5689   if (!player_relocated)        // no need to relocate the player
5690     return;
5691
5692   if (IS_PLAYER(jx, jy))        // player already placed at new position
5693   {
5694     RemoveField(jx, jy);        // temporarily remove newly placed player
5695     DrawLevelField(jx, jy);
5696   }
5697
5698   if (player->present)
5699   {
5700     while (player->MovPos)
5701     {
5702       ScrollPlayer(player, SCROLL_GO_ON);
5703       ScrollScreen(NULL, SCROLL_GO_ON);
5704
5705       AdvanceFrameAndPlayerCounters(player->index_nr);
5706
5707       DrawPlayer(player);
5708
5709       BackToFront_WithFrameDelay(wait_delay_value);
5710     }
5711
5712     DrawPlayer(player);         // needed here only to cleanup last field
5713     DrawLevelField(player->jx, player->jy);     // remove player graphic
5714
5715     player->is_moving = FALSE;
5716   }
5717
5718   if (IS_CUSTOM_ELEMENT(old_element))
5719     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5720                                CE_LEFT_BY_PLAYER,
5721                                player->index_bit, leave_side);
5722
5723   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5724                                       CE_PLAYER_LEAVES_X,
5725                                       player->index_bit, leave_side);
5726
5727   Tile[jx][jy] = el_player;
5728   InitPlayerField(jx, jy, el_player, TRUE);
5729
5730   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5731      possible that the relocation target field did not contain a player element,
5732      but a walkable element, to which the new player was relocated -- in this
5733      case, restore that (already initialized!) element on the player field */
5734   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5735   {
5736     Tile[jx][jy] = element;     // restore previously existing element
5737   }
5738
5739   // only visually relocate centered player
5740   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5741                      FALSE, level.instant_relocation);
5742
5743   TestIfPlayerTouchesBadThing(jx, jy);
5744   TestIfPlayerTouchesCustomElement(jx, jy);
5745
5746   if (IS_CUSTOM_ELEMENT(element))
5747     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5748                                player->index_bit, enter_side);
5749
5750   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5751                                       player->index_bit, enter_side);
5752
5753   if (player->is_switching)
5754   {
5755     /* ensure that relocation while still switching an element does not cause
5756        a new element to be treated as also switched directly after relocation
5757        (this is important for teleporter switches that teleport the player to
5758        a place where another teleporter switch is in the same direction, which
5759        would then incorrectly be treated as immediately switched before the
5760        direction key that caused the switch was released) */
5761
5762     player->switch_x += jx - old_jx;
5763     player->switch_y += jy - old_jy;
5764   }
5765 }
5766
5767 static void Explode(int ex, int ey, int phase, int mode)
5768 {
5769   int x, y;
5770   int last_phase;
5771   int border_element;
5772
5773   // !!! eliminate this variable !!!
5774   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5775
5776   if (game.explosions_delayed)
5777   {
5778     ExplodeField[ex][ey] = mode;
5779     return;
5780   }
5781
5782   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5783   {
5784     int center_element = Tile[ex][ey];
5785     int artwork_element, explosion_element;     // set these values later
5786
5787     // remove things displayed in background while burning dynamite
5788     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5789       Back[ex][ey] = 0;
5790
5791     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5792     {
5793       // put moving element to center field (and let it explode there)
5794       center_element = MovingOrBlocked2Element(ex, ey);
5795       RemoveMovingField(ex, ey);
5796       Tile[ex][ey] = center_element;
5797     }
5798
5799     // now "center_element" is finally determined -- set related values now
5800     artwork_element = center_element;           // for custom player artwork
5801     explosion_element = center_element;         // for custom player artwork
5802
5803     if (IS_PLAYER(ex, ey))
5804     {
5805       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5806
5807       artwork_element = stored_player[player_nr].artwork_element;
5808
5809       if (level.use_explosion_element[player_nr])
5810       {
5811         explosion_element = level.explosion_element[player_nr];
5812         artwork_element = explosion_element;
5813       }
5814     }
5815
5816     if (mode == EX_TYPE_NORMAL ||
5817         mode == EX_TYPE_CENTER ||
5818         mode == EX_TYPE_CROSS)
5819       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5820
5821     last_phase = element_info[explosion_element].explosion_delay + 1;
5822
5823     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5824     {
5825       int xx = x - ex + 1;
5826       int yy = y - ey + 1;
5827       int element;
5828
5829       if (!IN_LEV_FIELD(x, y) ||
5830           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5831           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5832         continue;
5833
5834       element = Tile[x][y];
5835
5836       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5837       {
5838         element = MovingOrBlocked2Element(x, y);
5839
5840         if (!IS_EXPLOSION_PROOF(element))
5841           RemoveMovingField(x, y);
5842       }
5843
5844       // indestructible elements can only explode in center (but not flames)
5845       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5846                                            mode == EX_TYPE_BORDER)) ||
5847           element == EL_FLAMES)
5848         continue;
5849
5850       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5851          behaviour, for example when touching a yamyam that explodes to rocks
5852          with active deadly shield, a rock is created under the player !!! */
5853       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5854 #if 0
5855       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5856           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5857            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5858 #else
5859       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5860 #endif
5861       {
5862         if (IS_ACTIVE_BOMB(element))
5863         {
5864           // re-activate things under the bomb like gate or penguin
5865           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5866           Back[x][y] = 0;
5867         }
5868
5869         continue;
5870       }
5871
5872       // save walkable background elements while explosion on same tile
5873       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5874           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5875         Back[x][y] = element;
5876
5877       // ignite explodable elements reached by other explosion
5878       if (element == EL_EXPLOSION)
5879         element = Store2[x][y];
5880
5881       if (AmoebaNr[x][y] &&
5882           (element == EL_AMOEBA_FULL ||
5883            element == EL_BD_AMOEBA ||
5884            element == EL_AMOEBA_GROWING))
5885       {
5886         AmoebaCnt[AmoebaNr[x][y]]--;
5887         AmoebaCnt2[AmoebaNr[x][y]]--;
5888       }
5889
5890       RemoveField(x, y);
5891
5892       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5893       {
5894         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5895
5896         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5897
5898         if (PLAYERINFO(ex, ey)->use_murphy)
5899           Store[x][y] = EL_EMPTY;
5900       }
5901
5902       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5903       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5904       else if (IS_PLAYER_ELEMENT(center_element))
5905         Store[x][y] = EL_EMPTY;
5906       else if (center_element == EL_YAMYAM)
5907         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5908       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5909         Store[x][y] = element_info[center_element].content.e[xx][yy];
5910 #if 1
5911       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5912       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5913       // otherwise) -- FIX THIS !!!
5914       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5915         Store[x][y] = element_info[element].content.e[1][1];
5916 #else
5917       else if (!CAN_EXPLODE(element))
5918         Store[x][y] = element_info[element].content.e[1][1];
5919 #endif
5920       else
5921         Store[x][y] = EL_EMPTY;
5922
5923       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5924           center_element == EL_AMOEBA_TO_DIAMOND)
5925         Store2[x][y] = element;
5926
5927       Tile[x][y] = EL_EXPLOSION;
5928       GfxElement[x][y] = artwork_element;
5929
5930       ExplodePhase[x][y] = 1;
5931       ExplodeDelay[x][y] = last_phase;
5932
5933       Stop[x][y] = TRUE;
5934     }
5935
5936     if (center_element == EL_YAMYAM)
5937       game.yamyam_content_nr =
5938         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5939
5940     return;
5941   }
5942
5943   if (Stop[ex][ey])
5944     return;
5945
5946   x = ex;
5947   y = ey;
5948
5949   if (phase == 1)
5950     GfxFrame[x][y] = 0;         // restart explosion animation
5951
5952   last_phase = ExplodeDelay[x][y];
5953
5954   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5955
5956   // this can happen if the player leaves an explosion just in time
5957   if (GfxElement[x][y] == EL_UNDEFINED)
5958     GfxElement[x][y] = EL_EMPTY;
5959
5960   border_element = Store2[x][y];
5961   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5962     border_element = StorePlayer[x][y];
5963
5964   if (phase == element_info[border_element].ignition_delay ||
5965       phase == last_phase)
5966   {
5967     boolean border_explosion = FALSE;
5968
5969     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5970         !PLAYER_EXPLOSION_PROTECTED(x, y))
5971     {
5972       KillPlayerUnlessExplosionProtected(x, y);
5973       border_explosion = TRUE;
5974     }
5975     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5976     {
5977       Tile[x][y] = Store2[x][y];
5978       Store2[x][y] = 0;
5979       Bang(x, y);
5980       border_explosion = TRUE;
5981     }
5982     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5983     {
5984       AmoebaToDiamond(x, y);
5985       Store2[x][y] = 0;
5986       border_explosion = TRUE;
5987     }
5988
5989     // if an element just explodes due to another explosion (chain-reaction),
5990     // do not immediately end the new explosion when it was the last frame of
5991     // the explosion (as it would be done in the following "if"-statement!)
5992     if (border_explosion && phase == last_phase)
5993       return;
5994   }
5995
5996   if (phase == last_phase)
5997   {
5998     int element;
5999
6000     element = Tile[x][y] = Store[x][y];
6001     Store[x][y] = Store2[x][y] = 0;
6002     GfxElement[x][y] = EL_UNDEFINED;
6003
6004     // player can escape from explosions and might therefore be still alive
6005     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6006         element <= EL_PLAYER_IS_EXPLODING_4)
6007     {
6008       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6009       int explosion_element = EL_PLAYER_1 + player_nr;
6010       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6011       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6012
6013       if (level.use_explosion_element[player_nr])
6014         explosion_element = level.explosion_element[player_nr];
6015
6016       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6017                     element_info[explosion_element].content.e[xx][yy]);
6018     }
6019
6020     // restore probably existing indestructible background element
6021     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6022       element = Tile[x][y] = Back[x][y];
6023     Back[x][y] = 0;
6024
6025     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6026     GfxDir[x][y] = MV_NONE;
6027     ChangeDelay[x][y] = 0;
6028     ChangePage[x][y] = -1;
6029
6030     CustomValue[x][y] = 0;
6031
6032     InitField_WithBug2(x, y, FALSE);
6033
6034     TEST_DrawLevelField(x, y);
6035
6036     TestIfElementTouchesCustomElement(x, y);
6037
6038     if (GFX_CRUMBLED(element))
6039       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6040
6041     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6042       StorePlayer[x][y] = 0;
6043
6044     if (IS_PLAYER_ELEMENT(element))
6045       RelocatePlayer(x, y, element);
6046   }
6047   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6048   {
6049     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6050     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6051
6052     if (phase == delay)
6053       TEST_DrawLevelFieldCrumbled(x, y);
6054
6055     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6056     {
6057       DrawLevelElement(x, y, Back[x][y]);
6058       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6059     }
6060     else if (IS_WALKABLE_UNDER(Back[x][y]))
6061     {
6062       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6063       DrawLevelElementThruMask(x, y, Back[x][y]);
6064     }
6065     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6066       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6067   }
6068 }
6069
6070 static void DynaExplode(int ex, int ey)
6071 {
6072   int i, j;
6073   int dynabomb_element = Tile[ex][ey];
6074   int dynabomb_size = 1;
6075   boolean dynabomb_xl = FALSE;
6076   struct PlayerInfo *player;
6077   static int xy[4][2] =
6078   {
6079     { 0, -1 },
6080     { -1, 0 },
6081     { +1, 0 },
6082     { 0, +1 }
6083   };
6084
6085   if (IS_ACTIVE_BOMB(dynabomb_element))
6086   {
6087     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6088     dynabomb_size = player->dynabomb_size;
6089     dynabomb_xl = player->dynabomb_xl;
6090     player->dynabombs_left++;
6091   }
6092
6093   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6094
6095   for (i = 0; i < NUM_DIRECTIONS; i++)
6096   {
6097     for (j = 1; j <= dynabomb_size; j++)
6098     {
6099       int x = ex + j * xy[i][0];
6100       int y = ey + j * xy[i][1];
6101       int element;
6102
6103       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6104         break;
6105
6106       element = Tile[x][y];
6107
6108       // do not restart explosions of fields with active bombs
6109       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6110         continue;
6111
6112       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6113
6114       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6115           !IS_DIGGABLE(element) && !dynabomb_xl)
6116         break;
6117     }
6118   }
6119 }
6120
6121 void Bang(int x, int y)
6122 {
6123   int element = MovingOrBlocked2Element(x, y);
6124   int explosion_type = EX_TYPE_NORMAL;
6125
6126   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6127   {
6128     struct PlayerInfo *player = PLAYERINFO(x, y);
6129
6130     element = Tile[x][y] = player->initial_element;
6131
6132     if (level.use_explosion_element[player->index_nr])
6133     {
6134       int explosion_element = level.explosion_element[player->index_nr];
6135
6136       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6137         explosion_type = EX_TYPE_CROSS;
6138       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6139         explosion_type = EX_TYPE_CENTER;
6140     }
6141   }
6142
6143   switch (element)
6144   {
6145     case EL_BUG:
6146     case EL_SPACESHIP:
6147     case EL_BD_BUTTERFLY:
6148     case EL_BD_FIREFLY:
6149     case EL_YAMYAM:
6150     case EL_DARK_YAMYAM:
6151     case EL_ROBOT:
6152     case EL_PACMAN:
6153     case EL_MOLE:
6154       RaiseScoreElement(element);
6155       break;
6156
6157     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6158     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6159     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6160     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6161     case EL_DYNABOMB_INCREASE_NUMBER:
6162     case EL_DYNABOMB_INCREASE_SIZE:
6163     case EL_DYNABOMB_INCREASE_POWER:
6164       explosion_type = EX_TYPE_DYNA;
6165       break;
6166
6167     case EL_DC_LANDMINE:
6168       explosion_type = EX_TYPE_CENTER;
6169       break;
6170
6171     case EL_PENGUIN:
6172     case EL_LAMP:
6173     case EL_LAMP_ACTIVE:
6174     case EL_AMOEBA_TO_DIAMOND:
6175       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6176         explosion_type = EX_TYPE_CENTER;
6177       break;
6178
6179     default:
6180       if (element_info[element].explosion_type == EXPLODES_CROSS)
6181         explosion_type = EX_TYPE_CROSS;
6182       else if (element_info[element].explosion_type == EXPLODES_1X1)
6183         explosion_type = EX_TYPE_CENTER;
6184       break;
6185   }
6186
6187   if (explosion_type == EX_TYPE_DYNA)
6188     DynaExplode(x, y);
6189   else
6190     Explode(x, y, EX_PHASE_START, explosion_type);
6191
6192   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6193 }
6194
6195 static void SplashAcid(int x, int y)
6196 {
6197   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6198       (!IN_LEV_FIELD(x - 1, y - 2) ||
6199        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6200     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6201
6202   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6203       (!IN_LEV_FIELD(x + 1, y - 2) ||
6204        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6205     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6206
6207   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6208 }
6209
6210 static void InitBeltMovement(void)
6211 {
6212   static int belt_base_element[4] =
6213   {
6214     EL_CONVEYOR_BELT_1_LEFT,
6215     EL_CONVEYOR_BELT_2_LEFT,
6216     EL_CONVEYOR_BELT_3_LEFT,
6217     EL_CONVEYOR_BELT_4_LEFT
6218   };
6219   static int belt_base_active_element[4] =
6220   {
6221     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6222     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6223     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6224     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6225   };
6226
6227   int x, y, i, j;
6228
6229   // set frame order for belt animation graphic according to belt direction
6230   for (i = 0; i < NUM_BELTS; i++)
6231   {
6232     int belt_nr = i;
6233
6234     for (j = 0; j < NUM_BELT_PARTS; j++)
6235     {
6236       int element = belt_base_active_element[belt_nr] + j;
6237       int graphic_1 = el2img(element);
6238       int graphic_2 = el2panelimg(element);
6239
6240       if (game.belt_dir[i] == MV_LEFT)
6241       {
6242         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6243         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6244       }
6245       else
6246       {
6247         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6248         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6249       }
6250     }
6251   }
6252
6253   SCAN_PLAYFIELD(x, y)
6254   {
6255     int element = Tile[x][y];
6256
6257     for (i = 0; i < NUM_BELTS; i++)
6258     {
6259       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6260       {
6261         int e_belt_nr = getBeltNrFromBeltElement(element);
6262         int belt_nr = i;
6263
6264         if (e_belt_nr == belt_nr)
6265         {
6266           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6267
6268           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6269         }
6270       }
6271     }
6272   }
6273 }
6274
6275 static void ToggleBeltSwitch(int x, int y)
6276 {
6277   static int belt_base_element[4] =
6278   {
6279     EL_CONVEYOR_BELT_1_LEFT,
6280     EL_CONVEYOR_BELT_2_LEFT,
6281     EL_CONVEYOR_BELT_3_LEFT,
6282     EL_CONVEYOR_BELT_4_LEFT
6283   };
6284   static int belt_base_active_element[4] =
6285   {
6286     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6287     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6288     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6289     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6290   };
6291   static int belt_base_switch_element[4] =
6292   {
6293     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6294     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6295     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6296     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6297   };
6298   static int belt_move_dir[4] =
6299   {
6300     MV_LEFT,
6301     MV_NONE,
6302     MV_RIGHT,
6303     MV_NONE,
6304   };
6305
6306   int element = Tile[x][y];
6307   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6308   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6309   int belt_dir = belt_move_dir[belt_dir_nr];
6310   int xx, yy, i;
6311
6312   if (!IS_BELT_SWITCH(element))
6313     return;
6314
6315   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6316   game.belt_dir[belt_nr] = belt_dir;
6317
6318   if (belt_dir_nr == 3)
6319     belt_dir_nr = 1;
6320
6321   // set frame order for belt animation graphic according to belt direction
6322   for (i = 0; i < NUM_BELT_PARTS; i++)
6323   {
6324     int element = belt_base_active_element[belt_nr] + i;
6325     int graphic_1 = el2img(element);
6326     int graphic_2 = el2panelimg(element);
6327
6328     if (belt_dir == MV_LEFT)
6329     {
6330       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6331       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6332     }
6333     else
6334     {
6335       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6336       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6337     }
6338   }
6339
6340   SCAN_PLAYFIELD(xx, yy)
6341   {
6342     int element = Tile[xx][yy];
6343
6344     if (IS_BELT_SWITCH(element))
6345     {
6346       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6347
6348       if (e_belt_nr == belt_nr)
6349       {
6350         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6351         TEST_DrawLevelField(xx, yy);
6352       }
6353     }
6354     else if (IS_BELT(element) && belt_dir != MV_NONE)
6355     {
6356       int e_belt_nr = getBeltNrFromBeltElement(element);
6357
6358       if (e_belt_nr == belt_nr)
6359       {
6360         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6361
6362         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6363         TEST_DrawLevelField(xx, yy);
6364       }
6365     }
6366     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6367     {
6368       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6369
6370       if (e_belt_nr == belt_nr)
6371       {
6372         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6373
6374         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6375         TEST_DrawLevelField(xx, yy);
6376       }
6377     }
6378   }
6379 }
6380
6381 static void ToggleSwitchgateSwitch(int x, int y)
6382 {
6383   int xx, yy;
6384
6385   game.switchgate_pos = !game.switchgate_pos;
6386
6387   SCAN_PLAYFIELD(xx, yy)
6388   {
6389     int element = Tile[xx][yy];
6390
6391     if (element == EL_SWITCHGATE_SWITCH_UP)
6392     {
6393       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6394       TEST_DrawLevelField(xx, yy);
6395     }
6396     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6397     {
6398       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6399       TEST_DrawLevelField(xx, yy);
6400     }
6401     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6402     {
6403       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6404       TEST_DrawLevelField(xx, yy);
6405     }
6406     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6407     {
6408       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6409       TEST_DrawLevelField(xx, yy);
6410     }
6411     else if (element == EL_SWITCHGATE_OPEN ||
6412              element == EL_SWITCHGATE_OPENING)
6413     {
6414       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6415
6416       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6417     }
6418     else if (element == EL_SWITCHGATE_CLOSED ||
6419              element == EL_SWITCHGATE_CLOSING)
6420     {
6421       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6422
6423       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6424     }
6425   }
6426 }
6427
6428 static int getInvisibleActiveFromInvisibleElement(int element)
6429 {
6430   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6431           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6432           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6433           element);
6434 }
6435
6436 static int getInvisibleFromInvisibleActiveElement(int element)
6437 {
6438   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6439           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6440           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6441           element);
6442 }
6443
6444 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6445 {
6446   int x, y;
6447
6448   SCAN_PLAYFIELD(x, y)
6449   {
6450     int element = Tile[x][y];
6451
6452     if (element == EL_LIGHT_SWITCH &&
6453         game.light_time_left > 0)
6454     {
6455       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6456       TEST_DrawLevelField(x, y);
6457     }
6458     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6459              game.light_time_left == 0)
6460     {
6461       Tile[x][y] = EL_LIGHT_SWITCH;
6462       TEST_DrawLevelField(x, y);
6463     }
6464     else if (element == EL_EMC_DRIPPER &&
6465              game.light_time_left > 0)
6466     {
6467       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6468       TEST_DrawLevelField(x, y);
6469     }
6470     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6471              game.light_time_left == 0)
6472     {
6473       Tile[x][y] = EL_EMC_DRIPPER;
6474       TEST_DrawLevelField(x, y);
6475     }
6476     else if (element == EL_INVISIBLE_STEELWALL ||
6477              element == EL_INVISIBLE_WALL ||
6478              element == EL_INVISIBLE_SAND)
6479     {
6480       if (game.light_time_left > 0)
6481         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6482
6483       TEST_DrawLevelField(x, y);
6484
6485       // uncrumble neighbour fields, if needed
6486       if (element == EL_INVISIBLE_SAND)
6487         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6488     }
6489     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6490              element == EL_INVISIBLE_WALL_ACTIVE ||
6491              element == EL_INVISIBLE_SAND_ACTIVE)
6492     {
6493       if (game.light_time_left == 0)
6494         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6495
6496       TEST_DrawLevelField(x, y);
6497
6498       // re-crumble neighbour fields, if needed
6499       if (element == EL_INVISIBLE_SAND)
6500         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6501     }
6502   }
6503 }
6504
6505 static void RedrawAllInvisibleElementsForLenses(void)
6506 {
6507   int x, y;
6508
6509   SCAN_PLAYFIELD(x, y)
6510   {
6511     int element = Tile[x][y];
6512
6513     if (element == EL_EMC_DRIPPER &&
6514         game.lenses_time_left > 0)
6515     {
6516       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6517       TEST_DrawLevelField(x, y);
6518     }
6519     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6520              game.lenses_time_left == 0)
6521     {
6522       Tile[x][y] = EL_EMC_DRIPPER;
6523       TEST_DrawLevelField(x, y);
6524     }
6525     else if (element == EL_INVISIBLE_STEELWALL ||
6526              element == EL_INVISIBLE_WALL ||
6527              element == EL_INVISIBLE_SAND)
6528     {
6529       if (game.lenses_time_left > 0)
6530         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6531
6532       TEST_DrawLevelField(x, y);
6533
6534       // uncrumble neighbour fields, if needed
6535       if (element == EL_INVISIBLE_SAND)
6536         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6537     }
6538     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6539              element == EL_INVISIBLE_WALL_ACTIVE ||
6540              element == EL_INVISIBLE_SAND_ACTIVE)
6541     {
6542       if (game.lenses_time_left == 0)
6543         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6544
6545       TEST_DrawLevelField(x, y);
6546
6547       // re-crumble neighbour fields, if needed
6548       if (element == EL_INVISIBLE_SAND)
6549         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6550     }
6551   }
6552 }
6553
6554 static void RedrawAllInvisibleElementsForMagnifier(void)
6555 {
6556   int x, y;
6557
6558   SCAN_PLAYFIELD(x, y)
6559   {
6560     int element = Tile[x][y];
6561
6562     if (element == EL_EMC_FAKE_GRASS &&
6563         game.magnify_time_left > 0)
6564     {
6565       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6566       TEST_DrawLevelField(x, y);
6567     }
6568     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6569              game.magnify_time_left == 0)
6570     {
6571       Tile[x][y] = EL_EMC_FAKE_GRASS;
6572       TEST_DrawLevelField(x, y);
6573     }
6574     else if (IS_GATE_GRAY(element) &&
6575              game.magnify_time_left > 0)
6576     {
6577       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6578                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6579                     IS_EM_GATE_GRAY(element) ?
6580                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6581                     IS_EMC_GATE_GRAY(element) ?
6582                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6583                     IS_DC_GATE_GRAY(element) ?
6584                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6585                     element);
6586       TEST_DrawLevelField(x, y);
6587     }
6588     else if (IS_GATE_GRAY_ACTIVE(element) &&
6589              game.magnify_time_left == 0)
6590     {
6591       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6592                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6593                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6594                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6595                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6596                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6597                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6598                     EL_DC_GATE_WHITE_GRAY :
6599                     element);
6600       TEST_DrawLevelField(x, y);
6601     }
6602   }
6603 }
6604
6605 static void ToggleLightSwitch(int x, int y)
6606 {
6607   int element = Tile[x][y];
6608
6609   game.light_time_left =
6610     (element == EL_LIGHT_SWITCH ?
6611      level.time_light * FRAMES_PER_SECOND : 0);
6612
6613   RedrawAllLightSwitchesAndInvisibleElements();
6614 }
6615
6616 static void ActivateTimegateSwitch(int x, int y)
6617 {
6618   int xx, yy;
6619
6620   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6621
6622   SCAN_PLAYFIELD(xx, yy)
6623   {
6624     int element = Tile[xx][yy];
6625
6626     if (element == EL_TIMEGATE_CLOSED ||
6627         element == EL_TIMEGATE_CLOSING)
6628     {
6629       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6630       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6631     }
6632
6633     /*
6634     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6635     {
6636       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6637       TEST_DrawLevelField(xx, yy);
6638     }
6639     */
6640
6641   }
6642
6643   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6644                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6645 }
6646
6647 static void Impact(int x, int y)
6648 {
6649   boolean last_line = (y == lev_fieldy - 1);
6650   boolean object_hit = FALSE;
6651   boolean impact = (last_line || object_hit);
6652   int element = Tile[x][y];
6653   int smashed = EL_STEELWALL;
6654
6655   if (!last_line)       // check if element below was hit
6656   {
6657     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6658       return;
6659
6660     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6661                                          MovDir[x][y + 1] != MV_DOWN ||
6662                                          MovPos[x][y + 1] <= TILEY / 2));
6663
6664     // do not smash moving elements that left the smashed field in time
6665     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6666         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6667       object_hit = FALSE;
6668
6669 #if USE_QUICKSAND_IMPACT_BUGFIX
6670     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6671     {
6672       RemoveMovingField(x, y + 1);
6673       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6674       Tile[x][y + 2] = EL_ROCK;
6675       TEST_DrawLevelField(x, y + 2);
6676
6677       object_hit = TRUE;
6678     }
6679
6680     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6681     {
6682       RemoveMovingField(x, y + 1);
6683       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6684       Tile[x][y + 2] = EL_ROCK;
6685       TEST_DrawLevelField(x, y + 2);
6686
6687       object_hit = TRUE;
6688     }
6689 #endif
6690
6691     if (object_hit)
6692       smashed = MovingOrBlocked2Element(x, y + 1);
6693
6694     impact = (last_line || object_hit);
6695   }
6696
6697   if (!last_line && smashed == EL_ACID) // element falls into acid
6698   {
6699     SplashAcid(x, y + 1);
6700     return;
6701   }
6702
6703   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6704   // only reset graphic animation if graphic really changes after impact
6705   if (impact &&
6706       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6707   {
6708     ResetGfxAnimation(x, y);
6709     TEST_DrawLevelField(x, y);
6710   }
6711
6712   if (impact && CAN_EXPLODE_IMPACT(element))
6713   {
6714     Bang(x, y);
6715     return;
6716   }
6717   else if (impact && element == EL_PEARL &&
6718            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6719   {
6720     ResetGfxAnimation(x, y);
6721
6722     Tile[x][y] = EL_PEARL_BREAKING;
6723     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6724     return;
6725   }
6726   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6727   {
6728     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6729
6730     return;
6731   }
6732
6733   if (impact && element == EL_AMOEBA_DROP)
6734   {
6735     if (object_hit && IS_PLAYER(x, y + 1))
6736       KillPlayerUnlessEnemyProtected(x, y + 1);
6737     else if (object_hit && smashed == EL_PENGUIN)
6738       Bang(x, y + 1);
6739     else
6740     {
6741       Tile[x][y] = EL_AMOEBA_GROWING;
6742       Store[x][y] = EL_AMOEBA_WET;
6743
6744       ResetRandomAnimationValue(x, y);
6745     }
6746     return;
6747   }
6748
6749   if (object_hit)               // check which object was hit
6750   {
6751     if ((CAN_PASS_MAGIC_WALL(element) && 
6752          (smashed == EL_MAGIC_WALL ||
6753           smashed == EL_BD_MAGIC_WALL)) ||
6754         (CAN_PASS_DC_MAGIC_WALL(element) &&
6755          smashed == EL_DC_MAGIC_WALL))
6756     {
6757       int xx, yy;
6758       int activated_magic_wall =
6759         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6760          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6761          EL_DC_MAGIC_WALL_ACTIVE);
6762
6763       // activate magic wall / mill
6764       SCAN_PLAYFIELD(xx, yy)
6765       {
6766         if (Tile[xx][yy] == smashed)
6767           Tile[xx][yy] = activated_magic_wall;
6768       }
6769
6770       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6771       game.magic_wall_active = TRUE;
6772
6773       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6774                             SND_MAGIC_WALL_ACTIVATING :
6775                             smashed == EL_BD_MAGIC_WALL ?
6776                             SND_BD_MAGIC_WALL_ACTIVATING :
6777                             SND_DC_MAGIC_WALL_ACTIVATING));
6778     }
6779
6780     if (IS_PLAYER(x, y + 1))
6781     {
6782       if (CAN_SMASH_PLAYER(element))
6783       {
6784         KillPlayerUnlessEnemyProtected(x, y + 1);
6785         return;
6786       }
6787     }
6788     else if (smashed == EL_PENGUIN)
6789     {
6790       if (CAN_SMASH_PLAYER(element))
6791       {
6792         Bang(x, y + 1);
6793         return;
6794       }
6795     }
6796     else if (element == EL_BD_DIAMOND)
6797     {
6798       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6799       {
6800         Bang(x, y + 1);
6801         return;
6802       }
6803     }
6804     else if (((element == EL_SP_INFOTRON ||
6805                element == EL_SP_ZONK) &&
6806               (smashed == EL_SP_SNIKSNAK ||
6807                smashed == EL_SP_ELECTRON ||
6808                smashed == EL_SP_DISK_ORANGE)) ||
6809              (element == EL_SP_INFOTRON &&
6810               smashed == EL_SP_DISK_YELLOW))
6811     {
6812       Bang(x, y + 1);
6813       return;
6814     }
6815     else if (CAN_SMASH_EVERYTHING(element))
6816     {
6817       if (IS_CLASSIC_ENEMY(smashed) ||
6818           CAN_EXPLODE_SMASHED(smashed))
6819       {
6820         Bang(x, y + 1);
6821         return;
6822       }
6823       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6824       {
6825         if (smashed == EL_LAMP ||
6826             smashed == EL_LAMP_ACTIVE)
6827         {
6828           Bang(x, y + 1);
6829           return;
6830         }
6831         else if (smashed == EL_NUT)
6832         {
6833           Tile[x][y + 1] = EL_NUT_BREAKING;
6834           PlayLevelSound(x, y, SND_NUT_BREAKING);
6835           RaiseScoreElement(EL_NUT);
6836           return;
6837         }
6838         else if (smashed == EL_PEARL)
6839         {
6840           ResetGfxAnimation(x, y);
6841
6842           Tile[x][y + 1] = EL_PEARL_BREAKING;
6843           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6844           return;
6845         }
6846         else if (smashed == EL_DIAMOND)
6847         {
6848           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6849           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6850           return;
6851         }
6852         else if (IS_BELT_SWITCH(smashed))
6853         {
6854           ToggleBeltSwitch(x, y + 1);
6855         }
6856         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6857                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6858                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6859                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6860         {
6861           ToggleSwitchgateSwitch(x, y + 1);
6862         }
6863         else if (smashed == EL_LIGHT_SWITCH ||
6864                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6865         {
6866           ToggleLightSwitch(x, y + 1);
6867         }
6868         else
6869         {
6870           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6871
6872           CheckElementChangeBySide(x, y + 1, smashed, element,
6873                                    CE_SWITCHED, CH_SIDE_TOP);
6874           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6875                                             CH_SIDE_TOP);
6876         }
6877       }
6878       else
6879       {
6880         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6881       }
6882     }
6883   }
6884
6885   // play sound of magic wall / mill
6886   if (!last_line &&
6887       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6888        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6889        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6890   {
6891     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6892       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6893     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6894       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6895     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6896       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6897
6898     return;
6899   }
6900
6901   // play sound of object that hits the ground
6902   if (last_line || object_hit)
6903     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6904 }
6905
6906 static void TurnRoundExt(int x, int y)
6907 {
6908   static struct
6909   {
6910     int dx, dy;
6911   } move_xy[] =
6912   {
6913     {  0,  0 },
6914     { -1,  0 },
6915     { +1,  0 },
6916     {  0,  0 },
6917     {  0, -1 },
6918     {  0,  0 }, { 0, 0 }, { 0, 0 },
6919     {  0, +1 }
6920   };
6921   static struct
6922   {
6923     int left, right, back;
6924   } turn[] =
6925   {
6926     { 0,        0,              0        },
6927     { MV_DOWN,  MV_UP,          MV_RIGHT },
6928     { MV_UP,    MV_DOWN,        MV_LEFT  },
6929     { 0,        0,              0        },
6930     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6931     { 0,        0,              0        },
6932     { 0,        0,              0        },
6933     { 0,        0,              0        },
6934     { MV_RIGHT, MV_LEFT,        MV_UP    }
6935   };
6936
6937   int element = Tile[x][y];
6938   int move_pattern = element_info[element].move_pattern;
6939
6940   int old_move_dir = MovDir[x][y];
6941   int left_dir  = turn[old_move_dir].left;
6942   int right_dir = turn[old_move_dir].right;
6943   int back_dir  = turn[old_move_dir].back;
6944
6945   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6946   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6947   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6948   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6949
6950   int left_x  = x + left_dx,  left_y  = y + left_dy;
6951   int right_x = x + right_dx, right_y = y + right_dy;
6952   int move_x  = x + move_dx,  move_y  = y + move_dy;
6953
6954   int xx, yy;
6955
6956   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6957   {
6958     TestIfBadThingTouchesOtherBadThing(x, y);
6959
6960     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6961       MovDir[x][y] = right_dir;
6962     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6963       MovDir[x][y] = left_dir;
6964
6965     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6966       MovDelay[x][y] = 9;
6967     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6968       MovDelay[x][y] = 1;
6969   }
6970   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6971   {
6972     TestIfBadThingTouchesOtherBadThing(x, y);
6973
6974     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6975       MovDir[x][y] = left_dir;
6976     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6977       MovDir[x][y] = right_dir;
6978
6979     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6980       MovDelay[x][y] = 9;
6981     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6982       MovDelay[x][y] = 1;
6983   }
6984   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6985   {
6986     TestIfBadThingTouchesOtherBadThing(x, y);
6987
6988     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6989       MovDir[x][y] = left_dir;
6990     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6991       MovDir[x][y] = right_dir;
6992
6993     if (MovDir[x][y] != old_move_dir)
6994       MovDelay[x][y] = 9;
6995   }
6996   else if (element == EL_YAMYAM)
6997   {
6998     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6999     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7000
7001     if (can_turn_left && can_turn_right)
7002       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7003     else if (can_turn_left)
7004       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7005     else if (can_turn_right)
7006       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7007     else
7008       MovDir[x][y] = back_dir;
7009
7010     MovDelay[x][y] = 16 + 16 * RND(3);
7011   }
7012   else if (element == EL_DARK_YAMYAM)
7013   {
7014     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7015                                                          left_x, left_y);
7016     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7017                                                          right_x, right_y);
7018
7019     if (can_turn_left && can_turn_right)
7020       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7021     else if (can_turn_left)
7022       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7023     else if (can_turn_right)
7024       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7025     else
7026       MovDir[x][y] = back_dir;
7027
7028     MovDelay[x][y] = 16 + 16 * RND(3);
7029   }
7030   else if (element == EL_PACMAN)
7031   {
7032     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7033     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7034
7035     if (can_turn_left && can_turn_right)
7036       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7037     else if (can_turn_left)
7038       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7039     else if (can_turn_right)
7040       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7041     else
7042       MovDir[x][y] = back_dir;
7043
7044     MovDelay[x][y] = 6 + RND(40);
7045   }
7046   else if (element == EL_PIG)
7047   {
7048     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7049     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7050     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7051     boolean should_turn_left, should_turn_right, should_move_on;
7052     int rnd_value = 24;
7053     int rnd = RND(rnd_value);
7054
7055     should_turn_left = (can_turn_left &&
7056                         (!can_move_on ||
7057                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7058                                                    y + back_dy + left_dy)));
7059     should_turn_right = (can_turn_right &&
7060                          (!can_move_on ||
7061                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7062                                                     y + back_dy + right_dy)));
7063     should_move_on = (can_move_on &&
7064                       (!can_turn_left ||
7065                        !can_turn_right ||
7066                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7067                                                  y + move_dy + left_dy) ||
7068                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7069                                                  y + move_dy + right_dy)));
7070
7071     if (should_turn_left || should_turn_right || should_move_on)
7072     {
7073       if (should_turn_left && should_turn_right && should_move_on)
7074         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7075                         rnd < 2 * rnd_value / 3 ? right_dir :
7076                         old_move_dir);
7077       else if (should_turn_left && should_turn_right)
7078         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7079       else if (should_turn_left && should_move_on)
7080         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7081       else if (should_turn_right && should_move_on)
7082         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7083       else if (should_turn_left)
7084         MovDir[x][y] = left_dir;
7085       else if (should_turn_right)
7086         MovDir[x][y] = right_dir;
7087       else if (should_move_on)
7088         MovDir[x][y] = old_move_dir;
7089     }
7090     else if (can_move_on && rnd > rnd_value / 8)
7091       MovDir[x][y] = old_move_dir;
7092     else if (can_turn_left && can_turn_right)
7093       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7094     else if (can_turn_left && rnd > rnd_value / 8)
7095       MovDir[x][y] = left_dir;
7096     else if (can_turn_right && rnd > rnd_value/8)
7097       MovDir[x][y] = right_dir;
7098     else
7099       MovDir[x][y] = back_dir;
7100
7101     xx = x + move_xy[MovDir[x][y]].dx;
7102     yy = y + move_xy[MovDir[x][y]].dy;
7103
7104     if (!IN_LEV_FIELD(xx, yy) ||
7105         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7106       MovDir[x][y] = old_move_dir;
7107
7108     MovDelay[x][y] = 0;
7109   }
7110   else if (element == EL_DRAGON)
7111   {
7112     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7113     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7114     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7115     int rnd_value = 24;
7116     int rnd = RND(rnd_value);
7117
7118     if (can_move_on && rnd > rnd_value / 8)
7119       MovDir[x][y] = old_move_dir;
7120     else if (can_turn_left && can_turn_right)
7121       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7122     else if (can_turn_left && rnd > rnd_value / 8)
7123       MovDir[x][y] = left_dir;
7124     else if (can_turn_right && rnd > rnd_value / 8)
7125       MovDir[x][y] = right_dir;
7126     else
7127       MovDir[x][y] = back_dir;
7128
7129     xx = x + move_xy[MovDir[x][y]].dx;
7130     yy = y + move_xy[MovDir[x][y]].dy;
7131
7132     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7133       MovDir[x][y] = old_move_dir;
7134
7135     MovDelay[x][y] = 0;
7136   }
7137   else if (element == EL_MOLE)
7138   {
7139     boolean can_move_on =
7140       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7141                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7142                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7143     if (!can_move_on)
7144     {
7145       boolean can_turn_left =
7146         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7147                               IS_AMOEBOID(Tile[left_x][left_y])));
7148
7149       boolean can_turn_right =
7150         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7151                               IS_AMOEBOID(Tile[right_x][right_y])));
7152
7153       if (can_turn_left && can_turn_right)
7154         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7155       else if (can_turn_left)
7156         MovDir[x][y] = left_dir;
7157       else
7158         MovDir[x][y] = right_dir;
7159     }
7160
7161     if (MovDir[x][y] != old_move_dir)
7162       MovDelay[x][y] = 9;
7163   }
7164   else if (element == EL_BALLOON)
7165   {
7166     MovDir[x][y] = game.wind_direction;
7167     MovDelay[x][y] = 0;
7168   }
7169   else if (element == EL_SPRING)
7170   {
7171     if (MovDir[x][y] & MV_HORIZONTAL)
7172     {
7173       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7174           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7175       {
7176         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7177         ResetGfxAnimation(move_x, move_y);
7178         TEST_DrawLevelField(move_x, move_y);
7179
7180         MovDir[x][y] = back_dir;
7181       }
7182       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7183                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7184         MovDir[x][y] = MV_NONE;
7185     }
7186
7187     MovDelay[x][y] = 0;
7188   }
7189   else if (element == EL_ROBOT ||
7190            element == EL_SATELLITE ||
7191            element == EL_PENGUIN ||
7192            element == EL_EMC_ANDROID)
7193   {
7194     int attr_x = -1, attr_y = -1;
7195
7196     if (game.all_players_gone)
7197     {
7198       attr_x = game.exit_x;
7199       attr_y = game.exit_y;
7200     }
7201     else
7202     {
7203       int i;
7204
7205       for (i = 0; i < MAX_PLAYERS; i++)
7206       {
7207         struct PlayerInfo *player = &stored_player[i];
7208         int jx = player->jx, jy = player->jy;
7209
7210         if (!player->active)
7211           continue;
7212
7213         if (attr_x == -1 ||
7214             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7215         {
7216           attr_x = jx;
7217           attr_y = jy;
7218         }
7219       }
7220     }
7221
7222     if (element == EL_ROBOT &&
7223         game.robot_wheel_x >= 0 &&
7224         game.robot_wheel_y >= 0 &&
7225         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7226          game.engine_version < VERSION_IDENT(3,1,0,0)))
7227     {
7228       attr_x = game.robot_wheel_x;
7229       attr_y = game.robot_wheel_y;
7230     }
7231
7232     if (element == EL_PENGUIN)
7233     {
7234       int i;
7235       static int xy[4][2] =
7236       {
7237         { 0, -1 },
7238         { -1, 0 },
7239         { +1, 0 },
7240         { 0, +1 }
7241       };
7242
7243       for (i = 0; i < NUM_DIRECTIONS; i++)
7244       {
7245         int ex = x + xy[i][0];
7246         int ey = y + xy[i][1];
7247
7248         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7249                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7250                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7251                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7252         {
7253           attr_x = ex;
7254           attr_y = ey;
7255           break;
7256         }
7257       }
7258     }
7259
7260     MovDir[x][y] = MV_NONE;
7261     if (attr_x < x)
7262       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7263     else if (attr_x > x)
7264       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7265     if (attr_y < y)
7266       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7267     else if (attr_y > y)
7268       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7269
7270     if (element == EL_ROBOT)
7271     {
7272       int newx, newy;
7273
7274       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7275         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7276       Moving2Blocked(x, y, &newx, &newy);
7277
7278       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7279         MovDelay[x][y] = 8 + 8 * !RND(3);
7280       else
7281         MovDelay[x][y] = 16;
7282     }
7283     else if (element == EL_PENGUIN)
7284     {
7285       int newx, newy;
7286
7287       MovDelay[x][y] = 1;
7288
7289       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7290       {
7291         boolean first_horiz = RND(2);
7292         int new_move_dir = MovDir[x][y];
7293
7294         MovDir[x][y] =
7295           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7296         Moving2Blocked(x, y, &newx, &newy);
7297
7298         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7299           return;
7300
7301         MovDir[x][y] =
7302           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7303         Moving2Blocked(x, y, &newx, &newy);
7304
7305         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7306           return;
7307
7308         MovDir[x][y] = old_move_dir;
7309         return;
7310       }
7311     }
7312     else if (element == EL_SATELLITE)
7313     {
7314       int newx, newy;
7315
7316       MovDelay[x][y] = 1;
7317
7318       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7319       {
7320         boolean first_horiz = RND(2);
7321         int new_move_dir = MovDir[x][y];
7322
7323         MovDir[x][y] =
7324           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7325         Moving2Blocked(x, y, &newx, &newy);
7326
7327         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7328           return;
7329
7330         MovDir[x][y] =
7331           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7332         Moving2Blocked(x, y, &newx, &newy);
7333
7334         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7335           return;
7336
7337         MovDir[x][y] = old_move_dir;
7338         return;
7339       }
7340     }
7341     else if (element == EL_EMC_ANDROID)
7342     {
7343       static int check_pos[16] =
7344       {
7345         -1,             //  0 => (invalid)
7346         7,              //  1 => MV_LEFT
7347         3,              //  2 => MV_RIGHT
7348         -1,             //  3 => (invalid)
7349         1,              //  4 =>            MV_UP
7350         0,              //  5 => MV_LEFT  | MV_UP
7351         2,              //  6 => MV_RIGHT | MV_UP
7352         -1,             //  7 => (invalid)
7353         5,              //  8 =>            MV_DOWN
7354         6,              //  9 => MV_LEFT  | MV_DOWN
7355         4,              // 10 => MV_RIGHT | MV_DOWN
7356         -1,             // 11 => (invalid)
7357         -1,             // 12 => (invalid)
7358         -1,             // 13 => (invalid)
7359         -1,             // 14 => (invalid)
7360         -1,             // 15 => (invalid)
7361       };
7362       static struct
7363       {
7364         int dx, dy;
7365         int dir;
7366       } check_xy[8] =
7367       {
7368         { -1, -1,       MV_LEFT  | MV_UP   },
7369         {  0, -1,                  MV_UP   },
7370         { +1, -1,       MV_RIGHT | MV_UP   },
7371         { +1,  0,       MV_RIGHT           },
7372         { +1, +1,       MV_RIGHT | MV_DOWN },
7373         {  0, +1,                  MV_DOWN },
7374         { -1, +1,       MV_LEFT  | MV_DOWN },
7375         { -1,  0,       MV_LEFT            },
7376       };
7377       int start_pos, check_order;
7378       boolean can_clone = FALSE;
7379       int i;
7380
7381       // check if there is any free field around current position
7382       for (i = 0; i < 8; i++)
7383       {
7384         int newx = x + check_xy[i].dx;
7385         int newy = y + check_xy[i].dy;
7386
7387         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7388         {
7389           can_clone = TRUE;
7390
7391           break;
7392         }
7393       }
7394
7395       if (can_clone)            // randomly find an element to clone
7396       {
7397         can_clone = FALSE;
7398
7399         start_pos = check_pos[RND(8)];
7400         check_order = (RND(2) ? -1 : +1);
7401
7402         for (i = 0; i < 8; i++)
7403         {
7404           int pos_raw = start_pos + i * check_order;
7405           int pos = (pos_raw + 8) % 8;
7406           int newx = x + check_xy[pos].dx;
7407           int newy = y + check_xy[pos].dy;
7408
7409           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7410           {
7411             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7412             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7413
7414             Store[x][y] = Tile[newx][newy];
7415
7416             can_clone = TRUE;
7417
7418             break;
7419           }
7420         }
7421       }
7422
7423       if (can_clone)            // randomly find a direction to move
7424       {
7425         can_clone = FALSE;
7426
7427         start_pos = check_pos[RND(8)];
7428         check_order = (RND(2) ? -1 : +1);
7429
7430         for (i = 0; i < 8; i++)
7431         {
7432           int pos_raw = start_pos + i * check_order;
7433           int pos = (pos_raw + 8) % 8;
7434           int newx = x + check_xy[pos].dx;
7435           int newy = y + check_xy[pos].dy;
7436           int new_move_dir = check_xy[pos].dir;
7437
7438           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7439           {
7440             MovDir[x][y] = new_move_dir;
7441             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7442
7443             can_clone = TRUE;
7444
7445             break;
7446           }
7447         }
7448       }
7449
7450       if (can_clone)            // cloning and moving successful
7451         return;
7452
7453       // cannot clone -- try to move towards player
7454
7455       start_pos = check_pos[MovDir[x][y] & 0x0f];
7456       check_order = (RND(2) ? -1 : +1);
7457
7458       for (i = 0; i < 3; i++)
7459       {
7460         // first check start_pos, then previous/next or (next/previous) pos
7461         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7462         int pos = (pos_raw + 8) % 8;
7463         int newx = x + check_xy[pos].dx;
7464         int newy = y + check_xy[pos].dy;
7465         int new_move_dir = check_xy[pos].dir;
7466
7467         if (IS_PLAYER(newx, newy))
7468           break;
7469
7470         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7471         {
7472           MovDir[x][y] = new_move_dir;
7473           MovDelay[x][y] = level.android_move_time * 8 + 1;
7474
7475           break;
7476         }
7477       }
7478     }
7479   }
7480   else if (move_pattern == MV_TURNING_LEFT ||
7481            move_pattern == MV_TURNING_RIGHT ||
7482            move_pattern == MV_TURNING_LEFT_RIGHT ||
7483            move_pattern == MV_TURNING_RIGHT_LEFT ||
7484            move_pattern == MV_TURNING_RANDOM ||
7485            move_pattern == MV_ALL_DIRECTIONS)
7486   {
7487     boolean can_turn_left =
7488       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7489     boolean can_turn_right =
7490       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7491
7492     if (element_info[element].move_stepsize == 0)       // "not moving"
7493       return;
7494
7495     if (move_pattern == MV_TURNING_LEFT)
7496       MovDir[x][y] = left_dir;
7497     else if (move_pattern == MV_TURNING_RIGHT)
7498       MovDir[x][y] = right_dir;
7499     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7500       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7501     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7502       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7503     else if (move_pattern == MV_TURNING_RANDOM)
7504       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7505                       can_turn_right && !can_turn_left ? right_dir :
7506                       RND(2) ? left_dir : right_dir);
7507     else if (can_turn_left && can_turn_right)
7508       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7509     else if (can_turn_left)
7510       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7511     else if (can_turn_right)
7512       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7513     else
7514       MovDir[x][y] = back_dir;
7515
7516     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7517   }
7518   else if (move_pattern == MV_HORIZONTAL ||
7519            move_pattern == MV_VERTICAL)
7520   {
7521     if (move_pattern & old_move_dir)
7522       MovDir[x][y] = back_dir;
7523     else if (move_pattern == MV_HORIZONTAL)
7524       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7525     else if (move_pattern == MV_VERTICAL)
7526       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7527
7528     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7529   }
7530   else if (move_pattern & MV_ANY_DIRECTION)
7531   {
7532     MovDir[x][y] = move_pattern;
7533     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7534   }
7535   else if (move_pattern & MV_WIND_DIRECTION)
7536   {
7537     MovDir[x][y] = game.wind_direction;
7538     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7539   }
7540   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7541   {
7542     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7543       MovDir[x][y] = left_dir;
7544     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7545       MovDir[x][y] = right_dir;
7546
7547     if (MovDir[x][y] != old_move_dir)
7548       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7549   }
7550   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7551   {
7552     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7553       MovDir[x][y] = right_dir;
7554     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7555       MovDir[x][y] = left_dir;
7556
7557     if (MovDir[x][y] != old_move_dir)
7558       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7559   }
7560   else if (move_pattern == MV_TOWARDS_PLAYER ||
7561            move_pattern == MV_AWAY_FROM_PLAYER)
7562   {
7563     int attr_x = -1, attr_y = -1;
7564     int newx, newy;
7565     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7566
7567     if (game.all_players_gone)
7568     {
7569       attr_x = game.exit_x;
7570       attr_y = game.exit_y;
7571     }
7572     else
7573     {
7574       int i;
7575
7576       for (i = 0; i < MAX_PLAYERS; i++)
7577       {
7578         struct PlayerInfo *player = &stored_player[i];
7579         int jx = player->jx, jy = player->jy;
7580
7581         if (!player->active)
7582           continue;
7583
7584         if (attr_x == -1 ||
7585             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7586         {
7587           attr_x = jx;
7588           attr_y = jy;
7589         }
7590       }
7591     }
7592
7593     MovDir[x][y] = MV_NONE;
7594     if (attr_x < x)
7595       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7596     else if (attr_x > x)
7597       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7598     if (attr_y < y)
7599       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7600     else if (attr_y > y)
7601       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7602
7603     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7604
7605     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7606     {
7607       boolean first_horiz = RND(2);
7608       int new_move_dir = MovDir[x][y];
7609
7610       if (element_info[element].move_stepsize == 0)     // "not moving"
7611       {
7612         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7613         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7614
7615         return;
7616       }
7617
7618       MovDir[x][y] =
7619         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7620       Moving2Blocked(x, y, &newx, &newy);
7621
7622       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7623         return;
7624
7625       MovDir[x][y] =
7626         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7627       Moving2Blocked(x, y, &newx, &newy);
7628
7629       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7630         return;
7631
7632       MovDir[x][y] = old_move_dir;
7633     }
7634   }
7635   else if (move_pattern == MV_WHEN_PUSHED ||
7636            move_pattern == MV_WHEN_DROPPED)
7637   {
7638     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7639       MovDir[x][y] = MV_NONE;
7640
7641     MovDelay[x][y] = 0;
7642   }
7643   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7644   {
7645     static int test_xy[7][2] =
7646     {
7647       { 0, -1 },
7648       { -1, 0 },
7649       { +1, 0 },
7650       { 0, +1 },
7651       { 0, -1 },
7652       { -1, 0 },
7653       { +1, 0 },
7654     };
7655     static int test_dir[7] =
7656     {
7657       MV_UP,
7658       MV_LEFT,
7659       MV_RIGHT,
7660       MV_DOWN,
7661       MV_UP,
7662       MV_LEFT,
7663       MV_RIGHT,
7664     };
7665     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7666     int move_preference = -1000000;     // start with very low preference
7667     int new_move_dir = MV_NONE;
7668     int start_test = RND(4);
7669     int i;
7670
7671     for (i = 0; i < NUM_DIRECTIONS; i++)
7672     {
7673       int move_dir = test_dir[start_test + i];
7674       int move_dir_preference;
7675
7676       xx = x + test_xy[start_test + i][0];
7677       yy = y + test_xy[start_test + i][1];
7678
7679       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7680           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7681       {
7682         new_move_dir = move_dir;
7683
7684         break;
7685       }
7686
7687       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7688         continue;
7689
7690       move_dir_preference = -1 * RunnerVisit[xx][yy];
7691       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7692         move_dir_preference = PlayerVisit[xx][yy];
7693
7694       if (move_dir_preference > move_preference)
7695       {
7696         // prefer field that has not been visited for the longest time
7697         move_preference = move_dir_preference;
7698         new_move_dir = move_dir;
7699       }
7700       else if (move_dir_preference == move_preference &&
7701                move_dir == old_move_dir)
7702       {
7703         // prefer last direction when all directions are preferred equally
7704         move_preference = move_dir_preference;
7705         new_move_dir = move_dir;
7706       }
7707     }
7708
7709     MovDir[x][y] = new_move_dir;
7710     if (old_move_dir != new_move_dir)
7711       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7712   }
7713 }
7714
7715 static void TurnRound(int x, int y)
7716 {
7717   int direction = MovDir[x][y];
7718
7719   TurnRoundExt(x, y);
7720
7721   GfxDir[x][y] = MovDir[x][y];
7722
7723   if (direction != MovDir[x][y])
7724     GfxFrame[x][y] = 0;
7725
7726   if (MovDelay[x][y])
7727     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7728
7729   ResetGfxFrame(x, y);
7730 }
7731
7732 static boolean JustBeingPushed(int x, int y)
7733 {
7734   int i;
7735
7736   for (i = 0; i < MAX_PLAYERS; i++)
7737   {
7738     struct PlayerInfo *player = &stored_player[i];
7739
7740     if (player->active && player->is_pushing && player->MovPos)
7741     {
7742       int next_jx = player->jx + (player->jx - player->last_jx);
7743       int next_jy = player->jy + (player->jy - player->last_jy);
7744
7745       if (x == next_jx && y == next_jy)
7746         return TRUE;
7747     }
7748   }
7749
7750   return FALSE;
7751 }
7752
7753 static void StartMoving(int x, int y)
7754 {
7755   boolean started_moving = FALSE;       // some elements can fall _and_ move
7756   int element = Tile[x][y];
7757
7758   if (Stop[x][y])
7759     return;
7760
7761   if (MovDelay[x][y] == 0)
7762     GfxAction[x][y] = ACTION_DEFAULT;
7763
7764   if (CAN_FALL(element) && y < lev_fieldy - 1)
7765   {
7766     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7767         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7768       if (JustBeingPushed(x, y))
7769         return;
7770
7771     if (element == EL_QUICKSAND_FULL)
7772     {
7773       if (IS_FREE(x, y + 1))
7774       {
7775         InitMovingField(x, y, MV_DOWN);
7776         started_moving = TRUE;
7777
7778         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7779 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7780         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7781           Store[x][y] = EL_ROCK;
7782 #else
7783         Store[x][y] = EL_ROCK;
7784 #endif
7785
7786         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7787       }
7788       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7789       {
7790         if (!MovDelay[x][y])
7791         {
7792           MovDelay[x][y] = TILEY + 1;
7793
7794           ResetGfxAnimation(x, y);
7795           ResetGfxAnimation(x, y + 1);
7796         }
7797
7798         if (MovDelay[x][y])
7799         {
7800           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7801           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7802
7803           MovDelay[x][y]--;
7804           if (MovDelay[x][y])
7805             return;
7806         }
7807
7808         Tile[x][y] = EL_QUICKSAND_EMPTY;
7809         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7810         Store[x][y + 1] = Store[x][y];
7811         Store[x][y] = 0;
7812
7813         PlayLevelSoundAction(x, y, ACTION_FILLING);
7814       }
7815       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7816       {
7817         if (!MovDelay[x][y])
7818         {
7819           MovDelay[x][y] = TILEY + 1;
7820
7821           ResetGfxAnimation(x, y);
7822           ResetGfxAnimation(x, y + 1);
7823         }
7824
7825         if (MovDelay[x][y])
7826         {
7827           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7828           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7829
7830           MovDelay[x][y]--;
7831           if (MovDelay[x][y])
7832             return;
7833         }
7834
7835         Tile[x][y] = EL_QUICKSAND_EMPTY;
7836         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7837         Store[x][y + 1] = Store[x][y];
7838         Store[x][y] = 0;
7839
7840         PlayLevelSoundAction(x, y, ACTION_FILLING);
7841       }
7842     }
7843     else if (element == EL_QUICKSAND_FAST_FULL)
7844     {
7845       if (IS_FREE(x, y + 1))
7846       {
7847         InitMovingField(x, y, MV_DOWN);
7848         started_moving = TRUE;
7849
7850         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7851 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7852         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7853           Store[x][y] = EL_ROCK;
7854 #else
7855         Store[x][y] = EL_ROCK;
7856 #endif
7857
7858         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7859       }
7860       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7861       {
7862         if (!MovDelay[x][y])
7863         {
7864           MovDelay[x][y] = TILEY + 1;
7865
7866           ResetGfxAnimation(x, y);
7867           ResetGfxAnimation(x, y + 1);
7868         }
7869
7870         if (MovDelay[x][y])
7871         {
7872           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7873           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7874
7875           MovDelay[x][y]--;
7876           if (MovDelay[x][y])
7877             return;
7878         }
7879
7880         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7881         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7882         Store[x][y + 1] = Store[x][y];
7883         Store[x][y] = 0;
7884
7885         PlayLevelSoundAction(x, y, ACTION_FILLING);
7886       }
7887       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7888       {
7889         if (!MovDelay[x][y])
7890         {
7891           MovDelay[x][y] = TILEY + 1;
7892
7893           ResetGfxAnimation(x, y);
7894           ResetGfxAnimation(x, y + 1);
7895         }
7896
7897         if (MovDelay[x][y])
7898         {
7899           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7900           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7901
7902           MovDelay[x][y]--;
7903           if (MovDelay[x][y])
7904             return;
7905         }
7906
7907         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7908         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7909         Store[x][y + 1] = Store[x][y];
7910         Store[x][y] = 0;
7911
7912         PlayLevelSoundAction(x, y, ACTION_FILLING);
7913       }
7914     }
7915     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7916              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7917     {
7918       InitMovingField(x, y, MV_DOWN);
7919       started_moving = TRUE;
7920
7921       Tile[x][y] = EL_QUICKSAND_FILLING;
7922       Store[x][y] = element;
7923
7924       PlayLevelSoundAction(x, y, ACTION_FILLING);
7925     }
7926     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7927              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7928     {
7929       InitMovingField(x, y, MV_DOWN);
7930       started_moving = TRUE;
7931
7932       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7933       Store[x][y] = element;
7934
7935       PlayLevelSoundAction(x, y, ACTION_FILLING);
7936     }
7937     else if (element == EL_MAGIC_WALL_FULL)
7938     {
7939       if (IS_FREE(x, y + 1))
7940       {
7941         InitMovingField(x, y, MV_DOWN);
7942         started_moving = TRUE;
7943
7944         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7945         Store[x][y] = EL_CHANGED(Store[x][y]);
7946       }
7947       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7948       {
7949         if (!MovDelay[x][y])
7950           MovDelay[x][y] = TILEY / 4 + 1;
7951
7952         if (MovDelay[x][y])
7953         {
7954           MovDelay[x][y]--;
7955           if (MovDelay[x][y])
7956             return;
7957         }
7958
7959         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7960         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7961         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7962         Store[x][y] = 0;
7963       }
7964     }
7965     else if (element == EL_BD_MAGIC_WALL_FULL)
7966     {
7967       if (IS_FREE(x, y + 1))
7968       {
7969         InitMovingField(x, y, MV_DOWN);
7970         started_moving = TRUE;
7971
7972         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7973         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7974       }
7975       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7976       {
7977         if (!MovDelay[x][y])
7978           MovDelay[x][y] = TILEY / 4 + 1;
7979
7980         if (MovDelay[x][y])
7981         {
7982           MovDelay[x][y]--;
7983           if (MovDelay[x][y])
7984             return;
7985         }
7986
7987         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7988         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7989         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7990         Store[x][y] = 0;
7991       }
7992     }
7993     else if (element == EL_DC_MAGIC_WALL_FULL)
7994     {
7995       if (IS_FREE(x, y + 1))
7996       {
7997         InitMovingField(x, y, MV_DOWN);
7998         started_moving = TRUE;
7999
8000         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8001         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8002       }
8003       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8004       {
8005         if (!MovDelay[x][y])
8006           MovDelay[x][y] = TILEY / 4 + 1;
8007
8008         if (MovDelay[x][y])
8009         {
8010           MovDelay[x][y]--;
8011           if (MovDelay[x][y])
8012             return;
8013         }
8014
8015         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8016         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8017         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8018         Store[x][y] = 0;
8019       }
8020     }
8021     else if ((CAN_PASS_MAGIC_WALL(element) &&
8022               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8023                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8024              (CAN_PASS_DC_MAGIC_WALL(element) &&
8025               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8026
8027     {
8028       InitMovingField(x, y, MV_DOWN);
8029       started_moving = TRUE;
8030
8031       Tile[x][y] =
8032         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8033          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8034          EL_DC_MAGIC_WALL_FILLING);
8035       Store[x][y] = element;
8036     }
8037     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8038     {
8039       SplashAcid(x, y + 1);
8040
8041       InitMovingField(x, y, MV_DOWN);
8042       started_moving = TRUE;
8043
8044       Store[x][y] = EL_ACID;
8045     }
8046     else if (
8047              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8048               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8049              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8050               CAN_FALL(element) && WasJustFalling[x][y] &&
8051               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8052
8053              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8054               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8055               (Tile[x][y + 1] == EL_BLOCKED)))
8056     {
8057       /* this is needed for a special case not covered by calling "Impact()"
8058          from "ContinueMoving()": if an element moves to a tile directly below
8059          another element which was just falling on that tile (which was empty
8060          in the previous frame), the falling element above would just stop
8061          instead of smashing the element below (in previous version, the above
8062          element was just checked for "moving" instead of "falling", resulting
8063          in incorrect smashes caused by horizontal movement of the above
8064          element; also, the case of the player being the element to smash was
8065          simply not covered here... :-/ ) */
8066
8067       CheckCollision[x][y] = 0;
8068       CheckImpact[x][y] = 0;
8069
8070       Impact(x, y);
8071     }
8072     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8073     {
8074       if (MovDir[x][y] == MV_NONE)
8075       {
8076         InitMovingField(x, y, MV_DOWN);
8077         started_moving = TRUE;
8078       }
8079     }
8080     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8081     {
8082       if (WasJustFalling[x][y]) // prevent animation from being restarted
8083         MovDir[x][y] = MV_DOWN;
8084
8085       InitMovingField(x, y, MV_DOWN);
8086       started_moving = TRUE;
8087     }
8088     else if (element == EL_AMOEBA_DROP)
8089     {
8090       Tile[x][y] = EL_AMOEBA_GROWING;
8091       Store[x][y] = EL_AMOEBA_WET;
8092     }
8093     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8094               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8095              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8096              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8097     {
8098       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8099                                 (IS_FREE(x - 1, y + 1) ||
8100                                  Tile[x - 1][y + 1] == EL_ACID));
8101       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8102                                 (IS_FREE(x + 1, y + 1) ||
8103                                  Tile[x + 1][y + 1] == EL_ACID));
8104       boolean can_fall_any  = (can_fall_left || can_fall_right);
8105       boolean can_fall_both = (can_fall_left && can_fall_right);
8106       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8107
8108       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8109       {
8110         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8111           can_fall_right = FALSE;
8112         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8113           can_fall_left = FALSE;
8114         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8115           can_fall_right = FALSE;
8116         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8117           can_fall_left = FALSE;
8118
8119         can_fall_any  = (can_fall_left || can_fall_right);
8120         can_fall_both = FALSE;
8121       }
8122
8123       if (can_fall_both)
8124       {
8125         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8126           can_fall_right = FALSE;       // slip down on left side
8127         else
8128           can_fall_left = !(can_fall_right = RND(2));
8129
8130         can_fall_both = FALSE;
8131       }
8132
8133       if (can_fall_any)
8134       {
8135         // if not determined otherwise, prefer left side for slipping down
8136         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8137         started_moving = TRUE;
8138       }
8139     }
8140     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8141     {
8142       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8143       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8144       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8145       int belt_dir = game.belt_dir[belt_nr];
8146
8147       if ((belt_dir == MV_LEFT  && left_is_free) ||
8148           (belt_dir == MV_RIGHT && right_is_free))
8149       {
8150         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8151
8152         InitMovingField(x, y, belt_dir);
8153         started_moving = TRUE;
8154
8155         Pushed[x][y] = TRUE;
8156         Pushed[nextx][y] = TRUE;
8157
8158         GfxAction[x][y] = ACTION_DEFAULT;
8159       }
8160       else
8161       {
8162         MovDir[x][y] = 0;       // if element was moving, stop it
8163       }
8164     }
8165   }
8166
8167   // not "else if" because of elements that can fall and move (EL_SPRING)
8168   if (CAN_MOVE(element) && !started_moving)
8169   {
8170     int move_pattern = element_info[element].move_pattern;
8171     int newx, newy;
8172
8173     Moving2Blocked(x, y, &newx, &newy);
8174
8175     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8176       return;
8177
8178     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8179         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8180     {
8181       WasJustMoving[x][y] = 0;
8182       CheckCollision[x][y] = 0;
8183
8184       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8185
8186       if (Tile[x][y] != element)        // element has changed
8187         return;
8188     }
8189
8190     if (!MovDelay[x][y])        // start new movement phase
8191     {
8192       // all objects that can change their move direction after each step
8193       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8194
8195       if (element != EL_YAMYAM &&
8196           element != EL_DARK_YAMYAM &&
8197           element != EL_PACMAN &&
8198           !(move_pattern & MV_ANY_DIRECTION) &&
8199           move_pattern != MV_TURNING_LEFT &&
8200           move_pattern != MV_TURNING_RIGHT &&
8201           move_pattern != MV_TURNING_LEFT_RIGHT &&
8202           move_pattern != MV_TURNING_RIGHT_LEFT &&
8203           move_pattern != MV_TURNING_RANDOM)
8204       {
8205         TurnRound(x, y);
8206
8207         if (MovDelay[x][y] && (element == EL_BUG ||
8208                                element == EL_SPACESHIP ||
8209                                element == EL_SP_SNIKSNAK ||
8210                                element == EL_SP_ELECTRON ||
8211                                element == EL_MOLE))
8212           TEST_DrawLevelField(x, y);
8213       }
8214     }
8215
8216     if (MovDelay[x][y])         // wait some time before next movement
8217     {
8218       MovDelay[x][y]--;
8219
8220       if (element == EL_ROBOT ||
8221           element == EL_YAMYAM ||
8222           element == EL_DARK_YAMYAM)
8223       {
8224         DrawLevelElementAnimationIfNeeded(x, y, element);
8225         PlayLevelSoundAction(x, y, ACTION_WAITING);
8226       }
8227       else if (element == EL_SP_ELECTRON)
8228         DrawLevelElementAnimationIfNeeded(x, y, element);
8229       else if (element == EL_DRAGON)
8230       {
8231         int i;
8232         int dir = MovDir[x][y];
8233         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8234         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8235         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8236                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8237                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8238                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8239         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8240
8241         GfxAction[x][y] = ACTION_ATTACKING;
8242
8243         if (IS_PLAYER(x, y))
8244           DrawPlayerField(x, y);
8245         else
8246           TEST_DrawLevelField(x, y);
8247
8248         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8249
8250         for (i = 1; i <= 3; i++)
8251         {
8252           int xx = x + i * dx;
8253           int yy = y + i * dy;
8254           int sx = SCREENX(xx);
8255           int sy = SCREENY(yy);
8256           int flame_graphic = graphic + (i - 1);
8257
8258           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8259             break;
8260
8261           if (MovDelay[x][y])
8262           {
8263             int flamed = MovingOrBlocked2Element(xx, yy);
8264
8265             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8266               Bang(xx, yy);
8267             else
8268               RemoveMovingField(xx, yy);
8269
8270             ChangeDelay[xx][yy] = 0;
8271
8272             Tile[xx][yy] = EL_FLAMES;
8273
8274             if (IN_SCR_FIELD(sx, sy))
8275             {
8276               TEST_DrawLevelFieldCrumbled(xx, yy);
8277               DrawGraphic(sx, sy, flame_graphic, frame);
8278             }
8279           }
8280           else
8281           {
8282             if (Tile[xx][yy] == EL_FLAMES)
8283               Tile[xx][yy] = EL_EMPTY;
8284             TEST_DrawLevelField(xx, yy);
8285           }
8286         }
8287       }
8288
8289       if (MovDelay[x][y])       // element still has to wait some time
8290       {
8291         PlayLevelSoundAction(x, y, ACTION_WAITING);
8292
8293         return;
8294       }
8295     }
8296
8297     // now make next step
8298
8299     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8300
8301     if (DONT_COLLIDE_WITH(element) &&
8302         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8303         !PLAYER_ENEMY_PROTECTED(newx, newy))
8304     {
8305       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8306
8307       return;
8308     }
8309
8310     else if (CAN_MOVE_INTO_ACID(element) &&
8311              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8312              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8313              (MovDir[x][y] == MV_DOWN ||
8314               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8315     {
8316       SplashAcid(newx, newy);
8317       Store[x][y] = EL_ACID;
8318     }
8319     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8320     {
8321       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8322           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8323           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8324           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8325       {
8326         RemoveField(x, y);
8327         TEST_DrawLevelField(x, y);
8328
8329         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8330         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8331           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8332
8333         game.friends_still_needed--;
8334         if (!game.friends_still_needed &&
8335             !game.GameOver &&
8336             game.all_players_gone)
8337           LevelSolved();
8338
8339         return;
8340       }
8341       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8342       {
8343         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8344           TEST_DrawLevelField(newx, newy);
8345         else
8346           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8347       }
8348       else if (!IS_FREE(newx, newy))
8349       {
8350         GfxAction[x][y] = ACTION_WAITING;
8351
8352         if (IS_PLAYER(x, y))
8353           DrawPlayerField(x, y);
8354         else
8355           TEST_DrawLevelField(x, y);
8356
8357         return;
8358       }
8359     }
8360     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8361     {
8362       if (IS_FOOD_PIG(Tile[newx][newy]))
8363       {
8364         if (IS_MOVING(newx, newy))
8365           RemoveMovingField(newx, newy);
8366         else
8367         {
8368           Tile[newx][newy] = EL_EMPTY;
8369           TEST_DrawLevelField(newx, newy);
8370         }
8371
8372         PlayLevelSound(x, y, SND_PIG_DIGGING);
8373       }
8374       else if (!IS_FREE(newx, newy))
8375       {
8376         if (IS_PLAYER(x, y))
8377           DrawPlayerField(x, y);
8378         else
8379           TEST_DrawLevelField(x, y);
8380
8381         return;
8382       }
8383     }
8384     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8385     {
8386       if (Store[x][y] != EL_EMPTY)
8387       {
8388         boolean can_clone = FALSE;
8389         int xx, yy;
8390
8391         // check if element to clone is still there
8392         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8393         {
8394           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8395           {
8396             can_clone = TRUE;
8397
8398             break;
8399           }
8400         }
8401
8402         // cannot clone or target field not free anymore -- do not clone
8403         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8404           Store[x][y] = EL_EMPTY;
8405       }
8406
8407       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8408       {
8409         if (IS_MV_DIAGONAL(MovDir[x][y]))
8410         {
8411           int diagonal_move_dir = MovDir[x][y];
8412           int stored = Store[x][y];
8413           int change_delay = 8;
8414           int graphic;
8415
8416           // android is moving diagonally
8417
8418           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8419
8420           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8421           GfxElement[x][y] = EL_EMC_ANDROID;
8422           GfxAction[x][y] = ACTION_SHRINKING;
8423           GfxDir[x][y] = diagonal_move_dir;
8424           ChangeDelay[x][y] = change_delay;
8425
8426           if (Store[x][y] == EL_EMPTY)
8427             Store[x][y] = GfxElementEmpty[x][y];
8428
8429           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8430                                    GfxDir[x][y]);
8431
8432           DrawLevelGraphicAnimation(x, y, graphic);
8433           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8434
8435           if (Tile[newx][newy] == EL_ACID)
8436           {
8437             SplashAcid(newx, newy);
8438
8439             return;
8440           }
8441
8442           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8443
8444           Store[newx][newy] = EL_EMC_ANDROID;
8445           GfxElement[newx][newy] = EL_EMC_ANDROID;
8446           GfxAction[newx][newy] = ACTION_GROWING;
8447           GfxDir[newx][newy] = diagonal_move_dir;
8448           ChangeDelay[newx][newy] = change_delay;
8449
8450           graphic = el_act_dir2img(GfxElement[newx][newy],
8451                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8452
8453           DrawLevelGraphicAnimation(newx, newy, graphic);
8454           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8455
8456           return;
8457         }
8458         else
8459         {
8460           Tile[newx][newy] = EL_EMPTY;
8461           TEST_DrawLevelField(newx, newy);
8462
8463           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8464         }
8465       }
8466       else if (!IS_FREE(newx, newy))
8467       {
8468         return;
8469       }
8470     }
8471     else if (IS_CUSTOM_ELEMENT(element) &&
8472              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8473     {
8474       if (!DigFieldByCE(newx, newy, element))
8475         return;
8476
8477       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8478       {
8479         RunnerVisit[x][y] = FrameCounter;
8480         PlayerVisit[x][y] /= 8;         // expire player visit path
8481       }
8482     }
8483     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8484     {
8485       if (!IS_FREE(newx, newy))
8486       {
8487         if (IS_PLAYER(x, y))
8488           DrawPlayerField(x, y);
8489         else
8490           TEST_DrawLevelField(x, y);
8491
8492         return;
8493       }
8494       else
8495       {
8496         boolean wanna_flame = !RND(10);
8497         int dx = newx - x, dy = newy - y;
8498         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8499         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8500         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8501                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8502         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8503                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8504
8505         if ((wanna_flame ||
8506              IS_CLASSIC_ENEMY(element1) ||
8507              IS_CLASSIC_ENEMY(element2)) &&
8508             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8509             element1 != EL_FLAMES && element2 != EL_FLAMES)
8510         {
8511           ResetGfxAnimation(x, y);
8512           GfxAction[x][y] = ACTION_ATTACKING;
8513
8514           if (IS_PLAYER(x, y))
8515             DrawPlayerField(x, y);
8516           else
8517             TEST_DrawLevelField(x, y);
8518
8519           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8520
8521           MovDelay[x][y] = 50;
8522
8523           Tile[newx][newy] = EL_FLAMES;
8524           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8525             Tile[newx1][newy1] = EL_FLAMES;
8526           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8527             Tile[newx2][newy2] = EL_FLAMES;
8528
8529           return;
8530         }
8531       }
8532     }
8533     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8534              Tile[newx][newy] == EL_DIAMOND)
8535     {
8536       if (IS_MOVING(newx, newy))
8537         RemoveMovingField(newx, newy);
8538       else
8539       {
8540         Tile[newx][newy] = EL_EMPTY;
8541         TEST_DrawLevelField(newx, newy);
8542       }
8543
8544       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8545     }
8546     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8547              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8548     {
8549       if (AmoebaNr[newx][newy])
8550       {
8551         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8552         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8553             Tile[newx][newy] == EL_BD_AMOEBA)
8554           AmoebaCnt[AmoebaNr[newx][newy]]--;
8555       }
8556
8557       if (IS_MOVING(newx, newy))
8558       {
8559         RemoveMovingField(newx, newy);
8560       }
8561       else
8562       {
8563         Tile[newx][newy] = EL_EMPTY;
8564         TEST_DrawLevelField(newx, newy);
8565       }
8566
8567       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8568     }
8569     else if ((element == EL_PACMAN || element == EL_MOLE)
8570              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8571     {
8572       if (AmoebaNr[newx][newy])
8573       {
8574         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8575         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8576             Tile[newx][newy] == EL_BD_AMOEBA)
8577           AmoebaCnt[AmoebaNr[newx][newy]]--;
8578       }
8579
8580       if (element == EL_MOLE)
8581       {
8582         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8583         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8584
8585         ResetGfxAnimation(x, y);
8586         GfxAction[x][y] = ACTION_DIGGING;
8587         TEST_DrawLevelField(x, y);
8588
8589         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8590
8591         return;                         // wait for shrinking amoeba
8592       }
8593       else      // element == EL_PACMAN
8594       {
8595         Tile[newx][newy] = EL_EMPTY;
8596         TEST_DrawLevelField(newx, newy);
8597         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8598       }
8599     }
8600     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8601              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8602               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8603     {
8604       // wait for shrinking amoeba to completely disappear
8605       return;
8606     }
8607     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8608     {
8609       // object was running against a wall
8610
8611       TurnRound(x, y);
8612
8613       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8614         DrawLevelElementAnimation(x, y, element);
8615
8616       if (DONT_TOUCH(element))
8617         TestIfBadThingTouchesPlayer(x, y);
8618
8619       return;
8620     }
8621
8622     InitMovingField(x, y, MovDir[x][y]);
8623
8624     PlayLevelSoundAction(x, y, ACTION_MOVING);
8625   }
8626
8627   if (MovDir[x][y])
8628     ContinueMoving(x, y);
8629 }
8630
8631 void ContinueMoving(int x, int y)
8632 {
8633   int element = Tile[x][y];
8634   struct ElementInfo *ei = &element_info[element];
8635   int direction = MovDir[x][y];
8636   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8637   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8638   int newx = x + dx, newy = y + dy;
8639   int stored = Store[x][y];
8640   int stored_new = Store[newx][newy];
8641   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8642   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8643   boolean last_line = (newy == lev_fieldy - 1);
8644   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8645
8646   if (pushed_by_player)         // special case: moving object pushed by player
8647   {
8648     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8649   }
8650   else if (use_step_delay)      // special case: moving object has step delay
8651   {
8652     if (!MovDelay[x][y])
8653       MovPos[x][y] += getElementMoveStepsize(x, y);
8654
8655     if (MovDelay[x][y])
8656       MovDelay[x][y]--;
8657     else
8658       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8659
8660     if (MovDelay[x][y])
8661     {
8662       TEST_DrawLevelField(x, y);
8663
8664       return;   // element is still waiting
8665     }
8666   }
8667   else                          // normal case: generically moving object
8668   {
8669     MovPos[x][y] += getElementMoveStepsize(x, y);
8670   }
8671
8672   if (ABS(MovPos[x][y]) < TILEX)
8673   {
8674     TEST_DrawLevelField(x, y);
8675
8676     return;     // element is still moving
8677   }
8678
8679   // element reached destination field
8680
8681   Tile[x][y] = EL_EMPTY;
8682   Tile[newx][newy] = element;
8683   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8684
8685   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8686   {
8687     element = Tile[newx][newy] = EL_ACID;
8688   }
8689   else if (element == EL_MOLE)
8690   {
8691     Tile[x][y] = EL_SAND;
8692
8693     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8694   }
8695   else if (element == EL_QUICKSAND_FILLING)
8696   {
8697     element = Tile[newx][newy] = get_next_element(element);
8698     Store[newx][newy] = Store[x][y];
8699   }
8700   else if (element == EL_QUICKSAND_EMPTYING)
8701   {
8702     Tile[x][y] = get_next_element(element);
8703     element = Tile[newx][newy] = Store[x][y];
8704   }
8705   else if (element == EL_QUICKSAND_FAST_FILLING)
8706   {
8707     element = Tile[newx][newy] = get_next_element(element);
8708     Store[newx][newy] = Store[x][y];
8709   }
8710   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8711   {
8712     Tile[x][y] = get_next_element(element);
8713     element = Tile[newx][newy] = Store[x][y];
8714   }
8715   else if (element == EL_MAGIC_WALL_FILLING)
8716   {
8717     element = Tile[newx][newy] = get_next_element(element);
8718     if (!game.magic_wall_active)
8719       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8720     Store[newx][newy] = Store[x][y];
8721   }
8722   else if (element == EL_MAGIC_WALL_EMPTYING)
8723   {
8724     Tile[x][y] = get_next_element(element);
8725     if (!game.magic_wall_active)
8726       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8727     element = Tile[newx][newy] = Store[x][y];
8728
8729     InitField(newx, newy, FALSE);
8730   }
8731   else if (element == EL_BD_MAGIC_WALL_FILLING)
8732   {
8733     element = Tile[newx][newy] = get_next_element(element);
8734     if (!game.magic_wall_active)
8735       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8736     Store[newx][newy] = Store[x][y];
8737   }
8738   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8739   {
8740     Tile[x][y] = get_next_element(element);
8741     if (!game.magic_wall_active)
8742       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8743     element = Tile[newx][newy] = Store[x][y];
8744
8745     InitField(newx, newy, FALSE);
8746   }
8747   else if (element == EL_DC_MAGIC_WALL_FILLING)
8748   {
8749     element = Tile[newx][newy] = get_next_element(element);
8750     if (!game.magic_wall_active)
8751       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8752     Store[newx][newy] = Store[x][y];
8753   }
8754   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8755   {
8756     Tile[x][y] = get_next_element(element);
8757     if (!game.magic_wall_active)
8758       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8759     element = Tile[newx][newy] = Store[x][y];
8760
8761     InitField(newx, newy, FALSE);
8762   }
8763   else if (element == EL_AMOEBA_DROPPING)
8764   {
8765     Tile[x][y] = get_next_element(element);
8766     element = Tile[newx][newy] = Store[x][y];
8767   }
8768   else if (element == EL_SOKOBAN_OBJECT)
8769   {
8770     if (Back[x][y])
8771       Tile[x][y] = Back[x][y];
8772
8773     if (Back[newx][newy])
8774       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8775
8776     Back[x][y] = Back[newx][newy] = 0;
8777   }
8778
8779   Store[x][y] = EL_EMPTY;
8780   MovPos[x][y] = 0;
8781   MovDir[x][y] = 0;
8782   MovDelay[x][y] = 0;
8783
8784   MovDelay[newx][newy] = 0;
8785
8786   if (CAN_CHANGE_OR_HAS_ACTION(element))
8787   {
8788     // copy element change control values to new field
8789     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8790     ChangePage[newx][newy]  = ChangePage[x][y];
8791     ChangeCount[newx][newy] = ChangeCount[x][y];
8792     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8793   }
8794
8795   CustomValue[newx][newy] = CustomValue[x][y];
8796
8797   ChangeDelay[x][y] = 0;
8798   ChangePage[x][y] = -1;
8799   ChangeCount[x][y] = 0;
8800   ChangeEvent[x][y] = -1;
8801
8802   CustomValue[x][y] = 0;
8803
8804   // copy animation control values to new field
8805   GfxFrame[newx][newy]  = GfxFrame[x][y];
8806   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8807   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8808   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8809
8810   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8811
8812   // some elements can leave other elements behind after moving
8813   if (ei->move_leave_element != EL_EMPTY &&
8814       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8815       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8816   {
8817     int move_leave_element = ei->move_leave_element;
8818
8819     // this makes it possible to leave the removed element again
8820     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8821       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8822
8823     Tile[x][y] = move_leave_element;
8824
8825     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8826       MovDir[x][y] = direction;
8827
8828     InitField(x, y, FALSE);
8829
8830     if (GFX_CRUMBLED(Tile[x][y]))
8831       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8832
8833     if (IS_PLAYER_ELEMENT(move_leave_element))
8834       RelocatePlayer(x, y, move_leave_element);
8835   }
8836
8837   // do this after checking for left-behind element
8838   ResetGfxAnimation(x, y);      // reset animation values for old field
8839
8840   if (!CAN_MOVE(element) ||
8841       (CAN_FALL(element) && direction == MV_DOWN &&
8842        (element == EL_SPRING ||
8843         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8844         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8845     GfxDir[x][y] = MovDir[newx][newy] = 0;
8846
8847   TEST_DrawLevelField(x, y);
8848   TEST_DrawLevelField(newx, newy);
8849
8850   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8851
8852   // prevent pushed element from moving on in pushed direction
8853   if (pushed_by_player && CAN_MOVE(element) &&
8854       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8855       !(element_info[element].move_pattern & direction))
8856     TurnRound(newx, newy);
8857
8858   // prevent elements on conveyor belt from moving on in last direction
8859   if (pushed_by_conveyor && CAN_FALL(element) &&
8860       direction & MV_HORIZONTAL)
8861     MovDir[newx][newy] = 0;
8862
8863   if (!pushed_by_player)
8864   {
8865     int nextx = newx + dx, nexty = newy + dy;
8866     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8867
8868     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8869
8870     if (CAN_FALL(element) && direction == MV_DOWN)
8871       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8872
8873     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8874       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8875
8876     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8877       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8878   }
8879
8880   if (DONT_TOUCH(element))      // object may be nasty to player or others
8881   {
8882     TestIfBadThingTouchesPlayer(newx, newy);
8883     TestIfBadThingTouchesFriend(newx, newy);
8884
8885     if (!IS_CUSTOM_ELEMENT(element))
8886       TestIfBadThingTouchesOtherBadThing(newx, newy);
8887   }
8888   else if (element == EL_PENGUIN)
8889     TestIfFriendTouchesBadThing(newx, newy);
8890
8891   if (DONT_GET_HIT_BY(element))
8892   {
8893     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8894   }
8895
8896   // give the player one last chance (one more frame) to move away
8897   if (CAN_FALL(element) && direction == MV_DOWN &&
8898       (last_line || (!IS_FREE(x, newy + 1) &&
8899                      (!IS_PLAYER(x, newy + 1) ||
8900                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8901     Impact(x, newy);
8902
8903   if (pushed_by_player && !game.use_change_when_pushing_bug)
8904   {
8905     int push_side = MV_DIR_OPPOSITE(direction);
8906     struct PlayerInfo *player = PLAYERINFO(x, y);
8907
8908     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8909                                player->index_bit, push_side);
8910     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8911                                         player->index_bit, push_side);
8912   }
8913
8914   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8915     MovDelay[newx][newy] = 1;
8916
8917   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8918
8919   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8920   TestIfElementHitsCustomElement(newx, newy, direction);
8921   TestIfPlayerTouchesCustomElement(newx, newy);
8922   TestIfElementTouchesCustomElement(newx, newy);
8923
8924   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8925       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8926     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8927                              MV_DIR_OPPOSITE(direction));
8928 }
8929
8930 int AmoebaNeighbourNr(int ax, int ay)
8931 {
8932   int i;
8933   int element = Tile[ax][ay];
8934   int group_nr = 0;
8935   static int xy[4][2] =
8936   {
8937     { 0, -1 },
8938     { -1, 0 },
8939     { +1, 0 },
8940     { 0, +1 }
8941   };
8942
8943   for (i = 0; i < NUM_DIRECTIONS; i++)
8944   {
8945     int x = ax + xy[i][0];
8946     int y = ay + xy[i][1];
8947
8948     if (!IN_LEV_FIELD(x, y))
8949       continue;
8950
8951     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8952       group_nr = AmoebaNr[x][y];
8953   }
8954
8955   return group_nr;
8956 }
8957
8958 static void AmoebaMerge(int ax, int ay)
8959 {
8960   int i, x, y, xx, yy;
8961   int new_group_nr = AmoebaNr[ax][ay];
8962   static int xy[4][2] =
8963   {
8964     { 0, -1 },
8965     { -1, 0 },
8966     { +1, 0 },
8967     { 0, +1 }
8968   };
8969
8970   if (new_group_nr == 0)
8971     return;
8972
8973   for (i = 0; i < NUM_DIRECTIONS; i++)
8974   {
8975     x = ax + xy[i][0];
8976     y = ay + xy[i][1];
8977
8978     if (!IN_LEV_FIELD(x, y))
8979       continue;
8980
8981     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8982          Tile[x][y] == EL_BD_AMOEBA ||
8983          Tile[x][y] == EL_AMOEBA_DEAD) &&
8984         AmoebaNr[x][y] != new_group_nr)
8985     {
8986       int old_group_nr = AmoebaNr[x][y];
8987
8988       if (old_group_nr == 0)
8989         return;
8990
8991       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8992       AmoebaCnt[old_group_nr] = 0;
8993       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8994       AmoebaCnt2[old_group_nr] = 0;
8995
8996       SCAN_PLAYFIELD(xx, yy)
8997       {
8998         if (AmoebaNr[xx][yy] == old_group_nr)
8999           AmoebaNr[xx][yy] = new_group_nr;
9000       }
9001     }
9002   }
9003 }
9004
9005 void AmoebaToDiamond(int ax, int ay)
9006 {
9007   int i, x, y;
9008
9009   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9010   {
9011     int group_nr = AmoebaNr[ax][ay];
9012
9013 #ifdef DEBUG
9014     if (group_nr == 0)
9015     {
9016       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9017       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9018
9019       return;
9020     }
9021 #endif
9022
9023     SCAN_PLAYFIELD(x, y)
9024     {
9025       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9026       {
9027         AmoebaNr[x][y] = 0;
9028         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9029       }
9030     }
9031
9032     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9033                             SND_AMOEBA_TURNING_TO_GEM :
9034                             SND_AMOEBA_TURNING_TO_ROCK));
9035     Bang(ax, ay);
9036   }
9037   else
9038   {
9039     static int xy[4][2] =
9040     {
9041       { 0, -1 },
9042       { -1, 0 },
9043       { +1, 0 },
9044       { 0, +1 }
9045     };
9046
9047     for (i = 0; i < NUM_DIRECTIONS; i++)
9048     {
9049       x = ax + xy[i][0];
9050       y = ay + xy[i][1];
9051
9052       if (!IN_LEV_FIELD(x, y))
9053         continue;
9054
9055       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9056       {
9057         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9058                               SND_AMOEBA_TURNING_TO_GEM :
9059                               SND_AMOEBA_TURNING_TO_ROCK));
9060         Bang(x, y);
9061       }
9062     }
9063   }
9064 }
9065
9066 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9067 {
9068   int x, y;
9069   int group_nr = AmoebaNr[ax][ay];
9070   boolean done = FALSE;
9071
9072 #ifdef DEBUG
9073   if (group_nr == 0)
9074   {
9075     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9076     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9077
9078     return;
9079   }
9080 #endif
9081
9082   SCAN_PLAYFIELD(x, y)
9083   {
9084     if (AmoebaNr[x][y] == group_nr &&
9085         (Tile[x][y] == EL_AMOEBA_DEAD ||
9086          Tile[x][y] == EL_BD_AMOEBA ||
9087          Tile[x][y] == EL_AMOEBA_GROWING))
9088     {
9089       AmoebaNr[x][y] = 0;
9090       Tile[x][y] = new_element;
9091       InitField(x, y, FALSE);
9092       TEST_DrawLevelField(x, y);
9093       done = TRUE;
9094     }
9095   }
9096
9097   if (done)
9098     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9099                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9100                             SND_BD_AMOEBA_TURNING_TO_GEM));
9101 }
9102
9103 static void AmoebaGrowing(int x, int y)
9104 {
9105   static unsigned int sound_delay = 0;
9106   static unsigned int sound_delay_value = 0;
9107
9108   if (!MovDelay[x][y])          // start new growing cycle
9109   {
9110     MovDelay[x][y] = 7;
9111
9112     if (DelayReached(&sound_delay, sound_delay_value))
9113     {
9114       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9115       sound_delay_value = 30;
9116     }
9117   }
9118
9119   if (MovDelay[x][y])           // wait some time before growing bigger
9120   {
9121     MovDelay[x][y]--;
9122     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9123     {
9124       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9125                                            6 - MovDelay[x][y]);
9126
9127       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9128     }
9129
9130     if (!MovDelay[x][y])
9131     {
9132       Tile[x][y] = Store[x][y];
9133       Store[x][y] = 0;
9134       TEST_DrawLevelField(x, y);
9135     }
9136   }
9137 }
9138
9139 static void AmoebaShrinking(int x, int y)
9140 {
9141   static unsigned int sound_delay = 0;
9142   static unsigned int sound_delay_value = 0;
9143
9144   if (!MovDelay[x][y])          // start new shrinking cycle
9145   {
9146     MovDelay[x][y] = 7;
9147
9148     if (DelayReached(&sound_delay, sound_delay_value))
9149       sound_delay_value = 30;
9150   }
9151
9152   if (MovDelay[x][y])           // wait some time before shrinking
9153   {
9154     MovDelay[x][y]--;
9155     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9156     {
9157       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9158                                            6 - MovDelay[x][y]);
9159
9160       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9161     }
9162
9163     if (!MovDelay[x][y])
9164     {
9165       Tile[x][y] = EL_EMPTY;
9166       TEST_DrawLevelField(x, y);
9167
9168       // don't let mole enter this field in this cycle;
9169       // (give priority to objects falling to this field from above)
9170       Stop[x][y] = TRUE;
9171     }
9172   }
9173 }
9174
9175 static void AmoebaReproduce(int ax, int ay)
9176 {
9177   int i;
9178   int element = Tile[ax][ay];
9179   int graphic = el2img(element);
9180   int newax = ax, neway = ay;
9181   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9182   static int xy[4][2] =
9183   {
9184     { 0, -1 },
9185     { -1, 0 },
9186     { +1, 0 },
9187     { 0, +1 }
9188   };
9189
9190   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9191   {
9192     Tile[ax][ay] = EL_AMOEBA_DEAD;
9193     TEST_DrawLevelField(ax, ay);
9194     return;
9195   }
9196
9197   if (IS_ANIMATED(graphic))
9198     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9199
9200   if (!MovDelay[ax][ay])        // start making new amoeba field
9201     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9202
9203   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9204   {
9205     MovDelay[ax][ay]--;
9206     if (MovDelay[ax][ay])
9207       return;
9208   }
9209
9210   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9211   {
9212     int start = RND(4);
9213     int x = ax + xy[start][0];
9214     int y = ay + xy[start][1];
9215
9216     if (!IN_LEV_FIELD(x, y))
9217       return;
9218
9219     if (IS_FREE(x, y) ||
9220         CAN_GROW_INTO(Tile[x][y]) ||
9221         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9222         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9223     {
9224       newax = x;
9225       neway = y;
9226     }
9227
9228     if (newax == ax && neway == ay)
9229       return;
9230   }
9231   else                          // normal or "filled" (BD style) amoeba
9232   {
9233     int start = RND(4);
9234     boolean waiting_for_player = FALSE;
9235
9236     for (i = 0; i < NUM_DIRECTIONS; i++)
9237     {
9238       int j = (start + i) % 4;
9239       int x = ax + xy[j][0];
9240       int y = ay + xy[j][1];
9241
9242       if (!IN_LEV_FIELD(x, y))
9243         continue;
9244
9245       if (IS_FREE(x, y) ||
9246           CAN_GROW_INTO(Tile[x][y]) ||
9247           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9248           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9249       {
9250         newax = x;
9251         neway = y;
9252         break;
9253       }
9254       else if (IS_PLAYER(x, y))
9255         waiting_for_player = TRUE;
9256     }
9257
9258     if (newax == ax && neway == ay)             // amoeba cannot grow
9259     {
9260       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9261       {
9262         Tile[ax][ay] = EL_AMOEBA_DEAD;
9263         TEST_DrawLevelField(ax, ay);
9264         AmoebaCnt[AmoebaNr[ax][ay]]--;
9265
9266         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9267         {
9268           if (element == EL_AMOEBA_FULL)
9269             AmoebaToDiamond(ax, ay);
9270           else if (element == EL_BD_AMOEBA)
9271             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9272         }
9273       }
9274       return;
9275     }
9276     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9277     {
9278       // amoeba gets larger by growing in some direction
9279
9280       int new_group_nr = AmoebaNr[ax][ay];
9281
9282 #ifdef DEBUG
9283   if (new_group_nr == 0)
9284   {
9285     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9286           newax, neway);
9287     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9288
9289     return;
9290   }
9291 #endif
9292
9293       AmoebaNr[newax][neway] = new_group_nr;
9294       AmoebaCnt[new_group_nr]++;
9295       AmoebaCnt2[new_group_nr]++;
9296
9297       // if amoeba touches other amoeba(s) after growing, unify them
9298       AmoebaMerge(newax, neway);
9299
9300       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9301       {
9302         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9303         return;
9304       }
9305     }
9306   }
9307
9308   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9309       (neway == lev_fieldy - 1 && newax != ax))
9310   {
9311     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9312     Store[newax][neway] = element;
9313   }
9314   else if (neway == ay || element == EL_EMC_DRIPPER)
9315   {
9316     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9317
9318     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9319   }
9320   else
9321   {
9322     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9323     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9324     Store[ax][ay] = EL_AMOEBA_DROP;
9325     ContinueMoving(ax, ay);
9326     return;
9327   }
9328
9329   TEST_DrawLevelField(newax, neway);
9330 }
9331
9332 static void Life(int ax, int ay)
9333 {
9334   int x1, y1, x2, y2;
9335   int life_time = 40;
9336   int element = Tile[ax][ay];
9337   int graphic = el2img(element);
9338   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9339                          level.biomaze);
9340   boolean changed = FALSE;
9341
9342   if (IS_ANIMATED(graphic))
9343     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9344
9345   if (Stop[ax][ay])
9346     return;
9347
9348   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9349     MovDelay[ax][ay] = life_time;
9350
9351   if (MovDelay[ax][ay])         // wait some time before next cycle
9352   {
9353     MovDelay[ax][ay]--;
9354     if (MovDelay[ax][ay])
9355       return;
9356   }
9357
9358   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9359   {
9360     int xx = ax+x1, yy = ay+y1;
9361     int old_element = Tile[xx][yy];
9362     int num_neighbours = 0;
9363
9364     if (!IN_LEV_FIELD(xx, yy))
9365       continue;
9366
9367     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9368     {
9369       int x = xx+x2, y = yy+y2;
9370
9371       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9372         continue;
9373
9374       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9375       boolean is_neighbour = FALSE;
9376
9377       if (level.use_life_bugs)
9378         is_neighbour =
9379           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9380            (IS_FREE(x, y)                             &&  Stop[x][y]));
9381       else
9382         is_neighbour =
9383           (Last[x][y] == element || is_player_cell);
9384
9385       if (is_neighbour)
9386         num_neighbours++;
9387     }
9388
9389     boolean is_free = FALSE;
9390
9391     if (level.use_life_bugs)
9392       is_free = (IS_FREE(xx, yy));
9393     else
9394       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9395
9396     if (xx == ax && yy == ay)           // field in the middle
9397     {
9398       if (num_neighbours < life_parameter[0] ||
9399           num_neighbours > life_parameter[1])
9400       {
9401         Tile[xx][yy] = EL_EMPTY;
9402         if (Tile[xx][yy] != old_element)
9403           TEST_DrawLevelField(xx, yy);
9404         Stop[xx][yy] = TRUE;
9405         changed = TRUE;
9406       }
9407     }
9408     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9409     {                                   // free border field
9410       if (num_neighbours >= life_parameter[2] &&
9411           num_neighbours <= life_parameter[3])
9412       {
9413         Tile[xx][yy] = element;
9414         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9415         if (Tile[xx][yy] != old_element)
9416           TEST_DrawLevelField(xx, yy);
9417         Stop[xx][yy] = TRUE;
9418         changed = TRUE;
9419       }
9420     }
9421   }
9422
9423   if (changed)
9424     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9425                    SND_GAME_OF_LIFE_GROWING);
9426 }
9427
9428 static void InitRobotWheel(int x, int y)
9429 {
9430   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9431 }
9432
9433 static void RunRobotWheel(int x, int y)
9434 {
9435   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9436 }
9437
9438 static void StopRobotWheel(int x, int y)
9439 {
9440   if (game.robot_wheel_x == x &&
9441       game.robot_wheel_y == y)
9442   {
9443     game.robot_wheel_x = -1;
9444     game.robot_wheel_y = -1;
9445     game.robot_wheel_active = FALSE;
9446   }
9447 }
9448
9449 static void InitTimegateWheel(int x, int y)
9450 {
9451   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9452 }
9453
9454 static void RunTimegateWheel(int x, int y)
9455 {
9456   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9457 }
9458
9459 static void InitMagicBallDelay(int x, int y)
9460 {
9461   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9462 }
9463
9464 static void ActivateMagicBall(int bx, int by)
9465 {
9466   int x, y;
9467
9468   if (level.ball_random)
9469   {
9470     int pos_border = RND(8);    // select one of the eight border elements
9471     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9472     int xx = pos_content % 3;
9473     int yy = pos_content / 3;
9474
9475     x = bx - 1 + xx;
9476     y = by - 1 + yy;
9477
9478     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9479       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9480   }
9481   else
9482   {
9483     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9484     {
9485       int xx = x - bx + 1;
9486       int yy = y - by + 1;
9487
9488       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9489         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9490     }
9491   }
9492
9493   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9494 }
9495
9496 static void CheckExit(int x, int y)
9497 {
9498   if (game.gems_still_needed > 0 ||
9499       game.sokoban_fields_still_needed > 0 ||
9500       game.sokoban_objects_still_needed > 0 ||
9501       game.lights_still_needed > 0)
9502   {
9503     int element = Tile[x][y];
9504     int graphic = el2img(element);
9505
9506     if (IS_ANIMATED(graphic))
9507       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9508
9509     return;
9510   }
9511
9512   // do not re-open exit door closed after last player
9513   if (game.all_players_gone)
9514     return;
9515
9516   Tile[x][y] = EL_EXIT_OPENING;
9517
9518   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9519 }
9520
9521 static void CheckExitEM(int x, int y)
9522 {
9523   if (game.gems_still_needed > 0 ||
9524       game.sokoban_fields_still_needed > 0 ||
9525       game.sokoban_objects_still_needed > 0 ||
9526       game.lights_still_needed > 0)
9527   {
9528     int element = Tile[x][y];
9529     int graphic = el2img(element);
9530
9531     if (IS_ANIMATED(graphic))
9532       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9533
9534     return;
9535   }
9536
9537   // do not re-open exit door closed after last player
9538   if (game.all_players_gone)
9539     return;
9540
9541   Tile[x][y] = EL_EM_EXIT_OPENING;
9542
9543   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9544 }
9545
9546 static void CheckExitSteel(int x, int y)
9547 {
9548   if (game.gems_still_needed > 0 ||
9549       game.sokoban_fields_still_needed > 0 ||
9550       game.sokoban_objects_still_needed > 0 ||
9551       game.lights_still_needed > 0)
9552   {
9553     int element = Tile[x][y];
9554     int graphic = el2img(element);
9555
9556     if (IS_ANIMATED(graphic))
9557       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9558
9559     return;
9560   }
9561
9562   // do not re-open exit door closed after last player
9563   if (game.all_players_gone)
9564     return;
9565
9566   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9567
9568   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9569 }
9570
9571 static void CheckExitSteelEM(int x, int y)
9572 {
9573   if (game.gems_still_needed > 0 ||
9574       game.sokoban_fields_still_needed > 0 ||
9575       game.sokoban_objects_still_needed > 0 ||
9576       game.lights_still_needed > 0)
9577   {
9578     int element = Tile[x][y];
9579     int graphic = el2img(element);
9580
9581     if (IS_ANIMATED(graphic))
9582       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9583
9584     return;
9585   }
9586
9587   // do not re-open exit door closed after last player
9588   if (game.all_players_gone)
9589     return;
9590
9591   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9592
9593   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9594 }
9595
9596 static void CheckExitSP(int x, int y)
9597 {
9598   if (game.gems_still_needed > 0)
9599   {
9600     int element = Tile[x][y];
9601     int graphic = el2img(element);
9602
9603     if (IS_ANIMATED(graphic))
9604       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9605
9606     return;
9607   }
9608
9609   // do not re-open exit door closed after last player
9610   if (game.all_players_gone)
9611     return;
9612
9613   Tile[x][y] = EL_SP_EXIT_OPENING;
9614
9615   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9616 }
9617
9618 static void CloseAllOpenTimegates(void)
9619 {
9620   int x, y;
9621
9622   SCAN_PLAYFIELD(x, y)
9623   {
9624     int element = Tile[x][y];
9625
9626     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9627     {
9628       Tile[x][y] = EL_TIMEGATE_CLOSING;
9629
9630       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9631     }
9632   }
9633 }
9634
9635 static void DrawTwinkleOnField(int x, int y)
9636 {
9637   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9638     return;
9639
9640   if (Tile[x][y] == EL_BD_DIAMOND)
9641     return;
9642
9643   if (MovDelay[x][y] == 0)      // next animation frame
9644     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9645
9646   if (MovDelay[x][y] != 0)      // wait some time before next frame
9647   {
9648     MovDelay[x][y]--;
9649
9650     DrawLevelElementAnimation(x, y, Tile[x][y]);
9651
9652     if (MovDelay[x][y] != 0)
9653     {
9654       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9655                                            10 - MovDelay[x][y]);
9656
9657       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9658     }
9659   }
9660 }
9661
9662 static void MauerWaechst(int x, int y)
9663 {
9664   int delay = 6;
9665
9666   if (!MovDelay[x][y])          // next animation frame
9667     MovDelay[x][y] = 3 * delay;
9668
9669   if (MovDelay[x][y])           // wait some time before next frame
9670   {
9671     MovDelay[x][y]--;
9672
9673     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9674     {
9675       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9676       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9677
9678       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9679     }
9680
9681     if (!MovDelay[x][y])
9682     {
9683       if (MovDir[x][y] == MV_LEFT)
9684       {
9685         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9686           TEST_DrawLevelField(x - 1, y);
9687       }
9688       else if (MovDir[x][y] == MV_RIGHT)
9689       {
9690         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9691           TEST_DrawLevelField(x + 1, y);
9692       }
9693       else if (MovDir[x][y] == MV_UP)
9694       {
9695         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9696           TEST_DrawLevelField(x, y - 1);
9697       }
9698       else
9699       {
9700         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9701           TEST_DrawLevelField(x, y + 1);
9702       }
9703
9704       Tile[x][y] = Store[x][y];
9705       Store[x][y] = 0;
9706       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9707       TEST_DrawLevelField(x, y);
9708     }
9709   }
9710 }
9711
9712 static void MauerAbleger(int ax, int ay)
9713 {
9714   int element = Tile[ax][ay];
9715   int graphic = el2img(element);
9716   boolean oben_frei = FALSE, unten_frei = FALSE;
9717   boolean links_frei = FALSE, rechts_frei = FALSE;
9718   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9719   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9720   boolean new_wall = FALSE;
9721
9722   if (IS_ANIMATED(graphic))
9723     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9724
9725   if (!MovDelay[ax][ay])        // start building new wall
9726     MovDelay[ax][ay] = 6;
9727
9728   if (MovDelay[ax][ay])         // wait some time before building new wall
9729   {
9730     MovDelay[ax][ay]--;
9731     if (MovDelay[ax][ay])
9732       return;
9733   }
9734
9735   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9736     oben_frei = TRUE;
9737   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9738     unten_frei = TRUE;
9739   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9740     links_frei = TRUE;
9741   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9742     rechts_frei = TRUE;
9743
9744   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9745       element == EL_EXPANDABLE_WALL_ANY)
9746   {
9747     if (oben_frei)
9748     {
9749       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9750       Store[ax][ay-1] = element;
9751       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9752       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9753         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9754                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9755       new_wall = TRUE;
9756     }
9757     if (unten_frei)
9758     {
9759       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9760       Store[ax][ay+1] = element;
9761       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9762       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9763         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9764                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9765       new_wall = TRUE;
9766     }
9767   }
9768
9769   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9770       element == EL_EXPANDABLE_WALL_ANY ||
9771       element == EL_EXPANDABLE_WALL ||
9772       element == EL_BD_EXPANDABLE_WALL)
9773   {
9774     if (links_frei)
9775     {
9776       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9777       Store[ax-1][ay] = element;
9778       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9779       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9780         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9781                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9782       new_wall = TRUE;
9783     }
9784
9785     if (rechts_frei)
9786     {
9787       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9788       Store[ax+1][ay] = element;
9789       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9790       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9791         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9792                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9793       new_wall = TRUE;
9794     }
9795   }
9796
9797   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9798     TEST_DrawLevelField(ax, ay);
9799
9800   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9801     oben_massiv = TRUE;
9802   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9803     unten_massiv = TRUE;
9804   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9805     links_massiv = TRUE;
9806   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9807     rechts_massiv = TRUE;
9808
9809   if (((oben_massiv && unten_massiv) ||
9810        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9811        element == EL_EXPANDABLE_WALL) &&
9812       ((links_massiv && rechts_massiv) ||
9813        element == EL_EXPANDABLE_WALL_VERTICAL))
9814     Tile[ax][ay] = EL_WALL;
9815
9816   if (new_wall)
9817     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9818 }
9819
9820 static void MauerAblegerStahl(int ax, int ay)
9821 {
9822   int element = Tile[ax][ay];
9823   int graphic = el2img(element);
9824   boolean oben_frei = FALSE, unten_frei = FALSE;
9825   boolean links_frei = FALSE, rechts_frei = FALSE;
9826   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9827   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9828   boolean new_wall = FALSE;
9829
9830   if (IS_ANIMATED(graphic))
9831     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9832
9833   if (!MovDelay[ax][ay])        // start building new wall
9834     MovDelay[ax][ay] = 6;
9835
9836   if (MovDelay[ax][ay])         // wait some time before building new wall
9837   {
9838     MovDelay[ax][ay]--;
9839     if (MovDelay[ax][ay])
9840       return;
9841   }
9842
9843   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9844     oben_frei = TRUE;
9845   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9846     unten_frei = TRUE;
9847   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9848     links_frei = TRUE;
9849   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9850     rechts_frei = TRUE;
9851
9852   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9853       element == EL_EXPANDABLE_STEELWALL_ANY)
9854   {
9855     if (oben_frei)
9856     {
9857       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9858       Store[ax][ay-1] = element;
9859       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9860       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9861         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9862                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9863       new_wall = TRUE;
9864     }
9865     if (unten_frei)
9866     {
9867       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9868       Store[ax][ay+1] = element;
9869       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9870       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9871         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9872                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9873       new_wall = TRUE;
9874     }
9875   }
9876
9877   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9878       element == EL_EXPANDABLE_STEELWALL_ANY)
9879   {
9880     if (links_frei)
9881     {
9882       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9883       Store[ax-1][ay] = element;
9884       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9885       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9886         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9887                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9888       new_wall = TRUE;
9889     }
9890
9891     if (rechts_frei)
9892     {
9893       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9894       Store[ax+1][ay] = element;
9895       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9896       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9897         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9898                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9899       new_wall = TRUE;
9900     }
9901   }
9902
9903   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9904     oben_massiv = TRUE;
9905   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9906     unten_massiv = TRUE;
9907   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9908     links_massiv = TRUE;
9909   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9910     rechts_massiv = TRUE;
9911
9912   if (((oben_massiv && unten_massiv) ||
9913        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9914       ((links_massiv && rechts_massiv) ||
9915        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9916     Tile[ax][ay] = EL_STEELWALL;
9917
9918   if (new_wall)
9919     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9920 }
9921
9922 static void CheckForDragon(int x, int y)
9923 {
9924   int i, j;
9925   boolean dragon_found = FALSE;
9926   static int xy[4][2] =
9927   {
9928     { 0, -1 },
9929     { -1, 0 },
9930     { +1, 0 },
9931     { 0, +1 }
9932   };
9933
9934   for (i = 0; i < NUM_DIRECTIONS; i++)
9935   {
9936     for (j = 0; j < 4; j++)
9937     {
9938       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9939
9940       if (IN_LEV_FIELD(xx, yy) &&
9941           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9942       {
9943         if (Tile[xx][yy] == EL_DRAGON)
9944           dragon_found = TRUE;
9945       }
9946       else
9947         break;
9948     }
9949   }
9950
9951   if (!dragon_found)
9952   {
9953     for (i = 0; i < NUM_DIRECTIONS; i++)
9954     {
9955       for (j = 0; j < 3; j++)
9956       {
9957         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9958   
9959         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9960         {
9961           Tile[xx][yy] = EL_EMPTY;
9962           TEST_DrawLevelField(xx, yy);
9963         }
9964         else
9965           break;
9966       }
9967     }
9968   }
9969 }
9970
9971 static void InitBuggyBase(int x, int y)
9972 {
9973   int element = Tile[x][y];
9974   int activating_delay = FRAMES_PER_SECOND / 4;
9975
9976   ChangeDelay[x][y] =
9977     (element == EL_SP_BUGGY_BASE ?
9978      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9979      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9980      activating_delay :
9981      element == EL_SP_BUGGY_BASE_ACTIVE ?
9982      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9983 }
9984
9985 static void WarnBuggyBase(int x, int y)
9986 {
9987   int i;
9988   static int xy[4][2] =
9989   {
9990     { 0, -1 },
9991     { -1, 0 },
9992     { +1, 0 },
9993     { 0, +1 }
9994   };
9995
9996   for (i = 0; i < NUM_DIRECTIONS; i++)
9997   {
9998     int xx = x + xy[i][0];
9999     int yy = y + xy[i][1];
10000
10001     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10002     {
10003       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10004
10005       break;
10006     }
10007   }
10008 }
10009
10010 static void InitTrap(int x, int y)
10011 {
10012   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10013 }
10014
10015 static void ActivateTrap(int x, int y)
10016 {
10017   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10018 }
10019
10020 static void ChangeActiveTrap(int x, int y)
10021 {
10022   int graphic = IMG_TRAP_ACTIVE;
10023
10024   // if new animation frame was drawn, correct crumbled sand border
10025   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10026     TEST_DrawLevelFieldCrumbled(x, y);
10027 }
10028
10029 static int getSpecialActionElement(int element, int number, int base_element)
10030 {
10031   return (element != EL_EMPTY ? element :
10032           number != -1 ? base_element + number - 1 :
10033           EL_EMPTY);
10034 }
10035
10036 static int getModifiedActionNumber(int value_old, int operator, int operand,
10037                                    int value_min, int value_max)
10038 {
10039   int value_new = (operator == CA_MODE_SET      ? operand :
10040                    operator == CA_MODE_ADD      ? value_old + operand :
10041                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10042                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10043                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10044                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10045                    value_old);
10046
10047   return (value_new < value_min ? value_min :
10048           value_new > value_max ? value_max :
10049           value_new);
10050 }
10051
10052 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10053 {
10054   struct ElementInfo *ei = &element_info[element];
10055   struct ElementChangeInfo *change = &ei->change_page[page];
10056   int target_element = change->target_element;
10057   int action_type = change->action_type;
10058   int action_mode = change->action_mode;
10059   int action_arg = change->action_arg;
10060   int action_element = change->action_element;
10061   int i;
10062
10063   if (!change->has_action)
10064     return;
10065
10066   // ---------- determine action paramater values -----------------------------
10067
10068   int level_time_value =
10069     (level.time > 0 ? TimeLeft :
10070      TimePlayed);
10071
10072   int action_arg_element_raw =
10073     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10074      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10075      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10076      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10077      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10078      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10079      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10080      EL_EMPTY);
10081   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10082
10083   int action_arg_direction =
10084     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10085      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10086      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10087      change->actual_trigger_side :
10088      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10089      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10090      MV_NONE);
10091
10092   int action_arg_number_min =
10093     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10094      CA_ARG_MIN);
10095
10096   int action_arg_number_max =
10097     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10098      action_type == CA_SET_LEVEL_GEMS ? 999 :
10099      action_type == CA_SET_LEVEL_TIME ? 9999 :
10100      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10101      action_type == CA_SET_CE_VALUE ? 9999 :
10102      action_type == CA_SET_CE_SCORE ? 9999 :
10103      CA_ARG_MAX);
10104
10105   int action_arg_number_reset =
10106     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10107      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10108      action_type == CA_SET_LEVEL_TIME ? level.time :
10109      action_type == CA_SET_LEVEL_SCORE ? 0 :
10110      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10111      action_type == CA_SET_CE_SCORE ? 0 :
10112      0);
10113
10114   int action_arg_number =
10115     (action_arg <= CA_ARG_MAX ? action_arg :
10116      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10117      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10118      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10119      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10120      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10121      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10122      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10123      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10124      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10125      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10126      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10127      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10128      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10129      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10130      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10131      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10132      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10133      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10134      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10135      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10136      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10137      -1);
10138
10139   int action_arg_number_old =
10140     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10141      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10142      action_type == CA_SET_LEVEL_SCORE ? game.score :
10143      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10144      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10145      0);
10146
10147   int action_arg_number_new =
10148     getModifiedActionNumber(action_arg_number_old,
10149                             action_mode, action_arg_number,
10150                             action_arg_number_min, action_arg_number_max);
10151
10152   int trigger_player_bits =
10153     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10154      change->actual_trigger_player_bits : change->trigger_player);
10155
10156   int action_arg_player_bits =
10157     (action_arg >= CA_ARG_PLAYER_1 &&
10158      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10159      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10160      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10161      PLAYER_BITS_ANY);
10162
10163   // ---------- execute action  -----------------------------------------------
10164
10165   switch (action_type)
10166   {
10167     case CA_NO_ACTION:
10168     {
10169       return;
10170     }
10171
10172     // ---------- level actions  ----------------------------------------------
10173
10174     case CA_RESTART_LEVEL:
10175     {
10176       game.restart_level = TRUE;
10177
10178       break;
10179     }
10180
10181     case CA_SHOW_ENVELOPE:
10182     {
10183       int element = getSpecialActionElement(action_arg_element,
10184                                             action_arg_number, EL_ENVELOPE_1);
10185
10186       if (IS_ENVELOPE(element))
10187         local_player->show_envelope = element;
10188
10189       break;
10190     }
10191
10192     case CA_SET_LEVEL_TIME:
10193     {
10194       if (level.time > 0)       // only modify limited time value
10195       {
10196         TimeLeft = action_arg_number_new;
10197
10198         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10199
10200         DisplayGameControlValues();
10201
10202         if (!TimeLeft && setup.time_limit)
10203           for (i = 0; i < MAX_PLAYERS; i++)
10204             KillPlayer(&stored_player[i]);
10205       }
10206
10207       break;
10208     }
10209
10210     case CA_SET_LEVEL_SCORE:
10211     {
10212       game.score = action_arg_number_new;
10213
10214       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10215
10216       DisplayGameControlValues();
10217
10218       break;
10219     }
10220
10221     case CA_SET_LEVEL_GEMS:
10222     {
10223       game.gems_still_needed = action_arg_number_new;
10224
10225       game.snapshot.collected_item = TRUE;
10226
10227       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10228
10229       DisplayGameControlValues();
10230
10231       break;
10232     }
10233
10234     case CA_SET_LEVEL_WIND:
10235     {
10236       game.wind_direction = action_arg_direction;
10237
10238       break;
10239     }
10240
10241     case CA_SET_LEVEL_RANDOM_SEED:
10242     {
10243       // ensure that setting a new random seed while playing is predictable
10244       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10245
10246       break;
10247     }
10248
10249     // ---------- player actions  ---------------------------------------------
10250
10251     case CA_MOVE_PLAYER:
10252     case CA_MOVE_PLAYER_NEW:
10253     {
10254       // automatically move to the next field in specified direction
10255       for (i = 0; i < MAX_PLAYERS; i++)
10256         if (trigger_player_bits & (1 << i))
10257           if (action_type == CA_MOVE_PLAYER ||
10258               stored_player[i].MovPos == 0)
10259             stored_player[i].programmed_action = action_arg_direction;
10260
10261       break;
10262     }
10263
10264     case CA_EXIT_PLAYER:
10265     {
10266       for (i = 0; i < MAX_PLAYERS; i++)
10267         if (action_arg_player_bits & (1 << i))
10268           ExitPlayer(&stored_player[i]);
10269
10270       if (game.players_still_needed == 0)
10271         LevelSolved();
10272
10273       break;
10274     }
10275
10276     case CA_KILL_PLAYER:
10277     {
10278       for (i = 0; i < MAX_PLAYERS; i++)
10279         if (action_arg_player_bits & (1 << i))
10280           KillPlayer(&stored_player[i]);
10281
10282       break;
10283     }
10284
10285     case CA_SET_PLAYER_KEYS:
10286     {
10287       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10288       int element = getSpecialActionElement(action_arg_element,
10289                                             action_arg_number, EL_KEY_1);
10290
10291       if (IS_KEY(element))
10292       {
10293         for (i = 0; i < MAX_PLAYERS; i++)
10294         {
10295           if (trigger_player_bits & (1 << i))
10296           {
10297             stored_player[i].key[KEY_NR(element)] = key_state;
10298
10299             DrawGameDoorValues();
10300           }
10301         }
10302       }
10303
10304       break;
10305     }
10306
10307     case CA_SET_PLAYER_SPEED:
10308     {
10309       for (i = 0; i < MAX_PLAYERS; i++)
10310       {
10311         if (trigger_player_bits & (1 << i))
10312         {
10313           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10314
10315           if (action_arg == CA_ARG_SPEED_FASTER &&
10316               stored_player[i].cannot_move)
10317           {
10318             action_arg_number = STEPSIZE_VERY_SLOW;
10319           }
10320           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10321                    action_arg == CA_ARG_SPEED_FASTER)
10322           {
10323             action_arg_number = 2;
10324             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10325                            CA_MODE_MULTIPLY);
10326           }
10327           else if (action_arg == CA_ARG_NUMBER_RESET)
10328           {
10329             action_arg_number = level.initial_player_stepsize[i];
10330           }
10331
10332           move_stepsize =
10333             getModifiedActionNumber(move_stepsize,
10334                                     action_mode,
10335                                     action_arg_number,
10336                                     action_arg_number_min,
10337                                     action_arg_number_max);
10338
10339           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10340         }
10341       }
10342
10343       break;
10344     }
10345
10346     case CA_SET_PLAYER_SHIELD:
10347     {
10348       for (i = 0; i < MAX_PLAYERS; i++)
10349       {
10350         if (trigger_player_bits & (1 << i))
10351         {
10352           if (action_arg == CA_ARG_SHIELD_OFF)
10353           {
10354             stored_player[i].shield_normal_time_left = 0;
10355             stored_player[i].shield_deadly_time_left = 0;
10356           }
10357           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10358           {
10359             stored_player[i].shield_normal_time_left = 999999;
10360           }
10361           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10362           {
10363             stored_player[i].shield_normal_time_left = 999999;
10364             stored_player[i].shield_deadly_time_left = 999999;
10365           }
10366         }
10367       }
10368
10369       break;
10370     }
10371
10372     case CA_SET_PLAYER_GRAVITY:
10373     {
10374       for (i = 0; i < MAX_PLAYERS; i++)
10375       {
10376         if (trigger_player_bits & (1 << i))
10377         {
10378           stored_player[i].gravity =
10379             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10380              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10381              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10382              stored_player[i].gravity);
10383         }
10384       }
10385
10386       break;
10387     }
10388
10389     case CA_SET_PLAYER_ARTWORK:
10390     {
10391       for (i = 0; i < MAX_PLAYERS; i++)
10392       {
10393         if (trigger_player_bits & (1 << i))
10394         {
10395           int artwork_element = action_arg_element;
10396
10397           if (action_arg == CA_ARG_ELEMENT_RESET)
10398             artwork_element =
10399               (level.use_artwork_element[i] ? level.artwork_element[i] :
10400                stored_player[i].element_nr);
10401
10402           if (stored_player[i].artwork_element != artwork_element)
10403             stored_player[i].Frame = 0;
10404
10405           stored_player[i].artwork_element = artwork_element;
10406
10407           SetPlayerWaiting(&stored_player[i], FALSE);
10408
10409           // set number of special actions for bored and sleeping animation
10410           stored_player[i].num_special_action_bored =
10411             get_num_special_action(artwork_element,
10412                                    ACTION_BORING_1, ACTION_BORING_LAST);
10413           stored_player[i].num_special_action_sleeping =
10414             get_num_special_action(artwork_element,
10415                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10416         }
10417       }
10418
10419       break;
10420     }
10421
10422     case CA_SET_PLAYER_INVENTORY:
10423     {
10424       for (i = 0; i < MAX_PLAYERS; i++)
10425       {
10426         struct PlayerInfo *player = &stored_player[i];
10427         int j, k;
10428
10429         if (trigger_player_bits & (1 << i))
10430         {
10431           int inventory_element = action_arg_element;
10432
10433           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10434               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10435               action_arg == CA_ARG_ELEMENT_ACTION)
10436           {
10437             int element = inventory_element;
10438             int collect_count = element_info[element].collect_count_initial;
10439
10440             if (!IS_CUSTOM_ELEMENT(element))
10441               collect_count = 1;
10442
10443             if (collect_count == 0)
10444               player->inventory_infinite_element = element;
10445             else
10446               for (k = 0; k < collect_count; k++)
10447                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10448                   player->inventory_element[player->inventory_size++] =
10449                     element;
10450           }
10451           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10452                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10453                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10454           {
10455             if (player->inventory_infinite_element != EL_UNDEFINED &&
10456                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10457                                      action_arg_element_raw))
10458               player->inventory_infinite_element = EL_UNDEFINED;
10459
10460             for (k = 0, j = 0; j < player->inventory_size; j++)
10461             {
10462               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10463                                         action_arg_element_raw))
10464                 player->inventory_element[k++] = player->inventory_element[j];
10465             }
10466
10467             player->inventory_size = k;
10468           }
10469           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10470           {
10471             if (player->inventory_size > 0)
10472             {
10473               for (j = 0; j < player->inventory_size - 1; j++)
10474                 player->inventory_element[j] = player->inventory_element[j + 1];
10475
10476               player->inventory_size--;
10477             }
10478           }
10479           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10480           {
10481             if (player->inventory_size > 0)
10482               player->inventory_size--;
10483           }
10484           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10485           {
10486             player->inventory_infinite_element = EL_UNDEFINED;
10487             player->inventory_size = 0;
10488           }
10489           else if (action_arg == CA_ARG_INVENTORY_RESET)
10490           {
10491             player->inventory_infinite_element = EL_UNDEFINED;
10492             player->inventory_size = 0;
10493
10494             if (level.use_initial_inventory[i])
10495             {
10496               for (j = 0; j < level.initial_inventory_size[i]; j++)
10497               {
10498                 int element = level.initial_inventory_content[i][j];
10499                 int collect_count = element_info[element].collect_count_initial;
10500
10501                 if (!IS_CUSTOM_ELEMENT(element))
10502                   collect_count = 1;
10503
10504                 if (collect_count == 0)
10505                   player->inventory_infinite_element = element;
10506                 else
10507                   for (k = 0; k < collect_count; k++)
10508                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10509                       player->inventory_element[player->inventory_size++] =
10510                         element;
10511               }
10512             }
10513           }
10514         }
10515       }
10516
10517       break;
10518     }
10519
10520     // ---------- CE actions  -------------------------------------------------
10521
10522     case CA_SET_CE_VALUE:
10523     {
10524       int last_ce_value = CustomValue[x][y];
10525
10526       CustomValue[x][y] = action_arg_number_new;
10527
10528       if (CustomValue[x][y] != last_ce_value)
10529       {
10530         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10531         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10532
10533         if (CustomValue[x][y] == 0)
10534         {
10535           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10536           ChangeCount[x][y] = 0;        // allow at least one more change
10537
10538           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10539           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10540         }
10541       }
10542
10543       break;
10544     }
10545
10546     case CA_SET_CE_SCORE:
10547     {
10548       int last_ce_score = ei->collect_score;
10549
10550       ei->collect_score = action_arg_number_new;
10551
10552       if (ei->collect_score != last_ce_score)
10553       {
10554         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10555         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10556
10557         if (ei->collect_score == 0)
10558         {
10559           int xx, yy;
10560
10561           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10562           ChangeCount[x][y] = 0;        // allow at least one more change
10563
10564           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10565           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10566
10567           /*
10568             This is a very special case that seems to be a mixture between
10569             CheckElementChange() and CheckTriggeredElementChange(): while
10570             the first one only affects single elements that are triggered
10571             directly, the second one affects multiple elements in the playfield
10572             that are triggered indirectly by another element. This is a third
10573             case: Changing the CE score always affects multiple identical CEs,
10574             so every affected CE must be checked, not only the single CE for
10575             which the CE score was changed in the first place (as every instance
10576             of that CE shares the same CE score, and therefore also can change)!
10577           */
10578           SCAN_PLAYFIELD(xx, yy)
10579           {
10580             if (Tile[xx][yy] == element)
10581               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10582                                  CE_SCORE_GETS_ZERO);
10583           }
10584         }
10585       }
10586
10587       break;
10588     }
10589
10590     case CA_SET_CE_ARTWORK:
10591     {
10592       int artwork_element = action_arg_element;
10593       boolean reset_frame = FALSE;
10594       int xx, yy;
10595
10596       if (action_arg == CA_ARG_ELEMENT_RESET)
10597         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10598                            element);
10599
10600       if (ei->gfx_element != artwork_element)
10601         reset_frame = TRUE;
10602
10603       ei->gfx_element = artwork_element;
10604
10605       SCAN_PLAYFIELD(xx, yy)
10606       {
10607         if (Tile[xx][yy] == element)
10608         {
10609           if (reset_frame)
10610           {
10611             ResetGfxAnimation(xx, yy);
10612             ResetRandomAnimationValue(xx, yy);
10613           }
10614
10615           TEST_DrawLevelField(xx, yy);
10616         }
10617       }
10618
10619       break;
10620     }
10621
10622     // ---------- engine actions  ---------------------------------------------
10623
10624     case CA_SET_ENGINE_SCAN_MODE:
10625     {
10626       InitPlayfieldScanMode(action_arg);
10627
10628       break;
10629     }
10630
10631     default:
10632       break;
10633   }
10634 }
10635
10636 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10637 {
10638   int old_element = Tile[x][y];
10639   int new_element = GetElementFromGroupElement(element);
10640   int previous_move_direction = MovDir[x][y];
10641   int last_ce_value = CustomValue[x][y];
10642   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10643   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10644   boolean add_player_onto_element = (new_element_is_player &&
10645                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10646                                      IS_WALKABLE(old_element));
10647
10648   if (!add_player_onto_element)
10649   {
10650     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10651       RemoveMovingField(x, y);
10652     else
10653       RemoveField(x, y);
10654
10655     Tile[x][y] = new_element;
10656
10657     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10658       MovDir[x][y] = previous_move_direction;
10659
10660     if (element_info[new_element].use_last_ce_value)
10661       CustomValue[x][y] = last_ce_value;
10662
10663     InitField_WithBug1(x, y, FALSE);
10664
10665     new_element = Tile[x][y];   // element may have changed
10666
10667     ResetGfxAnimation(x, y);
10668     ResetRandomAnimationValue(x, y);
10669
10670     TEST_DrawLevelField(x, y);
10671
10672     if (GFX_CRUMBLED(new_element))
10673       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10674   }
10675
10676   // check if element under the player changes from accessible to unaccessible
10677   // (needed for special case of dropping element which then changes)
10678   // (must be checked after creating new element for walkable group elements)
10679   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10680       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10681   {
10682     Bang(x, y);
10683
10684     return;
10685   }
10686
10687   // "ChangeCount" not set yet to allow "entered by player" change one time
10688   if (new_element_is_player)
10689     RelocatePlayer(x, y, new_element);
10690
10691   if (is_change)
10692     ChangeCount[x][y]++;        // count number of changes in the same frame
10693
10694   TestIfBadThingTouchesPlayer(x, y);
10695   TestIfPlayerTouchesCustomElement(x, y);
10696   TestIfElementTouchesCustomElement(x, y);
10697 }
10698
10699 static void CreateField(int x, int y, int element)
10700 {
10701   CreateFieldExt(x, y, element, FALSE);
10702 }
10703
10704 static void CreateElementFromChange(int x, int y, int element)
10705 {
10706   element = GET_VALID_RUNTIME_ELEMENT(element);
10707
10708   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10709   {
10710     int old_element = Tile[x][y];
10711
10712     // prevent changed element from moving in same engine frame
10713     // unless both old and new element can either fall or move
10714     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10715         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10716       Stop[x][y] = TRUE;
10717   }
10718
10719   CreateFieldExt(x, y, element, TRUE);
10720 }
10721
10722 static boolean ChangeElement(int x, int y, int element, int page)
10723 {
10724   struct ElementInfo *ei = &element_info[element];
10725   struct ElementChangeInfo *change = &ei->change_page[page];
10726   int ce_value = CustomValue[x][y];
10727   int ce_score = ei->collect_score;
10728   int target_element;
10729   int old_element = Tile[x][y];
10730
10731   // always use default change event to prevent running into a loop
10732   if (ChangeEvent[x][y] == -1)
10733     ChangeEvent[x][y] = CE_DELAY;
10734
10735   if (ChangeEvent[x][y] == CE_DELAY)
10736   {
10737     // reset actual trigger element, trigger player and action element
10738     change->actual_trigger_element = EL_EMPTY;
10739     change->actual_trigger_player = EL_EMPTY;
10740     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10741     change->actual_trigger_side = CH_SIDE_NONE;
10742     change->actual_trigger_ce_value = 0;
10743     change->actual_trigger_ce_score = 0;
10744   }
10745
10746   // do not change elements more than a specified maximum number of changes
10747   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10748     return FALSE;
10749
10750   ChangeCount[x][y]++;          // count number of changes in the same frame
10751
10752   if (change->explode)
10753   {
10754     Bang(x, y);
10755
10756     return TRUE;
10757   }
10758
10759   if (change->use_target_content)
10760   {
10761     boolean complete_replace = TRUE;
10762     boolean can_replace[3][3];
10763     int xx, yy;
10764
10765     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10766     {
10767       boolean is_empty;
10768       boolean is_walkable;
10769       boolean is_diggable;
10770       boolean is_collectible;
10771       boolean is_removable;
10772       boolean is_destructible;
10773       int ex = x + xx - 1;
10774       int ey = y + yy - 1;
10775       int content_element = change->target_content.e[xx][yy];
10776       int e;
10777
10778       can_replace[xx][yy] = TRUE;
10779
10780       if (ex == x && ey == y)   // do not check changing element itself
10781         continue;
10782
10783       if (content_element == EL_EMPTY_SPACE)
10784       {
10785         can_replace[xx][yy] = FALSE;    // do not replace border with space
10786
10787         continue;
10788       }
10789
10790       if (!IN_LEV_FIELD(ex, ey))
10791       {
10792         can_replace[xx][yy] = FALSE;
10793         complete_replace = FALSE;
10794
10795         continue;
10796       }
10797
10798       e = Tile[ex][ey];
10799
10800       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10801         e = MovingOrBlocked2Element(ex, ey);
10802
10803       is_empty = (IS_FREE(ex, ey) ||
10804                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10805
10806       is_walkable     = (is_empty || IS_WALKABLE(e));
10807       is_diggable     = (is_empty || IS_DIGGABLE(e));
10808       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10809       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10810       is_removable    = (is_diggable || is_collectible);
10811
10812       can_replace[xx][yy] =
10813         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10814           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10815           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10816           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10817           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10818           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10819          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10820
10821       if (!can_replace[xx][yy])
10822         complete_replace = FALSE;
10823     }
10824
10825     if (!change->only_if_complete || complete_replace)
10826     {
10827       boolean something_has_changed = FALSE;
10828
10829       if (change->only_if_complete && change->use_random_replace &&
10830           RND(100) < change->random_percentage)
10831         return FALSE;
10832
10833       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10834       {
10835         int ex = x + xx - 1;
10836         int ey = y + yy - 1;
10837         int content_element;
10838
10839         if (can_replace[xx][yy] && (!change->use_random_replace ||
10840                                     RND(100) < change->random_percentage))
10841         {
10842           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10843             RemoveMovingField(ex, ey);
10844
10845           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10846
10847           content_element = change->target_content.e[xx][yy];
10848           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10849                                               ce_value, ce_score);
10850
10851           CreateElementFromChange(ex, ey, target_element);
10852
10853           something_has_changed = TRUE;
10854
10855           // for symmetry reasons, freeze newly created border elements
10856           if (ex != x || ey != y)
10857             Stop[ex][ey] = TRUE;        // no more moving in this frame
10858         }
10859       }
10860
10861       if (something_has_changed)
10862       {
10863         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10864         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10865       }
10866     }
10867   }
10868   else
10869   {
10870     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10871                                         ce_value, ce_score);
10872
10873     if (element == EL_DIAGONAL_GROWING ||
10874         element == EL_DIAGONAL_SHRINKING)
10875     {
10876       target_element = Store[x][y];
10877
10878       Store[x][y] = EL_EMPTY;
10879     }
10880
10881     // special case: element changes to player (and may be kept if walkable)
10882     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10883       CreateElementFromChange(x, y, EL_EMPTY);
10884
10885     CreateElementFromChange(x, y, target_element);
10886
10887     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10888     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10889   }
10890
10891   // this uses direct change before indirect change
10892   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10893
10894   return TRUE;
10895 }
10896
10897 static void HandleElementChange(int x, int y, int page)
10898 {
10899   int element = MovingOrBlocked2Element(x, y);
10900   struct ElementInfo *ei = &element_info[element];
10901   struct ElementChangeInfo *change = &ei->change_page[page];
10902   boolean handle_action_before_change = FALSE;
10903
10904 #ifdef DEBUG
10905   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10906       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10907   {
10908     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10909           x, y, element, element_info[element].token_name);
10910     Debug("game:playing:HandleElementChange", "This should never happen!");
10911   }
10912 #endif
10913
10914   // this can happen with classic bombs on walkable, changing elements
10915   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10916   {
10917     return;
10918   }
10919
10920   if (ChangeDelay[x][y] == 0)           // initialize element change
10921   {
10922     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10923
10924     if (change->can_change)
10925     {
10926       // !!! not clear why graphic animation should be reset at all here !!!
10927       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10928       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10929
10930       /*
10931         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10932
10933         When using an animation frame delay of 1 (this only happens with
10934         "sp_zonk.moving.left/right" in the classic graphics), the default
10935         (non-moving) animation shows wrong animation frames (while the
10936         moving animation, like "sp_zonk.moving.left/right", is correct,
10937         so this graphical bug never shows up with the classic graphics).
10938         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10939         be drawn instead of the correct frames 0,1,2,3. This is caused by
10940         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10941         an element change: First when the change delay ("ChangeDelay[][]")
10942         counter has reached zero after decrementing, then a second time in
10943         the next frame (after "GfxFrame[][]" was already incremented) when
10944         "ChangeDelay[][]" is reset to the initial delay value again.
10945
10946         This causes frame 0 to be drawn twice, while the last frame won't
10947         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10948
10949         As some animations may already be cleverly designed around this bug
10950         (at least the "Snake Bite" snake tail animation does this), it cannot
10951         simply be fixed here without breaking such existing animations.
10952         Unfortunately, it cannot easily be detected if a graphics set was
10953         designed "before" or "after" the bug was fixed. As a workaround,
10954         a new graphics set option "game.graphics_engine_version" was added
10955         to be able to specify the game's major release version for which the
10956         graphics set was designed, which can then be used to decide if the
10957         bugfix should be used (version 4 and above) or not (version 3 or
10958         below, or if no version was specified at all, as with old sets).
10959
10960         (The wrong/fixed animation frames can be tested with the test level set
10961         "test_gfxframe" and level "000", which contains a specially prepared
10962         custom element at level position (x/y) == (11/9) which uses the zonk
10963         animation mentioned above. Using "game.graphics_engine_version: 4"
10964         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10965         This can also be seen from the debug output for this test element.)
10966       */
10967
10968       // when a custom element is about to change (for example by change delay),
10969       // do not reset graphic animation when the custom element is moving
10970       if (game.graphics_engine_version < 4 &&
10971           !IS_MOVING(x, y))
10972       {
10973         ResetGfxAnimation(x, y);
10974         ResetRandomAnimationValue(x, y);
10975       }
10976
10977       if (change->pre_change_function)
10978         change->pre_change_function(x, y);
10979     }
10980   }
10981
10982   ChangeDelay[x][y]--;
10983
10984   if (ChangeDelay[x][y] != 0)           // continue element change
10985   {
10986     if (change->can_change)
10987     {
10988       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10989
10990       if (IS_ANIMATED(graphic))
10991         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10992
10993       if (change->change_function)
10994         change->change_function(x, y);
10995     }
10996   }
10997   else                                  // finish element change
10998   {
10999     if (ChangePage[x][y] != -1)         // remember page from delayed change
11000     {
11001       page = ChangePage[x][y];
11002       ChangePage[x][y] = -1;
11003
11004       change = &ei->change_page[page];
11005     }
11006
11007     if (IS_MOVING(x, y))                // never change a running system ;-)
11008     {
11009       ChangeDelay[x][y] = 1;            // try change after next move step
11010       ChangePage[x][y] = page;          // remember page to use for change
11011
11012       return;
11013     }
11014
11015     // special case: set new level random seed before changing element
11016     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11017       handle_action_before_change = TRUE;
11018
11019     if (change->has_action && handle_action_before_change)
11020       ExecuteCustomElementAction(x, y, element, page);
11021
11022     if (change->can_change)
11023     {
11024       if (ChangeElement(x, y, element, page))
11025       {
11026         if (change->post_change_function)
11027           change->post_change_function(x, y);
11028       }
11029     }
11030
11031     if (change->has_action && !handle_action_before_change)
11032       ExecuteCustomElementAction(x, y, element, page);
11033   }
11034 }
11035
11036 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11037                                               int trigger_element,
11038                                               int trigger_event,
11039                                               int trigger_player,
11040                                               int trigger_side,
11041                                               int trigger_page)
11042 {
11043   boolean change_done_any = FALSE;
11044   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11045   int i;
11046
11047   if (!(trigger_events[trigger_element][trigger_event]))
11048     return FALSE;
11049
11050   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11051
11052   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11053   {
11054     int element = EL_CUSTOM_START + i;
11055     boolean change_done = FALSE;
11056     int p;
11057
11058     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11059         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11060       continue;
11061
11062     for (p = 0; p < element_info[element].num_change_pages; p++)
11063     {
11064       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11065
11066       if (change->can_change_or_has_action &&
11067           change->has_event[trigger_event] &&
11068           change->trigger_side & trigger_side &&
11069           change->trigger_player & trigger_player &&
11070           change->trigger_page & trigger_page_bits &&
11071           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11072       {
11073         change->actual_trigger_element = trigger_element;
11074         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11075         change->actual_trigger_player_bits = trigger_player;
11076         change->actual_trigger_side = trigger_side;
11077         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11078         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11079
11080         if ((change->can_change && !change_done) || change->has_action)
11081         {
11082           int x, y;
11083
11084           SCAN_PLAYFIELD(x, y)
11085           {
11086             if (Tile[x][y] == element)
11087             {
11088               if (change->can_change && !change_done)
11089               {
11090                 // if element already changed in this frame, not only prevent
11091                 // another element change (checked in ChangeElement()), but
11092                 // also prevent additional element actions for this element
11093
11094                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11095                     !level.use_action_after_change_bug)
11096                   continue;
11097
11098                 ChangeDelay[x][y] = 1;
11099                 ChangeEvent[x][y] = trigger_event;
11100
11101                 HandleElementChange(x, y, p);
11102               }
11103               else if (change->has_action)
11104               {
11105                 // if element already changed in this frame, not only prevent
11106                 // another element change (checked in ChangeElement()), but
11107                 // also prevent additional element actions for this element
11108
11109                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11110                     !level.use_action_after_change_bug)
11111                   continue;
11112
11113                 ExecuteCustomElementAction(x, y, element, p);
11114                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11115               }
11116             }
11117           }
11118
11119           if (change->can_change)
11120           {
11121             change_done = TRUE;
11122             change_done_any = TRUE;
11123           }
11124         }
11125       }
11126     }
11127   }
11128
11129   RECURSION_LOOP_DETECTION_END();
11130
11131   return change_done_any;
11132 }
11133
11134 static boolean CheckElementChangeExt(int x, int y,
11135                                      int element,
11136                                      int trigger_element,
11137                                      int trigger_event,
11138                                      int trigger_player,
11139                                      int trigger_side)
11140 {
11141   boolean change_done = FALSE;
11142   int p;
11143
11144   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11145       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11146     return FALSE;
11147
11148   if (Tile[x][y] == EL_BLOCKED)
11149   {
11150     Blocked2Moving(x, y, &x, &y);
11151     element = Tile[x][y];
11152   }
11153
11154   // check if element has already changed or is about to change after moving
11155   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11156        Tile[x][y] != element) ||
11157
11158       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11159        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11160         ChangePage[x][y] != -1)))
11161     return FALSE;
11162
11163   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11164
11165   for (p = 0; p < element_info[element].num_change_pages; p++)
11166   {
11167     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11168
11169     /* check trigger element for all events where the element that is checked
11170        for changing interacts with a directly adjacent element -- this is
11171        different to element changes that affect other elements to change on the
11172        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11173     boolean check_trigger_element =
11174       (trigger_event == CE_NEXT_TO_X ||
11175        trigger_event == CE_TOUCHING_X ||
11176        trigger_event == CE_HITTING_X ||
11177        trigger_event == CE_HIT_BY_X ||
11178        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11179
11180     if (change->can_change_or_has_action &&
11181         change->has_event[trigger_event] &&
11182         change->trigger_side & trigger_side &&
11183         change->trigger_player & trigger_player &&
11184         (!check_trigger_element ||
11185          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11186     {
11187       change->actual_trigger_element = trigger_element;
11188       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11189       change->actual_trigger_player_bits = trigger_player;
11190       change->actual_trigger_side = trigger_side;
11191       change->actual_trigger_ce_value = CustomValue[x][y];
11192       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11193
11194       // special case: trigger element not at (x,y) position for some events
11195       if (check_trigger_element)
11196       {
11197         static struct
11198         {
11199           int dx, dy;
11200         } move_xy[] =
11201           {
11202             {  0,  0 },
11203             { -1,  0 },
11204             { +1,  0 },
11205             {  0,  0 },
11206             {  0, -1 },
11207             {  0,  0 }, { 0, 0 }, { 0, 0 },
11208             {  0, +1 }
11209           };
11210
11211         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11212         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11213
11214         change->actual_trigger_ce_value = CustomValue[xx][yy];
11215         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11216       }
11217
11218       if (change->can_change && !change_done)
11219       {
11220         ChangeDelay[x][y] = 1;
11221         ChangeEvent[x][y] = trigger_event;
11222
11223         HandleElementChange(x, y, p);
11224
11225         change_done = TRUE;
11226       }
11227       else if (change->has_action)
11228       {
11229         ExecuteCustomElementAction(x, y, element, p);
11230         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11231       }
11232     }
11233   }
11234
11235   RECURSION_LOOP_DETECTION_END();
11236
11237   return change_done;
11238 }
11239
11240 static void PlayPlayerSound(struct PlayerInfo *player)
11241 {
11242   int jx = player->jx, jy = player->jy;
11243   int sound_element = player->artwork_element;
11244   int last_action = player->last_action_waiting;
11245   int action = player->action_waiting;
11246
11247   if (player->is_waiting)
11248   {
11249     if (action != last_action)
11250       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11251     else
11252       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11253   }
11254   else
11255   {
11256     if (action != last_action)
11257       StopSound(element_info[sound_element].sound[last_action]);
11258
11259     if (last_action == ACTION_SLEEPING)
11260       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11261   }
11262 }
11263
11264 static void PlayAllPlayersSound(void)
11265 {
11266   int i;
11267
11268   for (i = 0; i < MAX_PLAYERS; i++)
11269     if (stored_player[i].active)
11270       PlayPlayerSound(&stored_player[i]);
11271 }
11272
11273 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11274 {
11275   boolean last_waiting = player->is_waiting;
11276   int move_dir = player->MovDir;
11277
11278   player->dir_waiting = move_dir;
11279   player->last_action_waiting = player->action_waiting;
11280
11281   if (is_waiting)
11282   {
11283     if (!last_waiting)          // not waiting -> waiting
11284     {
11285       player->is_waiting = TRUE;
11286
11287       player->frame_counter_bored =
11288         FrameCounter +
11289         game.player_boring_delay_fixed +
11290         GetSimpleRandom(game.player_boring_delay_random);
11291       player->frame_counter_sleeping =
11292         FrameCounter +
11293         game.player_sleeping_delay_fixed +
11294         GetSimpleRandom(game.player_sleeping_delay_random);
11295
11296       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11297     }
11298
11299     if (game.player_sleeping_delay_fixed +
11300         game.player_sleeping_delay_random > 0 &&
11301         player->anim_delay_counter == 0 &&
11302         player->post_delay_counter == 0 &&
11303         FrameCounter >= player->frame_counter_sleeping)
11304       player->is_sleeping = TRUE;
11305     else if (game.player_boring_delay_fixed +
11306              game.player_boring_delay_random > 0 &&
11307              FrameCounter >= player->frame_counter_bored)
11308       player->is_bored = TRUE;
11309
11310     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11311                               player->is_bored ? ACTION_BORING :
11312                               ACTION_WAITING);
11313
11314     if (player->is_sleeping && player->use_murphy)
11315     {
11316       // special case for sleeping Murphy when leaning against non-free tile
11317
11318       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11319           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11320            !IS_MOVING(player->jx - 1, player->jy)))
11321         move_dir = MV_LEFT;
11322       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11323                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11324                 !IS_MOVING(player->jx + 1, player->jy)))
11325         move_dir = MV_RIGHT;
11326       else
11327         player->is_sleeping = FALSE;
11328
11329       player->dir_waiting = move_dir;
11330     }
11331
11332     if (player->is_sleeping)
11333     {
11334       if (player->num_special_action_sleeping > 0)
11335       {
11336         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11337         {
11338           int last_special_action = player->special_action_sleeping;
11339           int num_special_action = player->num_special_action_sleeping;
11340           int special_action =
11341             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11342              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11343              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11344              last_special_action + 1 : ACTION_SLEEPING);
11345           int special_graphic =
11346             el_act_dir2img(player->artwork_element, special_action, move_dir);
11347
11348           player->anim_delay_counter =
11349             graphic_info[special_graphic].anim_delay_fixed +
11350             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11351           player->post_delay_counter =
11352             graphic_info[special_graphic].post_delay_fixed +
11353             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11354
11355           player->special_action_sleeping = special_action;
11356         }
11357
11358         if (player->anim_delay_counter > 0)
11359         {
11360           player->action_waiting = player->special_action_sleeping;
11361           player->anim_delay_counter--;
11362         }
11363         else if (player->post_delay_counter > 0)
11364         {
11365           player->post_delay_counter--;
11366         }
11367       }
11368     }
11369     else if (player->is_bored)
11370     {
11371       if (player->num_special_action_bored > 0)
11372       {
11373         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11374         {
11375           int special_action =
11376             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11377           int special_graphic =
11378             el_act_dir2img(player->artwork_element, special_action, move_dir);
11379
11380           player->anim_delay_counter =
11381             graphic_info[special_graphic].anim_delay_fixed +
11382             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11383           player->post_delay_counter =
11384             graphic_info[special_graphic].post_delay_fixed +
11385             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11386
11387           player->special_action_bored = special_action;
11388         }
11389
11390         if (player->anim_delay_counter > 0)
11391         {
11392           player->action_waiting = player->special_action_bored;
11393           player->anim_delay_counter--;
11394         }
11395         else if (player->post_delay_counter > 0)
11396         {
11397           player->post_delay_counter--;
11398         }
11399       }
11400     }
11401   }
11402   else if (last_waiting)        // waiting -> not waiting
11403   {
11404     player->is_waiting = FALSE;
11405     player->is_bored = FALSE;
11406     player->is_sleeping = FALSE;
11407
11408     player->frame_counter_bored = -1;
11409     player->frame_counter_sleeping = -1;
11410
11411     player->anim_delay_counter = 0;
11412     player->post_delay_counter = 0;
11413
11414     player->dir_waiting = player->MovDir;
11415     player->action_waiting = ACTION_DEFAULT;
11416
11417     player->special_action_bored = ACTION_DEFAULT;
11418     player->special_action_sleeping = ACTION_DEFAULT;
11419   }
11420 }
11421
11422 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11423 {
11424   if ((!player->is_moving  && player->was_moving) ||
11425       (player->MovPos == 0 && player->was_moving) ||
11426       (player->is_snapping && !player->was_snapping) ||
11427       (player->is_dropping && !player->was_dropping))
11428   {
11429     if (!CheckSaveEngineSnapshotToList())
11430       return;
11431
11432     player->was_moving = FALSE;
11433     player->was_snapping = TRUE;
11434     player->was_dropping = TRUE;
11435   }
11436   else
11437   {
11438     if (player->is_moving)
11439       player->was_moving = TRUE;
11440
11441     if (!player->is_snapping)
11442       player->was_snapping = FALSE;
11443
11444     if (!player->is_dropping)
11445       player->was_dropping = FALSE;
11446   }
11447
11448   static struct MouseActionInfo mouse_action_last = { 0 };
11449   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11450   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11451
11452   if (new_released)
11453     CheckSaveEngineSnapshotToList();
11454
11455   mouse_action_last = mouse_action;
11456 }
11457
11458 static void CheckSingleStepMode(struct PlayerInfo *player)
11459 {
11460   if (tape.single_step && tape.recording && !tape.pausing)
11461   {
11462     // as it is called "single step mode", just return to pause mode when the
11463     // player stopped moving after one tile (or never starts moving at all)
11464     // (reverse logic needed here in case single step mode used in team mode)
11465     if (player->is_moving ||
11466         player->is_pushing ||
11467         player->is_dropping_pressed ||
11468         player->effective_mouse_action.button)
11469       game.enter_single_step_mode = FALSE;
11470   }
11471
11472   CheckSaveEngineSnapshot(player);
11473 }
11474
11475 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11476 {
11477   int left      = player_action & JOY_LEFT;
11478   int right     = player_action & JOY_RIGHT;
11479   int up        = player_action & JOY_UP;
11480   int down      = player_action & JOY_DOWN;
11481   int button1   = player_action & JOY_BUTTON_1;
11482   int button2   = player_action & JOY_BUTTON_2;
11483   int dx        = (left ? -1 : right ? 1 : 0);
11484   int dy        = (up   ? -1 : down  ? 1 : 0);
11485
11486   if (!player->active || tape.pausing)
11487     return 0;
11488
11489   if (player_action)
11490   {
11491     if (button1)
11492       SnapField(player, dx, dy);
11493     else
11494     {
11495       if (button2)
11496         DropElement(player);
11497
11498       MovePlayer(player, dx, dy);
11499     }
11500
11501     CheckSingleStepMode(player);
11502
11503     SetPlayerWaiting(player, FALSE);
11504
11505     return player_action;
11506   }
11507   else
11508   {
11509     // no actions for this player (no input at player's configured device)
11510
11511     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11512     SnapField(player, 0, 0);
11513     CheckGravityMovementWhenNotMoving(player);
11514
11515     if (player->MovPos == 0)
11516       SetPlayerWaiting(player, TRUE);
11517
11518     if (player->MovPos == 0)    // needed for tape.playing
11519       player->is_moving = FALSE;
11520
11521     player->is_dropping = FALSE;
11522     player->is_dropping_pressed = FALSE;
11523     player->drop_pressed_delay = 0;
11524
11525     CheckSingleStepMode(player);
11526
11527     return 0;
11528   }
11529 }
11530
11531 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11532                                          byte *tape_action)
11533 {
11534   if (!tape.use_mouse_actions)
11535     return;
11536
11537   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11538   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11539   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11540 }
11541
11542 static void SetTapeActionFromMouseAction(byte *tape_action,
11543                                          struct MouseActionInfo *mouse_action)
11544 {
11545   if (!tape.use_mouse_actions)
11546     return;
11547
11548   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11549   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11550   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11551 }
11552
11553 static void CheckLevelSolved(void)
11554 {
11555   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11556   {
11557     if (game_em.level_solved &&
11558         !game_em.game_over)                             // game won
11559     {
11560       LevelSolved();
11561
11562       game_em.game_over = TRUE;
11563
11564       game.all_players_gone = TRUE;
11565     }
11566
11567     if (game_em.game_over)                              // game lost
11568       game.all_players_gone = TRUE;
11569   }
11570   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11571   {
11572     if (game_sp.level_solved &&
11573         !game_sp.game_over)                             // game won
11574     {
11575       LevelSolved();
11576
11577       game_sp.game_over = TRUE;
11578
11579       game.all_players_gone = TRUE;
11580     }
11581
11582     if (game_sp.game_over)                              // game lost
11583       game.all_players_gone = TRUE;
11584   }
11585   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11586   {
11587     if (game_mm.level_solved &&
11588         !game_mm.game_over)                             // game won
11589     {
11590       LevelSolved();
11591
11592       game_mm.game_over = TRUE;
11593
11594       game.all_players_gone = TRUE;
11595     }
11596
11597     if (game_mm.game_over)                              // game lost
11598       game.all_players_gone = TRUE;
11599   }
11600 }
11601
11602 static void CheckLevelTime_StepCounter(void)
11603 {
11604   int i;
11605
11606   TimePlayed++;
11607
11608   if (TimeLeft > 0)
11609   {
11610     TimeLeft--;
11611
11612     if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
11613       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11614
11615     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11616
11617     DisplayGameControlValues();
11618
11619     if (!TimeLeft && setup.time_limit && !game.LevelSolved)
11620       for (i = 0; i < MAX_PLAYERS; i++)
11621         KillPlayer(&stored_player[i]);
11622   }
11623   else if (game.no_time_limit && !game.all_players_gone)
11624   {
11625     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11626
11627     DisplayGameControlValues();
11628   }
11629 }
11630
11631 static void CheckLevelTime(void)
11632 {
11633   int i;
11634
11635   if (TimeFrames >= FRAMES_PER_SECOND)
11636   {
11637     TimeFrames = 0;
11638     TapeTime++;
11639
11640     for (i = 0; i < MAX_PLAYERS; i++)
11641     {
11642       struct PlayerInfo *player = &stored_player[i];
11643
11644       if (SHIELD_ON(player))
11645       {
11646         player->shield_normal_time_left--;
11647
11648         if (player->shield_deadly_time_left > 0)
11649           player->shield_deadly_time_left--;
11650       }
11651     }
11652
11653     if (!game.LevelSolved && !level.use_step_counter)
11654     {
11655       TimePlayed++;
11656
11657       if (TimeLeft > 0)
11658       {
11659         TimeLeft--;
11660
11661         if (TimeLeft <= 10 && setup.time_limit)
11662           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11663
11664         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11665            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11666
11667         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11668
11669         if (!TimeLeft && setup.time_limit)
11670         {
11671           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11672             game_em.lev->killed_out_of_time = TRUE;
11673           else
11674             for (i = 0; i < MAX_PLAYERS; i++)
11675               KillPlayer(&stored_player[i]);
11676         }
11677       }
11678       else if (game.no_time_limit && !game.all_players_gone)
11679       {
11680         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11681       }
11682
11683       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11684     }
11685
11686     if (tape.recording || tape.playing)
11687       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11688   }
11689
11690   if (tape.recording || tape.playing)
11691     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11692
11693   UpdateAndDisplayGameControlValues();
11694 }
11695
11696 void AdvanceFrameAndPlayerCounters(int player_nr)
11697 {
11698   int i;
11699
11700   // advance frame counters (global frame counter and time frame counter)
11701   FrameCounter++;
11702   TimeFrames++;
11703
11704   // advance player counters (counters for move delay, move animation etc.)
11705   for (i = 0; i < MAX_PLAYERS; i++)
11706   {
11707     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11708     int move_delay_value = stored_player[i].move_delay_value;
11709     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11710
11711     if (!advance_player_counters)       // not all players may be affected
11712       continue;
11713
11714     if (move_frames == 0)       // less than one move per game frame
11715     {
11716       int stepsize = TILEX / move_delay_value;
11717       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11718       int count = (stored_player[i].is_moving ?
11719                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11720
11721       if (count % delay == 0)
11722         move_frames = 1;
11723     }
11724
11725     stored_player[i].Frame += move_frames;
11726
11727     if (stored_player[i].MovPos != 0)
11728       stored_player[i].StepFrame += move_frames;
11729
11730     if (stored_player[i].move_delay > 0)
11731       stored_player[i].move_delay--;
11732
11733     // due to bugs in previous versions, counter must count up, not down
11734     if (stored_player[i].push_delay != -1)
11735       stored_player[i].push_delay++;
11736
11737     if (stored_player[i].drop_delay > 0)
11738       stored_player[i].drop_delay--;
11739
11740     if (stored_player[i].is_dropping_pressed)
11741       stored_player[i].drop_pressed_delay++;
11742   }
11743 }
11744
11745 void StartGameActions(boolean init_network_game, boolean record_tape,
11746                       int random_seed)
11747 {
11748   unsigned int new_random_seed = InitRND(random_seed);
11749
11750   if (record_tape)
11751     TapeStartRecording(new_random_seed);
11752
11753   if (init_network_game)
11754   {
11755     SendToServer_LevelFile();
11756     SendToServer_StartPlaying();
11757
11758     return;
11759   }
11760
11761   InitGame();
11762 }
11763
11764 static void GameActionsExt(void)
11765 {
11766 #if 0
11767   static unsigned int game_frame_delay = 0;
11768 #endif
11769   unsigned int game_frame_delay_value;
11770   byte *recorded_player_action;
11771   byte summarized_player_action = 0;
11772   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11773   int i;
11774
11775   // detect endless loops, caused by custom element programming
11776   if (recursion_loop_detected && recursion_loop_depth == 0)
11777   {
11778     char *message = getStringCat3("Internal Error! Element ",
11779                                   EL_NAME(recursion_loop_element),
11780                                   " caused endless loop! Quit the game?");
11781
11782     Warn("element '%s' caused endless loop in game engine",
11783          EL_NAME(recursion_loop_element));
11784
11785     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11786
11787     recursion_loop_detected = FALSE;    // if game should be continued
11788
11789     free(message);
11790
11791     return;
11792   }
11793
11794   if (game.restart_level)
11795     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11796
11797   CheckLevelSolved();
11798
11799   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11800     GameWon();
11801
11802   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11803     TapeStop();
11804
11805   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11806     return;
11807
11808   game_frame_delay_value =
11809     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11810
11811   if (tape.playing && tape.warp_forward && !tape.pausing)
11812     game_frame_delay_value = 0;
11813
11814   SetVideoFrameDelay(game_frame_delay_value);
11815
11816   // (de)activate virtual buttons depending on current game status
11817   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11818   {
11819     if (game.all_players_gone)  // if no players there to be controlled anymore
11820       SetOverlayActive(FALSE);
11821     else if (!tape.playing)     // if game continues after tape stopped playing
11822       SetOverlayActive(TRUE);
11823   }
11824
11825 #if 0
11826 #if 0
11827   // ---------- main game synchronization point ----------
11828
11829   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11830
11831   Debug("game:playing:skip", "skip == %d", skip);
11832
11833 #else
11834   // ---------- main game synchronization point ----------
11835
11836   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11837 #endif
11838 #endif
11839
11840   if (network_playing && !network_player_action_received)
11841   {
11842     // try to get network player actions in time
11843
11844     // last chance to get network player actions without main loop delay
11845     HandleNetworking();
11846
11847     // game was quit by network peer
11848     if (game_status != GAME_MODE_PLAYING)
11849       return;
11850
11851     // check if network player actions still missing and game still running
11852     if (!network_player_action_received && !checkGameEnded())
11853       return;           // failed to get network player actions in time
11854
11855     // do not yet reset "network_player_action_received" (for tape.pausing)
11856   }
11857
11858   if (tape.pausing)
11859     return;
11860
11861   // at this point we know that we really continue executing the game
11862
11863   network_player_action_received = FALSE;
11864
11865   // when playing tape, read previously recorded player input from tape data
11866   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11867
11868   local_player->effective_mouse_action = local_player->mouse_action;
11869
11870   if (recorded_player_action != NULL)
11871     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11872                                  recorded_player_action);
11873
11874   // TapePlayAction() may return NULL when toggling to "pause before death"
11875   if (tape.pausing)
11876     return;
11877
11878   if (tape.set_centered_player)
11879   {
11880     game.centered_player_nr_next = tape.centered_player_nr_next;
11881     game.set_centered_player = TRUE;
11882   }
11883
11884   for (i = 0; i < MAX_PLAYERS; i++)
11885   {
11886     summarized_player_action |= stored_player[i].action;
11887
11888     if (!network_playing && (game.team_mode || tape.playing))
11889       stored_player[i].effective_action = stored_player[i].action;
11890   }
11891
11892   if (network_playing && !checkGameEnded())
11893     SendToServer_MovePlayer(summarized_player_action);
11894
11895   // summarize all actions at local players mapped input device position
11896   // (this allows using different input devices in single player mode)
11897   if (!network.enabled && !game.team_mode)
11898     stored_player[map_player_action[local_player->index_nr]].effective_action =
11899       summarized_player_action;
11900
11901   // summarize all actions at centered player in local team mode
11902   if (tape.recording &&
11903       setup.team_mode && !network.enabled &&
11904       setup.input_on_focus &&
11905       game.centered_player_nr != -1)
11906   {
11907     for (i = 0; i < MAX_PLAYERS; i++)
11908       stored_player[map_player_action[i]].effective_action =
11909         (i == game.centered_player_nr ? summarized_player_action : 0);
11910   }
11911
11912   if (recorded_player_action != NULL)
11913     for (i = 0; i < MAX_PLAYERS; i++)
11914       stored_player[i].effective_action = recorded_player_action[i];
11915
11916   for (i = 0; i < MAX_PLAYERS; i++)
11917   {
11918     tape_action[i] = stored_player[i].effective_action;
11919
11920     /* (this may happen in the RND game engine if a player was not present on
11921        the playfield on level start, but appeared later from a custom element */
11922     if (setup.team_mode &&
11923         tape.recording &&
11924         tape_action[i] &&
11925         !tape.player_participates[i])
11926       tape.player_participates[i] = TRUE;
11927   }
11928
11929   SetTapeActionFromMouseAction(tape_action,
11930                                &local_player->effective_mouse_action);
11931
11932   // only record actions from input devices, but not programmed actions
11933   if (tape.recording)
11934     TapeRecordAction(tape_action);
11935
11936   // remember if game was played (especially after tape stopped playing)
11937   if (!tape.playing && summarized_player_action)
11938     game.GamePlayed = TRUE;
11939
11940 #if USE_NEW_PLAYER_ASSIGNMENTS
11941   // !!! also map player actions in single player mode !!!
11942   // if (game.team_mode)
11943   if (1)
11944   {
11945     byte mapped_action[MAX_PLAYERS];
11946
11947 #if DEBUG_PLAYER_ACTIONS
11948     for (i = 0; i < MAX_PLAYERS; i++)
11949       DebugContinued("", "%d, ", stored_player[i].effective_action);
11950 #endif
11951
11952     for (i = 0; i < MAX_PLAYERS; i++)
11953       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11954
11955     for (i = 0; i < MAX_PLAYERS; i++)
11956       stored_player[i].effective_action = mapped_action[i];
11957
11958 #if DEBUG_PLAYER_ACTIONS
11959     DebugContinued("", "=> ");
11960     for (i = 0; i < MAX_PLAYERS; i++)
11961       DebugContinued("", "%d, ", stored_player[i].effective_action);
11962     DebugContinued("game:playing:player", "\n");
11963 #endif
11964   }
11965 #if DEBUG_PLAYER_ACTIONS
11966   else
11967   {
11968     for (i = 0; i < MAX_PLAYERS; i++)
11969       DebugContinued("", "%d, ", stored_player[i].effective_action);
11970     DebugContinued("game:playing:player", "\n");
11971   }
11972 #endif
11973 #endif
11974
11975   for (i = 0; i < MAX_PLAYERS; i++)
11976   {
11977     // allow engine snapshot in case of changed movement attempt
11978     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11979         (stored_player[i].effective_action & KEY_MOTION))
11980       game.snapshot.changed_action = TRUE;
11981
11982     // allow engine snapshot in case of snapping/dropping attempt
11983     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11984         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11985       game.snapshot.changed_action = TRUE;
11986
11987     game.snapshot.last_action[i] = stored_player[i].effective_action;
11988   }
11989
11990   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11991   {
11992     GameActions_EM_Main();
11993   }
11994   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11995   {
11996     GameActions_SP_Main();
11997   }
11998   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11999   {
12000     GameActions_MM_Main();
12001   }
12002   else
12003   {
12004     GameActions_RND_Main();
12005   }
12006
12007   BlitScreenToBitmap(backbuffer);
12008
12009   CheckLevelSolved();
12010   CheckLevelTime();
12011
12012   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12013
12014   if (global.show_frames_per_second)
12015   {
12016     static unsigned int fps_counter = 0;
12017     static int fps_frames = 0;
12018     unsigned int fps_delay_ms = Counter() - fps_counter;
12019
12020     fps_frames++;
12021
12022     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12023     {
12024       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12025
12026       fps_frames = 0;
12027       fps_counter = Counter();
12028
12029       // always draw FPS to screen after FPS value was updated
12030       redraw_mask |= REDRAW_FPS;
12031     }
12032
12033     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12034     if (GetDrawDeactivationMask() == REDRAW_NONE)
12035       redraw_mask |= REDRAW_FPS;
12036   }
12037 }
12038
12039 static void GameActions_CheckSaveEngineSnapshot(void)
12040 {
12041   if (!game.snapshot.save_snapshot)
12042     return;
12043
12044   // clear flag for saving snapshot _before_ saving snapshot
12045   game.snapshot.save_snapshot = FALSE;
12046
12047   SaveEngineSnapshotToList();
12048 }
12049
12050 void GameActions(void)
12051 {
12052   GameActionsExt();
12053
12054   GameActions_CheckSaveEngineSnapshot();
12055 }
12056
12057 void GameActions_EM_Main(void)
12058 {
12059   byte effective_action[MAX_PLAYERS];
12060   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12061   int i;
12062
12063   for (i = 0; i < MAX_PLAYERS; i++)
12064     effective_action[i] = stored_player[i].effective_action;
12065
12066   GameActions_EM(effective_action, warp_mode);
12067 }
12068
12069 void GameActions_SP_Main(void)
12070 {
12071   byte effective_action[MAX_PLAYERS];
12072   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12073   int i;
12074
12075   for (i = 0; i < MAX_PLAYERS; i++)
12076     effective_action[i] = stored_player[i].effective_action;
12077
12078   GameActions_SP(effective_action, warp_mode);
12079
12080   for (i = 0; i < MAX_PLAYERS; i++)
12081   {
12082     if (stored_player[i].force_dropping)
12083       stored_player[i].action |= KEY_BUTTON_DROP;
12084
12085     stored_player[i].force_dropping = FALSE;
12086   }
12087 }
12088
12089 void GameActions_MM_Main(void)
12090 {
12091   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12092
12093   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12094 }
12095
12096 void GameActions_RND_Main(void)
12097 {
12098   GameActions_RND();
12099 }
12100
12101 void GameActions_RND(void)
12102 {
12103   static struct MouseActionInfo mouse_action_last = { 0 };
12104   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12105   int magic_wall_x = 0, magic_wall_y = 0;
12106   int i, x, y, element, graphic, last_gfx_frame;
12107
12108   InitPlayfieldScanModeVars();
12109
12110   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12111   {
12112     SCAN_PLAYFIELD(x, y)
12113     {
12114       ChangeCount[x][y] = 0;
12115       ChangeEvent[x][y] = -1;
12116     }
12117   }
12118
12119   if (game.set_centered_player)
12120   {
12121     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12122
12123     // switching to "all players" only possible if all players fit to screen
12124     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12125     {
12126       game.centered_player_nr_next = game.centered_player_nr;
12127       game.set_centered_player = FALSE;
12128     }
12129
12130     // do not switch focus to non-existing (or non-active) player
12131     if (game.centered_player_nr_next >= 0 &&
12132         !stored_player[game.centered_player_nr_next].active)
12133     {
12134       game.centered_player_nr_next = game.centered_player_nr;
12135       game.set_centered_player = FALSE;
12136     }
12137   }
12138
12139   if (game.set_centered_player &&
12140       ScreenMovPos == 0)        // screen currently aligned at tile position
12141   {
12142     int sx, sy;
12143
12144     if (game.centered_player_nr_next == -1)
12145     {
12146       setScreenCenteredToAllPlayers(&sx, &sy);
12147     }
12148     else
12149     {
12150       sx = stored_player[game.centered_player_nr_next].jx;
12151       sy = stored_player[game.centered_player_nr_next].jy;
12152     }
12153
12154     game.centered_player_nr = game.centered_player_nr_next;
12155     game.set_centered_player = FALSE;
12156
12157     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12158     DrawGameDoorValues();
12159   }
12160
12161   // check single step mode (set flag and clear again if any player is active)
12162   game.enter_single_step_mode =
12163     (tape.single_step && tape.recording && !tape.pausing);
12164
12165   for (i = 0; i < MAX_PLAYERS; i++)
12166   {
12167     int actual_player_action = stored_player[i].effective_action;
12168
12169 #if 1
12170     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12171        - rnd_equinox_tetrachloride 048
12172        - rnd_equinox_tetrachloride_ii 096
12173        - rnd_emanuel_schmieg 002
12174        - doctor_sloan_ww 001, 020
12175     */
12176     if (stored_player[i].MovPos == 0)
12177       CheckGravityMovement(&stored_player[i]);
12178 #endif
12179
12180     // overwrite programmed action with tape action
12181     if (stored_player[i].programmed_action)
12182       actual_player_action = stored_player[i].programmed_action;
12183
12184     PlayerActions(&stored_player[i], actual_player_action);
12185
12186     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12187   }
12188
12189   // single step pause mode may already have been toggled by "ScrollPlayer()"
12190   if (game.enter_single_step_mode && !tape.pausing)
12191     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12192
12193   ScrollScreen(NULL, SCROLL_GO_ON);
12194
12195   /* for backwards compatibility, the following code emulates a fixed bug that
12196      occured when pushing elements (causing elements that just made their last
12197      pushing step to already (if possible) make their first falling step in the
12198      same game frame, which is bad); this code is also needed to use the famous
12199      "spring push bug" which is used in older levels and might be wanted to be
12200      used also in newer levels, but in this case the buggy pushing code is only
12201      affecting the "spring" element and no other elements */
12202
12203   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12204   {
12205     for (i = 0; i < MAX_PLAYERS; i++)
12206     {
12207       struct PlayerInfo *player = &stored_player[i];
12208       int x = player->jx;
12209       int y = player->jy;
12210
12211       if (player->active && player->is_pushing && player->is_moving &&
12212           IS_MOVING(x, y) &&
12213           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12214            Tile[x][y] == EL_SPRING))
12215       {
12216         ContinueMoving(x, y);
12217
12218         // continue moving after pushing (this is actually a bug)
12219         if (!IS_MOVING(x, y))
12220           Stop[x][y] = FALSE;
12221       }
12222     }
12223   }
12224
12225   SCAN_PLAYFIELD(x, y)
12226   {
12227     Last[x][y] = Tile[x][y];
12228
12229     ChangeCount[x][y] = 0;
12230     ChangeEvent[x][y] = -1;
12231
12232     // this must be handled before main playfield loop
12233     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12234     {
12235       MovDelay[x][y]--;
12236       if (MovDelay[x][y] <= 0)
12237         RemoveField(x, y);
12238     }
12239
12240     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12241     {
12242       MovDelay[x][y]--;
12243       if (MovDelay[x][y] <= 0)
12244       {
12245         int element = Store[x][y];
12246         int move_direction = MovDir[x][y];
12247         int player_index_bit = Store2[x][y];
12248
12249         Store[x][y] = 0;
12250         Store2[x][y] = 0;
12251
12252         RemoveField(x, y);
12253         TEST_DrawLevelField(x, y);
12254
12255         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12256
12257         if (IS_ENVELOPE(element))
12258           local_player->show_envelope = element;
12259       }
12260     }
12261
12262 #if DEBUG
12263     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12264     {
12265       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12266             x, y);
12267       Debug("game:playing:GameActions_RND", "This should never happen!");
12268
12269       ChangePage[x][y] = -1;
12270     }
12271 #endif
12272
12273     Stop[x][y] = FALSE;
12274     if (WasJustMoving[x][y] > 0)
12275       WasJustMoving[x][y]--;
12276     if (WasJustFalling[x][y] > 0)
12277       WasJustFalling[x][y]--;
12278     if (CheckCollision[x][y] > 0)
12279       CheckCollision[x][y]--;
12280     if (CheckImpact[x][y] > 0)
12281       CheckImpact[x][y]--;
12282
12283     GfxFrame[x][y]++;
12284
12285     /* reset finished pushing action (not done in ContinueMoving() to allow
12286        continuous pushing animation for elements with zero push delay) */
12287     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12288     {
12289       ResetGfxAnimation(x, y);
12290       TEST_DrawLevelField(x, y);
12291     }
12292
12293 #if DEBUG
12294     if (IS_BLOCKED(x, y))
12295     {
12296       int oldx, oldy;
12297
12298       Blocked2Moving(x, y, &oldx, &oldy);
12299       if (!IS_MOVING(oldx, oldy))
12300       {
12301         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12302         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12303         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12304         Debug("game:playing:GameActions_RND", "This should never happen!");
12305       }
12306     }
12307 #endif
12308   }
12309
12310   if (mouse_action.button)
12311   {
12312     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12313     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12314
12315     x = mouse_action.lx;
12316     y = mouse_action.ly;
12317     element = Tile[x][y];
12318
12319     if (new_button)
12320     {
12321       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12322       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12323                                          ch_button);
12324     }
12325
12326     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12327     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12328                                        ch_button);
12329   }
12330
12331   SCAN_PLAYFIELD(x, y)
12332   {
12333     element = Tile[x][y];
12334     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12335     last_gfx_frame = GfxFrame[x][y];
12336
12337     if (element == EL_EMPTY)
12338       graphic = el2img(GfxElementEmpty[x][y]);
12339
12340     ResetGfxFrame(x, y);
12341
12342     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12343       DrawLevelGraphicAnimation(x, y, graphic);
12344
12345     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12346         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12347       ResetRandomAnimationValue(x, y);
12348
12349     SetRandomAnimationValue(x, y);
12350
12351     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12352
12353     if (IS_INACTIVE(element))
12354     {
12355       if (IS_ANIMATED(graphic))
12356         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12357
12358       continue;
12359     }
12360
12361     // this may take place after moving, so 'element' may have changed
12362     if (IS_CHANGING(x, y) &&
12363         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12364     {
12365       int page = element_info[element].event_page_nr[CE_DELAY];
12366
12367       HandleElementChange(x, y, page);
12368
12369       element = Tile[x][y];
12370       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12371     }
12372
12373     CheckNextToConditions(x, y);
12374
12375     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12376     {
12377       StartMoving(x, y);
12378
12379       element = Tile[x][y];
12380       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12381
12382       if (IS_ANIMATED(graphic) &&
12383           !IS_MOVING(x, y) &&
12384           !Stop[x][y])
12385         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12386
12387       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12388         TEST_DrawTwinkleOnField(x, y);
12389     }
12390     else if (element == EL_ACID)
12391     {
12392       if (!Stop[x][y])
12393         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12394     }
12395     else if ((element == EL_EXIT_OPEN ||
12396               element == EL_EM_EXIT_OPEN ||
12397               element == EL_SP_EXIT_OPEN ||
12398               element == EL_STEEL_EXIT_OPEN ||
12399               element == EL_EM_STEEL_EXIT_OPEN ||
12400               element == EL_SP_TERMINAL ||
12401               element == EL_SP_TERMINAL_ACTIVE ||
12402               element == EL_EXTRA_TIME ||
12403               element == EL_SHIELD_NORMAL ||
12404               element == EL_SHIELD_DEADLY) &&
12405              IS_ANIMATED(graphic))
12406       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12407     else if (IS_MOVING(x, y))
12408       ContinueMoving(x, y);
12409     else if (IS_ACTIVE_BOMB(element))
12410       CheckDynamite(x, y);
12411     else if (element == EL_AMOEBA_GROWING)
12412       AmoebaGrowing(x, y);
12413     else if (element == EL_AMOEBA_SHRINKING)
12414       AmoebaShrinking(x, y);
12415
12416 #if !USE_NEW_AMOEBA_CODE
12417     else if (IS_AMOEBALIVE(element))
12418       AmoebaReproduce(x, y);
12419 #endif
12420
12421     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12422       Life(x, y);
12423     else if (element == EL_EXIT_CLOSED)
12424       CheckExit(x, y);
12425     else if (element == EL_EM_EXIT_CLOSED)
12426       CheckExitEM(x, y);
12427     else if (element == EL_STEEL_EXIT_CLOSED)
12428       CheckExitSteel(x, y);
12429     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12430       CheckExitSteelEM(x, y);
12431     else if (element == EL_SP_EXIT_CLOSED)
12432       CheckExitSP(x, y);
12433     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12434              element == EL_EXPANDABLE_STEELWALL_GROWING)
12435       MauerWaechst(x, y);
12436     else if (element == EL_EXPANDABLE_WALL ||
12437              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12438              element == EL_EXPANDABLE_WALL_VERTICAL ||
12439              element == EL_EXPANDABLE_WALL_ANY ||
12440              element == EL_BD_EXPANDABLE_WALL)
12441       MauerAbleger(x, y);
12442     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12443              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12444              element == EL_EXPANDABLE_STEELWALL_ANY)
12445       MauerAblegerStahl(x, y);
12446     else if (element == EL_FLAMES)
12447       CheckForDragon(x, y);
12448     else if (element == EL_EXPLOSION)
12449       ; // drawing of correct explosion animation is handled separately
12450     else if (element == EL_ELEMENT_SNAPPING ||
12451              element == EL_DIAGONAL_SHRINKING ||
12452              element == EL_DIAGONAL_GROWING)
12453     {
12454       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12455
12456       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12457     }
12458     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12459       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12460
12461     if (IS_BELT_ACTIVE(element))
12462       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12463
12464     if (game.magic_wall_active)
12465     {
12466       int jx = local_player->jx, jy = local_player->jy;
12467
12468       // play the element sound at the position nearest to the player
12469       if ((element == EL_MAGIC_WALL_FULL ||
12470            element == EL_MAGIC_WALL_ACTIVE ||
12471            element == EL_MAGIC_WALL_EMPTYING ||
12472            element == EL_BD_MAGIC_WALL_FULL ||
12473            element == EL_BD_MAGIC_WALL_ACTIVE ||
12474            element == EL_BD_MAGIC_WALL_EMPTYING ||
12475            element == EL_DC_MAGIC_WALL_FULL ||
12476            element == EL_DC_MAGIC_WALL_ACTIVE ||
12477            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12478           ABS(x - jx) + ABS(y - jy) <
12479           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12480       {
12481         magic_wall_x = x;
12482         magic_wall_y = y;
12483       }
12484     }
12485   }
12486
12487 #if USE_NEW_AMOEBA_CODE
12488   // new experimental amoeba growth stuff
12489   if (!(FrameCounter % 8))
12490   {
12491     static unsigned int random = 1684108901;
12492
12493     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12494     {
12495       x = RND(lev_fieldx);
12496       y = RND(lev_fieldy);
12497       element = Tile[x][y];
12498
12499       if (!IS_PLAYER(x,y) &&
12500           (element == EL_EMPTY ||
12501            CAN_GROW_INTO(element) ||
12502            element == EL_QUICKSAND_EMPTY ||
12503            element == EL_QUICKSAND_FAST_EMPTY ||
12504            element == EL_ACID_SPLASH_LEFT ||
12505            element == EL_ACID_SPLASH_RIGHT))
12506       {
12507         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12508             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12509             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12510             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12511           Tile[x][y] = EL_AMOEBA_DROP;
12512       }
12513
12514       random = random * 129 + 1;
12515     }
12516   }
12517 #endif
12518
12519   game.explosions_delayed = FALSE;
12520
12521   SCAN_PLAYFIELD(x, y)
12522   {
12523     element = Tile[x][y];
12524
12525     if (ExplodeField[x][y])
12526       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12527     else if (element == EL_EXPLOSION)
12528       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12529
12530     ExplodeField[x][y] = EX_TYPE_NONE;
12531   }
12532
12533   game.explosions_delayed = TRUE;
12534
12535   if (game.magic_wall_active)
12536   {
12537     if (!(game.magic_wall_time_left % 4))
12538     {
12539       int element = Tile[magic_wall_x][magic_wall_y];
12540
12541       if (element == EL_BD_MAGIC_WALL_FULL ||
12542           element == EL_BD_MAGIC_WALL_ACTIVE ||
12543           element == EL_BD_MAGIC_WALL_EMPTYING)
12544         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12545       else if (element == EL_DC_MAGIC_WALL_FULL ||
12546                element == EL_DC_MAGIC_WALL_ACTIVE ||
12547                element == EL_DC_MAGIC_WALL_EMPTYING)
12548         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12549       else
12550         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12551     }
12552
12553     if (game.magic_wall_time_left > 0)
12554     {
12555       game.magic_wall_time_left--;
12556
12557       if (!game.magic_wall_time_left)
12558       {
12559         SCAN_PLAYFIELD(x, y)
12560         {
12561           element = Tile[x][y];
12562
12563           if (element == EL_MAGIC_WALL_ACTIVE ||
12564               element == EL_MAGIC_WALL_FULL)
12565           {
12566             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12567             TEST_DrawLevelField(x, y);
12568           }
12569           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12570                    element == EL_BD_MAGIC_WALL_FULL)
12571           {
12572             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12573             TEST_DrawLevelField(x, y);
12574           }
12575           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12576                    element == EL_DC_MAGIC_WALL_FULL)
12577           {
12578             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12579             TEST_DrawLevelField(x, y);
12580           }
12581         }
12582
12583         game.magic_wall_active = FALSE;
12584       }
12585     }
12586   }
12587
12588   if (game.light_time_left > 0)
12589   {
12590     game.light_time_left--;
12591
12592     if (game.light_time_left == 0)
12593       RedrawAllLightSwitchesAndInvisibleElements();
12594   }
12595
12596   if (game.timegate_time_left > 0)
12597   {
12598     game.timegate_time_left--;
12599
12600     if (game.timegate_time_left == 0)
12601       CloseAllOpenTimegates();
12602   }
12603
12604   if (game.lenses_time_left > 0)
12605   {
12606     game.lenses_time_left--;
12607
12608     if (game.lenses_time_left == 0)
12609       RedrawAllInvisibleElementsForLenses();
12610   }
12611
12612   if (game.magnify_time_left > 0)
12613   {
12614     game.magnify_time_left--;
12615
12616     if (game.magnify_time_left == 0)
12617       RedrawAllInvisibleElementsForMagnifier();
12618   }
12619
12620   for (i = 0; i < MAX_PLAYERS; i++)
12621   {
12622     struct PlayerInfo *player = &stored_player[i];
12623
12624     if (SHIELD_ON(player))
12625     {
12626       if (player->shield_deadly_time_left)
12627         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12628       else if (player->shield_normal_time_left)
12629         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12630     }
12631   }
12632
12633 #if USE_DELAYED_GFX_REDRAW
12634   SCAN_PLAYFIELD(x, y)
12635   {
12636     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12637     {
12638       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12639          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12640
12641       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12642         DrawLevelField(x, y);
12643
12644       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12645         DrawLevelFieldCrumbled(x, y);
12646
12647       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12648         DrawLevelFieldCrumbledNeighbours(x, y);
12649
12650       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12651         DrawTwinkleOnField(x, y);
12652     }
12653
12654     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12655   }
12656 #endif
12657
12658   DrawAllPlayers();
12659   PlayAllPlayersSound();
12660
12661   for (i = 0; i < MAX_PLAYERS; i++)
12662   {
12663     struct PlayerInfo *player = &stored_player[i];
12664
12665     if (player->show_envelope != 0 && (!player->active ||
12666                                        player->MovPos == 0))
12667     {
12668       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12669
12670       player->show_envelope = 0;
12671     }
12672   }
12673
12674   // use random number generator in every frame to make it less predictable
12675   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12676     RND(1);
12677
12678   mouse_action_last = mouse_action;
12679 }
12680
12681 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12682 {
12683   int min_x = x, min_y = y, max_x = x, max_y = y;
12684   int scr_fieldx = getScreenFieldSizeX();
12685   int scr_fieldy = getScreenFieldSizeY();
12686   int i;
12687
12688   for (i = 0; i < MAX_PLAYERS; i++)
12689   {
12690     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12691
12692     if (!stored_player[i].active || &stored_player[i] == player)
12693       continue;
12694
12695     min_x = MIN(min_x, jx);
12696     min_y = MIN(min_y, jy);
12697     max_x = MAX(max_x, jx);
12698     max_y = MAX(max_y, jy);
12699   }
12700
12701   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12702 }
12703
12704 static boolean AllPlayersInVisibleScreen(void)
12705 {
12706   int i;
12707
12708   for (i = 0; i < MAX_PLAYERS; i++)
12709   {
12710     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12711
12712     if (!stored_player[i].active)
12713       continue;
12714
12715     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12716       return FALSE;
12717   }
12718
12719   return TRUE;
12720 }
12721
12722 void ScrollLevel(int dx, int dy)
12723 {
12724   int scroll_offset = 2 * TILEX_VAR;
12725   int x, y;
12726
12727   BlitBitmap(drawto_field, drawto_field,
12728              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12729              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12730              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12731              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12732              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12733              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12734
12735   if (dx != 0)
12736   {
12737     x = (dx == 1 ? BX1 : BX2);
12738     for (y = BY1; y <= BY2; y++)
12739       DrawScreenField(x, y);
12740   }
12741
12742   if (dy != 0)
12743   {
12744     y = (dy == 1 ? BY1 : BY2);
12745     for (x = BX1; x <= BX2; x++)
12746       DrawScreenField(x, y);
12747   }
12748
12749   redraw_mask |= REDRAW_FIELD;
12750 }
12751
12752 static boolean canFallDown(struct PlayerInfo *player)
12753 {
12754   int jx = player->jx, jy = player->jy;
12755
12756   return (IN_LEV_FIELD(jx, jy + 1) &&
12757           (IS_FREE(jx, jy + 1) ||
12758            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12759           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12760           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12761 }
12762
12763 static boolean canPassField(int x, int y, int move_dir)
12764 {
12765   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12766   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12767   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12768   int nextx = x + dx;
12769   int nexty = y + dy;
12770   int element = Tile[x][y];
12771
12772   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12773           !CAN_MOVE(element) &&
12774           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12775           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12776           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12777 }
12778
12779 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12780 {
12781   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12782   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12783   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12784   int newx = x + dx;
12785   int newy = y + dy;
12786
12787   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12788           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12789           (IS_DIGGABLE(Tile[newx][newy]) ||
12790            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12791            canPassField(newx, newy, move_dir)));
12792 }
12793
12794 static void CheckGravityMovement(struct PlayerInfo *player)
12795 {
12796   if (player->gravity && !player->programmed_action)
12797   {
12798     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12799     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12800     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12801     int jx = player->jx, jy = player->jy;
12802     boolean player_is_moving_to_valid_field =
12803       (!player_is_snapping &&
12804        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12805         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12806     boolean player_can_fall_down = canFallDown(player);
12807
12808     if (player_can_fall_down &&
12809         !player_is_moving_to_valid_field)
12810       player->programmed_action = MV_DOWN;
12811   }
12812 }
12813
12814 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12815 {
12816   return CheckGravityMovement(player);
12817
12818   if (player->gravity && !player->programmed_action)
12819   {
12820     int jx = player->jx, jy = player->jy;
12821     boolean field_under_player_is_free =
12822       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12823     boolean player_is_standing_on_valid_field =
12824       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12825        (IS_WALKABLE(Tile[jx][jy]) &&
12826         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12827
12828     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12829       player->programmed_action = MV_DOWN;
12830   }
12831 }
12832
12833 /*
12834   MovePlayerOneStep()
12835   -----------------------------------------------------------------------------
12836   dx, dy:               direction (non-diagonal) to try to move the player to
12837   real_dx, real_dy:     direction as read from input device (can be diagonal)
12838 */
12839
12840 boolean MovePlayerOneStep(struct PlayerInfo *player,
12841                           int dx, int dy, int real_dx, int real_dy)
12842 {
12843   int jx = player->jx, jy = player->jy;
12844   int new_jx = jx + dx, new_jy = jy + dy;
12845   int can_move;
12846   boolean player_can_move = !player->cannot_move;
12847
12848   if (!player->active || (!dx && !dy))
12849     return MP_NO_ACTION;
12850
12851   player->MovDir = (dx < 0 ? MV_LEFT :
12852                     dx > 0 ? MV_RIGHT :
12853                     dy < 0 ? MV_UP :
12854                     dy > 0 ? MV_DOWN :  MV_NONE);
12855
12856   if (!IN_LEV_FIELD(new_jx, new_jy))
12857     return MP_NO_ACTION;
12858
12859   if (!player_can_move)
12860   {
12861     if (player->MovPos == 0)
12862     {
12863       player->is_moving = FALSE;
12864       player->is_digging = FALSE;
12865       player->is_collecting = FALSE;
12866       player->is_snapping = FALSE;
12867       player->is_pushing = FALSE;
12868     }
12869   }
12870
12871   if (!network.enabled && game.centered_player_nr == -1 &&
12872       !AllPlayersInSight(player, new_jx, new_jy))
12873     return MP_NO_ACTION;
12874
12875   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12876   if (can_move != MP_MOVING)
12877     return can_move;
12878
12879   // check if DigField() has caused relocation of the player
12880   if (player->jx != jx || player->jy != jy)
12881     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12882
12883   StorePlayer[jx][jy] = 0;
12884   player->last_jx = jx;
12885   player->last_jy = jy;
12886   player->jx = new_jx;
12887   player->jy = new_jy;
12888   StorePlayer[new_jx][new_jy] = player->element_nr;
12889
12890   if (player->move_delay_value_next != -1)
12891   {
12892     player->move_delay_value = player->move_delay_value_next;
12893     player->move_delay_value_next = -1;
12894   }
12895
12896   player->MovPos =
12897     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12898
12899   player->step_counter++;
12900
12901   PlayerVisit[jx][jy] = FrameCounter;
12902
12903   player->is_moving = TRUE;
12904
12905 #if 1
12906   // should better be called in MovePlayer(), but this breaks some tapes
12907   ScrollPlayer(player, SCROLL_INIT);
12908 #endif
12909
12910   return MP_MOVING;
12911 }
12912
12913 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12914 {
12915   int jx = player->jx, jy = player->jy;
12916   int old_jx = jx, old_jy = jy;
12917   int moved = MP_NO_ACTION;
12918
12919   if (!player->active)
12920     return FALSE;
12921
12922   if (!dx && !dy)
12923   {
12924     if (player->MovPos == 0)
12925     {
12926       player->is_moving = FALSE;
12927       player->is_digging = FALSE;
12928       player->is_collecting = FALSE;
12929       player->is_snapping = FALSE;
12930       player->is_pushing = FALSE;
12931     }
12932
12933     return FALSE;
12934   }
12935
12936   if (player->move_delay > 0)
12937     return FALSE;
12938
12939   player->move_delay = -1;              // set to "uninitialized" value
12940
12941   // store if player is automatically moved to next field
12942   player->is_auto_moving = (player->programmed_action != MV_NONE);
12943
12944   // remove the last programmed player action
12945   player->programmed_action = 0;
12946
12947   if (player->MovPos)
12948   {
12949     // should only happen if pre-1.2 tape recordings are played
12950     // this is only for backward compatibility
12951
12952     int original_move_delay_value = player->move_delay_value;
12953
12954 #if DEBUG
12955     Debug("game:playing:MovePlayer",
12956           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12957           tape.counter);
12958 #endif
12959
12960     // scroll remaining steps with finest movement resolution
12961     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12962
12963     while (player->MovPos)
12964     {
12965       ScrollPlayer(player, SCROLL_GO_ON);
12966       ScrollScreen(NULL, SCROLL_GO_ON);
12967
12968       AdvanceFrameAndPlayerCounters(player->index_nr);
12969
12970       DrawAllPlayers();
12971       BackToFront_WithFrameDelay(0);
12972     }
12973
12974     player->move_delay_value = original_move_delay_value;
12975   }
12976
12977   player->is_active = FALSE;
12978
12979   if (player->last_move_dir & MV_HORIZONTAL)
12980   {
12981     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12982       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12983   }
12984   else
12985   {
12986     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12987       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12988   }
12989
12990   if (!moved && !player->is_active)
12991   {
12992     player->is_moving = FALSE;
12993     player->is_digging = FALSE;
12994     player->is_collecting = FALSE;
12995     player->is_snapping = FALSE;
12996     player->is_pushing = FALSE;
12997   }
12998
12999   jx = player->jx;
13000   jy = player->jy;
13001
13002   if (moved & MP_MOVING && !ScreenMovPos &&
13003       (player->index_nr == game.centered_player_nr ||
13004        game.centered_player_nr == -1))
13005   {
13006     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13007
13008     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13009     {
13010       // actual player has left the screen -- scroll in that direction
13011       if (jx != old_jx)         // player has moved horizontally
13012         scroll_x += (jx - old_jx);
13013       else                      // player has moved vertically
13014         scroll_y += (jy - old_jy);
13015     }
13016     else
13017     {
13018       int offset_raw = game.scroll_delay_value;
13019
13020       if (jx != old_jx)         // player has moved horizontally
13021       {
13022         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13023         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13024         int new_scroll_x = jx - MIDPOSX + offset_x;
13025
13026         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13027             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13028           scroll_x = new_scroll_x;
13029
13030         // don't scroll over playfield boundaries
13031         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13032
13033         // don't scroll more than one field at a time
13034         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13035
13036         // don't scroll against the player's moving direction
13037         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13038             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13039           scroll_x = old_scroll_x;
13040       }
13041       else                      // player has moved vertically
13042       {
13043         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13044         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13045         int new_scroll_y = jy - MIDPOSY + offset_y;
13046
13047         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13048             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13049           scroll_y = new_scroll_y;
13050
13051         // don't scroll over playfield boundaries
13052         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13053
13054         // don't scroll more than one field at a time
13055         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13056
13057         // don't scroll against the player's moving direction
13058         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13059             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13060           scroll_y = old_scroll_y;
13061       }
13062     }
13063
13064     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13065     {
13066       if (!network.enabled && game.centered_player_nr == -1 &&
13067           !AllPlayersInVisibleScreen())
13068       {
13069         scroll_x = old_scroll_x;
13070         scroll_y = old_scroll_y;
13071       }
13072       else
13073       {
13074         ScrollScreen(player, SCROLL_INIT);
13075         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13076       }
13077     }
13078   }
13079
13080   player->StepFrame = 0;
13081
13082   if (moved & MP_MOVING)
13083   {
13084     if (old_jx != jx && old_jy == jy)
13085       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13086     else if (old_jx == jx && old_jy != jy)
13087       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13088
13089     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13090
13091     player->last_move_dir = player->MovDir;
13092     player->is_moving = TRUE;
13093     player->is_snapping = FALSE;
13094     player->is_switching = FALSE;
13095     player->is_dropping = FALSE;
13096     player->is_dropping_pressed = FALSE;
13097     player->drop_pressed_delay = 0;
13098
13099 #if 0
13100     // should better be called here than above, but this breaks some tapes
13101     ScrollPlayer(player, SCROLL_INIT);
13102 #endif
13103   }
13104   else
13105   {
13106     CheckGravityMovementWhenNotMoving(player);
13107
13108     player->is_moving = FALSE;
13109
13110     /* at this point, the player is allowed to move, but cannot move right now
13111        (e.g. because of something blocking the way) -- ensure that the player
13112        is also allowed to move in the next frame (in old versions before 3.1.1,
13113        the player was forced to wait again for eight frames before next try) */
13114
13115     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13116       player->move_delay = 0;   // allow direct movement in the next frame
13117   }
13118
13119   if (player->move_delay == -1)         // not yet initialized by DigField()
13120     player->move_delay = player->move_delay_value;
13121
13122   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13123   {
13124     TestIfPlayerTouchesBadThing(jx, jy);
13125     TestIfPlayerTouchesCustomElement(jx, jy);
13126   }
13127
13128   if (!player->active)
13129     RemovePlayer(player);
13130
13131   return moved;
13132 }
13133
13134 void ScrollPlayer(struct PlayerInfo *player, int mode)
13135 {
13136   int jx = player->jx, jy = player->jy;
13137   int last_jx = player->last_jx, last_jy = player->last_jy;
13138   int move_stepsize = TILEX / player->move_delay_value;
13139
13140   if (!player->active)
13141     return;
13142
13143   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13144     return;
13145
13146   if (mode == SCROLL_INIT)
13147   {
13148     player->actual_frame_counter = FrameCounter;
13149     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13150
13151     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13152         Tile[last_jx][last_jy] == EL_EMPTY)
13153     {
13154       int last_field_block_delay = 0;   // start with no blocking at all
13155       int block_delay_adjustment = player->block_delay_adjustment;
13156
13157       // if player blocks last field, add delay for exactly one move
13158       if (player->block_last_field)
13159       {
13160         last_field_block_delay += player->move_delay_value;
13161
13162         // when blocking enabled, prevent moving up despite gravity
13163         if (player->gravity && player->MovDir == MV_UP)
13164           block_delay_adjustment = -1;
13165       }
13166
13167       // add block delay adjustment (also possible when not blocking)
13168       last_field_block_delay += block_delay_adjustment;
13169
13170       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13171       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13172     }
13173
13174     if (player->MovPos != 0)    // player has not yet reached destination
13175       return;
13176   }
13177   else if (!FrameReached(&player->actual_frame_counter, 1))
13178     return;
13179
13180   if (player->MovPos != 0)
13181   {
13182     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13183     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13184
13185     // before DrawPlayer() to draw correct player graphic for this case
13186     if (player->MovPos == 0)
13187       CheckGravityMovement(player);
13188   }
13189
13190   if (player->MovPos == 0)      // player reached destination field
13191   {
13192     if (player->move_delay_reset_counter > 0)
13193     {
13194       player->move_delay_reset_counter--;
13195
13196       if (player->move_delay_reset_counter == 0)
13197       {
13198         // continue with normal speed after quickly moving through gate
13199         HALVE_PLAYER_SPEED(player);
13200
13201         // be able to make the next move without delay
13202         player->move_delay = 0;
13203       }
13204     }
13205
13206     player->last_jx = jx;
13207     player->last_jy = jy;
13208
13209     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13210         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13211         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13212         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13213         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13214         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13215         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13216         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13217     {
13218       ExitPlayer(player);
13219
13220       if (game.players_still_needed == 0 &&
13221           (game.friends_still_needed == 0 ||
13222            IS_SP_ELEMENT(Tile[jx][jy])))
13223         LevelSolved();
13224     }
13225
13226     // this breaks one level: "machine", level 000
13227     {
13228       int move_direction = player->MovDir;
13229       int enter_side = MV_DIR_OPPOSITE(move_direction);
13230       int leave_side = move_direction;
13231       int old_jx = last_jx;
13232       int old_jy = last_jy;
13233       int old_element = Tile[old_jx][old_jy];
13234       int new_element = Tile[jx][jy];
13235
13236       if (IS_CUSTOM_ELEMENT(old_element))
13237         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13238                                    CE_LEFT_BY_PLAYER,
13239                                    player->index_bit, leave_side);
13240
13241       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13242                                           CE_PLAYER_LEAVES_X,
13243                                           player->index_bit, leave_side);
13244
13245       if (IS_CUSTOM_ELEMENT(new_element))
13246         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13247                                    player->index_bit, enter_side);
13248
13249       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13250                                           CE_PLAYER_ENTERS_X,
13251                                           player->index_bit, enter_side);
13252
13253       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13254                                         CE_MOVE_OF_X, move_direction);
13255     }
13256
13257     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13258     {
13259       TestIfPlayerTouchesBadThing(jx, jy);
13260       TestIfPlayerTouchesCustomElement(jx, jy);
13261
13262       /* needed because pushed element has not yet reached its destination,
13263          so it would trigger a change event at its previous field location */
13264       if (!player->is_pushing)
13265         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13266
13267       if (level.finish_dig_collect &&
13268           (player->is_digging || player->is_collecting))
13269       {
13270         int last_element = player->last_removed_element;
13271         int move_direction = player->MovDir;
13272         int enter_side = MV_DIR_OPPOSITE(move_direction);
13273         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13274                             CE_PLAYER_COLLECTS_X);
13275
13276         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13277                                             player->index_bit, enter_side);
13278
13279         player->last_removed_element = EL_UNDEFINED;
13280       }
13281
13282       if (!player->active)
13283         RemovePlayer(player);
13284     }
13285
13286     if (level.use_step_counter)
13287       CheckLevelTime_StepCounter();
13288
13289     if (tape.single_step && tape.recording && !tape.pausing &&
13290         !player->programmed_action)
13291       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13292
13293     if (!player->programmed_action)
13294       CheckSaveEngineSnapshot(player);
13295   }
13296 }
13297
13298 void ScrollScreen(struct PlayerInfo *player, int mode)
13299 {
13300   static unsigned int screen_frame_counter = 0;
13301
13302   if (mode == SCROLL_INIT)
13303   {
13304     // set scrolling step size according to actual player's moving speed
13305     ScrollStepSize = TILEX / player->move_delay_value;
13306
13307     screen_frame_counter = FrameCounter;
13308     ScreenMovDir = player->MovDir;
13309     ScreenMovPos = player->MovPos;
13310     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13311     return;
13312   }
13313   else if (!FrameReached(&screen_frame_counter, 1))
13314     return;
13315
13316   if (ScreenMovPos)
13317   {
13318     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13319     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13320     redraw_mask |= REDRAW_FIELD;
13321   }
13322   else
13323     ScreenMovDir = MV_NONE;
13324 }
13325
13326 void CheckNextToConditions(int x, int y)
13327 {
13328   int element = Tile[x][y];
13329
13330   if (IS_PLAYER(x, y))
13331     TestIfPlayerNextToCustomElement(x, y);
13332
13333   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13334       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13335     TestIfElementNextToCustomElement(x, y);
13336 }
13337
13338 void TestIfPlayerNextToCustomElement(int x, int y)
13339 {
13340   static int xy[4][2] =
13341   {
13342     { 0, -1 },
13343     { -1, 0 },
13344     { +1, 0 },
13345     { 0, +1 }
13346   };
13347   static int trigger_sides[4][2] =
13348   {
13349     // center side       border side
13350     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13351     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13352     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13353     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13354   };
13355   int i;
13356
13357   if (!IS_PLAYER(x, y))
13358     return;
13359
13360   struct PlayerInfo *player = PLAYERINFO(x, y);
13361
13362   if (player->is_moving)
13363     return;
13364
13365   for (i = 0; i < NUM_DIRECTIONS; i++)
13366   {
13367     int xx = x + xy[i][0];
13368     int yy = y + xy[i][1];
13369     int border_side = trigger_sides[i][1];
13370     int border_element;
13371
13372     if (!IN_LEV_FIELD(xx, yy))
13373       continue;
13374
13375     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13376       continue;         // center and border element not connected
13377
13378     border_element = Tile[xx][yy];
13379
13380     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13381                                player->index_bit, border_side);
13382     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13383                                         CE_PLAYER_NEXT_TO_X,
13384                                         player->index_bit, border_side);
13385
13386     /* use player element that is initially defined in the level playfield,
13387        not the player element that corresponds to the runtime player number
13388        (example: a level that contains EL_PLAYER_3 as the only player would
13389        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13390
13391     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13392                              CE_NEXT_TO_X, border_side);
13393   }
13394 }
13395
13396 void TestIfPlayerTouchesCustomElement(int x, int y)
13397 {
13398   static int xy[4][2] =
13399   {
13400     { 0, -1 },
13401     { -1, 0 },
13402     { +1, 0 },
13403     { 0, +1 }
13404   };
13405   static int trigger_sides[4][2] =
13406   {
13407     // center side       border side
13408     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13409     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13410     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13411     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13412   };
13413   static int touch_dir[4] =
13414   {
13415     MV_LEFT | MV_RIGHT,
13416     MV_UP   | MV_DOWN,
13417     MV_UP   | MV_DOWN,
13418     MV_LEFT | MV_RIGHT
13419   };
13420   int center_element = Tile[x][y];      // should always be non-moving!
13421   int i;
13422
13423   for (i = 0; i < NUM_DIRECTIONS; i++)
13424   {
13425     int xx = x + xy[i][0];
13426     int yy = y + xy[i][1];
13427     int center_side = trigger_sides[i][0];
13428     int border_side = trigger_sides[i][1];
13429     int border_element;
13430
13431     if (!IN_LEV_FIELD(xx, yy))
13432       continue;
13433
13434     if (IS_PLAYER(x, y))                // player found at center element
13435     {
13436       struct PlayerInfo *player = PLAYERINFO(x, y);
13437
13438       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13439         border_element = Tile[xx][yy];          // may be moving!
13440       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13441         border_element = Tile[xx][yy];
13442       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13443         border_element = MovingOrBlocked2Element(xx, yy);
13444       else
13445         continue;               // center and border element do not touch
13446
13447       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13448                                  player->index_bit, border_side);
13449       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13450                                           CE_PLAYER_TOUCHES_X,
13451                                           player->index_bit, border_side);
13452
13453       {
13454         /* use player element that is initially defined in the level playfield,
13455            not the player element that corresponds to the runtime player number
13456            (example: a level that contains EL_PLAYER_3 as the only player would
13457            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13458         int player_element = PLAYERINFO(x, y)->initial_element;
13459
13460         CheckElementChangeBySide(xx, yy, border_element, player_element,
13461                                  CE_TOUCHING_X, border_side);
13462       }
13463     }
13464     else if (IS_PLAYER(xx, yy))         // player found at border element
13465     {
13466       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13467
13468       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13469       {
13470         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13471           continue;             // center and border element do not touch
13472       }
13473
13474       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13475                                  player->index_bit, center_side);
13476       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13477                                           CE_PLAYER_TOUCHES_X,
13478                                           player->index_bit, center_side);
13479
13480       {
13481         /* use player element that is initially defined in the level playfield,
13482            not the player element that corresponds to the runtime player number
13483            (example: a level that contains EL_PLAYER_3 as the only player would
13484            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13485         int player_element = PLAYERINFO(xx, yy)->initial_element;
13486
13487         CheckElementChangeBySide(x, y, center_element, player_element,
13488                                  CE_TOUCHING_X, center_side);
13489       }
13490
13491       break;
13492     }
13493   }
13494 }
13495
13496 void TestIfElementNextToCustomElement(int x, int y)
13497 {
13498   static int xy[4][2] =
13499   {
13500     { 0, -1 },
13501     { -1, 0 },
13502     { +1, 0 },
13503     { 0, +1 }
13504   };
13505   static int trigger_sides[4][2] =
13506   {
13507     // center side      border side
13508     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13509     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13510     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13511     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13512   };
13513   int center_element = Tile[x][y];      // should always be non-moving!
13514   int i;
13515
13516   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13517     return;
13518
13519   for (i = 0; i < NUM_DIRECTIONS; i++)
13520   {
13521     int xx = x + xy[i][0];
13522     int yy = y + xy[i][1];
13523     int border_side = trigger_sides[i][1];
13524     int border_element;
13525
13526     if (!IN_LEV_FIELD(xx, yy))
13527       continue;
13528
13529     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13530       continue;                 // center and border element not connected
13531
13532     border_element = Tile[xx][yy];
13533
13534     // check for change of center element (but change it only once)
13535     if (CheckElementChangeBySide(x, y, center_element, border_element,
13536                                  CE_NEXT_TO_X, border_side))
13537       break;
13538   }
13539 }
13540
13541 void TestIfElementTouchesCustomElement(int x, int y)
13542 {
13543   static int xy[4][2] =
13544   {
13545     { 0, -1 },
13546     { -1, 0 },
13547     { +1, 0 },
13548     { 0, +1 }
13549   };
13550   static int trigger_sides[4][2] =
13551   {
13552     // center side      border side
13553     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13554     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13555     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13556     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13557   };
13558   static int touch_dir[4] =
13559   {
13560     MV_LEFT | MV_RIGHT,
13561     MV_UP   | MV_DOWN,
13562     MV_UP   | MV_DOWN,
13563     MV_LEFT | MV_RIGHT
13564   };
13565   boolean change_center_element = FALSE;
13566   int center_element = Tile[x][y];      // should always be non-moving!
13567   int border_element_old[NUM_DIRECTIONS];
13568   int i;
13569
13570   for (i = 0; i < NUM_DIRECTIONS; i++)
13571   {
13572     int xx = x + xy[i][0];
13573     int yy = y + xy[i][1];
13574     int border_element;
13575
13576     border_element_old[i] = -1;
13577
13578     if (!IN_LEV_FIELD(xx, yy))
13579       continue;
13580
13581     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13582       border_element = Tile[xx][yy];    // may be moving!
13583     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13584       border_element = Tile[xx][yy];
13585     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13586       border_element = MovingOrBlocked2Element(xx, yy);
13587     else
13588       continue;                 // center and border element do not touch
13589
13590     border_element_old[i] = border_element;
13591   }
13592
13593   for (i = 0; i < NUM_DIRECTIONS; i++)
13594   {
13595     int xx = x + xy[i][0];
13596     int yy = y + xy[i][1];
13597     int center_side = trigger_sides[i][0];
13598     int border_element = border_element_old[i];
13599
13600     if (border_element == -1)
13601       continue;
13602
13603     // check for change of border element
13604     CheckElementChangeBySide(xx, yy, border_element, center_element,
13605                              CE_TOUCHING_X, center_side);
13606
13607     // (center element cannot be player, so we dont have to check this here)
13608   }
13609
13610   for (i = 0; i < NUM_DIRECTIONS; i++)
13611   {
13612     int xx = x + xy[i][0];
13613     int yy = y + xy[i][1];
13614     int border_side = trigger_sides[i][1];
13615     int border_element = border_element_old[i];
13616
13617     if (border_element == -1)
13618       continue;
13619
13620     // check for change of center element (but change it only once)
13621     if (!change_center_element)
13622       change_center_element =
13623         CheckElementChangeBySide(x, y, center_element, border_element,
13624                                  CE_TOUCHING_X, border_side);
13625
13626     if (IS_PLAYER(xx, yy))
13627     {
13628       /* use player element that is initially defined in the level playfield,
13629          not the player element that corresponds to the runtime player number
13630          (example: a level that contains EL_PLAYER_3 as the only player would
13631          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13632       int player_element = PLAYERINFO(xx, yy)->initial_element;
13633
13634       CheckElementChangeBySide(x, y, center_element, player_element,
13635                                CE_TOUCHING_X, border_side);
13636     }
13637   }
13638 }
13639
13640 void TestIfElementHitsCustomElement(int x, int y, int direction)
13641 {
13642   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13643   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13644   int hitx = x + dx, hity = y + dy;
13645   int hitting_element = Tile[x][y];
13646   int touched_element;
13647
13648   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13649     return;
13650
13651   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13652                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13653
13654   if (IN_LEV_FIELD(hitx, hity))
13655   {
13656     int opposite_direction = MV_DIR_OPPOSITE(direction);
13657     int hitting_side = direction;
13658     int touched_side = opposite_direction;
13659     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13660                           MovDir[hitx][hity] != direction ||
13661                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13662
13663     object_hit = TRUE;
13664
13665     if (object_hit)
13666     {
13667       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13668                                CE_HITTING_X, touched_side);
13669
13670       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13671                                CE_HIT_BY_X, hitting_side);
13672
13673       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13674                                CE_HIT_BY_SOMETHING, opposite_direction);
13675
13676       if (IS_PLAYER(hitx, hity))
13677       {
13678         /* use player element that is initially defined in the level playfield,
13679            not the player element that corresponds to the runtime player number
13680            (example: a level that contains EL_PLAYER_3 as the only player would
13681            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13682         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13683
13684         CheckElementChangeBySide(x, y, hitting_element, player_element,
13685                                  CE_HITTING_X, touched_side);
13686       }
13687     }
13688   }
13689
13690   // "hitting something" is also true when hitting the playfield border
13691   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13692                            CE_HITTING_SOMETHING, direction);
13693 }
13694
13695 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13696 {
13697   int i, kill_x = -1, kill_y = -1;
13698
13699   int bad_element = -1;
13700   static int test_xy[4][2] =
13701   {
13702     { 0, -1 },
13703     { -1, 0 },
13704     { +1, 0 },
13705     { 0, +1 }
13706   };
13707   static int test_dir[4] =
13708   {
13709     MV_UP,
13710     MV_LEFT,
13711     MV_RIGHT,
13712     MV_DOWN
13713   };
13714
13715   for (i = 0; i < NUM_DIRECTIONS; i++)
13716   {
13717     int test_x, test_y, test_move_dir, test_element;
13718
13719     test_x = good_x + test_xy[i][0];
13720     test_y = good_y + test_xy[i][1];
13721
13722     if (!IN_LEV_FIELD(test_x, test_y))
13723       continue;
13724
13725     test_move_dir =
13726       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13727
13728     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13729
13730     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13731        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13732     */
13733     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13734         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13735     {
13736       kill_x = test_x;
13737       kill_y = test_y;
13738       bad_element = test_element;
13739
13740       break;
13741     }
13742   }
13743
13744   if (kill_x != -1 || kill_y != -1)
13745   {
13746     if (IS_PLAYER(good_x, good_y))
13747     {
13748       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13749
13750       if (player->shield_deadly_time_left > 0 &&
13751           !IS_INDESTRUCTIBLE(bad_element))
13752         Bang(kill_x, kill_y);
13753       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13754         KillPlayer(player);
13755     }
13756     else
13757       Bang(good_x, good_y);
13758   }
13759 }
13760
13761 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13762 {
13763   int i, kill_x = -1, kill_y = -1;
13764   int bad_element = Tile[bad_x][bad_y];
13765   static int test_xy[4][2] =
13766   {
13767     { 0, -1 },
13768     { -1, 0 },
13769     { +1, 0 },
13770     { 0, +1 }
13771   };
13772   static int touch_dir[4] =
13773   {
13774     MV_LEFT | MV_RIGHT,
13775     MV_UP   | MV_DOWN,
13776     MV_UP   | MV_DOWN,
13777     MV_LEFT | MV_RIGHT
13778   };
13779   static int test_dir[4] =
13780   {
13781     MV_UP,
13782     MV_LEFT,
13783     MV_RIGHT,
13784     MV_DOWN
13785   };
13786
13787   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13788     return;
13789
13790   for (i = 0; i < NUM_DIRECTIONS; i++)
13791   {
13792     int test_x, test_y, test_move_dir, test_element;
13793
13794     test_x = bad_x + test_xy[i][0];
13795     test_y = bad_y + test_xy[i][1];
13796
13797     if (!IN_LEV_FIELD(test_x, test_y))
13798       continue;
13799
13800     test_move_dir =
13801       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13802
13803     test_element = Tile[test_x][test_y];
13804
13805     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13806        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13807     */
13808     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13809         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13810     {
13811       // good thing is player or penguin that does not move away
13812       if (IS_PLAYER(test_x, test_y))
13813       {
13814         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13815
13816         if (bad_element == EL_ROBOT && player->is_moving)
13817           continue;     // robot does not kill player if he is moving
13818
13819         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13820         {
13821           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13822             continue;           // center and border element do not touch
13823         }
13824
13825         kill_x = test_x;
13826         kill_y = test_y;
13827
13828         break;
13829       }
13830       else if (test_element == EL_PENGUIN)
13831       {
13832         kill_x = test_x;
13833         kill_y = test_y;
13834
13835         break;
13836       }
13837     }
13838   }
13839
13840   if (kill_x != -1 || kill_y != -1)
13841   {
13842     if (IS_PLAYER(kill_x, kill_y))
13843     {
13844       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13845
13846       if (player->shield_deadly_time_left > 0 &&
13847           !IS_INDESTRUCTIBLE(bad_element))
13848         Bang(bad_x, bad_y);
13849       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13850         KillPlayer(player);
13851     }
13852     else
13853       Bang(kill_x, kill_y);
13854   }
13855 }
13856
13857 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13858 {
13859   int bad_element = Tile[bad_x][bad_y];
13860   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13861   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13862   int test_x = bad_x + dx, test_y = bad_y + dy;
13863   int test_move_dir, test_element;
13864   int kill_x = -1, kill_y = -1;
13865
13866   if (!IN_LEV_FIELD(test_x, test_y))
13867     return;
13868
13869   test_move_dir =
13870     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13871
13872   test_element = Tile[test_x][test_y];
13873
13874   if (test_move_dir != bad_move_dir)
13875   {
13876     // good thing can be player or penguin that does not move away
13877     if (IS_PLAYER(test_x, test_y))
13878     {
13879       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13880
13881       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13882          player as being hit when he is moving towards the bad thing, because
13883          the "get hit by" condition would be lost after the player stops) */
13884       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13885         return;         // player moves away from bad thing
13886
13887       kill_x = test_x;
13888       kill_y = test_y;
13889     }
13890     else if (test_element == EL_PENGUIN)
13891     {
13892       kill_x = test_x;
13893       kill_y = test_y;
13894     }
13895   }
13896
13897   if (kill_x != -1 || kill_y != -1)
13898   {
13899     if (IS_PLAYER(kill_x, kill_y))
13900     {
13901       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13902
13903       if (player->shield_deadly_time_left > 0 &&
13904           !IS_INDESTRUCTIBLE(bad_element))
13905         Bang(bad_x, bad_y);
13906       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13907         KillPlayer(player);
13908     }
13909     else
13910       Bang(kill_x, kill_y);
13911   }
13912 }
13913
13914 void TestIfPlayerTouchesBadThing(int x, int y)
13915 {
13916   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13917 }
13918
13919 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13920 {
13921   TestIfGoodThingHitsBadThing(x, y, move_dir);
13922 }
13923
13924 void TestIfBadThingTouchesPlayer(int x, int y)
13925 {
13926   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13927 }
13928
13929 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13930 {
13931   TestIfBadThingHitsGoodThing(x, y, move_dir);
13932 }
13933
13934 void TestIfFriendTouchesBadThing(int x, int y)
13935 {
13936   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13937 }
13938
13939 void TestIfBadThingTouchesFriend(int x, int y)
13940 {
13941   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13942 }
13943
13944 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13945 {
13946   int i, kill_x = bad_x, kill_y = bad_y;
13947   static int xy[4][2] =
13948   {
13949     { 0, -1 },
13950     { -1, 0 },
13951     { +1, 0 },
13952     { 0, +1 }
13953   };
13954
13955   for (i = 0; i < NUM_DIRECTIONS; i++)
13956   {
13957     int x, y, element;
13958
13959     x = bad_x + xy[i][0];
13960     y = bad_y + xy[i][1];
13961     if (!IN_LEV_FIELD(x, y))
13962       continue;
13963
13964     element = Tile[x][y];
13965     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13966         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13967     {
13968       kill_x = x;
13969       kill_y = y;
13970       break;
13971     }
13972   }
13973
13974   if (kill_x != bad_x || kill_y != bad_y)
13975     Bang(bad_x, bad_y);
13976 }
13977
13978 void KillPlayer(struct PlayerInfo *player)
13979 {
13980   int jx = player->jx, jy = player->jy;
13981
13982   if (!player->active)
13983     return;
13984
13985 #if 0
13986   Debug("game:playing:KillPlayer",
13987         "0: killed == %d, active == %d, reanimated == %d",
13988         player->killed, player->active, player->reanimated);
13989 #endif
13990
13991   /* the following code was introduced to prevent an infinite loop when calling
13992      -> Bang()
13993      -> CheckTriggeredElementChangeExt()
13994      -> ExecuteCustomElementAction()
13995      -> KillPlayer()
13996      -> (infinitely repeating the above sequence of function calls)
13997      which occurs when killing the player while having a CE with the setting
13998      "kill player X when explosion of <player X>"; the solution using a new
13999      field "player->killed" was chosen for backwards compatibility, although
14000      clever use of the fields "player->active" etc. would probably also work */
14001 #if 1
14002   if (player->killed)
14003     return;
14004 #endif
14005
14006   player->killed = TRUE;
14007
14008   // remove accessible field at the player's position
14009   Tile[jx][jy] = EL_EMPTY;
14010
14011   // deactivate shield (else Bang()/Explode() would not work right)
14012   player->shield_normal_time_left = 0;
14013   player->shield_deadly_time_left = 0;
14014
14015 #if 0
14016   Debug("game:playing:KillPlayer",
14017         "1: killed == %d, active == %d, reanimated == %d",
14018         player->killed, player->active, player->reanimated);
14019 #endif
14020
14021   Bang(jx, jy);
14022
14023 #if 0
14024   Debug("game:playing:KillPlayer",
14025         "2: killed == %d, active == %d, reanimated == %d",
14026         player->killed, player->active, player->reanimated);
14027 #endif
14028
14029   if (player->reanimated)       // killed player may have been reanimated
14030     player->killed = player->reanimated = FALSE;
14031   else
14032     BuryPlayer(player);
14033 }
14034
14035 static void KillPlayerUnlessEnemyProtected(int x, int y)
14036 {
14037   if (!PLAYER_ENEMY_PROTECTED(x, y))
14038     KillPlayer(PLAYERINFO(x, y));
14039 }
14040
14041 static void KillPlayerUnlessExplosionProtected(int x, int y)
14042 {
14043   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14044     KillPlayer(PLAYERINFO(x, y));
14045 }
14046
14047 void BuryPlayer(struct PlayerInfo *player)
14048 {
14049   int jx = player->jx, jy = player->jy;
14050
14051   if (!player->active)
14052     return;
14053
14054   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14055   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14056
14057   RemovePlayer(player);
14058
14059   player->buried = TRUE;
14060
14061   if (game.all_players_gone)
14062     game.GameOver = TRUE;
14063 }
14064
14065 void RemovePlayer(struct PlayerInfo *player)
14066 {
14067   int jx = player->jx, jy = player->jy;
14068   int i, found = FALSE;
14069
14070   player->present = FALSE;
14071   player->active = FALSE;
14072
14073   // required for some CE actions (even if the player is not active anymore)
14074   player->MovPos = 0;
14075
14076   if (!ExplodeField[jx][jy])
14077     StorePlayer[jx][jy] = 0;
14078
14079   if (player->is_moving)
14080     TEST_DrawLevelField(player->last_jx, player->last_jy);
14081
14082   for (i = 0; i < MAX_PLAYERS; i++)
14083     if (stored_player[i].active)
14084       found = TRUE;
14085
14086   if (!found)
14087   {
14088     game.all_players_gone = TRUE;
14089     game.GameOver = TRUE;
14090   }
14091
14092   game.exit_x = game.robot_wheel_x = jx;
14093   game.exit_y = game.robot_wheel_y = jy;
14094 }
14095
14096 void ExitPlayer(struct PlayerInfo *player)
14097 {
14098   DrawPlayer(player);   // needed here only to cleanup last field
14099   RemovePlayer(player);
14100
14101   if (game.players_still_needed > 0)
14102     game.players_still_needed--;
14103 }
14104
14105 static void SetFieldForSnapping(int x, int y, int element, int direction,
14106                                 int player_index_bit)
14107 {
14108   struct ElementInfo *ei = &element_info[element];
14109   int direction_bit = MV_DIR_TO_BIT(direction);
14110   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14111   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14112                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14113
14114   Tile[x][y] = EL_ELEMENT_SNAPPING;
14115   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14116   MovDir[x][y] = direction;
14117   Store[x][y] = element;
14118   Store2[x][y] = player_index_bit;
14119
14120   ResetGfxAnimation(x, y);
14121
14122   GfxElement[x][y] = element;
14123   GfxAction[x][y] = action;
14124   GfxDir[x][y] = direction;
14125   GfxFrame[x][y] = -1;
14126 }
14127
14128 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14129                                    int player_index_bit)
14130 {
14131   TestIfElementTouchesCustomElement(x, y);      // for empty space
14132
14133   if (level.finish_dig_collect)
14134   {
14135     int dig_side = MV_DIR_OPPOSITE(direction);
14136     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14137                         CE_PLAYER_COLLECTS_X);
14138
14139     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14140                                         player_index_bit, dig_side);
14141     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14142                                         player_index_bit, dig_side);
14143   }
14144 }
14145
14146 /*
14147   =============================================================================
14148   checkDiagonalPushing()
14149   -----------------------------------------------------------------------------
14150   check if diagonal input device direction results in pushing of object
14151   (by checking if the alternative direction is walkable, diggable, ...)
14152   =============================================================================
14153 */
14154
14155 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14156                                     int x, int y, int real_dx, int real_dy)
14157 {
14158   int jx, jy, dx, dy, xx, yy;
14159
14160   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14161     return TRUE;
14162
14163   // diagonal direction: check alternative direction
14164   jx = player->jx;
14165   jy = player->jy;
14166   dx = x - jx;
14167   dy = y - jy;
14168   xx = jx + (dx == 0 ? real_dx : 0);
14169   yy = jy + (dy == 0 ? real_dy : 0);
14170
14171   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14172 }
14173
14174 /*
14175   =============================================================================
14176   DigField()
14177   -----------------------------------------------------------------------------
14178   x, y:                 field next to player (non-diagonal) to try to dig to
14179   real_dx, real_dy:     direction as read from input device (can be diagonal)
14180   =============================================================================
14181 */
14182
14183 static int DigField(struct PlayerInfo *player,
14184                     int oldx, int oldy, int x, int y,
14185                     int real_dx, int real_dy, int mode)
14186 {
14187   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14188   boolean player_was_pushing = player->is_pushing;
14189   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14190   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14191   int jx = oldx, jy = oldy;
14192   int dx = x - jx, dy = y - jy;
14193   int nextx = x + dx, nexty = y + dy;
14194   int move_direction = (dx == -1 ? MV_LEFT  :
14195                         dx == +1 ? MV_RIGHT :
14196                         dy == -1 ? MV_UP    :
14197                         dy == +1 ? MV_DOWN  : MV_NONE);
14198   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14199   int dig_side = MV_DIR_OPPOSITE(move_direction);
14200   int old_element = Tile[jx][jy];
14201   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14202   int collect_count;
14203
14204   if (is_player)                // function can also be called by EL_PENGUIN
14205   {
14206     if (player->MovPos == 0)
14207     {
14208       player->is_digging = FALSE;
14209       player->is_collecting = FALSE;
14210     }
14211
14212     if (player->MovPos == 0)    // last pushing move finished
14213       player->is_pushing = FALSE;
14214
14215     if (mode == DF_NO_PUSH)     // player just stopped pushing
14216     {
14217       player->is_switching = FALSE;
14218       player->push_delay = -1;
14219
14220       return MP_NO_ACTION;
14221     }
14222   }
14223   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14224     old_element = Back[jx][jy];
14225
14226   // in case of element dropped at player position, check background
14227   else if (Back[jx][jy] != EL_EMPTY &&
14228            game.engine_version >= VERSION_IDENT(2,2,0,0))
14229     old_element = Back[jx][jy];
14230
14231   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14232     return MP_NO_ACTION;        // field has no opening in this direction
14233
14234   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14235     return MP_NO_ACTION;        // field has no opening in this direction
14236
14237   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14238   {
14239     SplashAcid(x, y);
14240
14241     Tile[jx][jy] = player->artwork_element;
14242     InitMovingField(jx, jy, MV_DOWN);
14243     Store[jx][jy] = EL_ACID;
14244     ContinueMoving(jx, jy);
14245     BuryPlayer(player);
14246
14247     return MP_DONT_RUN_INTO;
14248   }
14249
14250   if (player_can_move && DONT_RUN_INTO(element))
14251   {
14252     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14253
14254     return MP_DONT_RUN_INTO;
14255   }
14256
14257   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14258     return MP_NO_ACTION;
14259
14260   collect_count = element_info[element].collect_count_initial;
14261
14262   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14263     return MP_NO_ACTION;
14264
14265   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14266     player_can_move = player_can_move_or_snap;
14267
14268   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14269       game.engine_version >= VERSION_IDENT(2,2,0,0))
14270   {
14271     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14272                                player->index_bit, dig_side);
14273     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14274                                         player->index_bit, dig_side);
14275
14276     if (element == EL_DC_LANDMINE)
14277       Bang(x, y);
14278
14279     if (Tile[x][y] != element)          // field changed by snapping
14280       return MP_ACTION;
14281
14282     return MP_NO_ACTION;
14283   }
14284
14285   if (player->gravity && is_player && !player->is_auto_moving &&
14286       canFallDown(player) && move_direction != MV_DOWN &&
14287       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14288     return MP_NO_ACTION;        // player cannot walk here due to gravity
14289
14290   if (player_can_move &&
14291       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14292   {
14293     int sound_element = SND_ELEMENT(element);
14294     int sound_action = ACTION_WALKING;
14295
14296     if (IS_RND_GATE(element))
14297     {
14298       if (!player->key[RND_GATE_NR(element)])
14299         return MP_NO_ACTION;
14300     }
14301     else if (IS_RND_GATE_GRAY(element))
14302     {
14303       if (!player->key[RND_GATE_GRAY_NR(element)])
14304         return MP_NO_ACTION;
14305     }
14306     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14307     {
14308       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14309         return MP_NO_ACTION;
14310     }
14311     else if (element == EL_EXIT_OPEN ||
14312              element == EL_EM_EXIT_OPEN ||
14313              element == EL_EM_EXIT_OPENING ||
14314              element == EL_STEEL_EXIT_OPEN ||
14315              element == EL_EM_STEEL_EXIT_OPEN ||
14316              element == EL_EM_STEEL_EXIT_OPENING ||
14317              element == EL_SP_EXIT_OPEN ||
14318              element == EL_SP_EXIT_OPENING)
14319     {
14320       sound_action = ACTION_PASSING;    // player is passing exit
14321     }
14322     else if (element == EL_EMPTY)
14323     {
14324       sound_action = ACTION_MOVING;             // nothing to walk on
14325     }
14326
14327     // play sound from background or player, whatever is available
14328     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14329       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14330     else
14331       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14332   }
14333   else if (player_can_move &&
14334            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14335   {
14336     if (!ACCESS_FROM(element, opposite_direction))
14337       return MP_NO_ACTION;      // field not accessible from this direction
14338
14339     if (CAN_MOVE(element))      // only fixed elements can be passed!
14340       return MP_NO_ACTION;
14341
14342     if (IS_EM_GATE(element))
14343     {
14344       if (!player->key[EM_GATE_NR(element)])
14345         return MP_NO_ACTION;
14346     }
14347     else if (IS_EM_GATE_GRAY(element))
14348     {
14349       if (!player->key[EM_GATE_GRAY_NR(element)])
14350         return MP_NO_ACTION;
14351     }
14352     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14353     {
14354       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14355         return MP_NO_ACTION;
14356     }
14357     else if (IS_EMC_GATE(element))
14358     {
14359       if (!player->key[EMC_GATE_NR(element)])
14360         return MP_NO_ACTION;
14361     }
14362     else if (IS_EMC_GATE_GRAY(element))
14363     {
14364       if (!player->key[EMC_GATE_GRAY_NR(element)])
14365         return MP_NO_ACTION;
14366     }
14367     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14368     {
14369       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14370         return MP_NO_ACTION;
14371     }
14372     else if (element == EL_DC_GATE_WHITE ||
14373              element == EL_DC_GATE_WHITE_GRAY ||
14374              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14375     {
14376       if (player->num_white_keys == 0)
14377         return MP_NO_ACTION;
14378
14379       player->num_white_keys--;
14380     }
14381     else if (IS_SP_PORT(element))
14382     {
14383       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14384           element == EL_SP_GRAVITY_PORT_RIGHT ||
14385           element == EL_SP_GRAVITY_PORT_UP ||
14386           element == EL_SP_GRAVITY_PORT_DOWN)
14387         player->gravity = !player->gravity;
14388       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14389                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14390                element == EL_SP_GRAVITY_ON_PORT_UP ||
14391                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14392         player->gravity = TRUE;
14393       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14394                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14395                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14396                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14397         player->gravity = FALSE;
14398     }
14399
14400     // automatically move to the next field with double speed
14401     player->programmed_action = move_direction;
14402
14403     if (player->move_delay_reset_counter == 0)
14404     {
14405       player->move_delay_reset_counter = 2;     // two double speed steps
14406
14407       DOUBLE_PLAYER_SPEED(player);
14408     }
14409
14410     PlayLevelSoundAction(x, y, ACTION_PASSING);
14411   }
14412   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14413   {
14414     RemoveField(x, y);
14415
14416     if (mode != DF_SNAP)
14417     {
14418       GfxElement[x][y] = GFX_ELEMENT(element);
14419       player->is_digging = TRUE;
14420     }
14421
14422     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14423
14424     // use old behaviour for old levels (digging)
14425     if (!level.finish_dig_collect)
14426     {
14427       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14428                                           player->index_bit, dig_side);
14429
14430       // if digging triggered player relocation, finish digging tile
14431       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14432         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14433     }
14434
14435     if (mode == DF_SNAP)
14436     {
14437       if (level.block_snap_field)
14438         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14439       else
14440         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14441
14442       // use old behaviour for old levels (snapping)
14443       if (!level.finish_dig_collect)
14444         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14445                                             player->index_bit, dig_side);
14446     }
14447   }
14448   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14449   {
14450     RemoveField(x, y);
14451
14452     if (is_player && mode != DF_SNAP)
14453     {
14454       GfxElement[x][y] = element;
14455       player->is_collecting = TRUE;
14456     }
14457
14458     if (element == EL_SPEED_PILL)
14459     {
14460       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14461     }
14462     else if (element == EL_EXTRA_TIME && level.time > 0)
14463     {
14464       TimeLeft += level.extra_time;
14465
14466       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14467
14468       DisplayGameControlValues();
14469     }
14470     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14471     {
14472       player->shield_normal_time_left += level.shield_normal_time;
14473       if (element == EL_SHIELD_DEADLY)
14474         player->shield_deadly_time_left += level.shield_deadly_time;
14475     }
14476     else if (element == EL_DYNAMITE ||
14477              element == EL_EM_DYNAMITE ||
14478              element == EL_SP_DISK_RED)
14479     {
14480       if (player->inventory_size < MAX_INVENTORY_SIZE)
14481         player->inventory_element[player->inventory_size++] = element;
14482
14483       DrawGameDoorValues();
14484     }
14485     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14486     {
14487       player->dynabomb_count++;
14488       player->dynabombs_left++;
14489     }
14490     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14491     {
14492       player->dynabomb_size++;
14493     }
14494     else if (element == EL_DYNABOMB_INCREASE_POWER)
14495     {
14496       player->dynabomb_xl = TRUE;
14497     }
14498     else if (IS_KEY(element))
14499     {
14500       player->key[KEY_NR(element)] = TRUE;
14501
14502       DrawGameDoorValues();
14503     }
14504     else if (element == EL_DC_KEY_WHITE)
14505     {
14506       player->num_white_keys++;
14507
14508       // display white keys?
14509       // DrawGameDoorValues();
14510     }
14511     else if (IS_ENVELOPE(element))
14512     {
14513       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14514
14515       if (!wait_for_snapping)
14516         player->show_envelope = element;
14517     }
14518     else if (element == EL_EMC_LENSES)
14519     {
14520       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14521
14522       RedrawAllInvisibleElementsForLenses();
14523     }
14524     else if (element == EL_EMC_MAGNIFIER)
14525     {
14526       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14527
14528       RedrawAllInvisibleElementsForMagnifier();
14529     }
14530     else if (IS_DROPPABLE(element) ||
14531              IS_THROWABLE(element))     // can be collected and dropped
14532     {
14533       int i;
14534
14535       if (collect_count == 0)
14536         player->inventory_infinite_element = element;
14537       else
14538         for (i = 0; i < collect_count; i++)
14539           if (player->inventory_size < MAX_INVENTORY_SIZE)
14540             player->inventory_element[player->inventory_size++] = element;
14541
14542       DrawGameDoorValues();
14543     }
14544     else if (collect_count > 0)
14545     {
14546       game.gems_still_needed -= collect_count;
14547       if (game.gems_still_needed < 0)
14548         game.gems_still_needed = 0;
14549
14550       game.snapshot.collected_item = TRUE;
14551
14552       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14553
14554       DisplayGameControlValues();
14555     }
14556
14557     RaiseScoreElement(element);
14558     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14559
14560     // use old behaviour for old levels (collecting)
14561     if (!level.finish_dig_collect && is_player)
14562     {
14563       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14564                                           player->index_bit, dig_side);
14565
14566       // if collecting triggered player relocation, finish collecting tile
14567       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14568         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14569     }
14570
14571     if (mode == DF_SNAP)
14572     {
14573       if (level.block_snap_field)
14574         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14575       else
14576         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14577
14578       // use old behaviour for old levels (snapping)
14579       if (!level.finish_dig_collect)
14580         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14581                                             player->index_bit, dig_side);
14582     }
14583   }
14584   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14585   {
14586     if (mode == DF_SNAP && element != EL_BD_ROCK)
14587       return MP_NO_ACTION;
14588
14589     if (CAN_FALL(element) && dy)
14590       return MP_NO_ACTION;
14591
14592     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14593         !(element == EL_SPRING && level.use_spring_bug))
14594       return MP_NO_ACTION;
14595
14596     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14597         ((move_direction & MV_VERTICAL &&
14598           ((element_info[element].move_pattern & MV_LEFT &&
14599             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14600            (element_info[element].move_pattern & MV_RIGHT &&
14601             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14602          (move_direction & MV_HORIZONTAL &&
14603           ((element_info[element].move_pattern & MV_UP &&
14604             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14605            (element_info[element].move_pattern & MV_DOWN &&
14606             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14607       return MP_NO_ACTION;
14608
14609     // do not push elements already moving away faster than player
14610     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14611         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14612       return MP_NO_ACTION;
14613
14614     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14615     {
14616       if (player->push_delay_value == -1 || !player_was_pushing)
14617         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14618     }
14619     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14620     {
14621       if (player->push_delay_value == -1)
14622         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14623     }
14624     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14625     {
14626       if (!player->is_pushing)
14627         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14628     }
14629
14630     player->is_pushing = TRUE;
14631     player->is_active = TRUE;
14632
14633     if (!(IN_LEV_FIELD(nextx, nexty) &&
14634           (IS_FREE(nextx, nexty) ||
14635            (IS_SB_ELEMENT(element) &&
14636             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14637            (IS_CUSTOM_ELEMENT(element) &&
14638             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14639       return MP_NO_ACTION;
14640
14641     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14642       return MP_NO_ACTION;
14643
14644     if (player->push_delay == -1)       // new pushing; restart delay
14645       player->push_delay = 0;
14646
14647     if (player->push_delay < player->push_delay_value &&
14648         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14649         element != EL_SPRING && element != EL_BALLOON)
14650     {
14651       // make sure that there is no move delay before next try to push
14652       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14653         player->move_delay = 0;
14654
14655       return MP_NO_ACTION;
14656     }
14657
14658     if (IS_CUSTOM_ELEMENT(element) &&
14659         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14660     {
14661       if (!DigFieldByCE(nextx, nexty, element))
14662         return MP_NO_ACTION;
14663     }
14664
14665     if (IS_SB_ELEMENT(element))
14666     {
14667       boolean sokoban_task_solved = FALSE;
14668
14669       if (element == EL_SOKOBAN_FIELD_FULL)
14670       {
14671         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14672
14673         IncrementSokobanFieldsNeeded();
14674         IncrementSokobanObjectsNeeded();
14675       }
14676
14677       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14678       {
14679         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14680
14681         DecrementSokobanFieldsNeeded();
14682         DecrementSokobanObjectsNeeded();
14683
14684         // sokoban object was pushed from empty field to sokoban field
14685         if (Back[x][y] == EL_EMPTY)
14686           sokoban_task_solved = TRUE;
14687       }
14688
14689       Tile[x][y] = EL_SOKOBAN_OBJECT;
14690
14691       if (Back[x][y] == Back[nextx][nexty])
14692         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14693       else if (Back[x][y] != 0)
14694         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14695                                     ACTION_EMPTYING);
14696       else
14697         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14698                                     ACTION_FILLING);
14699
14700       if (sokoban_task_solved &&
14701           game.sokoban_fields_still_needed == 0 &&
14702           game.sokoban_objects_still_needed == 0 &&
14703           level.auto_exit_sokoban)
14704       {
14705         game.players_still_needed = 0;
14706
14707         LevelSolved();
14708
14709         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14710       }
14711     }
14712     else
14713       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14714
14715     InitMovingField(x, y, move_direction);
14716     GfxAction[x][y] = ACTION_PUSHING;
14717
14718     if (mode == DF_SNAP)
14719       ContinueMoving(x, y);
14720     else
14721       MovPos[x][y] = (dx != 0 ? dx : dy);
14722
14723     Pushed[x][y] = TRUE;
14724     Pushed[nextx][nexty] = TRUE;
14725
14726     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14727       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14728     else
14729       player->push_delay_value = -1;    // get new value later
14730
14731     // check for element change _after_ element has been pushed
14732     if (game.use_change_when_pushing_bug)
14733     {
14734       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14735                                  player->index_bit, dig_side);
14736       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14737                                           player->index_bit, dig_side);
14738     }
14739   }
14740   else if (IS_SWITCHABLE(element))
14741   {
14742     if (PLAYER_SWITCHING(player, x, y))
14743     {
14744       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14745                                           player->index_bit, dig_side);
14746
14747       return MP_ACTION;
14748     }
14749
14750     player->is_switching = TRUE;
14751     player->switch_x = x;
14752     player->switch_y = y;
14753
14754     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14755
14756     if (element == EL_ROBOT_WHEEL)
14757     {
14758       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14759
14760       game.robot_wheel_x = x;
14761       game.robot_wheel_y = y;
14762       game.robot_wheel_active = TRUE;
14763
14764       TEST_DrawLevelField(x, y);
14765     }
14766     else if (element == EL_SP_TERMINAL)
14767     {
14768       int xx, yy;
14769
14770       SCAN_PLAYFIELD(xx, yy)
14771       {
14772         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14773         {
14774           Bang(xx, yy);
14775         }
14776         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14777         {
14778           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14779
14780           ResetGfxAnimation(xx, yy);
14781           TEST_DrawLevelField(xx, yy);
14782         }
14783       }
14784     }
14785     else if (IS_BELT_SWITCH(element))
14786     {
14787       ToggleBeltSwitch(x, y);
14788     }
14789     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14790              element == EL_SWITCHGATE_SWITCH_DOWN ||
14791              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14792              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14793     {
14794       ToggleSwitchgateSwitch(x, y);
14795     }
14796     else if (element == EL_LIGHT_SWITCH ||
14797              element == EL_LIGHT_SWITCH_ACTIVE)
14798     {
14799       ToggleLightSwitch(x, y);
14800     }
14801     else if (element == EL_TIMEGATE_SWITCH ||
14802              element == EL_DC_TIMEGATE_SWITCH)
14803     {
14804       ActivateTimegateSwitch(x, y);
14805     }
14806     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14807              element == EL_BALLOON_SWITCH_RIGHT ||
14808              element == EL_BALLOON_SWITCH_UP    ||
14809              element == EL_BALLOON_SWITCH_DOWN  ||
14810              element == EL_BALLOON_SWITCH_NONE  ||
14811              element == EL_BALLOON_SWITCH_ANY)
14812     {
14813       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14814                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14815                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14816                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14817                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14818                              move_direction);
14819     }
14820     else if (element == EL_LAMP)
14821     {
14822       Tile[x][y] = EL_LAMP_ACTIVE;
14823       game.lights_still_needed--;
14824
14825       ResetGfxAnimation(x, y);
14826       TEST_DrawLevelField(x, y);
14827     }
14828     else if (element == EL_TIME_ORB_FULL)
14829     {
14830       Tile[x][y] = EL_TIME_ORB_EMPTY;
14831
14832       if (level.time > 0 || level.use_time_orb_bug)
14833       {
14834         TimeLeft += level.time_orb_time;
14835         game.no_time_limit = FALSE;
14836
14837         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14838
14839         DisplayGameControlValues();
14840       }
14841
14842       ResetGfxAnimation(x, y);
14843       TEST_DrawLevelField(x, y);
14844     }
14845     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14846              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14847     {
14848       int xx, yy;
14849
14850       game.ball_active = !game.ball_active;
14851
14852       SCAN_PLAYFIELD(xx, yy)
14853       {
14854         int e = Tile[xx][yy];
14855
14856         if (game.ball_active)
14857         {
14858           if (e == EL_EMC_MAGIC_BALL)
14859             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14860           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14861             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14862         }
14863         else
14864         {
14865           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14866             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14867           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14868             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14869         }
14870       }
14871     }
14872
14873     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14874                                         player->index_bit, dig_side);
14875
14876     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14877                                         player->index_bit, dig_side);
14878
14879     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14880                                         player->index_bit, dig_side);
14881
14882     return MP_ACTION;
14883   }
14884   else
14885   {
14886     if (!PLAYER_SWITCHING(player, x, y))
14887     {
14888       player->is_switching = TRUE;
14889       player->switch_x = x;
14890       player->switch_y = y;
14891
14892       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14893                                  player->index_bit, dig_side);
14894       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14895                                           player->index_bit, dig_side);
14896
14897       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14898                                  player->index_bit, dig_side);
14899       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14900                                           player->index_bit, dig_side);
14901     }
14902
14903     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14904                                player->index_bit, dig_side);
14905     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14906                                         player->index_bit, dig_side);
14907
14908     return MP_NO_ACTION;
14909   }
14910
14911   player->push_delay = -1;
14912
14913   if (is_player)                // function can also be called by EL_PENGUIN
14914   {
14915     if (Tile[x][y] != element)          // really digged/collected something
14916     {
14917       player->is_collecting = !player->is_digging;
14918       player->is_active = TRUE;
14919
14920       player->last_removed_element = element;
14921     }
14922   }
14923
14924   return MP_MOVING;
14925 }
14926
14927 static boolean DigFieldByCE(int x, int y, int digging_element)
14928 {
14929   int element = Tile[x][y];
14930
14931   if (!IS_FREE(x, y))
14932   {
14933     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14934                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14935                   ACTION_BREAKING);
14936
14937     // no element can dig solid indestructible elements
14938     if (IS_INDESTRUCTIBLE(element) &&
14939         !IS_DIGGABLE(element) &&
14940         !IS_COLLECTIBLE(element))
14941       return FALSE;
14942
14943     if (AmoebaNr[x][y] &&
14944         (element == EL_AMOEBA_FULL ||
14945          element == EL_BD_AMOEBA ||
14946          element == EL_AMOEBA_GROWING))
14947     {
14948       AmoebaCnt[AmoebaNr[x][y]]--;
14949       AmoebaCnt2[AmoebaNr[x][y]]--;
14950     }
14951
14952     if (IS_MOVING(x, y))
14953       RemoveMovingField(x, y);
14954     else
14955     {
14956       RemoveField(x, y);
14957       TEST_DrawLevelField(x, y);
14958     }
14959
14960     // if digged element was about to explode, prevent the explosion
14961     ExplodeField[x][y] = EX_TYPE_NONE;
14962
14963     PlayLevelSoundAction(x, y, action);
14964   }
14965
14966   Store[x][y] = EL_EMPTY;
14967
14968   // this makes it possible to leave the removed element again
14969   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14970     Store[x][y] = element;
14971
14972   return TRUE;
14973 }
14974
14975 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14976 {
14977   int jx = player->jx, jy = player->jy;
14978   int x = jx + dx, y = jy + dy;
14979   int snap_direction = (dx == -1 ? MV_LEFT  :
14980                         dx == +1 ? MV_RIGHT :
14981                         dy == -1 ? MV_UP    :
14982                         dy == +1 ? MV_DOWN  : MV_NONE);
14983   boolean can_continue_snapping = (level.continuous_snapping &&
14984                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14985
14986   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14987     return FALSE;
14988
14989   if (!player->active || !IN_LEV_FIELD(x, y))
14990     return FALSE;
14991
14992   if (dx && dy)
14993     return FALSE;
14994
14995   if (!dx && !dy)
14996   {
14997     if (player->MovPos == 0)
14998       player->is_pushing = FALSE;
14999
15000     player->is_snapping = FALSE;
15001
15002     if (player->MovPos == 0)
15003     {
15004       player->is_moving = FALSE;
15005       player->is_digging = FALSE;
15006       player->is_collecting = FALSE;
15007     }
15008
15009     return FALSE;
15010   }
15011
15012   // prevent snapping with already pressed snap key when not allowed
15013   if (player->is_snapping && !can_continue_snapping)
15014     return FALSE;
15015
15016   player->MovDir = snap_direction;
15017
15018   if (player->MovPos == 0)
15019   {
15020     player->is_moving = FALSE;
15021     player->is_digging = FALSE;
15022     player->is_collecting = FALSE;
15023   }
15024
15025   player->is_dropping = FALSE;
15026   player->is_dropping_pressed = FALSE;
15027   player->drop_pressed_delay = 0;
15028
15029   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15030     return FALSE;
15031
15032   player->is_snapping = TRUE;
15033   player->is_active = TRUE;
15034
15035   if (player->MovPos == 0)
15036   {
15037     player->is_moving = FALSE;
15038     player->is_digging = FALSE;
15039     player->is_collecting = FALSE;
15040   }
15041
15042   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15043     TEST_DrawLevelField(player->last_jx, player->last_jy);
15044
15045   TEST_DrawLevelField(x, y);
15046
15047   return TRUE;
15048 }
15049
15050 static boolean DropElement(struct PlayerInfo *player)
15051 {
15052   int old_element, new_element;
15053   int dropx = player->jx, dropy = player->jy;
15054   int drop_direction = player->MovDir;
15055   int drop_side = drop_direction;
15056   int drop_element = get_next_dropped_element(player);
15057
15058   /* do not drop an element on top of another element; when holding drop key
15059      pressed without moving, dropped element must move away before the next
15060      element can be dropped (this is especially important if the next element
15061      is dynamite, which can be placed on background for historical reasons) */
15062   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15063     return MP_ACTION;
15064
15065   if (IS_THROWABLE(drop_element))
15066   {
15067     dropx += GET_DX_FROM_DIR(drop_direction);
15068     dropy += GET_DY_FROM_DIR(drop_direction);
15069
15070     if (!IN_LEV_FIELD(dropx, dropy))
15071       return FALSE;
15072   }
15073
15074   old_element = Tile[dropx][dropy];     // old element at dropping position
15075   new_element = drop_element;           // default: no change when dropping
15076
15077   // check if player is active, not moving and ready to drop
15078   if (!player->active || player->MovPos || player->drop_delay > 0)
15079     return FALSE;
15080
15081   // check if player has anything that can be dropped
15082   if (new_element == EL_UNDEFINED)
15083     return FALSE;
15084
15085   // only set if player has anything that can be dropped
15086   player->is_dropping_pressed = TRUE;
15087
15088   // check if drop key was pressed long enough for EM style dynamite
15089   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15090     return FALSE;
15091
15092   // check if anything can be dropped at the current position
15093   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15094     return FALSE;
15095
15096   // collected custom elements can only be dropped on empty fields
15097   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15098     return FALSE;
15099
15100   if (old_element != EL_EMPTY)
15101     Back[dropx][dropy] = old_element;   // store old element on this field
15102
15103   ResetGfxAnimation(dropx, dropy);
15104   ResetRandomAnimationValue(dropx, dropy);
15105
15106   if (player->inventory_size > 0 ||
15107       player->inventory_infinite_element != EL_UNDEFINED)
15108   {
15109     if (player->inventory_size > 0)
15110     {
15111       player->inventory_size--;
15112
15113       DrawGameDoorValues();
15114
15115       if (new_element == EL_DYNAMITE)
15116         new_element = EL_DYNAMITE_ACTIVE;
15117       else if (new_element == EL_EM_DYNAMITE)
15118         new_element = EL_EM_DYNAMITE_ACTIVE;
15119       else if (new_element == EL_SP_DISK_RED)
15120         new_element = EL_SP_DISK_RED_ACTIVE;
15121     }
15122
15123     Tile[dropx][dropy] = new_element;
15124
15125     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15126       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15127                           el2img(Tile[dropx][dropy]), 0);
15128
15129     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15130
15131     // needed if previous element just changed to "empty" in the last frame
15132     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15133
15134     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15135                                player->index_bit, drop_side);
15136     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15137                                         CE_PLAYER_DROPS_X,
15138                                         player->index_bit, drop_side);
15139
15140     TestIfElementTouchesCustomElement(dropx, dropy);
15141   }
15142   else          // player is dropping a dyna bomb
15143   {
15144     player->dynabombs_left--;
15145
15146     Tile[dropx][dropy] = new_element;
15147
15148     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15149       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15150                           el2img(Tile[dropx][dropy]), 0);
15151
15152     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15153   }
15154
15155   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15156     InitField_WithBug1(dropx, dropy, FALSE);
15157
15158   new_element = Tile[dropx][dropy];     // element might have changed
15159
15160   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15161       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15162   {
15163     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15164       MovDir[dropx][dropy] = drop_direction;
15165
15166     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15167
15168     // do not cause impact style collision by dropping elements that can fall
15169     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15170   }
15171
15172   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15173   player->is_dropping = TRUE;
15174
15175   player->drop_pressed_delay = 0;
15176   player->is_dropping_pressed = FALSE;
15177
15178   player->drop_x = dropx;
15179   player->drop_y = dropy;
15180
15181   return TRUE;
15182 }
15183
15184 // ----------------------------------------------------------------------------
15185 // game sound playing functions
15186 // ----------------------------------------------------------------------------
15187
15188 static int *loop_sound_frame = NULL;
15189 static int *loop_sound_volume = NULL;
15190
15191 void InitPlayLevelSound(void)
15192 {
15193   int num_sounds = getSoundListSize();
15194
15195   checked_free(loop_sound_frame);
15196   checked_free(loop_sound_volume);
15197
15198   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15199   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15200 }
15201
15202 static void PlayLevelSound(int x, int y, int nr)
15203 {
15204   int sx = SCREENX(x), sy = SCREENY(y);
15205   int volume, stereo_position;
15206   int max_distance = 8;
15207   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15208
15209   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15210       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15211     return;
15212
15213   if (!IN_LEV_FIELD(x, y) ||
15214       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15215       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15216     return;
15217
15218   volume = SOUND_MAX_VOLUME;
15219
15220   if (!IN_SCR_FIELD(sx, sy))
15221   {
15222     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15223     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15224
15225     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15226   }
15227
15228   stereo_position = (SOUND_MAX_LEFT +
15229                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15230                      (SCR_FIELDX + 2 * max_distance));
15231
15232   if (IS_LOOP_SOUND(nr))
15233   {
15234     /* This assures that quieter loop sounds do not overwrite louder ones,
15235        while restarting sound volume comparison with each new game frame. */
15236
15237     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15238       return;
15239
15240     loop_sound_volume[nr] = volume;
15241     loop_sound_frame[nr] = FrameCounter;
15242   }
15243
15244   PlaySoundExt(nr, volume, stereo_position, type);
15245 }
15246
15247 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15248 {
15249   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15250                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15251                  y < LEVELY(BY1) ? LEVELY(BY1) :
15252                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15253                  sound_action);
15254 }
15255
15256 static void PlayLevelSoundAction(int x, int y, int action)
15257 {
15258   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15259 }
15260
15261 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15262 {
15263   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15264
15265   if (sound_effect != SND_UNDEFINED)
15266     PlayLevelSound(x, y, sound_effect);
15267 }
15268
15269 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15270                                               int action)
15271 {
15272   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15273
15274   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15275     PlayLevelSound(x, y, sound_effect);
15276 }
15277
15278 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15279 {
15280   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15281
15282   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15283     PlayLevelSound(x, y, sound_effect);
15284 }
15285
15286 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15287 {
15288   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15289
15290   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15291     StopSound(sound_effect);
15292 }
15293
15294 static int getLevelMusicNr(void)
15295 {
15296   if (levelset.music[level_nr] != MUS_UNDEFINED)
15297     return levelset.music[level_nr];            // from config file
15298   else
15299     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15300 }
15301
15302 static void FadeLevelSounds(void)
15303 {
15304   FadeSounds();
15305 }
15306
15307 static void FadeLevelMusic(void)
15308 {
15309   int music_nr = getLevelMusicNr();
15310   char *curr_music = getCurrentlyPlayingMusicFilename();
15311   char *next_music = getMusicInfoEntryFilename(music_nr);
15312
15313   if (!strEqual(curr_music, next_music))
15314     FadeMusic();
15315 }
15316
15317 void FadeLevelSoundsAndMusic(void)
15318 {
15319   FadeLevelSounds();
15320   FadeLevelMusic();
15321 }
15322
15323 static void PlayLevelMusic(void)
15324 {
15325   int music_nr = getLevelMusicNr();
15326   char *curr_music = getCurrentlyPlayingMusicFilename();
15327   char *next_music = getMusicInfoEntryFilename(music_nr);
15328
15329   if (!strEqual(curr_music, next_music))
15330     PlayMusicLoop(music_nr);
15331 }
15332
15333 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15334 {
15335   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15336   int offset = 0;
15337   int x = xx - offset;
15338   int y = yy - offset;
15339
15340   switch (sample)
15341   {
15342     case SOUND_blank:
15343       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15344       break;
15345
15346     case SOUND_roll:
15347       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15348       break;
15349
15350     case SOUND_stone:
15351       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15352       break;
15353
15354     case SOUND_nut:
15355       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15356       break;
15357
15358     case SOUND_crack:
15359       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15360       break;
15361
15362     case SOUND_bug:
15363       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15364       break;
15365
15366     case SOUND_tank:
15367       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15368       break;
15369
15370     case SOUND_android_clone:
15371       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15372       break;
15373
15374     case SOUND_android_move:
15375       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15376       break;
15377
15378     case SOUND_spring:
15379       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15380       break;
15381
15382     case SOUND_slurp:
15383       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15384       break;
15385
15386     case SOUND_eater:
15387       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15388       break;
15389
15390     case SOUND_eater_eat:
15391       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15392       break;
15393
15394     case SOUND_alien:
15395       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15396       break;
15397
15398     case SOUND_collect:
15399       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15400       break;
15401
15402     case SOUND_diamond:
15403       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15404       break;
15405
15406     case SOUND_squash:
15407       // !!! CHECK THIS !!!
15408 #if 1
15409       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15410 #else
15411       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15412 #endif
15413       break;
15414
15415     case SOUND_wonderfall:
15416       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15417       break;
15418
15419     case SOUND_drip:
15420       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15421       break;
15422
15423     case SOUND_push:
15424       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15425       break;
15426
15427     case SOUND_dirt:
15428       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15429       break;
15430
15431     case SOUND_acid:
15432       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15433       break;
15434
15435     case SOUND_ball:
15436       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15437       break;
15438
15439     case SOUND_slide:
15440       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15441       break;
15442
15443     case SOUND_wonder:
15444       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15445       break;
15446
15447     case SOUND_door:
15448       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15449       break;
15450
15451     case SOUND_exit_open:
15452       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15453       break;
15454
15455     case SOUND_exit_leave:
15456       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15457       break;
15458
15459     case SOUND_dynamite:
15460       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15461       break;
15462
15463     case SOUND_tick:
15464       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15465       break;
15466
15467     case SOUND_press:
15468       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15469       break;
15470
15471     case SOUND_wheel:
15472       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15473       break;
15474
15475     case SOUND_boom:
15476       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15477       break;
15478
15479     case SOUND_die:
15480       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15481       break;
15482
15483     case SOUND_time:
15484       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15485       break;
15486
15487     default:
15488       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15489       break;
15490   }
15491 }
15492
15493 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15494 {
15495   int element = map_element_SP_to_RND(element_sp);
15496   int action = map_action_SP_to_RND(action_sp);
15497   int offset = (setup.sp_show_border_elements ? 0 : 1);
15498   int x = xx - offset;
15499   int y = yy - offset;
15500
15501   PlayLevelSoundElementAction(x, y, element, action);
15502 }
15503
15504 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15505 {
15506   int element = map_element_MM_to_RND(element_mm);
15507   int action = map_action_MM_to_RND(action_mm);
15508   int offset = 0;
15509   int x = xx - offset;
15510   int y = yy - offset;
15511
15512   if (!IS_MM_ELEMENT(element))
15513     element = EL_MM_DEFAULT;
15514
15515   PlayLevelSoundElementAction(x, y, element, action);
15516 }
15517
15518 void PlaySound_MM(int sound_mm)
15519 {
15520   int sound = map_sound_MM_to_RND(sound_mm);
15521
15522   if (sound == SND_UNDEFINED)
15523     return;
15524
15525   PlaySound(sound);
15526 }
15527
15528 void PlaySoundLoop_MM(int sound_mm)
15529 {
15530   int sound = map_sound_MM_to_RND(sound_mm);
15531
15532   if (sound == SND_UNDEFINED)
15533     return;
15534
15535   PlaySoundLoop(sound);
15536 }
15537
15538 void StopSound_MM(int sound_mm)
15539 {
15540   int sound = map_sound_MM_to_RND(sound_mm);
15541
15542   if (sound == SND_UNDEFINED)
15543     return;
15544
15545   StopSound(sound);
15546 }
15547
15548 void RaiseScore(int value)
15549 {
15550   game.score += value;
15551
15552   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15553
15554   DisplayGameControlValues();
15555 }
15556
15557 void RaiseScoreElement(int element)
15558 {
15559   switch (element)
15560   {
15561     case EL_EMERALD:
15562     case EL_BD_DIAMOND:
15563     case EL_EMERALD_YELLOW:
15564     case EL_EMERALD_RED:
15565     case EL_EMERALD_PURPLE:
15566     case EL_SP_INFOTRON:
15567       RaiseScore(level.score[SC_EMERALD]);
15568       break;
15569     case EL_DIAMOND:
15570       RaiseScore(level.score[SC_DIAMOND]);
15571       break;
15572     case EL_CRYSTAL:
15573       RaiseScore(level.score[SC_CRYSTAL]);
15574       break;
15575     case EL_PEARL:
15576       RaiseScore(level.score[SC_PEARL]);
15577       break;
15578     case EL_BUG:
15579     case EL_BD_BUTTERFLY:
15580     case EL_SP_ELECTRON:
15581       RaiseScore(level.score[SC_BUG]);
15582       break;
15583     case EL_SPACESHIP:
15584     case EL_BD_FIREFLY:
15585     case EL_SP_SNIKSNAK:
15586       RaiseScore(level.score[SC_SPACESHIP]);
15587       break;
15588     case EL_YAMYAM:
15589     case EL_DARK_YAMYAM:
15590       RaiseScore(level.score[SC_YAMYAM]);
15591       break;
15592     case EL_ROBOT:
15593       RaiseScore(level.score[SC_ROBOT]);
15594       break;
15595     case EL_PACMAN:
15596       RaiseScore(level.score[SC_PACMAN]);
15597       break;
15598     case EL_NUT:
15599       RaiseScore(level.score[SC_NUT]);
15600       break;
15601     case EL_DYNAMITE:
15602     case EL_EM_DYNAMITE:
15603     case EL_SP_DISK_RED:
15604     case EL_DYNABOMB_INCREASE_NUMBER:
15605     case EL_DYNABOMB_INCREASE_SIZE:
15606     case EL_DYNABOMB_INCREASE_POWER:
15607       RaiseScore(level.score[SC_DYNAMITE]);
15608       break;
15609     case EL_SHIELD_NORMAL:
15610     case EL_SHIELD_DEADLY:
15611       RaiseScore(level.score[SC_SHIELD]);
15612       break;
15613     case EL_EXTRA_TIME:
15614       RaiseScore(level.extra_time_score);
15615       break;
15616     case EL_KEY_1:
15617     case EL_KEY_2:
15618     case EL_KEY_3:
15619     case EL_KEY_4:
15620     case EL_EM_KEY_1:
15621     case EL_EM_KEY_2:
15622     case EL_EM_KEY_3:
15623     case EL_EM_KEY_4:
15624     case EL_EMC_KEY_5:
15625     case EL_EMC_KEY_6:
15626     case EL_EMC_KEY_7:
15627     case EL_EMC_KEY_8:
15628     case EL_DC_KEY_WHITE:
15629       RaiseScore(level.score[SC_KEY]);
15630       break;
15631     default:
15632       RaiseScore(element_info[element].collect_score);
15633       break;
15634   }
15635 }
15636
15637 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15638 {
15639   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15640   {
15641     if (!quick_quit)
15642     {
15643       // prevent short reactivation of overlay buttons while closing door
15644       SetOverlayActive(FALSE);
15645
15646       // door may still be open due to skipped or envelope style request
15647       CloseDoor(DOOR_CLOSE_1);
15648     }
15649
15650     if (network.enabled)
15651       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15652     else
15653     {
15654       if (quick_quit)
15655         FadeSkipNextFadeIn();
15656
15657       SetGameStatus(GAME_MODE_MAIN);
15658
15659       DrawMainMenu();
15660     }
15661   }
15662   else          // continue playing the game
15663   {
15664     if (tape.playing && tape.deactivate_display)
15665       TapeDeactivateDisplayOff(TRUE);
15666
15667     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15668
15669     if (tape.playing && tape.deactivate_display)
15670       TapeDeactivateDisplayOn();
15671   }
15672 }
15673
15674 void RequestQuitGame(boolean escape_key_pressed)
15675 {
15676   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15677   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15678                         level_editor_test_game);
15679   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15680                           quick_quit);
15681
15682   RequestQuitGameExt(skip_request, quick_quit,
15683                      "Do you really want to quit the game?");
15684 }
15685
15686 void RequestRestartGame(char *message)
15687 {
15688   game.restart_game_message = NULL;
15689
15690   boolean has_started_game = hasStartedNetworkGame();
15691   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15692
15693   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15694   {
15695     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15696   }
15697   else
15698   {
15699     // needed in case of envelope request to close game panel
15700     CloseDoor(DOOR_CLOSE_1);
15701
15702     SetGameStatus(GAME_MODE_MAIN);
15703
15704     DrawMainMenu();
15705   }
15706 }
15707
15708 void CheckGameOver(void)
15709 {
15710   static boolean last_game_over = FALSE;
15711   static int game_over_delay = 0;
15712   int game_over_delay_value = 50;
15713   boolean game_over = checkGameFailed();
15714
15715   // do not handle game over if request dialog is already active
15716   if (game.request_active)
15717     return;
15718
15719   // do not ask to play again if game was never actually played
15720   if (!game.GamePlayed)
15721     return;
15722
15723   if (!game_over)
15724   {
15725     last_game_over = FALSE;
15726     game_over_delay = game_over_delay_value;
15727
15728     return;
15729   }
15730
15731   if (game_over_delay > 0)
15732   {
15733     game_over_delay--;
15734
15735     return;
15736   }
15737
15738   if (last_game_over != game_over)
15739     game.restart_game_message = (hasStartedNetworkGame() ?
15740                                  "Game over! Play it again?" :
15741                                  "Game over!");
15742
15743   last_game_over = game_over;
15744 }
15745
15746 boolean checkGameSolved(void)
15747 {
15748   // set for all game engines if level was solved
15749   return game.LevelSolved_GameEnd;
15750 }
15751
15752 boolean checkGameFailed(void)
15753 {
15754   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15755     return (game_em.game_over && !game_em.level_solved);
15756   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15757     return (game_sp.game_over && !game_sp.level_solved);
15758   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15759     return (game_mm.game_over && !game_mm.level_solved);
15760   else                          // GAME_ENGINE_TYPE_RND
15761     return (game.GameOver && !game.LevelSolved);
15762 }
15763
15764 boolean checkGameEnded(void)
15765 {
15766   return (checkGameSolved() || checkGameFailed());
15767 }
15768
15769
15770 // ----------------------------------------------------------------------------
15771 // random generator functions
15772 // ----------------------------------------------------------------------------
15773
15774 unsigned int InitEngineRandom_RND(int seed)
15775 {
15776   game.num_random_calls = 0;
15777
15778   return InitEngineRandom(seed);
15779 }
15780
15781 unsigned int RND(int max)
15782 {
15783   if (max > 0)
15784   {
15785     game.num_random_calls++;
15786
15787     return GetEngineRandom(max);
15788   }
15789
15790   return 0;
15791 }
15792
15793
15794 // ----------------------------------------------------------------------------
15795 // game engine snapshot handling functions
15796 // ----------------------------------------------------------------------------
15797
15798 struct EngineSnapshotInfo
15799 {
15800   // runtime values for custom element collect score
15801   int collect_score[NUM_CUSTOM_ELEMENTS];
15802
15803   // runtime values for group element choice position
15804   int choice_pos[NUM_GROUP_ELEMENTS];
15805
15806   // runtime values for belt position animations
15807   int belt_graphic[4][NUM_BELT_PARTS];
15808   int belt_anim_mode[4][NUM_BELT_PARTS];
15809 };
15810
15811 static struct EngineSnapshotInfo engine_snapshot_rnd;
15812 static char *snapshot_level_identifier = NULL;
15813 static int snapshot_level_nr = -1;
15814
15815 static void SaveEngineSnapshotValues_RND(void)
15816 {
15817   static int belt_base_active_element[4] =
15818   {
15819     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15820     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15821     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15822     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15823   };
15824   int i, j;
15825
15826   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15827   {
15828     int element = EL_CUSTOM_START + i;
15829
15830     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15831   }
15832
15833   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15834   {
15835     int element = EL_GROUP_START + i;
15836
15837     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15838   }
15839
15840   for (i = 0; i < 4; i++)
15841   {
15842     for (j = 0; j < NUM_BELT_PARTS; j++)
15843     {
15844       int element = belt_base_active_element[i] + j;
15845       int graphic = el2img(element);
15846       int anim_mode = graphic_info[graphic].anim_mode;
15847
15848       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15849       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15850     }
15851   }
15852 }
15853
15854 static void LoadEngineSnapshotValues_RND(void)
15855 {
15856   unsigned int num_random_calls = game.num_random_calls;
15857   int i, j;
15858
15859   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15860   {
15861     int element = EL_CUSTOM_START + i;
15862
15863     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15864   }
15865
15866   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15867   {
15868     int element = EL_GROUP_START + i;
15869
15870     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15871   }
15872
15873   for (i = 0; i < 4; i++)
15874   {
15875     for (j = 0; j < NUM_BELT_PARTS; j++)
15876     {
15877       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15878       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15879
15880       graphic_info[graphic].anim_mode = anim_mode;
15881     }
15882   }
15883
15884   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15885   {
15886     InitRND(tape.random_seed);
15887     for (i = 0; i < num_random_calls; i++)
15888       RND(1);
15889   }
15890
15891   if (game.num_random_calls != num_random_calls)
15892   {
15893     Error("number of random calls out of sync");
15894     Error("number of random calls should be %d", num_random_calls);
15895     Error("number of random calls is %d", game.num_random_calls);
15896
15897     Fail("this should not happen -- please debug");
15898   }
15899 }
15900
15901 void FreeEngineSnapshotSingle(void)
15902 {
15903   FreeSnapshotSingle();
15904
15905   setString(&snapshot_level_identifier, NULL);
15906   snapshot_level_nr = -1;
15907 }
15908
15909 void FreeEngineSnapshotList(void)
15910 {
15911   FreeSnapshotList();
15912 }
15913
15914 static ListNode *SaveEngineSnapshotBuffers(void)
15915 {
15916   ListNode *buffers = NULL;
15917
15918   // copy some special values to a structure better suited for the snapshot
15919
15920   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15921     SaveEngineSnapshotValues_RND();
15922   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15923     SaveEngineSnapshotValues_EM();
15924   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15925     SaveEngineSnapshotValues_SP(&buffers);
15926   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15927     SaveEngineSnapshotValues_MM(&buffers);
15928
15929   // save values stored in special snapshot structure
15930
15931   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15932     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15933   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15934     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15935   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15936     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15937   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15938     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15939
15940   // save further RND engine values
15941
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15945
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15951
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15955
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15957
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15960
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15979
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15982
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15986
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15989
15990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15996
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15999
16000 #if 0
16001   ListNode *node = engine_snapshot_list_rnd;
16002   int num_bytes = 0;
16003
16004   while (node != NULL)
16005   {
16006     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16007
16008     node = node->next;
16009   }
16010
16011   Debug("game:playing:SaveEngineSnapshotBuffers",
16012         "size of engine snapshot: %d bytes", num_bytes);
16013 #endif
16014
16015   return buffers;
16016 }
16017
16018 void SaveEngineSnapshotSingle(void)
16019 {
16020   ListNode *buffers = SaveEngineSnapshotBuffers();
16021
16022   // finally save all snapshot buffers to single snapshot
16023   SaveSnapshotSingle(buffers);
16024
16025   // save level identification information
16026   setString(&snapshot_level_identifier, leveldir_current->identifier);
16027   snapshot_level_nr = level_nr;
16028 }
16029
16030 boolean CheckSaveEngineSnapshotToList(void)
16031 {
16032   boolean save_snapshot =
16033     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16034      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16035       game.snapshot.changed_action) ||
16036      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16037       game.snapshot.collected_item));
16038
16039   game.snapshot.changed_action = FALSE;
16040   game.snapshot.collected_item = FALSE;
16041   game.snapshot.save_snapshot = save_snapshot;
16042
16043   return save_snapshot;
16044 }
16045
16046 void SaveEngineSnapshotToList(void)
16047 {
16048   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16049       tape.quick_resume)
16050     return;
16051
16052   ListNode *buffers = SaveEngineSnapshotBuffers();
16053
16054   // finally save all snapshot buffers to snapshot list
16055   SaveSnapshotToList(buffers);
16056 }
16057
16058 void SaveEngineSnapshotToListInitial(void)
16059 {
16060   FreeEngineSnapshotList();
16061
16062   SaveEngineSnapshotToList();
16063 }
16064
16065 static void LoadEngineSnapshotValues(void)
16066 {
16067   // restore special values from snapshot structure
16068
16069   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16070     LoadEngineSnapshotValues_RND();
16071   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16072     LoadEngineSnapshotValues_EM();
16073   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16074     LoadEngineSnapshotValues_SP();
16075   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16076     LoadEngineSnapshotValues_MM();
16077 }
16078
16079 void LoadEngineSnapshotSingle(void)
16080 {
16081   LoadSnapshotSingle();
16082
16083   LoadEngineSnapshotValues();
16084 }
16085
16086 static void LoadEngineSnapshot_Undo(int steps)
16087 {
16088   LoadSnapshotFromList_Older(steps);
16089
16090   LoadEngineSnapshotValues();
16091 }
16092
16093 static void LoadEngineSnapshot_Redo(int steps)
16094 {
16095   LoadSnapshotFromList_Newer(steps);
16096
16097   LoadEngineSnapshotValues();
16098 }
16099
16100 boolean CheckEngineSnapshotSingle(void)
16101 {
16102   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16103           snapshot_level_nr == level_nr);
16104 }
16105
16106 boolean CheckEngineSnapshotList(void)
16107 {
16108   return CheckSnapshotList();
16109 }
16110
16111
16112 // ---------- new game button stuff -------------------------------------------
16113
16114 static struct
16115 {
16116   int graphic;
16117   struct XY *pos;
16118   int gadget_id;
16119   boolean *setup_value;
16120   boolean allowed_on_tape;
16121   boolean is_touch_button;
16122   char *infotext;
16123 } gamebutton_info[NUM_GAME_BUTTONS] =
16124 {
16125   {
16126     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16127     GAME_CTRL_ID_STOP,                          NULL,
16128     TRUE, FALSE,                                "stop game"
16129   },
16130   {
16131     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16132     GAME_CTRL_ID_PAUSE,                         NULL,
16133     TRUE, FALSE,                                "pause game"
16134   },
16135   {
16136     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16137     GAME_CTRL_ID_PLAY,                          NULL,
16138     TRUE, FALSE,                                "play game"
16139   },
16140   {
16141     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16142     GAME_CTRL_ID_UNDO,                          NULL,
16143     TRUE, FALSE,                                "undo step"
16144   },
16145   {
16146     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16147     GAME_CTRL_ID_REDO,                          NULL,
16148     TRUE, FALSE,                                "redo step"
16149   },
16150   {
16151     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16152     GAME_CTRL_ID_SAVE,                          NULL,
16153     TRUE, FALSE,                                "save game"
16154   },
16155   {
16156     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16157     GAME_CTRL_ID_PAUSE2,                        NULL,
16158     TRUE, FALSE,                                "pause game"
16159   },
16160   {
16161     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16162     GAME_CTRL_ID_LOAD,                          NULL,
16163     TRUE, FALSE,                                "load game"
16164   },
16165   {
16166     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16167     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16168     FALSE, FALSE,                               "stop game"
16169   },
16170   {
16171     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16172     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16173     FALSE, FALSE,                               "pause game"
16174   },
16175   {
16176     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16177     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16178     FALSE, FALSE,                               "play game"
16179   },
16180   {
16181     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16182     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16183     FALSE, TRUE,                                "stop game"
16184   },
16185   {
16186     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16187     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16188     FALSE, TRUE,                                "pause game"
16189   },
16190   {
16191     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16192     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16193     TRUE, FALSE,                                "background music on/off"
16194   },
16195   {
16196     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16197     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16198     TRUE, FALSE,                                "sound loops on/off"
16199   },
16200   {
16201     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16202     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16203     TRUE, FALSE,                                "normal sounds on/off"
16204   },
16205   {
16206     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16207     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16208     FALSE, FALSE,                               "background music on/off"
16209   },
16210   {
16211     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16212     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16213     FALSE, FALSE,                               "sound loops on/off"
16214   },
16215   {
16216     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16217     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16218     FALSE, FALSE,                               "normal sounds on/off"
16219   }
16220 };
16221
16222 void CreateGameButtons(void)
16223 {
16224   int i;
16225
16226   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16227   {
16228     int graphic = gamebutton_info[i].graphic;
16229     struct GraphicInfo *gfx = &graphic_info[graphic];
16230     struct XY *pos = gamebutton_info[i].pos;
16231     struct GadgetInfo *gi;
16232     int button_type;
16233     boolean checked;
16234     unsigned int event_mask;
16235     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16236     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16237     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16238     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16239     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16240     int gd_x   = gfx->src_x;
16241     int gd_y   = gfx->src_y;
16242     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16243     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16244     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16245     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16246     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16247     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16248     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16249     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16250     int id = i;
16251
16252     if (gfx->bitmap == NULL)
16253     {
16254       game_gadget[id] = NULL;
16255
16256       continue;
16257     }
16258
16259     if (id == GAME_CTRL_ID_STOP ||
16260         id == GAME_CTRL_ID_PANEL_STOP ||
16261         id == GAME_CTRL_ID_TOUCH_STOP ||
16262         id == GAME_CTRL_ID_PLAY ||
16263         id == GAME_CTRL_ID_PANEL_PLAY ||
16264         id == GAME_CTRL_ID_SAVE ||
16265         id == GAME_CTRL_ID_LOAD)
16266     {
16267       button_type = GD_TYPE_NORMAL_BUTTON;
16268       checked = FALSE;
16269       event_mask = GD_EVENT_RELEASED;
16270     }
16271     else if (id == GAME_CTRL_ID_UNDO ||
16272              id == GAME_CTRL_ID_REDO)
16273     {
16274       button_type = GD_TYPE_NORMAL_BUTTON;
16275       checked = FALSE;
16276       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16277     }
16278     else
16279     {
16280       button_type = GD_TYPE_CHECK_BUTTON;
16281       checked = (gamebutton_info[i].setup_value != NULL ?
16282                  *gamebutton_info[i].setup_value : FALSE);
16283       event_mask = GD_EVENT_PRESSED;
16284     }
16285
16286     gi = CreateGadget(GDI_CUSTOM_ID, id,
16287                       GDI_IMAGE_ID, graphic,
16288                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16289                       GDI_X, base_x + x,
16290                       GDI_Y, base_y + y,
16291                       GDI_WIDTH, gfx->width,
16292                       GDI_HEIGHT, gfx->height,
16293                       GDI_TYPE, button_type,
16294                       GDI_STATE, GD_BUTTON_UNPRESSED,
16295                       GDI_CHECKED, checked,
16296                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16297                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16298                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16299                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16300                       GDI_DIRECT_DRAW, FALSE,
16301                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16302                       GDI_EVENT_MASK, event_mask,
16303                       GDI_CALLBACK_ACTION, HandleGameButtons,
16304                       GDI_END);
16305
16306     if (gi == NULL)
16307       Fail("cannot create gadget");
16308
16309     game_gadget[id] = gi;
16310   }
16311 }
16312
16313 void FreeGameButtons(void)
16314 {
16315   int i;
16316
16317   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16318     FreeGadget(game_gadget[i]);
16319 }
16320
16321 static void UnmapGameButtonsAtSamePosition(int id)
16322 {
16323   int i;
16324
16325   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16326     if (i != id &&
16327         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16328         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16329       UnmapGadget(game_gadget[i]);
16330 }
16331
16332 static void UnmapGameButtonsAtSamePosition_All(void)
16333 {
16334   if (setup.show_load_save_buttons)
16335   {
16336     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16337     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16338     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16339   }
16340   else if (setup.show_undo_redo_buttons)
16341   {
16342     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16343     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16344     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16345   }
16346   else
16347   {
16348     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16349     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16350     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16351
16352     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16353     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16354     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16355   }
16356 }
16357
16358 void MapLoadSaveButtons(void)
16359 {
16360   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16361   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16362
16363   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16364   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16365 }
16366
16367 void MapUndoRedoButtons(void)
16368 {
16369   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16370   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16371
16372   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16373   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16374 }
16375
16376 void ModifyPauseButtons(void)
16377 {
16378   static int ids[] =
16379   {
16380     GAME_CTRL_ID_PAUSE,
16381     GAME_CTRL_ID_PAUSE2,
16382     GAME_CTRL_ID_PANEL_PAUSE,
16383     GAME_CTRL_ID_TOUCH_PAUSE,
16384     -1
16385   };
16386   int i;
16387
16388   for (i = 0; ids[i] > -1; i++)
16389     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16390 }
16391
16392 static void MapGameButtonsExt(boolean on_tape)
16393 {
16394   int i;
16395
16396   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16397     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16398       MapGadget(game_gadget[i]);
16399
16400   UnmapGameButtonsAtSamePosition_All();
16401
16402   RedrawGameButtons();
16403 }
16404
16405 static void UnmapGameButtonsExt(boolean on_tape)
16406 {
16407   int i;
16408
16409   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16410     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16411       UnmapGadget(game_gadget[i]);
16412 }
16413
16414 static void RedrawGameButtonsExt(boolean on_tape)
16415 {
16416   int i;
16417
16418   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16419     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16420       RedrawGadget(game_gadget[i]);
16421 }
16422
16423 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16424 {
16425   if (gi == NULL)
16426     return;
16427
16428   gi->checked = state;
16429 }
16430
16431 static void RedrawSoundButtonGadget(int id)
16432 {
16433   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16434              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16435              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16436              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16437              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16438              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16439              id);
16440
16441   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16442   RedrawGadget(game_gadget[id2]);
16443 }
16444
16445 void MapGameButtons(void)
16446 {
16447   MapGameButtonsExt(FALSE);
16448 }
16449
16450 void UnmapGameButtons(void)
16451 {
16452   UnmapGameButtonsExt(FALSE);
16453 }
16454
16455 void RedrawGameButtons(void)
16456 {
16457   RedrawGameButtonsExt(FALSE);
16458 }
16459
16460 void MapGameButtonsOnTape(void)
16461 {
16462   MapGameButtonsExt(TRUE);
16463 }
16464
16465 void UnmapGameButtonsOnTape(void)
16466 {
16467   UnmapGameButtonsExt(TRUE);
16468 }
16469
16470 void RedrawGameButtonsOnTape(void)
16471 {
16472   RedrawGameButtonsExt(TRUE);
16473 }
16474
16475 static void GameUndoRedoExt(void)
16476 {
16477   ClearPlayerAction();
16478
16479   tape.pausing = TRUE;
16480
16481   RedrawPlayfield();
16482   UpdateAndDisplayGameControlValues();
16483
16484   DrawCompleteVideoDisplay();
16485   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16486   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16487   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16488
16489   ModifyPauseButtons();
16490
16491   BackToFront();
16492 }
16493
16494 static void GameUndo(int steps)
16495 {
16496   if (!CheckEngineSnapshotList())
16497     return;
16498
16499   int tape_property_bits = tape.property_bits;
16500
16501   LoadEngineSnapshot_Undo(steps);
16502
16503   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16504
16505   GameUndoRedoExt();
16506 }
16507
16508 static void GameRedo(int steps)
16509 {
16510   if (!CheckEngineSnapshotList())
16511     return;
16512
16513   int tape_property_bits = tape.property_bits;
16514
16515   LoadEngineSnapshot_Redo(steps);
16516
16517   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16518
16519   GameUndoRedoExt();
16520 }
16521
16522 static void HandleGameButtonsExt(int id, int button)
16523 {
16524   static boolean game_undo_executed = FALSE;
16525   int steps = BUTTON_STEPSIZE(button);
16526   boolean handle_game_buttons =
16527     (game_status == GAME_MODE_PLAYING ||
16528      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16529
16530   if (!handle_game_buttons)
16531     return;
16532
16533   switch (id)
16534   {
16535     case GAME_CTRL_ID_STOP:
16536     case GAME_CTRL_ID_PANEL_STOP:
16537     case GAME_CTRL_ID_TOUCH_STOP:
16538       if (game_status == GAME_MODE_MAIN)
16539         break;
16540
16541       if (tape.playing)
16542         TapeStop();
16543       else
16544         RequestQuitGame(FALSE);
16545
16546       break;
16547
16548     case GAME_CTRL_ID_PAUSE:
16549     case GAME_CTRL_ID_PAUSE2:
16550     case GAME_CTRL_ID_PANEL_PAUSE:
16551     case GAME_CTRL_ID_TOUCH_PAUSE:
16552       if (network.enabled && game_status == GAME_MODE_PLAYING)
16553       {
16554         if (tape.pausing)
16555           SendToServer_ContinuePlaying();
16556         else
16557           SendToServer_PausePlaying();
16558       }
16559       else
16560         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16561
16562       game_undo_executed = FALSE;
16563
16564       break;
16565
16566     case GAME_CTRL_ID_PLAY:
16567     case GAME_CTRL_ID_PANEL_PLAY:
16568       if (game_status == GAME_MODE_MAIN)
16569       {
16570         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16571       }
16572       else if (tape.pausing)
16573       {
16574         if (network.enabled)
16575           SendToServer_ContinuePlaying();
16576         else
16577           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16578       }
16579       break;
16580
16581     case GAME_CTRL_ID_UNDO:
16582       // Important: When using "save snapshot when collecting an item" mode,
16583       // load last (current) snapshot for first "undo" after pressing "pause"
16584       // (else the last-but-one snapshot would be loaded, because the snapshot
16585       // pointer already points to the last snapshot when pressing "pause",
16586       // which is fine for "every step/move" mode, but not for "every collect")
16587       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16588           !game_undo_executed)
16589         steps--;
16590
16591       game_undo_executed = TRUE;
16592
16593       GameUndo(steps);
16594       break;
16595
16596     case GAME_CTRL_ID_REDO:
16597       GameRedo(steps);
16598       break;
16599
16600     case GAME_CTRL_ID_SAVE:
16601       TapeQuickSave();
16602       break;
16603
16604     case GAME_CTRL_ID_LOAD:
16605       TapeQuickLoad();
16606       break;
16607
16608     case SOUND_CTRL_ID_MUSIC:
16609     case SOUND_CTRL_ID_PANEL_MUSIC:
16610       if (setup.sound_music)
16611       { 
16612         setup.sound_music = FALSE;
16613
16614         FadeMusic();
16615       }
16616       else if (audio.music_available)
16617       { 
16618         setup.sound = setup.sound_music = TRUE;
16619
16620         SetAudioMode(setup.sound);
16621
16622         if (game_status == GAME_MODE_PLAYING)
16623           PlayLevelMusic();
16624       }
16625
16626       RedrawSoundButtonGadget(id);
16627
16628       break;
16629
16630     case SOUND_CTRL_ID_LOOPS:
16631     case SOUND_CTRL_ID_PANEL_LOOPS:
16632       if (setup.sound_loops)
16633         setup.sound_loops = FALSE;
16634       else if (audio.loops_available)
16635       {
16636         setup.sound = setup.sound_loops = TRUE;
16637
16638         SetAudioMode(setup.sound);
16639       }
16640
16641       RedrawSoundButtonGadget(id);
16642
16643       break;
16644
16645     case SOUND_CTRL_ID_SIMPLE:
16646     case SOUND_CTRL_ID_PANEL_SIMPLE:
16647       if (setup.sound_simple)
16648         setup.sound_simple = FALSE;
16649       else if (audio.sound_available)
16650       {
16651         setup.sound = setup.sound_simple = TRUE;
16652
16653         SetAudioMode(setup.sound);
16654       }
16655
16656       RedrawSoundButtonGadget(id);
16657
16658       break;
16659
16660     default:
16661       break;
16662   }
16663 }
16664
16665 static void HandleGameButtons(struct GadgetInfo *gi)
16666 {
16667   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16668 }
16669
16670 void HandleSoundButtonKeys(Key key)
16671 {
16672   if (key == setup.shortcut.sound_simple)
16673     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16674   else if (key == setup.shortcut.sound_loops)
16675     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16676   else if (key == setup.shortcut.sound_music)
16677     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16678 }