fixed crash bug when player was killed by explosion
[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   // this can happen if the player was just killed by an explosion
5997   if (GfxElement[x][y] == EL_UNDEFINED)
5998     GfxElement[x][y] = EL_EMPTY;
5999
6000   if (phase == last_phase)
6001   {
6002     int element;
6003
6004     element = Tile[x][y] = Store[x][y];
6005     Store[x][y] = Store2[x][y] = 0;
6006     GfxElement[x][y] = EL_UNDEFINED;
6007
6008     // player can escape from explosions and might therefore be still alive
6009     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6010         element <= EL_PLAYER_IS_EXPLODING_4)
6011     {
6012       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6013       int explosion_element = EL_PLAYER_1 + player_nr;
6014       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6015       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6016
6017       if (level.use_explosion_element[player_nr])
6018         explosion_element = level.explosion_element[player_nr];
6019
6020       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6021                     element_info[explosion_element].content.e[xx][yy]);
6022     }
6023
6024     // restore probably existing indestructible background element
6025     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6026       element = Tile[x][y] = Back[x][y];
6027     Back[x][y] = 0;
6028
6029     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6030     GfxDir[x][y] = MV_NONE;
6031     ChangeDelay[x][y] = 0;
6032     ChangePage[x][y] = -1;
6033
6034     CustomValue[x][y] = 0;
6035
6036     InitField_WithBug2(x, y, FALSE);
6037
6038     TEST_DrawLevelField(x, y);
6039
6040     TestIfElementTouchesCustomElement(x, y);
6041
6042     if (GFX_CRUMBLED(element))
6043       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6044
6045     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6046       StorePlayer[x][y] = 0;
6047
6048     if (IS_PLAYER_ELEMENT(element))
6049       RelocatePlayer(x, y, element);
6050   }
6051   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6052   {
6053     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6054     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6055
6056     if (phase == delay)
6057       TEST_DrawLevelFieldCrumbled(x, y);
6058
6059     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6060     {
6061       DrawLevelElement(x, y, Back[x][y]);
6062       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6063     }
6064     else if (IS_WALKABLE_UNDER(Back[x][y]))
6065     {
6066       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6067       DrawLevelElementThruMask(x, y, Back[x][y]);
6068     }
6069     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6070       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6071   }
6072 }
6073
6074 static void DynaExplode(int ex, int ey)
6075 {
6076   int i, j;
6077   int dynabomb_element = Tile[ex][ey];
6078   int dynabomb_size = 1;
6079   boolean dynabomb_xl = FALSE;
6080   struct PlayerInfo *player;
6081   static int xy[4][2] =
6082   {
6083     { 0, -1 },
6084     { -1, 0 },
6085     { +1, 0 },
6086     { 0, +1 }
6087   };
6088
6089   if (IS_ACTIVE_BOMB(dynabomb_element))
6090   {
6091     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6092     dynabomb_size = player->dynabomb_size;
6093     dynabomb_xl = player->dynabomb_xl;
6094     player->dynabombs_left++;
6095   }
6096
6097   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6098
6099   for (i = 0; i < NUM_DIRECTIONS; i++)
6100   {
6101     for (j = 1; j <= dynabomb_size; j++)
6102     {
6103       int x = ex + j * xy[i][0];
6104       int y = ey + j * xy[i][1];
6105       int element;
6106
6107       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6108         break;
6109
6110       element = Tile[x][y];
6111
6112       // do not restart explosions of fields with active bombs
6113       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6114         continue;
6115
6116       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6117
6118       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6119           !IS_DIGGABLE(element) && !dynabomb_xl)
6120         break;
6121     }
6122   }
6123 }
6124
6125 void Bang(int x, int y)
6126 {
6127   int element = MovingOrBlocked2Element(x, y);
6128   int explosion_type = EX_TYPE_NORMAL;
6129
6130   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6131   {
6132     struct PlayerInfo *player = PLAYERINFO(x, y);
6133
6134     element = Tile[x][y] = player->initial_element;
6135
6136     if (level.use_explosion_element[player->index_nr])
6137     {
6138       int explosion_element = level.explosion_element[player->index_nr];
6139
6140       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6141         explosion_type = EX_TYPE_CROSS;
6142       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6143         explosion_type = EX_TYPE_CENTER;
6144     }
6145   }
6146
6147   switch (element)
6148   {
6149     case EL_BUG:
6150     case EL_SPACESHIP:
6151     case EL_BD_BUTTERFLY:
6152     case EL_BD_FIREFLY:
6153     case EL_YAMYAM:
6154     case EL_DARK_YAMYAM:
6155     case EL_ROBOT:
6156     case EL_PACMAN:
6157     case EL_MOLE:
6158       RaiseScoreElement(element);
6159       break;
6160
6161     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6162     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6163     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6164     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6165     case EL_DYNABOMB_INCREASE_NUMBER:
6166     case EL_DYNABOMB_INCREASE_SIZE:
6167     case EL_DYNABOMB_INCREASE_POWER:
6168       explosion_type = EX_TYPE_DYNA;
6169       break;
6170
6171     case EL_DC_LANDMINE:
6172       explosion_type = EX_TYPE_CENTER;
6173       break;
6174
6175     case EL_PENGUIN:
6176     case EL_LAMP:
6177     case EL_LAMP_ACTIVE:
6178     case EL_AMOEBA_TO_DIAMOND:
6179       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6180         explosion_type = EX_TYPE_CENTER;
6181       break;
6182
6183     default:
6184       if (element_info[element].explosion_type == EXPLODES_CROSS)
6185         explosion_type = EX_TYPE_CROSS;
6186       else if (element_info[element].explosion_type == EXPLODES_1X1)
6187         explosion_type = EX_TYPE_CENTER;
6188       break;
6189   }
6190
6191   if (explosion_type == EX_TYPE_DYNA)
6192     DynaExplode(x, y);
6193   else
6194     Explode(x, y, EX_PHASE_START, explosion_type);
6195
6196   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6197 }
6198
6199 static void SplashAcid(int x, int y)
6200 {
6201   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6202       (!IN_LEV_FIELD(x - 1, y - 2) ||
6203        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6204     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6205
6206   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6207       (!IN_LEV_FIELD(x + 1, y - 2) ||
6208        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6209     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6210
6211   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6212 }
6213
6214 static void InitBeltMovement(void)
6215 {
6216   static int belt_base_element[4] =
6217   {
6218     EL_CONVEYOR_BELT_1_LEFT,
6219     EL_CONVEYOR_BELT_2_LEFT,
6220     EL_CONVEYOR_BELT_3_LEFT,
6221     EL_CONVEYOR_BELT_4_LEFT
6222   };
6223   static int belt_base_active_element[4] =
6224   {
6225     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6226     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6227     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6228     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6229   };
6230
6231   int x, y, i, j;
6232
6233   // set frame order for belt animation graphic according to belt direction
6234   for (i = 0; i < NUM_BELTS; i++)
6235   {
6236     int belt_nr = i;
6237
6238     for (j = 0; j < NUM_BELT_PARTS; j++)
6239     {
6240       int element = belt_base_active_element[belt_nr] + j;
6241       int graphic_1 = el2img(element);
6242       int graphic_2 = el2panelimg(element);
6243
6244       if (game.belt_dir[i] == MV_LEFT)
6245       {
6246         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6247         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6248       }
6249       else
6250       {
6251         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6252         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6253       }
6254     }
6255   }
6256
6257   SCAN_PLAYFIELD(x, y)
6258   {
6259     int element = Tile[x][y];
6260
6261     for (i = 0; i < NUM_BELTS; i++)
6262     {
6263       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6264       {
6265         int e_belt_nr = getBeltNrFromBeltElement(element);
6266         int belt_nr = i;
6267
6268         if (e_belt_nr == belt_nr)
6269         {
6270           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6271
6272           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6273         }
6274       }
6275     }
6276   }
6277 }
6278
6279 static void ToggleBeltSwitch(int x, int y)
6280 {
6281   static int belt_base_element[4] =
6282   {
6283     EL_CONVEYOR_BELT_1_LEFT,
6284     EL_CONVEYOR_BELT_2_LEFT,
6285     EL_CONVEYOR_BELT_3_LEFT,
6286     EL_CONVEYOR_BELT_4_LEFT
6287   };
6288   static int belt_base_active_element[4] =
6289   {
6290     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6291     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6292     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6293     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6294   };
6295   static int belt_base_switch_element[4] =
6296   {
6297     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6298     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6299     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6300     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6301   };
6302   static int belt_move_dir[4] =
6303   {
6304     MV_LEFT,
6305     MV_NONE,
6306     MV_RIGHT,
6307     MV_NONE,
6308   };
6309
6310   int element = Tile[x][y];
6311   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6312   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6313   int belt_dir = belt_move_dir[belt_dir_nr];
6314   int xx, yy, i;
6315
6316   if (!IS_BELT_SWITCH(element))
6317     return;
6318
6319   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6320   game.belt_dir[belt_nr] = belt_dir;
6321
6322   if (belt_dir_nr == 3)
6323     belt_dir_nr = 1;
6324
6325   // set frame order for belt animation graphic according to belt direction
6326   for (i = 0; i < NUM_BELT_PARTS; i++)
6327   {
6328     int element = belt_base_active_element[belt_nr] + i;
6329     int graphic_1 = el2img(element);
6330     int graphic_2 = el2panelimg(element);
6331
6332     if (belt_dir == MV_LEFT)
6333     {
6334       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6335       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6336     }
6337     else
6338     {
6339       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6340       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6341     }
6342   }
6343
6344   SCAN_PLAYFIELD(xx, yy)
6345   {
6346     int element = Tile[xx][yy];
6347
6348     if (IS_BELT_SWITCH(element))
6349     {
6350       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6351
6352       if (e_belt_nr == belt_nr)
6353       {
6354         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6355         TEST_DrawLevelField(xx, yy);
6356       }
6357     }
6358     else if (IS_BELT(element) && belt_dir != MV_NONE)
6359     {
6360       int e_belt_nr = getBeltNrFromBeltElement(element);
6361
6362       if (e_belt_nr == belt_nr)
6363       {
6364         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6365
6366         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6367         TEST_DrawLevelField(xx, yy);
6368       }
6369     }
6370     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6371     {
6372       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6373
6374       if (e_belt_nr == belt_nr)
6375       {
6376         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6377
6378         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6379         TEST_DrawLevelField(xx, yy);
6380       }
6381     }
6382   }
6383 }
6384
6385 static void ToggleSwitchgateSwitch(int x, int y)
6386 {
6387   int xx, yy;
6388
6389   game.switchgate_pos = !game.switchgate_pos;
6390
6391   SCAN_PLAYFIELD(xx, yy)
6392   {
6393     int element = Tile[xx][yy];
6394
6395     if (element == EL_SWITCHGATE_SWITCH_UP)
6396     {
6397       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6398       TEST_DrawLevelField(xx, yy);
6399     }
6400     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6401     {
6402       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6403       TEST_DrawLevelField(xx, yy);
6404     }
6405     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6406     {
6407       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6408       TEST_DrawLevelField(xx, yy);
6409     }
6410     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6411     {
6412       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6413       TEST_DrawLevelField(xx, yy);
6414     }
6415     else if (element == EL_SWITCHGATE_OPEN ||
6416              element == EL_SWITCHGATE_OPENING)
6417     {
6418       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6419
6420       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6421     }
6422     else if (element == EL_SWITCHGATE_CLOSED ||
6423              element == EL_SWITCHGATE_CLOSING)
6424     {
6425       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6426
6427       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6428     }
6429   }
6430 }
6431
6432 static int getInvisibleActiveFromInvisibleElement(int element)
6433 {
6434   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6435           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6436           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6437           element);
6438 }
6439
6440 static int getInvisibleFromInvisibleActiveElement(int element)
6441 {
6442   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6443           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6444           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6445           element);
6446 }
6447
6448 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6449 {
6450   int x, y;
6451
6452   SCAN_PLAYFIELD(x, y)
6453   {
6454     int element = Tile[x][y];
6455
6456     if (element == EL_LIGHT_SWITCH &&
6457         game.light_time_left > 0)
6458     {
6459       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6460       TEST_DrawLevelField(x, y);
6461     }
6462     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6463              game.light_time_left == 0)
6464     {
6465       Tile[x][y] = EL_LIGHT_SWITCH;
6466       TEST_DrawLevelField(x, y);
6467     }
6468     else if (element == EL_EMC_DRIPPER &&
6469              game.light_time_left > 0)
6470     {
6471       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6472       TEST_DrawLevelField(x, y);
6473     }
6474     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6475              game.light_time_left == 0)
6476     {
6477       Tile[x][y] = EL_EMC_DRIPPER;
6478       TEST_DrawLevelField(x, y);
6479     }
6480     else if (element == EL_INVISIBLE_STEELWALL ||
6481              element == EL_INVISIBLE_WALL ||
6482              element == EL_INVISIBLE_SAND)
6483     {
6484       if (game.light_time_left > 0)
6485         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6486
6487       TEST_DrawLevelField(x, y);
6488
6489       // uncrumble neighbour fields, if needed
6490       if (element == EL_INVISIBLE_SAND)
6491         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6492     }
6493     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6494              element == EL_INVISIBLE_WALL_ACTIVE ||
6495              element == EL_INVISIBLE_SAND_ACTIVE)
6496     {
6497       if (game.light_time_left == 0)
6498         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6499
6500       TEST_DrawLevelField(x, y);
6501
6502       // re-crumble neighbour fields, if needed
6503       if (element == EL_INVISIBLE_SAND)
6504         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6505     }
6506   }
6507 }
6508
6509 static void RedrawAllInvisibleElementsForLenses(void)
6510 {
6511   int x, y;
6512
6513   SCAN_PLAYFIELD(x, y)
6514   {
6515     int element = Tile[x][y];
6516
6517     if (element == EL_EMC_DRIPPER &&
6518         game.lenses_time_left > 0)
6519     {
6520       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6521       TEST_DrawLevelField(x, y);
6522     }
6523     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6524              game.lenses_time_left == 0)
6525     {
6526       Tile[x][y] = EL_EMC_DRIPPER;
6527       TEST_DrawLevelField(x, y);
6528     }
6529     else if (element == EL_INVISIBLE_STEELWALL ||
6530              element == EL_INVISIBLE_WALL ||
6531              element == EL_INVISIBLE_SAND)
6532     {
6533       if (game.lenses_time_left > 0)
6534         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6535
6536       TEST_DrawLevelField(x, y);
6537
6538       // uncrumble neighbour fields, if needed
6539       if (element == EL_INVISIBLE_SAND)
6540         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6541     }
6542     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6543              element == EL_INVISIBLE_WALL_ACTIVE ||
6544              element == EL_INVISIBLE_SAND_ACTIVE)
6545     {
6546       if (game.lenses_time_left == 0)
6547         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6548
6549       TEST_DrawLevelField(x, y);
6550
6551       // re-crumble neighbour fields, if needed
6552       if (element == EL_INVISIBLE_SAND)
6553         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6554     }
6555   }
6556 }
6557
6558 static void RedrawAllInvisibleElementsForMagnifier(void)
6559 {
6560   int x, y;
6561
6562   SCAN_PLAYFIELD(x, y)
6563   {
6564     int element = Tile[x][y];
6565
6566     if (element == EL_EMC_FAKE_GRASS &&
6567         game.magnify_time_left > 0)
6568     {
6569       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6570       TEST_DrawLevelField(x, y);
6571     }
6572     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6573              game.magnify_time_left == 0)
6574     {
6575       Tile[x][y] = EL_EMC_FAKE_GRASS;
6576       TEST_DrawLevelField(x, y);
6577     }
6578     else if (IS_GATE_GRAY(element) &&
6579              game.magnify_time_left > 0)
6580     {
6581       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6582                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6583                     IS_EM_GATE_GRAY(element) ?
6584                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6585                     IS_EMC_GATE_GRAY(element) ?
6586                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6587                     IS_DC_GATE_GRAY(element) ?
6588                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6589                     element);
6590       TEST_DrawLevelField(x, y);
6591     }
6592     else if (IS_GATE_GRAY_ACTIVE(element) &&
6593              game.magnify_time_left == 0)
6594     {
6595       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6596                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6597                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6598                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6599                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6600                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6601                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6602                     EL_DC_GATE_WHITE_GRAY :
6603                     element);
6604       TEST_DrawLevelField(x, y);
6605     }
6606   }
6607 }
6608
6609 static void ToggleLightSwitch(int x, int y)
6610 {
6611   int element = Tile[x][y];
6612
6613   game.light_time_left =
6614     (element == EL_LIGHT_SWITCH ?
6615      level.time_light * FRAMES_PER_SECOND : 0);
6616
6617   RedrawAllLightSwitchesAndInvisibleElements();
6618 }
6619
6620 static void ActivateTimegateSwitch(int x, int y)
6621 {
6622   int xx, yy;
6623
6624   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6625
6626   SCAN_PLAYFIELD(xx, yy)
6627   {
6628     int element = Tile[xx][yy];
6629
6630     if (element == EL_TIMEGATE_CLOSED ||
6631         element == EL_TIMEGATE_CLOSING)
6632     {
6633       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6634       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6635     }
6636
6637     /*
6638     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6639     {
6640       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6641       TEST_DrawLevelField(xx, yy);
6642     }
6643     */
6644
6645   }
6646
6647   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6648                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6649 }
6650
6651 static void Impact(int x, int y)
6652 {
6653   boolean last_line = (y == lev_fieldy - 1);
6654   boolean object_hit = FALSE;
6655   boolean impact = (last_line || object_hit);
6656   int element = Tile[x][y];
6657   int smashed = EL_STEELWALL;
6658
6659   if (!last_line)       // check if element below was hit
6660   {
6661     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6662       return;
6663
6664     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6665                                          MovDir[x][y + 1] != MV_DOWN ||
6666                                          MovPos[x][y + 1] <= TILEY / 2));
6667
6668     // do not smash moving elements that left the smashed field in time
6669     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6670         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6671       object_hit = FALSE;
6672
6673 #if USE_QUICKSAND_IMPACT_BUGFIX
6674     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6675     {
6676       RemoveMovingField(x, y + 1);
6677       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6678       Tile[x][y + 2] = EL_ROCK;
6679       TEST_DrawLevelField(x, y + 2);
6680
6681       object_hit = TRUE;
6682     }
6683
6684     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6685     {
6686       RemoveMovingField(x, y + 1);
6687       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6688       Tile[x][y + 2] = EL_ROCK;
6689       TEST_DrawLevelField(x, y + 2);
6690
6691       object_hit = TRUE;
6692     }
6693 #endif
6694
6695     if (object_hit)
6696       smashed = MovingOrBlocked2Element(x, y + 1);
6697
6698     impact = (last_line || object_hit);
6699   }
6700
6701   if (!last_line && smashed == EL_ACID) // element falls into acid
6702   {
6703     SplashAcid(x, y + 1);
6704     return;
6705   }
6706
6707   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6708   // only reset graphic animation if graphic really changes after impact
6709   if (impact &&
6710       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6711   {
6712     ResetGfxAnimation(x, y);
6713     TEST_DrawLevelField(x, y);
6714   }
6715
6716   if (impact && CAN_EXPLODE_IMPACT(element))
6717   {
6718     Bang(x, y);
6719     return;
6720   }
6721   else if (impact && element == EL_PEARL &&
6722            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6723   {
6724     ResetGfxAnimation(x, y);
6725
6726     Tile[x][y] = EL_PEARL_BREAKING;
6727     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6728     return;
6729   }
6730   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6731   {
6732     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6733
6734     return;
6735   }
6736
6737   if (impact && element == EL_AMOEBA_DROP)
6738   {
6739     if (object_hit && IS_PLAYER(x, y + 1))
6740       KillPlayerUnlessEnemyProtected(x, y + 1);
6741     else if (object_hit && smashed == EL_PENGUIN)
6742       Bang(x, y + 1);
6743     else
6744     {
6745       Tile[x][y] = EL_AMOEBA_GROWING;
6746       Store[x][y] = EL_AMOEBA_WET;
6747
6748       ResetRandomAnimationValue(x, y);
6749     }
6750     return;
6751   }
6752
6753   if (object_hit)               // check which object was hit
6754   {
6755     if ((CAN_PASS_MAGIC_WALL(element) && 
6756          (smashed == EL_MAGIC_WALL ||
6757           smashed == EL_BD_MAGIC_WALL)) ||
6758         (CAN_PASS_DC_MAGIC_WALL(element) &&
6759          smashed == EL_DC_MAGIC_WALL))
6760     {
6761       int xx, yy;
6762       int activated_magic_wall =
6763         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6764          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6765          EL_DC_MAGIC_WALL_ACTIVE);
6766
6767       // activate magic wall / mill
6768       SCAN_PLAYFIELD(xx, yy)
6769       {
6770         if (Tile[xx][yy] == smashed)
6771           Tile[xx][yy] = activated_magic_wall;
6772       }
6773
6774       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6775       game.magic_wall_active = TRUE;
6776
6777       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6778                             SND_MAGIC_WALL_ACTIVATING :
6779                             smashed == EL_BD_MAGIC_WALL ?
6780                             SND_BD_MAGIC_WALL_ACTIVATING :
6781                             SND_DC_MAGIC_WALL_ACTIVATING));
6782     }
6783
6784     if (IS_PLAYER(x, y + 1))
6785     {
6786       if (CAN_SMASH_PLAYER(element))
6787       {
6788         KillPlayerUnlessEnemyProtected(x, y + 1);
6789         return;
6790       }
6791     }
6792     else if (smashed == EL_PENGUIN)
6793     {
6794       if (CAN_SMASH_PLAYER(element))
6795       {
6796         Bang(x, y + 1);
6797         return;
6798       }
6799     }
6800     else if (element == EL_BD_DIAMOND)
6801     {
6802       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6803       {
6804         Bang(x, y + 1);
6805         return;
6806       }
6807     }
6808     else if (((element == EL_SP_INFOTRON ||
6809                element == EL_SP_ZONK) &&
6810               (smashed == EL_SP_SNIKSNAK ||
6811                smashed == EL_SP_ELECTRON ||
6812                smashed == EL_SP_DISK_ORANGE)) ||
6813              (element == EL_SP_INFOTRON &&
6814               smashed == EL_SP_DISK_YELLOW))
6815     {
6816       Bang(x, y + 1);
6817       return;
6818     }
6819     else if (CAN_SMASH_EVERYTHING(element))
6820     {
6821       if (IS_CLASSIC_ENEMY(smashed) ||
6822           CAN_EXPLODE_SMASHED(smashed))
6823       {
6824         Bang(x, y + 1);
6825         return;
6826       }
6827       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6828       {
6829         if (smashed == EL_LAMP ||
6830             smashed == EL_LAMP_ACTIVE)
6831         {
6832           Bang(x, y + 1);
6833           return;
6834         }
6835         else if (smashed == EL_NUT)
6836         {
6837           Tile[x][y + 1] = EL_NUT_BREAKING;
6838           PlayLevelSound(x, y, SND_NUT_BREAKING);
6839           RaiseScoreElement(EL_NUT);
6840           return;
6841         }
6842         else if (smashed == EL_PEARL)
6843         {
6844           ResetGfxAnimation(x, y);
6845
6846           Tile[x][y + 1] = EL_PEARL_BREAKING;
6847           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6848           return;
6849         }
6850         else if (smashed == EL_DIAMOND)
6851         {
6852           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6853           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6854           return;
6855         }
6856         else if (IS_BELT_SWITCH(smashed))
6857         {
6858           ToggleBeltSwitch(x, y + 1);
6859         }
6860         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6861                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6862                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6863                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6864         {
6865           ToggleSwitchgateSwitch(x, y + 1);
6866         }
6867         else if (smashed == EL_LIGHT_SWITCH ||
6868                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6869         {
6870           ToggleLightSwitch(x, y + 1);
6871         }
6872         else
6873         {
6874           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6875
6876           CheckElementChangeBySide(x, y + 1, smashed, element,
6877                                    CE_SWITCHED, CH_SIDE_TOP);
6878           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6879                                             CH_SIDE_TOP);
6880         }
6881       }
6882       else
6883       {
6884         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6885       }
6886     }
6887   }
6888
6889   // play sound of magic wall / mill
6890   if (!last_line &&
6891       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6892        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6893        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6894   {
6895     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6896       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6897     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6898       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6899     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6900       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6901
6902     return;
6903   }
6904
6905   // play sound of object that hits the ground
6906   if (last_line || object_hit)
6907     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6908 }
6909
6910 static void TurnRoundExt(int x, int y)
6911 {
6912   static struct
6913   {
6914     int dx, dy;
6915   } move_xy[] =
6916   {
6917     {  0,  0 },
6918     { -1,  0 },
6919     { +1,  0 },
6920     {  0,  0 },
6921     {  0, -1 },
6922     {  0,  0 }, { 0, 0 }, { 0, 0 },
6923     {  0, +1 }
6924   };
6925   static struct
6926   {
6927     int left, right, back;
6928   } turn[] =
6929   {
6930     { 0,        0,              0        },
6931     { MV_DOWN,  MV_UP,          MV_RIGHT },
6932     { MV_UP,    MV_DOWN,        MV_LEFT  },
6933     { 0,        0,              0        },
6934     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6935     { 0,        0,              0        },
6936     { 0,        0,              0        },
6937     { 0,        0,              0        },
6938     { MV_RIGHT, MV_LEFT,        MV_UP    }
6939   };
6940
6941   int element = Tile[x][y];
6942   int move_pattern = element_info[element].move_pattern;
6943
6944   int old_move_dir = MovDir[x][y];
6945   int left_dir  = turn[old_move_dir].left;
6946   int right_dir = turn[old_move_dir].right;
6947   int back_dir  = turn[old_move_dir].back;
6948
6949   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6950   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6951   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6952   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6953
6954   int left_x  = x + left_dx,  left_y  = y + left_dy;
6955   int right_x = x + right_dx, right_y = y + right_dy;
6956   int move_x  = x + move_dx,  move_y  = y + move_dy;
6957
6958   int xx, yy;
6959
6960   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6961   {
6962     TestIfBadThingTouchesOtherBadThing(x, y);
6963
6964     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6965       MovDir[x][y] = right_dir;
6966     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6967       MovDir[x][y] = left_dir;
6968
6969     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6970       MovDelay[x][y] = 9;
6971     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6972       MovDelay[x][y] = 1;
6973   }
6974   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6975   {
6976     TestIfBadThingTouchesOtherBadThing(x, y);
6977
6978     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6979       MovDir[x][y] = left_dir;
6980     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6981       MovDir[x][y] = right_dir;
6982
6983     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6984       MovDelay[x][y] = 9;
6985     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6986       MovDelay[x][y] = 1;
6987   }
6988   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6989   {
6990     TestIfBadThingTouchesOtherBadThing(x, y);
6991
6992     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6993       MovDir[x][y] = left_dir;
6994     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6995       MovDir[x][y] = right_dir;
6996
6997     if (MovDir[x][y] != old_move_dir)
6998       MovDelay[x][y] = 9;
6999   }
7000   else if (element == EL_YAMYAM)
7001   {
7002     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7003     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7004
7005     if (can_turn_left && can_turn_right)
7006       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7007     else if (can_turn_left)
7008       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7009     else if (can_turn_right)
7010       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7011     else
7012       MovDir[x][y] = back_dir;
7013
7014     MovDelay[x][y] = 16 + 16 * RND(3);
7015   }
7016   else if (element == EL_DARK_YAMYAM)
7017   {
7018     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7019                                                          left_x, left_y);
7020     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7021                                                          right_x, right_y);
7022
7023     if (can_turn_left && can_turn_right)
7024       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7025     else if (can_turn_left)
7026       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7027     else if (can_turn_right)
7028       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7029     else
7030       MovDir[x][y] = back_dir;
7031
7032     MovDelay[x][y] = 16 + 16 * RND(3);
7033   }
7034   else if (element == EL_PACMAN)
7035   {
7036     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7037     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7038
7039     if (can_turn_left && can_turn_right)
7040       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7041     else if (can_turn_left)
7042       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7043     else if (can_turn_right)
7044       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7045     else
7046       MovDir[x][y] = back_dir;
7047
7048     MovDelay[x][y] = 6 + RND(40);
7049   }
7050   else if (element == EL_PIG)
7051   {
7052     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7053     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7054     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7055     boolean should_turn_left, should_turn_right, should_move_on;
7056     int rnd_value = 24;
7057     int rnd = RND(rnd_value);
7058
7059     should_turn_left = (can_turn_left &&
7060                         (!can_move_on ||
7061                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7062                                                    y + back_dy + left_dy)));
7063     should_turn_right = (can_turn_right &&
7064                          (!can_move_on ||
7065                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7066                                                     y + back_dy + right_dy)));
7067     should_move_on = (can_move_on &&
7068                       (!can_turn_left ||
7069                        !can_turn_right ||
7070                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7071                                                  y + move_dy + left_dy) ||
7072                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7073                                                  y + move_dy + right_dy)));
7074
7075     if (should_turn_left || should_turn_right || should_move_on)
7076     {
7077       if (should_turn_left && should_turn_right && should_move_on)
7078         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7079                         rnd < 2 * rnd_value / 3 ? right_dir :
7080                         old_move_dir);
7081       else if (should_turn_left && should_turn_right)
7082         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7083       else if (should_turn_left && should_move_on)
7084         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7085       else if (should_turn_right && should_move_on)
7086         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7087       else if (should_turn_left)
7088         MovDir[x][y] = left_dir;
7089       else if (should_turn_right)
7090         MovDir[x][y] = right_dir;
7091       else if (should_move_on)
7092         MovDir[x][y] = old_move_dir;
7093     }
7094     else if (can_move_on && rnd > rnd_value / 8)
7095       MovDir[x][y] = old_move_dir;
7096     else if (can_turn_left && can_turn_right)
7097       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7098     else if (can_turn_left && rnd > rnd_value / 8)
7099       MovDir[x][y] = left_dir;
7100     else if (can_turn_right && rnd > rnd_value/8)
7101       MovDir[x][y] = right_dir;
7102     else
7103       MovDir[x][y] = back_dir;
7104
7105     xx = x + move_xy[MovDir[x][y]].dx;
7106     yy = y + move_xy[MovDir[x][y]].dy;
7107
7108     if (!IN_LEV_FIELD(xx, yy) ||
7109         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7110       MovDir[x][y] = old_move_dir;
7111
7112     MovDelay[x][y] = 0;
7113   }
7114   else if (element == EL_DRAGON)
7115   {
7116     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7117     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7118     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7119     int rnd_value = 24;
7120     int rnd = RND(rnd_value);
7121
7122     if (can_move_on && rnd > rnd_value / 8)
7123       MovDir[x][y] = old_move_dir;
7124     else if (can_turn_left && can_turn_right)
7125       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7126     else if (can_turn_left && rnd > rnd_value / 8)
7127       MovDir[x][y] = left_dir;
7128     else if (can_turn_right && rnd > rnd_value / 8)
7129       MovDir[x][y] = right_dir;
7130     else
7131       MovDir[x][y] = back_dir;
7132
7133     xx = x + move_xy[MovDir[x][y]].dx;
7134     yy = y + move_xy[MovDir[x][y]].dy;
7135
7136     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7137       MovDir[x][y] = old_move_dir;
7138
7139     MovDelay[x][y] = 0;
7140   }
7141   else if (element == EL_MOLE)
7142   {
7143     boolean can_move_on =
7144       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7145                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7146                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7147     if (!can_move_on)
7148     {
7149       boolean can_turn_left =
7150         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7151                               IS_AMOEBOID(Tile[left_x][left_y])));
7152
7153       boolean can_turn_right =
7154         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7155                               IS_AMOEBOID(Tile[right_x][right_y])));
7156
7157       if (can_turn_left && can_turn_right)
7158         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7159       else if (can_turn_left)
7160         MovDir[x][y] = left_dir;
7161       else
7162         MovDir[x][y] = right_dir;
7163     }
7164
7165     if (MovDir[x][y] != old_move_dir)
7166       MovDelay[x][y] = 9;
7167   }
7168   else if (element == EL_BALLOON)
7169   {
7170     MovDir[x][y] = game.wind_direction;
7171     MovDelay[x][y] = 0;
7172   }
7173   else if (element == EL_SPRING)
7174   {
7175     if (MovDir[x][y] & MV_HORIZONTAL)
7176     {
7177       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7178           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7179       {
7180         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7181         ResetGfxAnimation(move_x, move_y);
7182         TEST_DrawLevelField(move_x, move_y);
7183
7184         MovDir[x][y] = back_dir;
7185       }
7186       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7187                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7188         MovDir[x][y] = MV_NONE;
7189     }
7190
7191     MovDelay[x][y] = 0;
7192   }
7193   else if (element == EL_ROBOT ||
7194            element == EL_SATELLITE ||
7195            element == EL_PENGUIN ||
7196            element == EL_EMC_ANDROID)
7197   {
7198     int attr_x = -1, attr_y = -1;
7199
7200     if (game.all_players_gone)
7201     {
7202       attr_x = game.exit_x;
7203       attr_y = game.exit_y;
7204     }
7205     else
7206     {
7207       int i;
7208
7209       for (i = 0; i < MAX_PLAYERS; i++)
7210       {
7211         struct PlayerInfo *player = &stored_player[i];
7212         int jx = player->jx, jy = player->jy;
7213
7214         if (!player->active)
7215           continue;
7216
7217         if (attr_x == -1 ||
7218             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7219         {
7220           attr_x = jx;
7221           attr_y = jy;
7222         }
7223       }
7224     }
7225
7226     if (element == EL_ROBOT &&
7227         game.robot_wheel_x >= 0 &&
7228         game.robot_wheel_y >= 0 &&
7229         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7230          game.engine_version < VERSION_IDENT(3,1,0,0)))
7231     {
7232       attr_x = game.robot_wheel_x;
7233       attr_y = game.robot_wheel_y;
7234     }
7235
7236     if (element == EL_PENGUIN)
7237     {
7238       int i;
7239       static int xy[4][2] =
7240       {
7241         { 0, -1 },
7242         { -1, 0 },
7243         { +1, 0 },
7244         { 0, +1 }
7245       };
7246
7247       for (i = 0; i < NUM_DIRECTIONS; i++)
7248       {
7249         int ex = x + xy[i][0];
7250         int ey = y + xy[i][1];
7251
7252         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7253                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7254                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7255                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7256         {
7257           attr_x = ex;
7258           attr_y = ey;
7259           break;
7260         }
7261       }
7262     }
7263
7264     MovDir[x][y] = MV_NONE;
7265     if (attr_x < x)
7266       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7267     else if (attr_x > x)
7268       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7269     if (attr_y < y)
7270       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7271     else if (attr_y > y)
7272       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7273
7274     if (element == EL_ROBOT)
7275     {
7276       int newx, newy;
7277
7278       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7279         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7280       Moving2Blocked(x, y, &newx, &newy);
7281
7282       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7283         MovDelay[x][y] = 8 + 8 * !RND(3);
7284       else
7285         MovDelay[x][y] = 16;
7286     }
7287     else if (element == EL_PENGUIN)
7288     {
7289       int newx, newy;
7290
7291       MovDelay[x][y] = 1;
7292
7293       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7294       {
7295         boolean first_horiz = RND(2);
7296         int new_move_dir = MovDir[x][y];
7297
7298         MovDir[x][y] =
7299           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7300         Moving2Blocked(x, y, &newx, &newy);
7301
7302         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7303           return;
7304
7305         MovDir[x][y] =
7306           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7307         Moving2Blocked(x, y, &newx, &newy);
7308
7309         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7310           return;
7311
7312         MovDir[x][y] = old_move_dir;
7313         return;
7314       }
7315     }
7316     else if (element == EL_SATELLITE)
7317     {
7318       int newx, newy;
7319
7320       MovDelay[x][y] = 1;
7321
7322       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7323       {
7324         boolean first_horiz = RND(2);
7325         int new_move_dir = MovDir[x][y];
7326
7327         MovDir[x][y] =
7328           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7329         Moving2Blocked(x, y, &newx, &newy);
7330
7331         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7332           return;
7333
7334         MovDir[x][y] =
7335           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7336         Moving2Blocked(x, y, &newx, &newy);
7337
7338         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7339           return;
7340
7341         MovDir[x][y] = old_move_dir;
7342         return;
7343       }
7344     }
7345     else if (element == EL_EMC_ANDROID)
7346     {
7347       static int check_pos[16] =
7348       {
7349         -1,             //  0 => (invalid)
7350         7,              //  1 => MV_LEFT
7351         3,              //  2 => MV_RIGHT
7352         -1,             //  3 => (invalid)
7353         1,              //  4 =>            MV_UP
7354         0,              //  5 => MV_LEFT  | MV_UP
7355         2,              //  6 => MV_RIGHT | MV_UP
7356         -1,             //  7 => (invalid)
7357         5,              //  8 =>            MV_DOWN
7358         6,              //  9 => MV_LEFT  | MV_DOWN
7359         4,              // 10 => MV_RIGHT | MV_DOWN
7360         -1,             // 11 => (invalid)
7361         -1,             // 12 => (invalid)
7362         -1,             // 13 => (invalid)
7363         -1,             // 14 => (invalid)
7364         -1,             // 15 => (invalid)
7365       };
7366       static struct
7367       {
7368         int dx, dy;
7369         int dir;
7370       } check_xy[8] =
7371       {
7372         { -1, -1,       MV_LEFT  | MV_UP   },
7373         {  0, -1,                  MV_UP   },
7374         { +1, -1,       MV_RIGHT | MV_UP   },
7375         { +1,  0,       MV_RIGHT           },
7376         { +1, +1,       MV_RIGHT | MV_DOWN },
7377         {  0, +1,                  MV_DOWN },
7378         { -1, +1,       MV_LEFT  | MV_DOWN },
7379         { -1,  0,       MV_LEFT            },
7380       };
7381       int start_pos, check_order;
7382       boolean can_clone = FALSE;
7383       int i;
7384
7385       // check if there is any free field around current position
7386       for (i = 0; i < 8; i++)
7387       {
7388         int newx = x + check_xy[i].dx;
7389         int newy = y + check_xy[i].dy;
7390
7391         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7392         {
7393           can_clone = TRUE;
7394
7395           break;
7396         }
7397       }
7398
7399       if (can_clone)            // randomly find an element to clone
7400       {
7401         can_clone = FALSE;
7402
7403         start_pos = check_pos[RND(8)];
7404         check_order = (RND(2) ? -1 : +1);
7405
7406         for (i = 0; i < 8; i++)
7407         {
7408           int pos_raw = start_pos + i * check_order;
7409           int pos = (pos_raw + 8) % 8;
7410           int newx = x + check_xy[pos].dx;
7411           int newy = y + check_xy[pos].dy;
7412
7413           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7414           {
7415             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7416             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7417
7418             Store[x][y] = Tile[newx][newy];
7419
7420             can_clone = TRUE;
7421
7422             break;
7423           }
7424         }
7425       }
7426
7427       if (can_clone)            // randomly find a direction to move
7428       {
7429         can_clone = FALSE;
7430
7431         start_pos = check_pos[RND(8)];
7432         check_order = (RND(2) ? -1 : +1);
7433
7434         for (i = 0; i < 8; i++)
7435         {
7436           int pos_raw = start_pos + i * check_order;
7437           int pos = (pos_raw + 8) % 8;
7438           int newx = x + check_xy[pos].dx;
7439           int newy = y + check_xy[pos].dy;
7440           int new_move_dir = check_xy[pos].dir;
7441
7442           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7443           {
7444             MovDir[x][y] = new_move_dir;
7445             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7446
7447             can_clone = TRUE;
7448
7449             break;
7450           }
7451         }
7452       }
7453
7454       if (can_clone)            // cloning and moving successful
7455         return;
7456
7457       // cannot clone -- try to move towards player
7458
7459       start_pos = check_pos[MovDir[x][y] & 0x0f];
7460       check_order = (RND(2) ? -1 : +1);
7461
7462       for (i = 0; i < 3; i++)
7463       {
7464         // first check start_pos, then previous/next or (next/previous) pos
7465         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7466         int pos = (pos_raw + 8) % 8;
7467         int newx = x + check_xy[pos].dx;
7468         int newy = y + check_xy[pos].dy;
7469         int new_move_dir = check_xy[pos].dir;
7470
7471         if (IS_PLAYER(newx, newy))
7472           break;
7473
7474         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7475         {
7476           MovDir[x][y] = new_move_dir;
7477           MovDelay[x][y] = level.android_move_time * 8 + 1;
7478
7479           break;
7480         }
7481       }
7482     }
7483   }
7484   else if (move_pattern == MV_TURNING_LEFT ||
7485            move_pattern == MV_TURNING_RIGHT ||
7486            move_pattern == MV_TURNING_LEFT_RIGHT ||
7487            move_pattern == MV_TURNING_RIGHT_LEFT ||
7488            move_pattern == MV_TURNING_RANDOM ||
7489            move_pattern == MV_ALL_DIRECTIONS)
7490   {
7491     boolean can_turn_left =
7492       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7493     boolean can_turn_right =
7494       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7495
7496     if (element_info[element].move_stepsize == 0)       // "not moving"
7497       return;
7498
7499     if (move_pattern == MV_TURNING_LEFT)
7500       MovDir[x][y] = left_dir;
7501     else if (move_pattern == MV_TURNING_RIGHT)
7502       MovDir[x][y] = right_dir;
7503     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7504       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7505     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7506       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7507     else if (move_pattern == MV_TURNING_RANDOM)
7508       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7509                       can_turn_right && !can_turn_left ? right_dir :
7510                       RND(2) ? left_dir : right_dir);
7511     else if (can_turn_left && can_turn_right)
7512       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7513     else if (can_turn_left)
7514       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7515     else if (can_turn_right)
7516       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7517     else
7518       MovDir[x][y] = back_dir;
7519
7520     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7521   }
7522   else if (move_pattern == MV_HORIZONTAL ||
7523            move_pattern == MV_VERTICAL)
7524   {
7525     if (move_pattern & old_move_dir)
7526       MovDir[x][y] = back_dir;
7527     else if (move_pattern == MV_HORIZONTAL)
7528       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7529     else if (move_pattern == MV_VERTICAL)
7530       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7531
7532     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7533   }
7534   else if (move_pattern & MV_ANY_DIRECTION)
7535   {
7536     MovDir[x][y] = move_pattern;
7537     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7538   }
7539   else if (move_pattern & MV_WIND_DIRECTION)
7540   {
7541     MovDir[x][y] = game.wind_direction;
7542     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7543   }
7544   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7545   {
7546     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7547       MovDir[x][y] = left_dir;
7548     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7549       MovDir[x][y] = right_dir;
7550
7551     if (MovDir[x][y] != old_move_dir)
7552       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7553   }
7554   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7555   {
7556     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7557       MovDir[x][y] = right_dir;
7558     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7559       MovDir[x][y] = left_dir;
7560
7561     if (MovDir[x][y] != old_move_dir)
7562       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7563   }
7564   else if (move_pattern == MV_TOWARDS_PLAYER ||
7565            move_pattern == MV_AWAY_FROM_PLAYER)
7566   {
7567     int attr_x = -1, attr_y = -1;
7568     int newx, newy;
7569     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7570
7571     if (game.all_players_gone)
7572     {
7573       attr_x = game.exit_x;
7574       attr_y = game.exit_y;
7575     }
7576     else
7577     {
7578       int i;
7579
7580       for (i = 0; i < MAX_PLAYERS; i++)
7581       {
7582         struct PlayerInfo *player = &stored_player[i];
7583         int jx = player->jx, jy = player->jy;
7584
7585         if (!player->active)
7586           continue;
7587
7588         if (attr_x == -1 ||
7589             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7590         {
7591           attr_x = jx;
7592           attr_y = jy;
7593         }
7594       }
7595     }
7596
7597     MovDir[x][y] = MV_NONE;
7598     if (attr_x < x)
7599       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7600     else if (attr_x > x)
7601       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7602     if (attr_y < y)
7603       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7604     else if (attr_y > y)
7605       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7606
7607     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7608
7609     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7610     {
7611       boolean first_horiz = RND(2);
7612       int new_move_dir = MovDir[x][y];
7613
7614       if (element_info[element].move_stepsize == 0)     // "not moving"
7615       {
7616         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7617         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7618
7619         return;
7620       }
7621
7622       MovDir[x][y] =
7623         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7624       Moving2Blocked(x, y, &newx, &newy);
7625
7626       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7627         return;
7628
7629       MovDir[x][y] =
7630         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7631       Moving2Blocked(x, y, &newx, &newy);
7632
7633       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7634         return;
7635
7636       MovDir[x][y] = old_move_dir;
7637     }
7638   }
7639   else if (move_pattern == MV_WHEN_PUSHED ||
7640            move_pattern == MV_WHEN_DROPPED)
7641   {
7642     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7643       MovDir[x][y] = MV_NONE;
7644
7645     MovDelay[x][y] = 0;
7646   }
7647   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7648   {
7649     static int test_xy[7][2] =
7650     {
7651       { 0, -1 },
7652       { -1, 0 },
7653       { +1, 0 },
7654       { 0, +1 },
7655       { 0, -1 },
7656       { -1, 0 },
7657       { +1, 0 },
7658     };
7659     static int test_dir[7] =
7660     {
7661       MV_UP,
7662       MV_LEFT,
7663       MV_RIGHT,
7664       MV_DOWN,
7665       MV_UP,
7666       MV_LEFT,
7667       MV_RIGHT,
7668     };
7669     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7670     int move_preference = -1000000;     // start with very low preference
7671     int new_move_dir = MV_NONE;
7672     int start_test = RND(4);
7673     int i;
7674
7675     for (i = 0; i < NUM_DIRECTIONS; i++)
7676     {
7677       int move_dir = test_dir[start_test + i];
7678       int move_dir_preference;
7679
7680       xx = x + test_xy[start_test + i][0];
7681       yy = y + test_xy[start_test + i][1];
7682
7683       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7684           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7685       {
7686         new_move_dir = move_dir;
7687
7688         break;
7689       }
7690
7691       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7692         continue;
7693
7694       move_dir_preference = -1 * RunnerVisit[xx][yy];
7695       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7696         move_dir_preference = PlayerVisit[xx][yy];
7697
7698       if (move_dir_preference > move_preference)
7699       {
7700         // prefer field that has not been visited for the longest time
7701         move_preference = move_dir_preference;
7702         new_move_dir = move_dir;
7703       }
7704       else if (move_dir_preference == move_preference &&
7705                move_dir == old_move_dir)
7706       {
7707         // prefer last direction when all directions are preferred equally
7708         move_preference = move_dir_preference;
7709         new_move_dir = move_dir;
7710       }
7711     }
7712
7713     MovDir[x][y] = new_move_dir;
7714     if (old_move_dir != new_move_dir)
7715       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7716   }
7717 }
7718
7719 static void TurnRound(int x, int y)
7720 {
7721   int direction = MovDir[x][y];
7722
7723   TurnRoundExt(x, y);
7724
7725   GfxDir[x][y] = MovDir[x][y];
7726
7727   if (direction != MovDir[x][y])
7728     GfxFrame[x][y] = 0;
7729
7730   if (MovDelay[x][y])
7731     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7732
7733   ResetGfxFrame(x, y);
7734 }
7735
7736 static boolean JustBeingPushed(int x, int y)
7737 {
7738   int i;
7739
7740   for (i = 0; i < MAX_PLAYERS; i++)
7741   {
7742     struct PlayerInfo *player = &stored_player[i];
7743
7744     if (player->active && player->is_pushing && player->MovPos)
7745     {
7746       int next_jx = player->jx + (player->jx - player->last_jx);
7747       int next_jy = player->jy + (player->jy - player->last_jy);
7748
7749       if (x == next_jx && y == next_jy)
7750         return TRUE;
7751     }
7752   }
7753
7754   return FALSE;
7755 }
7756
7757 static void StartMoving(int x, int y)
7758 {
7759   boolean started_moving = FALSE;       // some elements can fall _and_ move
7760   int element = Tile[x][y];
7761
7762   if (Stop[x][y])
7763     return;
7764
7765   if (MovDelay[x][y] == 0)
7766     GfxAction[x][y] = ACTION_DEFAULT;
7767
7768   if (CAN_FALL(element) && y < lev_fieldy - 1)
7769   {
7770     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7771         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7772       if (JustBeingPushed(x, y))
7773         return;
7774
7775     if (element == EL_QUICKSAND_FULL)
7776     {
7777       if (IS_FREE(x, y + 1))
7778       {
7779         InitMovingField(x, y, MV_DOWN);
7780         started_moving = TRUE;
7781
7782         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7783 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7784         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7785           Store[x][y] = EL_ROCK;
7786 #else
7787         Store[x][y] = EL_ROCK;
7788 #endif
7789
7790         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7791       }
7792       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7793       {
7794         if (!MovDelay[x][y])
7795         {
7796           MovDelay[x][y] = TILEY + 1;
7797
7798           ResetGfxAnimation(x, y);
7799           ResetGfxAnimation(x, y + 1);
7800         }
7801
7802         if (MovDelay[x][y])
7803         {
7804           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7805           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7806
7807           MovDelay[x][y]--;
7808           if (MovDelay[x][y])
7809             return;
7810         }
7811
7812         Tile[x][y] = EL_QUICKSAND_EMPTY;
7813         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7814         Store[x][y + 1] = Store[x][y];
7815         Store[x][y] = 0;
7816
7817         PlayLevelSoundAction(x, y, ACTION_FILLING);
7818       }
7819       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7820       {
7821         if (!MovDelay[x][y])
7822         {
7823           MovDelay[x][y] = TILEY + 1;
7824
7825           ResetGfxAnimation(x, y);
7826           ResetGfxAnimation(x, y + 1);
7827         }
7828
7829         if (MovDelay[x][y])
7830         {
7831           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7832           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7833
7834           MovDelay[x][y]--;
7835           if (MovDelay[x][y])
7836             return;
7837         }
7838
7839         Tile[x][y] = EL_QUICKSAND_EMPTY;
7840         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7841         Store[x][y + 1] = Store[x][y];
7842         Store[x][y] = 0;
7843
7844         PlayLevelSoundAction(x, y, ACTION_FILLING);
7845       }
7846     }
7847     else if (element == EL_QUICKSAND_FAST_FULL)
7848     {
7849       if (IS_FREE(x, y + 1))
7850       {
7851         InitMovingField(x, y, MV_DOWN);
7852         started_moving = TRUE;
7853
7854         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7855 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7856         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7857           Store[x][y] = EL_ROCK;
7858 #else
7859         Store[x][y] = EL_ROCK;
7860 #endif
7861
7862         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7863       }
7864       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7865       {
7866         if (!MovDelay[x][y])
7867         {
7868           MovDelay[x][y] = TILEY + 1;
7869
7870           ResetGfxAnimation(x, y);
7871           ResetGfxAnimation(x, y + 1);
7872         }
7873
7874         if (MovDelay[x][y])
7875         {
7876           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7877           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7878
7879           MovDelay[x][y]--;
7880           if (MovDelay[x][y])
7881             return;
7882         }
7883
7884         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7885         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7886         Store[x][y + 1] = Store[x][y];
7887         Store[x][y] = 0;
7888
7889         PlayLevelSoundAction(x, y, ACTION_FILLING);
7890       }
7891       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7892       {
7893         if (!MovDelay[x][y])
7894         {
7895           MovDelay[x][y] = TILEY + 1;
7896
7897           ResetGfxAnimation(x, y);
7898           ResetGfxAnimation(x, y + 1);
7899         }
7900
7901         if (MovDelay[x][y])
7902         {
7903           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7904           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7905
7906           MovDelay[x][y]--;
7907           if (MovDelay[x][y])
7908             return;
7909         }
7910
7911         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7912         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7913         Store[x][y + 1] = Store[x][y];
7914         Store[x][y] = 0;
7915
7916         PlayLevelSoundAction(x, y, ACTION_FILLING);
7917       }
7918     }
7919     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7920              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7921     {
7922       InitMovingField(x, y, MV_DOWN);
7923       started_moving = TRUE;
7924
7925       Tile[x][y] = EL_QUICKSAND_FILLING;
7926       Store[x][y] = element;
7927
7928       PlayLevelSoundAction(x, y, ACTION_FILLING);
7929     }
7930     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7931              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7932     {
7933       InitMovingField(x, y, MV_DOWN);
7934       started_moving = TRUE;
7935
7936       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7937       Store[x][y] = element;
7938
7939       PlayLevelSoundAction(x, y, ACTION_FILLING);
7940     }
7941     else if (element == EL_MAGIC_WALL_FULL)
7942     {
7943       if (IS_FREE(x, y + 1))
7944       {
7945         InitMovingField(x, y, MV_DOWN);
7946         started_moving = TRUE;
7947
7948         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7949         Store[x][y] = EL_CHANGED(Store[x][y]);
7950       }
7951       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7952       {
7953         if (!MovDelay[x][y])
7954           MovDelay[x][y] = TILEY / 4 + 1;
7955
7956         if (MovDelay[x][y])
7957         {
7958           MovDelay[x][y]--;
7959           if (MovDelay[x][y])
7960             return;
7961         }
7962
7963         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7964         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7965         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7966         Store[x][y] = 0;
7967       }
7968     }
7969     else if (element == EL_BD_MAGIC_WALL_FULL)
7970     {
7971       if (IS_FREE(x, y + 1))
7972       {
7973         InitMovingField(x, y, MV_DOWN);
7974         started_moving = TRUE;
7975
7976         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7977         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7978       }
7979       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7980       {
7981         if (!MovDelay[x][y])
7982           MovDelay[x][y] = TILEY / 4 + 1;
7983
7984         if (MovDelay[x][y])
7985         {
7986           MovDelay[x][y]--;
7987           if (MovDelay[x][y])
7988             return;
7989         }
7990
7991         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7992         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7993         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7994         Store[x][y] = 0;
7995       }
7996     }
7997     else if (element == EL_DC_MAGIC_WALL_FULL)
7998     {
7999       if (IS_FREE(x, y + 1))
8000       {
8001         InitMovingField(x, y, MV_DOWN);
8002         started_moving = TRUE;
8003
8004         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8005         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8006       }
8007       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8008       {
8009         if (!MovDelay[x][y])
8010           MovDelay[x][y] = TILEY / 4 + 1;
8011
8012         if (MovDelay[x][y])
8013         {
8014           MovDelay[x][y]--;
8015           if (MovDelay[x][y])
8016             return;
8017         }
8018
8019         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8020         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8021         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8022         Store[x][y] = 0;
8023       }
8024     }
8025     else if ((CAN_PASS_MAGIC_WALL(element) &&
8026               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8027                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8028              (CAN_PASS_DC_MAGIC_WALL(element) &&
8029               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8030
8031     {
8032       InitMovingField(x, y, MV_DOWN);
8033       started_moving = TRUE;
8034
8035       Tile[x][y] =
8036         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8037          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8038          EL_DC_MAGIC_WALL_FILLING);
8039       Store[x][y] = element;
8040     }
8041     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8042     {
8043       SplashAcid(x, y + 1);
8044
8045       InitMovingField(x, y, MV_DOWN);
8046       started_moving = TRUE;
8047
8048       Store[x][y] = EL_ACID;
8049     }
8050     else if (
8051              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8052               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8053              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8054               CAN_FALL(element) && WasJustFalling[x][y] &&
8055               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8056
8057              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8058               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8059               (Tile[x][y + 1] == EL_BLOCKED)))
8060     {
8061       /* this is needed for a special case not covered by calling "Impact()"
8062          from "ContinueMoving()": if an element moves to a tile directly below
8063          another element which was just falling on that tile (which was empty
8064          in the previous frame), the falling element above would just stop
8065          instead of smashing the element below (in previous version, the above
8066          element was just checked for "moving" instead of "falling", resulting
8067          in incorrect smashes caused by horizontal movement of the above
8068          element; also, the case of the player being the element to smash was
8069          simply not covered here... :-/ ) */
8070
8071       CheckCollision[x][y] = 0;
8072       CheckImpact[x][y] = 0;
8073
8074       Impact(x, y);
8075     }
8076     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8077     {
8078       if (MovDir[x][y] == MV_NONE)
8079       {
8080         InitMovingField(x, y, MV_DOWN);
8081         started_moving = TRUE;
8082       }
8083     }
8084     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8085     {
8086       if (WasJustFalling[x][y]) // prevent animation from being restarted
8087         MovDir[x][y] = MV_DOWN;
8088
8089       InitMovingField(x, y, MV_DOWN);
8090       started_moving = TRUE;
8091     }
8092     else if (element == EL_AMOEBA_DROP)
8093     {
8094       Tile[x][y] = EL_AMOEBA_GROWING;
8095       Store[x][y] = EL_AMOEBA_WET;
8096     }
8097     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8098               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8099              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8100              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8101     {
8102       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8103                                 (IS_FREE(x - 1, y + 1) ||
8104                                  Tile[x - 1][y + 1] == EL_ACID));
8105       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8106                                 (IS_FREE(x + 1, y + 1) ||
8107                                  Tile[x + 1][y + 1] == EL_ACID));
8108       boolean can_fall_any  = (can_fall_left || can_fall_right);
8109       boolean can_fall_both = (can_fall_left && can_fall_right);
8110       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8111
8112       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8113       {
8114         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8115           can_fall_right = FALSE;
8116         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8117           can_fall_left = FALSE;
8118         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8119           can_fall_right = FALSE;
8120         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8121           can_fall_left = FALSE;
8122
8123         can_fall_any  = (can_fall_left || can_fall_right);
8124         can_fall_both = FALSE;
8125       }
8126
8127       if (can_fall_both)
8128       {
8129         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8130           can_fall_right = FALSE;       // slip down on left side
8131         else
8132           can_fall_left = !(can_fall_right = RND(2));
8133
8134         can_fall_both = FALSE;
8135       }
8136
8137       if (can_fall_any)
8138       {
8139         // if not determined otherwise, prefer left side for slipping down
8140         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8141         started_moving = TRUE;
8142       }
8143     }
8144     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8145     {
8146       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8147       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8148       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8149       int belt_dir = game.belt_dir[belt_nr];
8150
8151       if ((belt_dir == MV_LEFT  && left_is_free) ||
8152           (belt_dir == MV_RIGHT && right_is_free))
8153       {
8154         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8155
8156         InitMovingField(x, y, belt_dir);
8157         started_moving = TRUE;
8158
8159         Pushed[x][y] = TRUE;
8160         Pushed[nextx][y] = TRUE;
8161
8162         GfxAction[x][y] = ACTION_DEFAULT;
8163       }
8164       else
8165       {
8166         MovDir[x][y] = 0;       // if element was moving, stop it
8167       }
8168     }
8169   }
8170
8171   // not "else if" because of elements that can fall and move (EL_SPRING)
8172   if (CAN_MOVE(element) && !started_moving)
8173   {
8174     int move_pattern = element_info[element].move_pattern;
8175     int newx, newy;
8176
8177     Moving2Blocked(x, y, &newx, &newy);
8178
8179     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8180       return;
8181
8182     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8183         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8184     {
8185       WasJustMoving[x][y] = 0;
8186       CheckCollision[x][y] = 0;
8187
8188       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8189
8190       if (Tile[x][y] != element)        // element has changed
8191         return;
8192     }
8193
8194     if (!MovDelay[x][y])        // start new movement phase
8195     {
8196       // all objects that can change their move direction after each step
8197       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8198
8199       if (element != EL_YAMYAM &&
8200           element != EL_DARK_YAMYAM &&
8201           element != EL_PACMAN &&
8202           !(move_pattern & MV_ANY_DIRECTION) &&
8203           move_pattern != MV_TURNING_LEFT &&
8204           move_pattern != MV_TURNING_RIGHT &&
8205           move_pattern != MV_TURNING_LEFT_RIGHT &&
8206           move_pattern != MV_TURNING_RIGHT_LEFT &&
8207           move_pattern != MV_TURNING_RANDOM)
8208       {
8209         TurnRound(x, y);
8210
8211         if (MovDelay[x][y] && (element == EL_BUG ||
8212                                element == EL_SPACESHIP ||
8213                                element == EL_SP_SNIKSNAK ||
8214                                element == EL_SP_ELECTRON ||
8215                                element == EL_MOLE))
8216           TEST_DrawLevelField(x, y);
8217       }
8218     }
8219
8220     if (MovDelay[x][y])         // wait some time before next movement
8221     {
8222       MovDelay[x][y]--;
8223
8224       if (element == EL_ROBOT ||
8225           element == EL_YAMYAM ||
8226           element == EL_DARK_YAMYAM)
8227       {
8228         DrawLevelElementAnimationIfNeeded(x, y, element);
8229         PlayLevelSoundAction(x, y, ACTION_WAITING);
8230       }
8231       else if (element == EL_SP_ELECTRON)
8232         DrawLevelElementAnimationIfNeeded(x, y, element);
8233       else if (element == EL_DRAGON)
8234       {
8235         int i;
8236         int dir = MovDir[x][y];
8237         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8238         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8239         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8240                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8241                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8242                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8243         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8244
8245         GfxAction[x][y] = ACTION_ATTACKING;
8246
8247         if (IS_PLAYER(x, y))
8248           DrawPlayerField(x, y);
8249         else
8250           TEST_DrawLevelField(x, y);
8251
8252         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8253
8254         for (i = 1; i <= 3; i++)
8255         {
8256           int xx = x + i * dx;
8257           int yy = y + i * dy;
8258           int sx = SCREENX(xx);
8259           int sy = SCREENY(yy);
8260           int flame_graphic = graphic + (i - 1);
8261
8262           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8263             break;
8264
8265           if (MovDelay[x][y])
8266           {
8267             int flamed = MovingOrBlocked2Element(xx, yy);
8268
8269             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8270               Bang(xx, yy);
8271             else
8272               RemoveMovingField(xx, yy);
8273
8274             ChangeDelay[xx][yy] = 0;
8275
8276             Tile[xx][yy] = EL_FLAMES;
8277
8278             if (IN_SCR_FIELD(sx, sy))
8279             {
8280               TEST_DrawLevelFieldCrumbled(xx, yy);
8281               DrawGraphic(sx, sy, flame_graphic, frame);
8282             }
8283           }
8284           else
8285           {
8286             if (Tile[xx][yy] == EL_FLAMES)
8287               Tile[xx][yy] = EL_EMPTY;
8288             TEST_DrawLevelField(xx, yy);
8289           }
8290         }
8291       }
8292
8293       if (MovDelay[x][y])       // element still has to wait some time
8294       {
8295         PlayLevelSoundAction(x, y, ACTION_WAITING);
8296
8297         return;
8298       }
8299     }
8300
8301     // now make next step
8302
8303     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8304
8305     if (DONT_COLLIDE_WITH(element) &&
8306         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8307         !PLAYER_ENEMY_PROTECTED(newx, newy))
8308     {
8309       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8310
8311       return;
8312     }
8313
8314     else if (CAN_MOVE_INTO_ACID(element) &&
8315              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8316              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8317              (MovDir[x][y] == MV_DOWN ||
8318               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8319     {
8320       SplashAcid(newx, newy);
8321       Store[x][y] = EL_ACID;
8322     }
8323     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8324     {
8325       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8326           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8327           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8328           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8329       {
8330         RemoveField(x, y);
8331         TEST_DrawLevelField(x, y);
8332
8333         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8334         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8335           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8336
8337         game.friends_still_needed--;
8338         if (!game.friends_still_needed &&
8339             !game.GameOver &&
8340             game.all_players_gone)
8341           LevelSolved();
8342
8343         return;
8344       }
8345       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8346       {
8347         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8348           TEST_DrawLevelField(newx, newy);
8349         else
8350           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8351       }
8352       else if (!IS_FREE(newx, newy))
8353       {
8354         GfxAction[x][y] = ACTION_WAITING;
8355
8356         if (IS_PLAYER(x, y))
8357           DrawPlayerField(x, y);
8358         else
8359           TEST_DrawLevelField(x, y);
8360
8361         return;
8362       }
8363     }
8364     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8365     {
8366       if (IS_FOOD_PIG(Tile[newx][newy]))
8367       {
8368         if (IS_MOVING(newx, newy))
8369           RemoveMovingField(newx, newy);
8370         else
8371         {
8372           Tile[newx][newy] = EL_EMPTY;
8373           TEST_DrawLevelField(newx, newy);
8374         }
8375
8376         PlayLevelSound(x, y, SND_PIG_DIGGING);
8377       }
8378       else if (!IS_FREE(newx, newy))
8379       {
8380         if (IS_PLAYER(x, y))
8381           DrawPlayerField(x, y);
8382         else
8383           TEST_DrawLevelField(x, y);
8384
8385         return;
8386       }
8387     }
8388     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8389     {
8390       if (Store[x][y] != EL_EMPTY)
8391       {
8392         boolean can_clone = FALSE;
8393         int xx, yy;
8394
8395         // check if element to clone is still there
8396         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8397         {
8398           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8399           {
8400             can_clone = TRUE;
8401
8402             break;
8403           }
8404         }
8405
8406         // cannot clone or target field not free anymore -- do not clone
8407         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8408           Store[x][y] = EL_EMPTY;
8409       }
8410
8411       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8412       {
8413         if (IS_MV_DIAGONAL(MovDir[x][y]))
8414         {
8415           int diagonal_move_dir = MovDir[x][y];
8416           int stored = Store[x][y];
8417           int change_delay = 8;
8418           int graphic;
8419
8420           // android is moving diagonally
8421
8422           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8423
8424           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8425           GfxElement[x][y] = EL_EMC_ANDROID;
8426           GfxAction[x][y] = ACTION_SHRINKING;
8427           GfxDir[x][y] = diagonal_move_dir;
8428           ChangeDelay[x][y] = change_delay;
8429
8430           if (Store[x][y] == EL_EMPTY)
8431             Store[x][y] = GfxElementEmpty[x][y];
8432
8433           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8434                                    GfxDir[x][y]);
8435
8436           DrawLevelGraphicAnimation(x, y, graphic);
8437           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8438
8439           if (Tile[newx][newy] == EL_ACID)
8440           {
8441             SplashAcid(newx, newy);
8442
8443             return;
8444           }
8445
8446           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8447
8448           Store[newx][newy] = EL_EMC_ANDROID;
8449           GfxElement[newx][newy] = EL_EMC_ANDROID;
8450           GfxAction[newx][newy] = ACTION_GROWING;
8451           GfxDir[newx][newy] = diagonal_move_dir;
8452           ChangeDelay[newx][newy] = change_delay;
8453
8454           graphic = el_act_dir2img(GfxElement[newx][newy],
8455                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8456
8457           DrawLevelGraphicAnimation(newx, newy, graphic);
8458           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8459
8460           return;
8461         }
8462         else
8463         {
8464           Tile[newx][newy] = EL_EMPTY;
8465           TEST_DrawLevelField(newx, newy);
8466
8467           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8468         }
8469       }
8470       else if (!IS_FREE(newx, newy))
8471       {
8472         return;
8473       }
8474     }
8475     else if (IS_CUSTOM_ELEMENT(element) &&
8476              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8477     {
8478       if (!DigFieldByCE(newx, newy, element))
8479         return;
8480
8481       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8482       {
8483         RunnerVisit[x][y] = FrameCounter;
8484         PlayerVisit[x][y] /= 8;         // expire player visit path
8485       }
8486     }
8487     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8488     {
8489       if (!IS_FREE(newx, newy))
8490       {
8491         if (IS_PLAYER(x, y))
8492           DrawPlayerField(x, y);
8493         else
8494           TEST_DrawLevelField(x, y);
8495
8496         return;
8497       }
8498       else
8499       {
8500         boolean wanna_flame = !RND(10);
8501         int dx = newx - x, dy = newy - y;
8502         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8503         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8504         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8505                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8506         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8507                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8508
8509         if ((wanna_flame ||
8510              IS_CLASSIC_ENEMY(element1) ||
8511              IS_CLASSIC_ENEMY(element2)) &&
8512             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8513             element1 != EL_FLAMES && element2 != EL_FLAMES)
8514         {
8515           ResetGfxAnimation(x, y);
8516           GfxAction[x][y] = ACTION_ATTACKING;
8517
8518           if (IS_PLAYER(x, y))
8519             DrawPlayerField(x, y);
8520           else
8521             TEST_DrawLevelField(x, y);
8522
8523           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8524
8525           MovDelay[x][y] = 50;
8526
8527           Tile[newx][newy] = EL_FLAMES;
8528           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8529             Tile[newx1][newy1] = EL_FLAMES;
8530           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8531             Tile[newx2][newy2] = EL_FLAMES;
8532
8533           return;
8534         }
8535       }
8536     }
8537     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8538              Tile[newx][newy] == EL_DIAMOND)
8539     {
8540       if (IS_MOVING(newx, newy))
8541         RemoveMovingField(newx, newy);
8542       else
8543       {
8544         Tile[newx][newy] = EL_EMPTY;
8545         TEST_DrawLevelField(newx, newy);
8546       }
8547
8548       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8549     }
8550     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8551              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8552     {
8553       if (AmoebaNr[newx][newy])
8554       {
8555         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8556         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8557             Tile[newx][newy] == EL_BD_AMOEBA)
8558           AmoebaCnt[AmoebaNr[newx][newy]]--;
8559       }
8560
8561       if (IS_MOVING(newx, newy))
8562       {
8563         RemoveMovingField(newx, newy);
8564       }
8565       else
8566       {
8567         Tile[newx][newy] = EL_EMPTY;
8568         TEST_DrawLevelField(newx, newy);
8569       }
8570
8571       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8572     }
8573     else if ((element == EL_PACMAN || element == EL_MOLE)
8574              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8575     {
8576       if (AmoebaNr[newx][newy])
8577       {
8578         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8579         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8580             Tile[newx][newy] == EL_BD_AMOEBA)
8581           AmoebaCnt[AmoebaNr[newx][newy]]--;
8582       }
8583
8584       if (element == EL_MOLE)
8585       {
8586         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8587         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8588
8589         ResetGfxAnimation(x, y);
8590         GfxAction[x][y] = ACTION_DIGGING;
8591         TEST_DrawLevelField(x, y);
8592
8593         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8594
8595         return;                         // wait for shrinking amoeba
8596       }
8597       else      // element == EL_PACMAN
8598       {
8599         Tile[newx][newy] = EL_EMPTY;
8600         TEST_DrawLevelField(newx, newy);
8601         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8602       }
8603     }
8604     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8605              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8606               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8607     {
8608       // wait for shrinking amoeba to completely disappear
8609       return;
8610     }
8611     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8612     {
8613       // object was running against a wall
8614
8615       TurnRound(x, y);
8616
8617       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8618         DrawLevelElementAnimation(x, y, element);
8619
8620       if (DONT_TOUCH(element))
8621         TestIfBadThingTouchesPlayer(x, y);
8622
8623       return;
8624     }
8625
8626     InitMovingField(x, y, MovDir[x][y]);
8627
8628     PlayLevelSoundAction(x, y, ACTION_MOVING);
8629   }
8630
8631   if (MovDir[x][y])
8632     ContinueMoving(x, y);
8633 }
8634
8635 void ContinueMoving(int x, int y)
8636 {
8637   int element = Tile[x][y];
8638   struct ElementInfo *ei = &element_info[element];
8639   int direction = MovDir[x][y];
8640   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8641   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8642   int newx = x + dx, newy = y + dy;
8643   int stored = Store[x][y];
8644   int stored_new = Store[newx][newy];
8645   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8646   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8647   boolean last_line = (newy == lev_fieldy - 1);
8648   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8649
8650   if (pushed_by_player)         // special case: moving object pushed by player
8651   {
8652     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8653   }
8654   else if (use_step_delay)      // special case: moving object has step delay
8655   {
8656     if (!MovDelay[x][y])
8657       MovPos[x][y] += getElementMoveStepsize(x, y);
8658
8659     if (MovDelay[x][y])
8660       MovDelay[x][y]--;
8661     else
8662       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8663
8664     if (MovDelay[x][y])
8665     {
8666       TEST_DrawLevelField(x, y);
8667
8668       return;   // element is still waiting
8669     }
8670   }
8671   else                          // normal case: generically moving object
8672   {
8673     MovPos[x][y] += getElementMoveStepsize(x, y);
8674   }
8675
8676   if (ABS(MovPos[x][y]) < TILEX)
8677   {
8678     TEST_DrawLevelField(x, y);
8679
8680     return;     // element is still moving
8681   }
8682
8683   // element reached destination field
8684
8685   Tile[x][y] = EL_EMPTY;
8686   Tile[newx][newy] = element;
8687   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8688
8689   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8690   {
8691     element = Tile[newx][newy] = EL_ACID;
8692   }
8693   else if (element == EL_MOLE)
8694   {
8695     Tile[x][y] = EL_SAND;
8696
8697     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8698   }
8699   else if (element == EL_QUICKSAND_FILLING)
8700   {
8701     element = Tile[newx][newy] = get_next_element(element);
8702     Store[newx][newy] = Store[x][y];
8703   }
8704   else if (element == EL_QUICKSAND_EMPTYING)
8705   {
8706     Tile[x][y] = get_next_element(element);
8707     element = Tile[newx][newy] = Store[x][y];
8708   }
8709   else if (element == EL_QUICKSAND_FAST_FILLING)
8710   {
8711     element = Tile[newx][newy] = get_next_element(element);
8712     Store[newx][newy] = Store[x][y];
8713   }
8714   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8715   {
8716     Tile[x][y] = get_next_element(element);
8717     element = Tile[newx][newy] = Store[x][y];
8718   }
8719   else if (element == EL_MAGIC_WALL_FILLING)
8720   {
8721     element = Tile[newx][newy] = get_next_element(element);
8722     if (!game.magic_wall_active)
8723       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8724     Store[newx][newy] = Store[x][y];
8725   }
8726   else if (element == EL_MAGIC_WALL_EMPTYING)
8727   {
8728     Tile[x][y] = get_next_element(element);
8729     if (!game.magic_wall_active)
8730       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8731     element = Tile[newx][newy] = Store[x][y];
8732
8733     InitField(newx, newy, FALSE);
8734   }
8735   else if (element == EL_BD_MAGIC_WALL_FILLING)
8736   {
8737     element = Tile[newx][newy] = get_next_element(element);
8738     if (!game.magic_wall_active)
8739       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8740     Store[newx][newy] = Store[x][y];
8741   }
8742   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8743   {
8744     Tile[x][y] = get_next_element(element);
8745     if (!game.magic_wall_active)
8746       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8747     element = Tile[newx][newy] = Store[x][y];
8748
8749     InitField(newx, newy, FALSE);
8750   }
8751   else if (element == EL_DC_MAGIC_WALL_FILLING)
8752   {
8753     element = Tile[newx][newy] = get_next_element(element);
8754     if (!game.magic_wall_active)
8755       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8756     Store[newx][newy] = Store[x][y];
8757   }
8758   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8759   {
8760     Tile[x][y] = get_next_element(element);
8761     if (!game.magic_wall_active)
8762       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8763     element = Tile[newx][newy] = Store[x][y];
8764
8765     InitField(newx, newy, FALSE);
8766   }
8767   else if (element == EL_AMOEBA_DROPPING)
8768   {
8769     Tile[x][y] = get_next_element(element);
8770     element = Tile[newx][newy] = Store[x][y];
8771   }
8772   else if (element == EL_SOKOBAN_OBJECT)
8773   {
8774     if (Back[x][y])
8775       Tile[x][y] = Back[x][y];
8776
8777     if (Back[newx][newy])
8778       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8779
8780     Back[x][y] = Back[newx][newy] = 0;
8781   }
8782
8783   Store[x][y] = EL_EMPTY;
8784   MovPos[x][y] = 0;
8785   MovDir[x][y] = 0;
8786   MovDelay[x][y] = 0;
8787
8788   MovDelay[newx][newy] = 0;
8789
8790   if (CAN_CHANGE_OR_HAS_ACTION(element))
8791   {
8792     // copy element change control values to new field
8793     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8794     ChangePage[newx][newy]  = ChangePage[x][y];
8795     ChangeCount[newx][newy] = ChangeCount[x][y];
8796     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8797   }
8798
8799   CustomValue[newx][newy] = CustomValue[x][y];
8800
8801   ChangeDelay[x][y] = 0;
8802   ChangePage[x][y] = -1;
8803   ChangeCount[x][y] = 0;
8804   ChangeEvent[x][y] = -1;
8805
8806   CustomValue[x][y] = 0;
8807
8808   // copy animation control values to new field
8809   GfxFrame[newx][newy]  = GfxFrame[x][y];
8810   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8811   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8812   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8813
8814   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8815
8816   // some elements can leave other elements behind after moving
8817   if (ei->move_leave_element != EL_EMPTY &&
8818       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8819       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8820   {
8821     int move_leave_element = ei->move_leave_element;
8822
8823     // this makes it possible to leave the removed element again
8824     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8825       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8826
8827     Tile[x][y] = move_leave_element;
8828
8829     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8830       MovDir[x][y] = direction;
8831
8832     InitField(x, y, FALSE);
8833
8834     if (GFX_CRUMBLED(Tile[x][y]))
8835       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8836
8837     if (IS_PLAYER_ELEMENT(move_leave_element))
8838       RelocatePlayer(x, y, move_leave_element);
8839   }
8840
8841   // do this after checking for left-behind element
8842   ResetGfxAnimation(x, y);      // reset animation values for old field
8843
8844   if (!CAN_MOVE(element) ||
8845       (CAN_FALL(element) && direction == MV_DOWN &&
8846        (element == EL_SPRING ||
8847         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8848         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8849     GfxDir[x][y] = MovDir[newx][newy] = 0;
8850
8851   TEST_DrawLevelField(x, y);
8852   TEST_DrawLevelField(newx, newy);
8853
8854   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8855
8856   // prevent pushed element from moving on in pushed direction
8857   if (pushed_by_player && CAN_MOVE(element) &&
8858       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8859       !(element_info[element].move_pattern & direction))
8860     TurnRound(newx, newy);
8861
8862   // prevent elements on conveyor belt from moving on in last direction
8863   if (pushed_by_conveyor && CAN_FALL(element) &&
8864       direction & MV_HORIZONTAL)
8865     MovDir[newx][newy] = 0;
8866
8867   if (!pushed_by_player)
8868   {
8869     int nextx = newx + dx, nexty = newy + dy;
8870     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8871
8872     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8873
8874     if (CAN_FALL(element) && direction == MV_DOWN)
8875       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8876
8877     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8878       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8879
8880     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8881       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8882   }
8883
8884   if (DONT_TOUCH(element))      // object may be nasty to player or others
8885   {
8886     TestIfBadThingTouchesPlayer(newx, newy);
8887     TestIfBadThingTouchesFriend(newx, newy);
8888
8889     if (!IS_CUSTOM_ELEMENT(element))
8890       TestIfBadThingTouchesOtherBadThing(newx, newy);
8891   }
8892   else if (element == EL_PENGUIN)
8893     TestIfFriendTouchesBadThing(newx, newy);
8894
8895   if (DONT_GET_HIT_BY(element))
8896   {
8897     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8898   }
8899
8900   // give the player one last chance (one more frame) to move away
8901   if (CAN_FALL(element) && direction == MV_DOWN &&
8902       (last_line || (!IS_FREE(x, newy + 1) &&
8903                      (!IS_PLAYER(x, newy + 1) ||
8904                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8905     Impact(x, newy);
8906
8907   if (pushed_by_player && !game.use_change_when_pushing_bug)
8908   {
8909     int push_side = MV_DIR_OPPOSITE(direction);
8910     struct PlayerInfo *player = PLAYERINFO(x, y);
8911
8912     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8913                                player->index_bit, push_side);
8914     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8915                                         player->index_bit, push_side);
8916   }
8917
8918   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8919     MovDelay[newx][newy] = 1;
8920
8921   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8922
8923   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8924   TestIfElementHitsCustomElement(newx, newy, direction);
8925   TestIfPlayerTouchesCustomElement(newx, newy);
8926   TestIfElementTouchesCustomElement(newx, newy);
8927
8928   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8929       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8930     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8931                              MV_DIR_OPPOSITE(direction));
8932 }
8933
8934 int AmoebaNeighbourNr(int ax, int ay)
8935 {
8936   int i;
8937   int element = Tile[ax][ay];
8938   int group_nr = 0;
8939   static int xy[4][2] =
8940   {
8941     { 0, -1 },
8942     { -1, 0 },
8943     { +1, 0 },
8944     { 0, +1 }
8945   };
8946
8947   for (i = 0; i < NUM_DIRECTIONS; i++)
8948   {
8949     int x = ax + xy[i][0];
8950     int y = ay + xy[i][1];
8951
8952     if (!IN_LEV_FIELD(x, y))
8953       continue;
8954
8955     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8956       group_nr = AmoebaNr[x][y];
8957   }
8958
8959   return group_nr;
8960 }
8961
8962 static void AmoebaMerge(int ax, int ay)
8963 {
8964   int i, x, y, xx, yy;
8965   int new_group_nr = AmoebaNr[ax][ay];
8966   static int xy[4][2] =
8967   {
8968     { 0, -1 },
8969     { -1, 0 },
8970     { +1, 0 },
8971     { 0, +1 }
8972   };
8973
8974   if (new_group_nr == 0)
8975     return;
8976
8977   for (i = 0; i < NUM_DIRECTIONS; i++)
8978   {
8979     x = ax + xy[i][0];
8980     y = ay + xy[i][1];
8981
8982     if (!IN_LEV_FIELD(x, y))
8983       continue;
8984
8985     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8986          Tile[x][y] == EL_BD_AMOEBA ||
8987          Tile[x][y] == EL_AMOEBA_DEAD) &&
8988         AmoebaNr[x][y] != new_group_nr)
8989     {
8990       int old_group_nr = AmoebaNr[x][y];
8991
8992       if (old_group_nr == 0)
8993         return;
8994
8995       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8996       AmoebaCnt[old_group_nr] = 0;
8997       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8998       AmoebaCnt2[old_group_nr] = 0;
8999
9000       SCAN_PLAYFIELD(xx, yy)
9001       {
9002         if (AmoebaNr[xx][yy] == old_group_nr)
9003           AmoebaNr[xx][yy] = new_group_nr;
9004       }
9005     }
9006   }
9007 }
9008
9009 void AmoebaToDiamond(int ax, int ay)
9010 {
9011   int i, x, y;
9012
9013   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9014   {
9015     int group_nr = AmoebaNr[ax][ay];
9016
9017 #ifdef DEBUG
9018     if (group_nr == 0)
9019     {
9020       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9021       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9022
9023       return;
9024     }
9025 #endif
9026
9027     SCAN_PLAYFIELD(x, y)
9028     {
9029       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9030       {
9031         AmoebaNr[x][y] = 0;
9032         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9033       }
9034     }
9035
9036     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9037                             SND_AMOEBA_TURNING_TO_GEM :
9038                             SND_AMOEBA_TURNING_TO_ROCK));
9039     Bang(ax, ay);
9040   }
9041   else
9042   {
9043     static int xy[4][2] =
9044     {
9045       { 0, -1 },
9046       { -1, 0 },
9047       { +1, 0 },
9048       { 0, +1 }
9049     };
9050
9051     for (i = 0; i < NUM_DIRECTIONS; i++)
9052     {
9053       x = ax + xy[i][0];
9054       y = ay + xy[i][1];
9055
9056       if (!IN_LEV_FIELD(x, y))
9057         continue;
9058
9059       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9060       {
9061         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9062                               SND_AMOEBA_TURNING_TO_GEM :
9063                               SND_AMOEBA_TURNING_TO_ROCK));
9064         Bang(x, y);
9065       }
9066     }
9067   }
9068 }
9069
9070 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9071 {
9072   int x, y;
9073   int group_nr = AmoebaNr[ax][ay];
9074   boolean done = FALSE;
9075
9076 #ifdef DEBUG
9077   if (group_nr == 0)
9078   {
9079     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9080     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9081
9082     return;
9083   }
9084 #endif
9085
9086   SCAN_PLAYFIELD(x, y)
9087   {
9088     if (AmoebaNr[x][y] == group_nr &&
9089         (Tile[x][y] == EL_AMOEBA_DEAD ||
9090          Tile[x][y] == EL_BD_AMOEBA ||
9091          Tile[x][y] == EL_AMOEBA_GROWING))
9092     {
9093       AmoebaNr[x][y] = 0;
9094       Tile[x][y] = new_element;
9095       InitField(x, y, FALSE);
9096       TEST_DrawLevelField(x, y);
9097       done = TRUE;
9098     }
9099   }
9100
9101   if (done)
9102     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9103                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9104                             SND_BD_AMOEBA_TURNING_TO_GEM));
9105 }
9106
9107 static void AmoebaGrowing(int x, int y)
9108 {
9109   static unsigned int sound_delay = 0;
9110   static unsigned int sound_delay_value = 0;
9111
9112   if (!MovDelay[x][y])          // start new growing cycle
9113   {
9114     MovDelay[x][y] = 7;
9115
9116     if (DelayReached(&sound_delay, sound_delay_value))
9117     {
9118       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9119       sound_delay_value = 30;
9120     }
9121   }
9122
9123   if (MovDelay[x][y])           // wait some time before growing bigger
9124   {
9125     MovDelay[x][y]--;
9126     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9127     {
9128       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9129                                            6 - MovDelay[x][y]);
9130
9131       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9132     }
9133
9134     if (!MovDelay[x][y])
9135     {
9136       Tile[x][y] = Store[x][y];
9137       Store[x][y] = 0;
9138       TEST_DrawLevelField(x, y);
9139     }
9140   }
9141 }
9142
9143 static void AmoebaShrinking(int x, int y)
9144 {
9145   static unsigned int sound_delay = 0;
9146   static unsigned int sound_delay_value = 0;
9147
9148   if (!MovDelay[x][y])          // start new shrinking cycle
9149   {
9150     MovDelay[x][y] = 7;
9151
9152     if (DelayReached(&sound_delay, sound_delay_value))
9153       sound_delay_value = 30;
9154   }
9155
9156   if (MovDelay[x][y])           // wait some time before shrinking
9157   {
9158     MovDelay[x][y]--;
9159     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9160     {
9161       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9162                                            6 - MovDelay[x][y]);
9163
9164       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9165     }
9166
9167     if (!MovDelay[x][y])
9168     {
9169       Tile[x][y] = EL_EMPTY;
9170       TEST_DrawLevelField(x, y);
9171
9172       // don't let mole enter this field in this cycle;
9173       // (give priority to objects falling to this field from above)
9174       Stop[x][y] = TRUE;
9175     }
9176   }
9177 }
9178
9179 static void AmoebaReproduce(int ax, int ay)
9180 {
9181   int i;
9182   int element = Tile[ax][ay];
9183   int graphic = el2img(element);
9184   int newax = ax, neway = ay;
9185   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9186   static int xy[4][2] =
9187   {
9188     { 0, -1 },
9189     { -1, 0 },
9190     { +1, 0 },
9191     { 0, +1 }
9192   };
9193
9194   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9195   {
9196     Tile[ax][ay] = EL_AMOEBA_DEAD;
9197     TEST_DrawLevelField(ax, ay);
9198     return;
9199   }
9200
9201   if (IS_ANIMATED(graphic))
9202     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9203
9204   if (!MovDelay[ax][ay])        // start making new amoeba field
9205     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9206
9207   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9208   {
9209     MovDelay[ax][ay]--;
9210     if (MovDelay[ax][ay])
9211       return;
9212   }
9213
9214   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9215   {
9216     int start = RND(4);
9217     int x = ax + xy[start][0];
9218     int y = ay + xy[start][1];
9219
9220     if (!IN_LEV_FIELD(x, y))
9221       return;
9222
9223     if (IS_FREE(x, y) ||
9224         CAN_GROW_INTO(Tile[x][y]) ||
9225         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9226         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9227     {
9228       newax = x;
9229       neway = y;
9230     }
9231
9232     if (newax == ax && neway == ay)
9233       return;
9234   }
9235   else                          // normal or "filled" (BD style) amoeba
9236   {
9237     int start = RND(4);
9238     boolean waiting_for_player = FALSE;
9239
9240     for (i = 0; i < NUM_DIRECTIONS; i++)
9241     {
9242       int j = (start + i) % 4;
9243       int x = ax + xy[j][0];
9244       int y = ay + xy[j][1];
9245
9246       if (!IN_LEV_FIELD(x, y))
9247         continue;
9248
9249       if (IS_FREE(x, y) ||
9250           CAN_GROW_INTO(Tile[x][y]) ||
9251           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9252           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9253       {
9254         newax = x;
9255         neway = y;
9256         break;
9257       }
9258       else if (IS_PLAYER(x, y))
9259         waiting_for_player = TRUE;
9260     }
9261
9262     if (newax == ax && neway == ay)             // amoeba cannot grow
9263     {
9264       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9265       {
9266         Tile[ax][ay] = EL_AMOEBA_DEAD;
9267         TEST_DrawLevelField(ax, ay);
9268         AmoebaCnt[AmoebaNr[ax][ay]]--;
9269
9270         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9271         {
9272           if (element == EL_AMOEBA_FULL)
9273             AmoebaToDiamond(ax, ay);
9274           else if (element == EL_BD_AMOEBA)
9275             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9276         }
9277       }
9278       return;
9279     }
9280     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9281     {
9282       // amoeba gets larger by growing in some direction
9283
9284       int new_group_nr = AmoebaNr[ax][ay];
9285
9286 #ifdef DEBUG
9287   if (new_group_nr == 0)
9288   {
9289     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9290           newax, neway);
9291     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9292
9293     return;
9294   }
9295 #endif
9296
9297       AmoebaNr[newax][neway] = new_group_nr;
9298       AmoebaCnt[new_group_nr]++;
9299       AmoebaCnt2[new_group_nr]++;
9300
9301       // if amoeba touches other amoeba(s) after growing, unify them
9302       AmoebaMerge(newax, neway);
9303
9304       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9305       {
9306         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9307         return;
9308       }
9309     }
9310   }
9311
9312   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9313       (neway == lev_fieldy - 1 && newax != ax))
9314   {
9315     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9316     Store[newax][neway] = element;
9317   }
9318   else if (neway == ay || element == EL_EMC_DRIPPER)
9319   {
9320     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9321
9322     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9323   }
9324   else
9325   {
9326     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9327     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9328     Store[ax][ay] = EL_AMOEBA_DROP;
9329     ContinueMoving(ax, ay);
9330     return;
9331   }
9332
9333   TEST_DrawLevelField(newax, neway);
9334 }
9335
9336 static void Life(int ax, int ay)
9337 {
9338   int x1, y1, x2, y2;
9339   int life_time = 40;
9340   int element = Tile[ax][ay];
9341   int graphic = el2img(element);
9342   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9343                          level.biomaze);
9344   boolean changed = FALSE;
9345
9346   if (IS_ANIMATED(graphic))
9347     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9348
9349   if (Stop[ax][ay])
9350     return;
9351
9352   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9353     MovDelay[ax][ay] = life_time;
9354
9355   if (MovDelay[ax][ay])         // wait some time before next cycle
9356   {
9357     MovDelay[ax][ay]--;
9358     if (MovDelay[ax][ay])
9359       return;
9360   }
9361
9362   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9363   {
9364     int xx = ax+x1, yy = ay+y1;
9365     int old_element = Tile[xx][yy];
9366     int num_neighbours = 0;
9367
9368     if (!IN_LEV_FIELD(xx, yy))
9369       continue;
9370
9371     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9372     {
9373       int x = xx+x2, y = yy+y2;
9374
9375       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9376         continue;
9377
9378       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9379       boolean is_neighbour = FALSE;
9380
9381       if (level.use_life_bugs)
9382         is_neighbour =
9383           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9384            (IS_FREE(x, y)                             &&  Stop[x][y]));
9385       else
9386         is_neighbour =
9387           (Last[x][y] == element || is_player_cell);
9388
9389       if (is_neighbour)
9390         num_neighbours++;
9391     }
9392
9393     boolean is_free = FALSE;
9394
9395     if (level.use_life_bugs)
9396       is_free = (IS_FREE(xx, yy));
9397     else
9398       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9399
9400     if (xx == ax && yy == ay)           // field in the middle
9401     {
9402       if (num_neighbours < life_parameter[0] ||
9403           num_neighbours > life_parameter[1])
9404       {
9405         Tile[xx][yy] = EL_EMPTY;
9406         if (Tile[xx][yy] != old_element)
9407           TEST_DrawLevelField(xx, yy);
9408         Stop[xx][yy] = TRUE;
9409         changed = TRUE;
9410       }
9411     }
9412     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9413     {                                   // free border field
9414       if (num_neighbours >= life_parameter[2] &&
9415           num_neighbours <= life_parameter[3])
9416       {
9417         Tile[xx][yy] = element;
9418         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9419         if (Tile[xx][yy] != old_element)
9420           TEST_DrawLevelField(xx, yy);
9421         Stop[xx][yy] = TRUE;
9422         changed = TRUE;
9423       }
9424     }
9425   }
9426
9427   if (changed)
9428     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9429                    SND_GAME_OF_LIFE_GROWING);
9430 }
9431
9432 static void InitRobotWheel(int x, int y)
9433 {
9434   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9435 }
9436
9437 static void RunRobotWheel(int x, int y)
9438 {
9439   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9440 }
9441
9442 static void StopRobotWheel(int x, int y)
9443 {
9444   if (game.robot_wheel_x == x &&
9445       game.robot_wheel_y == y)
9446   {
9447     game.robot_wheel_x = -1;
9448     game.robot_wheel_y = -1;
9449     game.robot_wheel_active = FALSE;
9450   }
9451 }
9452
9453 static void InitTimegateWheel(int x, int y)
9454 {
9455   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9456 }
9457
9458 static void RunTimegateWheel(int x, int y)
9459 {
9460   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9461 }
9462
9463 static void InitMagicBallDelay(int x, int y)
9464 {
9465   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9466 }
9467
9468 static void ActivateMagicBall(int bx, int by)
9469 {
9470   int x, y;
9471
9472   if (level.ball_random)
9473   {
9474     int pos_border = RND(8);    // select one of the eight border elements
9475     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9476     int xx = pos_content % 3;
9477     int yy = pos_content / 3;
9478
9479     x = bx - 1 + xx;
9480     y = by - 1 + yy;
9481
9482     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9483       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9484   }
9485   else
9486   {
9487     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9488     {
9489       int xx = x - bx + 1;
9490       int yy = y - by + 1;
9491
9492       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9493         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9494     }
9495   }
9496
9497   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9498 }
9499
9500 static void CheckExit(int x, int y)
9501 {
9502   if (game.gems_still_needed > 0 ||
9503       game.sokoban_fields_still_needed > 0 ||
9504       game.sokoban_objects_still_needed > 0 ||
9505       game.lights_still_needed > 0)
9506   {
9507     int element = Tile[x][y];
9508     int graphic = el2img(element);
9509
9510     if (IS_ANIMATED(graphic))
9511       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9512
9513     return;
9514   }
9515
9516   // do not re-open exit door closed after last player
9517   if (game.all_players_gone)
9518     return;
9519
9520   Tile[x][y] = EL_EXIT_OPENING;
9521
9522   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9523 }
9524
9525 static void CheckExitEM(int x, int y)
9526 {
9527   if (game.gems_still_needed > 0 ||
9528       game.sokoban_fields_still_needed > 0 ||
9529       game.sokoban_objects_still_needed > 0 ||
9530       game.lights_still_needed > 0)
9531   {
9532     int element = Tile[x][y];
9533     int graphic = el2img(element);
9534
9535     if (IS_ANIMATED(graphic))
9536       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9537
9538     return;
9539   }
9540
9541   // do not re-open exit door closed after last player
9542   if (game.all_players_gone)
9543     return;
9544
9545   Tile[x][y] = EL_EM_EXIT_OPENING;
9546
9547   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9548 }
9549
9550 static void CheckExitSteel(int x, int y)
9551 {
9552   if (game.gems_still_needed > 0 ||
9553       game.sokoban_fields_still_needed > 0 ||
9554       game.sokoban_objects_still_needed > 0 ||
9555       game.lights_still_needed > 0)
9556   {
9557     int element = Tile[x][y];
9558     int graphic = el2img(element);
9559
9560     if (IS_ANIMATED(graphic))
9561       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9562
9563     return;
9564   }
9565
9566   // do not re-open exit door closed after last player
9567   if (game.all_players_gone)
9568     return;
9569
9570   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9571
9572   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9573 }
9574
9575 static void CheckExitSteelEM(int x, int y)
9576 {
9577   if (game.gems_still_needed > 0 ||
9578       game.sokoban_fields_still_needed > 0 ||
9579       game.sokoban_objects_still_needed > 0 ||
9580       game.lights_still_needed > 0)
9581   {
9582     int element = Tile[x][y];
9583     int graphic = el2img(element);
9584
9585     if (IS_ANIMATED(graphic))
9586       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9587
9588     return;
9589   }
9590
9591   // do not re-open exit door closed after last player
9592   if (game.all_players_gone)
9593     return;
9594
9595   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9596
9597   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9598 }
9599
9600 static void CheckExitSP(int x, int y)
9601 {
9602   if (game.gems_still_needed > 0)
9603   {
9604     int element = Tile[x][y];
9605     int graphic = el2img(element);
9606
9607     if (IS_ANIMATED(graphic))
9608       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9609
9610     return;
9611   }
9612
9613   // do not re-open exit door closed after last player
9614   if (game.all_players_gone)
9615     return;
9616
9617   Tile[x][y] = EL_SP_EXIT_OPENING;
9618
9619   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9620 }
9621
9622 static void CloseAllOpenTimegates(void)
9623 {
9624   int x, y;
9625
9626   SCAN_PLAYFIELD(x, y)
9627   {
9628     int element = Tile[x][y];
9629
9630     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9631     {
9632       Tile[x][y] = EL_TIMEGATE_CLOSING;
9633
9634       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9635     }
9636   }
9637 }
9638
9639 static void DrawTwinkleOnField(int x, int y)
9640 {
9641   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9642     return;
9643
9644   if (Tile[x][y] == EL_BD_DIAMOND)
9645     return;
9646
9647   if (MovDelay[x][y] == 0)      // next animation frame
9648     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9649
9650   if (MovDelay[x][y] != 0)      // wait some time before next frame
9651   {
9652     MovDelay[x][y]--;
9653
9654     DrawLevelElementAnimation(x, y, Tile[x][y]);
9655
9656     if (MovDelay[x][y] != 0)
9657     {
9658       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9659                                            10 - MovDelay[x][y]);
9660
9661       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9662     }
9663   }
9664 }
9665
9666 static void MauerWaechst(int x, int y)
9667 {
9668   int delay = 6;
9669
9670   if (!MovDelay[x][y])          // next animation frame
9671     MovDelay[x][y] = 3 * delay;
9672
9673   if (MovDelay[x][y])           // wait some time before next frame
9674   {
9675     MovDelay[x][y]--;
9676
9677     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9678     {
9679       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9680       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9681
9682       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9683     }
9684
9685     if (!MovDelay[x][y])
9686     {
9687       if (MovDir[x][y] == MV_LEFT)
9688       {
9689         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9690           TEST_DrawLevelField(x - 1, y);
9691       }
9692       else if (MovDir[x][y] == MV_RIGHT)
9693       {
9694         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9695           TEST_DrawLevelField(x + 1, y);
9696       }
9697       else if (MovDir[x][y] == MV_UP)
9698       {
9699         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9700           TEST_DrawLevelField(x, y - 1);
9701       }
9702       else
9703       {
9704         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9705           TEST_DrawLevelField(x, y + 1);
9706       }
9707
9708       Tile[x][y] = Store[x][y];
9709       Store[x][y] = 0;
9710       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9711       TEST_DrawLevelField(x, y);
9712     }
9713   }
9714 }
9715
9716 static void MauerAbleger(int ax, int ay)
9717 {
9718   int element = Tile[ax][ay];
9719   int graphic = el2img(element);
9720   boolean oben_frei = FALSE, unten_frei = FALSE;
9721   boolean links_frei = FALSE, rechts_frei = FALSE;
9722   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9723   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9724   boolean new_wall = FALSE;
9725
9726   if (IS_ANIMATED(graphic))
9727     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9728
9729   if (!MovDelay[ax][ay])        // start building new wall
9730     MovDelay[ax][ay] = 6;
9731
9732   if (MovDelay[ax][ay])         // wait some time before building new wall
9733   {
9734     MovDelay[ax][ay]--;
9735     if (MovDelay[ax][ay])
9736       return;
9737   }
9738
9739   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9740     oben_frei = TRUE;
9741   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9742     unten_frei = TRUE;
9743   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9744     links_frei = TRUE;
9745   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9746     rechts_frei = TRUE;
9747
9748   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9749       element == EL_EXPANDABLE_WALL_ANY)
9750   {
9751     if (oben_frei)
9752     {
9753       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9754       Store[ax][ay-1] = element;
9755       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9756       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9757         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9758                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9759       new_wall = TRUE;
9760     }
9761     if (unten_frei)
9762     {
9763       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9764       Store[ax][ay+1] = element;
9765       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9766       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9767         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9768                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9769       new_wall = TRUE;
9770     }
9771   }
9772
9773   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9774       element == EL_EXPANDABLE_WALL_ANY ||
9775       element == EL_EXPANDABLE_WALL ||
9776       element == EL_BD_EXPANDABLE_WALL)
9777   {
9778     if (links_frei)
9779     {
9780       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9781       Store[ax-1][ay] = element;
9782       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9783       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9784         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9785                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9786       new_wall = TRUE;
9787     }
9788
9789     if (rechts_frei)
9790     {
9791       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9792       Store[ax+1][ay] = element;
9793       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9794       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9795         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9796                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9797       new_wall = TRUE;
9798     }
9799   }
9800
9801   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9802     TEST_DrawLevelField(ax, ay);
9803
9804   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9805     oben_massiv = TRUE;
9806   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9807     unten_massiv = TRUE;
9808   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9809     links_massiv = TRUE;
9810   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9811     rechts_massiv = TRUE;
9812
9813   if (((oben_massiv && unten_massiv) ||
9814        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9815        element == EL_EXPANDABLE_WALL) &&
9816       ((links_massiv && rechts_massiv) ||
9817        element == EL_EXPANDABLE_WALL_VERTICAL))
9818     Tile[ax][ay] = EL_WALL;
9819
9820   if (new_wall)
9821     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9822 }
9823
9824 static void MauerAblegerStahl(int ax, int ay)
9825 {
9826   int element = Tile[ax][ay];
9827   int graphic = el2img(element);
9828   boolean oben_frei = FALSE, unten_frei = FALSE;
9829   boolean links_frei = FALSE, rechts_frei = FALSE;
9830   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9831   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9832   boolean new_wall = FALSE;
9833
9834   if (IS_ANIMATED(graphic))
9835     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9836
9837   if (!MovDelay[ax][ay])        // start building new wall
9838     MovDelay[ax][ay] = 6;
9839
9840   if (MovDelay[ax][ay])         // wait some time before building new wall
9841   {
9842     MovDelay[ax][ay]--;
9843     if (MovDelay[ax][ay])
9844       return;
9845   }
9846
9847   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9848     oben_frei = TRUE;
9849   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9850     unten_frei = TRUE;
9851   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9852     links_frei = TRUE;
9853   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9854     rechts_frei = TRUE;
9855
9856   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9857       element == EL_EXPANDABLE_STEELWALL_ANY)
9858   {
9859     if (oben_frei)
9860     {
9861       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9862       Store[ax][ay-1] = element;
9863       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9864       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9865         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9866                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9867       new_wall = TRUE;
9868     }
9869     if (unten_frei)
9870     {
9871       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9872       Store[ax][ay+1] = element;
9873       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9874       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9875         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9876                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9877       new_wall = TRUE;
9878     }
9879   }
9880
9881   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9882       element == EL_EXPANDABLE_STEELWALL_ANY)
9883   {
9884     if (links_frei)
9885     {
9886       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9887       Store[ax-1][ay] = element;
9888       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9889       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9890         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9891                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9892       new_wall = TRUE;
9893     }
9894
9895     if (rechts_frei)
9896     {
9897       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9898       Store[ax+1][ay] = element;
9899       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9900       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9901         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9902                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9903       new_wall = TRUE;
9904     }
9905   }
9906
9907   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9908     oben_massiv = TRUE;
9909   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9910     unten_massiv = TRUE;
9911   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9912     links_massiv = TRUE;
9913   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9914     rechts_massiv = TRUE;
9915
9916   if (((oben_massiv && unten_massiv) ||
9917        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9918       ((links_massiv && rechts_massiv) ||
9919        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9920     Tile[ax][ay] = EL_STEELWALL;
9921
9922   if (new_wall)
9923     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9924 }
9925
9926 static void CheckForDragon(int x, int y)
9927 {
9928   int i, j;
9929   boolean dragon_found = FALSE;
9930   static int xy[4][2] =
9931   {
9932     { 0, -1 },
9933     { -1, 0 },
9934     { +1, 0 },
9935     { 0, +1 }
9936   };
9937
9938   for (i = 0; i < NUM_DIRECTIONS; i++)
9939   {
9940     for (j = 0; j < 4; j++)
9941     {
9942       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9943
9944       if (IN_LEV_FIELD(xx, yy) &&
9945           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9946       {
9947         if (Tile[xx][yy] == EL_DRAGON)
9948           dragon_found = TRUE;
9949       }
9950       else
9951         break;
9952     }
9953   }
9954
9955   if (!dragon_found)
9956   {
9957     for (i = 0; i < NUM_DIRECTIONS; i++)
9958     {
9959       for (j = 0; j < 3; j++)
9960       {
9961         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9962   
9963         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9964         {
9965           Tile[xx][yy] = EL_EMPTY;
9966           TEST_DrawLevelField(xx, yy);
9967         }
9968         else
9969           break;
9970       }
9971     }
9972   }
9973 }
9974
9975 static void InitBuggyBase(int x, int y)
9976 {
9977   int element = Tile[x][y];
9978   int activating_delay = FRAMES_PER_SECOND / 4;
9979
9980   ChangeDelay[x][y] =
9981     (element == EL_SP_BUGGY_BASE ?
9982      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9983      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9984      activating_delay :
9985      element == EL_SP_BUGGY_BASE_ACTIVE ?
9986      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9987 }
9988
9989 static void WarnBuggyBase(int x, int y)
9990 {
9991   int i;
9992   static int xy[4][2] =
9993   {
9994     { 0, -1 },
9995     { -1, 0 },
9996     { +1, 0 },
9997     { 0, +1 }
9998   };
9999
10000   for (i = 0; i < NUM_DIRECTIONS; i++)
10001   {
10002     int xx = x + xy[i][0];
10003     int yy = y + xy[i][1];
10004
10005     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10006     {
10007       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10008
10009       break;
10010     }
10011   }
10012 }
10013
10014 static void InitTrap(int x, int y)
10015 {
10016   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10017 }
10018
10019 static void ActivateTrap(int x, int y)
10020 {
10021   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10022 }
10023
10024 static void ChangeActiveTrap(int x, int y)
10025 {
10026   int graphic = IMG_TRAP_ACTIVE;
10027
10028   // if new animation frame was drawn, correct crumbled sand border
10029   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10030     TEST_DrawLevelFieldCrumbled(x, y);
10031 }
10032
10033 static int getSpecialActionElement(int element, int number, int base_element)
10034 {
10035   return (element != EL_EMPTY ? element :
10036           number != -1 ? base_element + number - 1 :
10037           EL_EMPTY);
10038 }
10039
10040 static int getModifiedActionNumber(int value_old, int operator, int operand,
10041                                    int value_min, int value_max)
10042 {
10043   int value_new = (operator == CA_MODE_SET      ? operand :
10044                    operator == CA_MODE_ADD      ? value_old + operand :
10045                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10046                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10047                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10048                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10049                    value_old);
10050
10051   return (value_new < value_min ? value_min :
10052           value_new > value_max ? value_max :
10053           value_new);
10054 }
10055
10056 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10057 {
10058   struct ElementInfo *ei = &element_info[element];
10059   struct ElementChangeInfo *change = &ei->change_page[page];
10060   int target_element = change->target_element;
10061   int action_type = change->action_type;
10062   int action_mode = change->action_mode;
10063   int action_arg = change->action_arg;
10064   int action_element = change->action_element;
10065   int i;
10066
10067   if (!change->has_action)
10068     return;
10069
10070   // ---------- determine action paramater values -----------------------------
10071
10072   int level_time_value =
10073     (level.time > 0 ? TimeLeft :
10074      TimePlayed);
10075
10076   int action_arg_element_raw =
10077     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10078      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10079      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10080      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10081      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10082      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10083      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10084      EL_EMPTY);
10085   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10086
10087   int action_arg_direction =
10088     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10089      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10090      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10091      change->actual_trigger_side :
10092      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10093      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10094      MV_NONE);
10095
10096   int action_arg_number_min =
10097     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10098      CA_ARG_MIN);
10099
10100   int action_arg_number_max =
10101     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10102      action_type == CA_SET_LEVEL_GEMS ? 999 :
10103      action_type == CA_SET_LEVEL_TIME ? 9999 :
10104      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10105      action_type == CA_SET_CE_VALUE ? 9999 :
10106      action_type == CA_SET_CE_SCORE ? 9999 :
10107      CA_ARG_MAX);
10108
10109   int action_arg_number_reset =
10110     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10111      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10112      action_type == CA_SET_LEVEL_TIME ? level.time :
10113      action_type == CA_SET_LEVEL_SCORE ? 0 :
10114      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10115      action_type == CA_SET_CE_SCORE ? 0 :
10116      0);
10117
10118   int action_arg_number =
10119     (action_arg <= CA_ARG_MAX ? action_arg :
10120      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10121      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10122      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10123      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10124      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10125      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10126      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10127      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10128      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10129      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10130      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10131      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10132      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10133      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10134      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10135      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10136      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10137      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10138      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10139      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10140      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10141      -1);
10142
10143   int action_arg_number_old =
10144     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10145      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10146      action_type == CA_SET_LEVEL_SCORE ? game.score :
10147      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10148      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10149      0);
10150
10151   int action_arg_number_new =
10152     getModifiedActionNumber(action_arg_number_old,
10153                             action_mode, action_arg_number,
10154                             action_arg_number_min, action_arg_number_max);
10155
10156   int trigger_player_bits =
10157     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10158      change->actual_trigger_player_bits : change->trigger_player);
10159
10160   int action_arg_player_bits =
10161     (action_arg >= CA_ARG_PLAYER_1 &&
10162      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10163      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10164      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10165      PLAYER_BITS_ANY);
10166
10167   // ---------- execute action  -----------------------------------------------
10168
10169   switch (action_type)
10170   {
10171     case CA_NO_ACTION:
10172     {
10173       return;
10174     }
10175
10176     // ---------- level actions  ----------------------------------------------
10177
10178     case CA_RESTART_LEVEL:
10179     {
10180       game.restart_level = TRUE;
10181
10182       break;
10183     }
10184
10185     case CA_SHOW_ENVELOPE:
10186     {
10187       int element = getSpecialActionElement(action_arg_element,
10188                                             action_arg_number, EL_ENVELOPE_1);
10189
10190       if (IS_ENVELOPE(element))
10191         local_player->show_envelope = element;
10192
10193       break;
10194     }
10195
10196     case CA_SET_LEVEL_TIME:
10197     {
10198       if (level.time > 0)       // only modify limited time value
10199       {
10200         TimeLeft = action_arg_number_new;
10201
10202         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10203
10204         DisplayGameControlValues();
10205
10206         if (!TimeLeft && setup.time_limit)
10207           for (i = 0; i < MAX_PLAYERS; i++)
10208             KillPlayer(&stored_player[i]);
10209       }
10210
10211       break;
10212     }
10213
10214     case CA_SET_LEVEL_SCORE:
10215     {
10216       game.score = action_arg_number_new;
10217
10218       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10219
10220       DisplayGameControlValues();
10221
10222       break;
10223     }
10224
10225     case CA_SET_LEVEL_GEMS:
10226     {
10227       game.gems_still_needed = action_arg_number_new;
10228
10229       game.snapshot.collected_item = TRUE;
10230
10231       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10232
10233       DisplayGameControlValues();
10234
10235       break;
10236     }
10237
10238     case CA_SET_LEVEL_WIND:
10239     {
10240       game.wind_direction = action_arg_direction;
10241
10242       break;
10243     }
10244
10245     case CA_SET_LEVEL_RANDOM_SEED:
10246     {
10247       // ensure that setting a new random seed while playing is predictable
10248       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10249
10250       break;
10251     }
10252
10253     // ---------- player actions  ---------------------------------------------
10254
10255     case CA_MOVE_PLAYER:
10256     case CA_MOVE_PLAYER_NEW:
10257     {
10258       // automatically move to the next field in specified direction
10259       for (i = 0; i < MAX_PLAYERS; i++)
10260         if (trigger_player_bits & (1 << i))
10261           if (action_type == CA_MOVE_PLAYER ||
10262               stored_player[i].MovPos == 0)
10263             stored_player[i].programmed_action = action_arg_direction;
10264
10265       break;
10266     }
10267
10268     case CA_EXIT_PLAYER:
10269     {
10270       for (i = 0; i < MAX_PLAYERS; i++)
10271         if (action_arg_player_bits & (1 << i))
10272           ExitPlayer(&stored_player[i]);
10273
10274       if (game.players_still_needed == 0)
10275         LevelSolved();
10276
10277       break;
10278     }
10279
10280     case CA_KILL_PLAYER:
10281     {
10282       for (i = 0; i < MAX_PLAYERS; i++)
10283         if (action_arg_player_bits & (1 << i))
10284           KillPlayer(&stored_player[i]);
10285
10286       break;
10287     }
10288
10289     case CA_SET_PLAYER_KEYS:
10290     {
10291       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10292       int element = getSpecialActionElement(action_arg_element,
10293                                             action_arg_number, EL_KEY_1);
10294
10295       if (IS_KEY(element))
10296       {
10297         for (i = 0; i < MAX_PLAYERS; i++)
10298         {
10299           if (trigger_player_bits & (1 << i))
10300           {
10301             stored_player[i].key[KEY_NR(element)] = key_state;
10302
10303             DrawGameDoorValues();
10304           }
10305         }
10306       }
10307
10308       break;
10309     }
10310
10311     case CA_SET_PLAYER_SPEED:
10312     {
10313       for (i = 0; i < MAX_PLAYERS; i++)
10314       {
10315         if (trigger_player_bits & (1 << i))
10316         {
10317           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10318
10319           if (action_arg == CA_ARG_SPEED_FASTER &&
10320               stored_player[i].cannot_move)
10321           {
10322             action_arg_number = STEPSIZE_VERY_SLOW;
10323           }
10324           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10325                    action_arg == CA_ARG_SPEED_FASTER)
10326           {
10327             action_arg_number = 2;
10328             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10329                            CA_MODE_MULTIPLY);
10330           }
10331           else if (action_arg == CA_ARG_NUMBER_RESET)
10332           {
10333             action_arg_number = level.initial_player_stepsize[i];
10334           }
10335
10336           move_stepsize =
10337             getModifiedActionNumber(move_stepsize,
10338                                     action_mode,
10339                                     action_arg_number,
10340                                     action_arg_number_min,
10341                                     action_arg_number_max);
10342
10343           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10344         }
10345       }
10346
10347       break;
10348     }
10349
10350     case CA_SET_PLAYER_SHIELD:
10351     {
10352       for (i = 0; i < MAX_PLAYERS; i++)
10353       {
10354         if (trigger_player_bits & (1 << i))
10355         {
10356           if (action_arg == CA_ARG_SHIELD_OFF)
10357           {
10358             stored_player[i].shield_normal_time_left = 0;
10359             stored_player[i].shield_deadly_time_left = 0;
10360           }
10361           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10362           {
10363             stored_player[i].shield_normal_time_left = 999999;
10364           }
10365           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10366           {
10367             stored_player[i].shield_normal_time_left = 999999;
10368             stored_player[i].shield_deadly_time_left = 999999;
10369           }
10370         }
10371       }
10372
10373       break;
10374     }
10375
10376     case CA_SET_PLAYER_GRAVITY:
10377     {
10378       for (i = 0; i < MAX_PLAYERS; i++)
10379       {
10380         if (trigger_player_bits & (1 << i))
10381         {
10382           stored_player[i].gravity =
10383             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10384              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10385              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10386              stored_player[i].gravity);
10387         }
10388       }
10389
10390       break;
10391     }
10392
10393     case CA_SET_PLAYER_ARTWORK:
10394     {
10395       for (i = 0; i < MAX_PLAYERS; i++)
10396       {
10397         if (trigger_player_bits & (1 << i))
10398         {
10399           int artwork_element = action_arg_element;
10400
10401           if (action_arg == CA_ARG_ELEMENT_RESET)
10402             artwork_element =
10403               (level.use_artwork_element[i] ? level.artwork_element[i] :
10404                stored_player[i].element_nr);
10405
10406           if (stored_player[i].artwork_element != artwork_element)
10407             stored_player[i].Frame = 0;
10408
10409           stored_player[i].artwork_element = artwork_element;
10410
10411           SetPlayerWaiting(&stored_player[i], FALSE);
10412
10413           // set number of special actions for bored and sleeping animation
10414           stored_player[i].num_special_action_bored =
10415             get_num_special_action(artwork_element,
10416                                    ACTION_BORING_1, ACTION_BORING_LAST);
10417           stored_player[i].num_special_action_sleeping =
10418             get_num_special_action(artwork_element,
10419                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10420         }
10421       }
10422
10423       break;
10424     }
10425
10426     case CA_SET_PLAYER_INVENTORY:
10427     {
10428       for (i = 0; i < MAX_PLAYERS; i++)
10429       {
10430         struct PlayerInfo *player = &stored_player[i];
10431         int j, k;
10432
10433         if (trigger_player_bits & (1 << i))
10434         {
10435           int inventory_element = action_arg_element;
10436
10437           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10438               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10439               action_arg == CA_ARG_ELEMENT_ACTION)
10440           {
10441             int element = inventory_element;
10442             int collect_count = element_info[element].collect_count_initial;
10443
10444             if (!IS_CUSTOM_ELEMENT(element))
10445               collect_count = 1;
10446
10447             if (collect_count == 0)
10448               player->inventory_infinite_element = element;
10449             else
10450               for (k = 0; k < collect_count; k++)
10451                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10452                   player->inventory_element[player->inventory_size++] =
10453                     element;
10454           }
10455           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10456                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10457                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10458           {
10459             if (player->inventory_infinite_element != EL_UNDEFINED &&
10460                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10461                                      action_arg_element_raw))
10462               player->inventory_infinite_element = EL_UNDEFINED;
10463
10464             for (k = 0, j = 0; j < player->inventory_size; j++)
10465             {
10466               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10467                                         action_arg_element_raw))
10468                 player->inventory_element[k++] = player->inventory_element[j];
10469             }
10470
10471             player->inventory_size = k;
10472           }
10473           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10474           {
10475             if (player->inventory_size > 0)
10476             {
10477               for (j = 0; j < player->inventory_size - 1; j++)
10478                 player->inventory_element[j] = player->inventory_element[j + 1];
10479
10480               player->inventory_size--;
10481             }
10482           }
10483           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10484           {
10485             if (player->inventory_size > 0)
10486               player->inventory_size--;
10487           }
10488           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10489           {
10490             player->inventory_infinite_element = EL_UNDEFINED;
10491             player->inventory_size = 0;
10492           }
10493           else if (action_arg == CA_ARG_INVENTORY_RESET)
10494           {
10495             player->inventory_infinite_element = EL_UNDEFINED;
10496             player->inventory_size = 0;
10497
10498             if (level.use_initial_inventory[i])
10499             {
10500               for (j = 0; j < level.initial_inventory_size[i]; j++)
10501               {
10502                 int element = level.initial_inventory_content[i][j];
10503                 int collect_count = element_info[element].collect_count_initial;
10504
10505                 if (!IS_CUSTOM_ELEMENT(element))
10506                   collect_count = 1;
10507
10508                 if (collect_count == 0)
10509                   player->inventory_infinite_element = element;
10510                 else
10511                   for (k = 0; k < collect_count; k++)
10512                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10513                       player->inventory_element[player->inventory_size++] =
10514                         element;
10515               }
10516             }
10517           }
10518         }
10519       }
10520
10521       break;
10522     }
10523
10524     // ---------- CE actions  -------------------------------------------------
10525
10526     case CA_SET_CE_VALUE:
10527     {
10528       int last_ce_value = CustomValue[x][y];
10529
10530       CustomValue[x][y] = action_arg_number_new;
10531
10532       if (CustomValue[x][y] != last_ce_value)
10533       {
10534         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10535         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10536
10537         if (CustomValue[x][y] == 0)
10538         {
10539           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10540           ChangeCount[x][y] = 0;        // allow at least one more change
10541
10542           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10543           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10544         }
10545       }
10546
10547       break;
10548     }
10549
10550     case CA_SET_CE_SCORE:
10551     {
10552       int last_ce_score = ei->collect_score;
10553
10554       ei->collect_score = action_arg_number_new;
10555
10556       if (ei->collect_score != last_ce_score)
10557       {
10558         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10559         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10560
10561         if (ei->collect_score == 0)
10562         {
10563           int xx, yy;
10564
10565           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10566           ChangeCount[x][y] = 0;        // allow at least one more change
10567
10568           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10569           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10570
10571           /*
10572             This is a very special case that seems to be a mixture between
10573             CheckElementChange() and CheckTriggeredElementChange(): while
10574             the first one only affects single elements that are triggered
10575             directly, the second one affects multiple elements in the playfield
10576             that are triggered indirectly by another element. This is a third
10577             case: Changing the CE score always affects multiple identical CEs,
10578             so every affected CE must be checked, not only the single CE for
10579             which the CE score was changed in the first place (as every instance
10580             of that CE shares the same CE score, and therefore also can change)!
10581           */
10582           SCAN_PLAYFIELD(xx, yy)
10583           {
10584             if (Tile[xx][yy] == element)
10585               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10586                                  CE_SCORE_GETS_ZERO);
10587           }
10588         }
10589       }
10590
10591       break;
10592     }
10593
10594     case CA_SET_CE_ARTWORK:
10595     {
10596       int artwork_element = action_arg_element;
10597       boolean reset_frame = FALSE;
10598       int xx, yy;
10599
10600       if (action_arg == CA_ARG_ELEMENT_RESET)
10601         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10602                            element);
10603
10604       if (ei->gfx_element != artwork_element)
10605         reset_frame = TRUE;
10606
10607       ei->gfx_element = artwork_element;
10608
10609       SCAN_PLAYFIELD(xx, yy)
10610       {
10611         if (Tile[xx][yy] == element)
10612         {
10613           if (reset_frame)
10614           {
10615             ResetGfxAnimation(xx, yy);
10616             ResetRandomAnimationValue(xx, yy);
10617           }
10618
10619           TEST_DrawLevelField(xx, yy);
10620         }
10621       }
10622
10623       break;
10624     }
10625
10626     // ---------- engine actions  ---------------------------------------------
10627
10628     case CA_SET_ENGINE_SCAN_MODE:
10629     {
10630       InitPlayfieldScanMode(action_arg);
10631
10632       break;
10633     }
10634
10635     default:
10636       break;
10637   }
10638 }
10639
10640 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10641 {
10642   int old_element = Tile[x][y];
10643   int new_element = GetElementFromGroupElement(element);
10644   int previous_move_direction = MovDir[x][y];
10645   int last_ce_value = CustomValue[x][y];
10646   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10647   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10648   boolean add_player_onto_element = (new_element_is_player &&
10649                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10650                                      IS_WALKABLE(old_element));
10651
10652   if (!add_player_onto_element)
10653   {
10654     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10655       RemoveMovingField(x, y);
10656     else
10657       RemoveField(x, y);
10658
10659     Tile[x][y] = new_element;
10660
10661     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10662       MovDir[x][y] = previous_move_direction;
10663
10664     if (element_info[new_element].use_last_ce_value)
10665       CustomValue[x][y] = last_ce_value;
10666
10667     InitField_WithBug1(x, y, FALSE);
10668
10669     new_element = Tile[x][y];   // element may have changed
10670
10671     ResetGfxAnimation(x, y);
10672     ResetRandomAnimationValue(x, y);
10673
10674     TEST_DrawLevelField(x, y);
10675
10676     if (GFX_CRUMBLED(new_element))
10677       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10678   }
10679
10680   // check if element under the player changes from accessible to unaccessible
10681   // (needed for special case of dropping element which then changes)
10682   // (must be checked after creating new element for walkable group elements)
10683   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10684       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10685   {
10686     Bang(x, y);
10687
10688     return;
10689   }
10690
10691   // "ChangeCount" not set yet to allow "entered by player" change one time
10692   if (new_element_is_player)
10693     RelocatePlayer(x, y, new_element);
10694
10695   if (is_change)
10696     ChangeCount[x][y]++;        // count number of changes in the same frame
10697
10698   TestIfBadThingTouchesPlayer(x, y);
10699   TestIfPlayerTouchesCustomElement(x, y);
10700   TestIfElementTouchesCustomElement(x, y);
10701 }
10702
10703 static void CreateField(int x, int y, int element)
10704 {
10705   CreateFieldExt(x, y, element, FALSE);
10706 }
10707
10708 static void CreateElementFromChange(int x, int y, int element)
10709 {
10710   element = GET_VALID_RUNTIME_ELEMENT(element);
10711
10712   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10713   {
10714     int old_element = Tile[x][y];
10715
10716     // prevent changed element from moving in same engine frame
10717     // unless both old and new element can either fall or move
10718     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10719         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10720       Stop[x][y] = TRUE;
10721   }
10722
10723   CreateFieldExt(x, y, element, TRUE);
10724 }
10725
10726 static boolean ChangeElement(int x, int y, int element, int page)
10727 {
10728   struct ElementInfo *ei = &element_info[element];
10729   struct ElementChangeInfo *change = &ei->change_page[page];
10730   int ce_value = CustomValue[x][y];
10731   int ce_score = ei->collect_score;
10732   int target_element;
10733   int old_element = Tile[x][y];
10734
10735   // always use default change event to prevent running into a loop
10736   if (ChangeEvent[x][y] == -1)
10737     ChangeEvent[x][y] = CE_DELAY;
10738
10739   if (ChangeEvent[x][y] == CE_DELAY)
10740   {
10741     // reset actual trigger element, trigger player and action element
10742     change->actual_trigger_element = EL_EMPTY;
10743     change->actual_trigger_player = EL_EMPTY;
10744     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10745     change->actual_trigger_side = CH_SIDE_NONE;
10746     change->actual_trigger_ce_value = 0;
10747     change->actual_trigger_ce_score = 0;
10748   }
10749
10750   // do not change elements more than a specified maximum number of changes
10751   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10752     return FALSE;
10753
10754   ChangeCount[x][y]++;          // count number of changes in the same frame
10755
10756   if (change->explode)
10757   {
10758     Bang(x, y);
10759
10760     return TRUE;
10761   }
10762
10763   if (change->use_target_content)
10764   {
10765     boolean complete_replace = TRUE;
10766     boolean can_replace[3][3];
10767     int xx, yy;
10768
10769     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10770     {
10771       boolean is_empty;
10772       boolean is_walkable;
10773       boolean is_diggable;
10774       boolean is_collectible;
10775       boolean is_removable;
10776       boolean is_destructible;
10777       int ex = x + xx - 1;
10778       int ey = y + yy - 1;
10779       int content_element = change->target_content.e[xx][yy];
10780       int e;
10781
10782       can_replace[xx][yy] = TRUE;
10783
10784       if (ex == x && ey == y)   // do not check changing element itself
10785         continue;
10786
10787       if (content_element == EL_EMPTY_SPACE)
10788       {
10789         can_replace[xx][yy] = FALSE;    // do not replace border with space
10790
10791         continue;
10792       }
10793
10794       if (!IN_LEV_FIELD(ex, ey))
10795       {
10796         can_replace[xx][yy] = FALSE;
10797         complete_replace = FALSE;
10798
10799         continue;
10800       }
10801
10802       e = Tile[ex][ey];
10803
10804       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10805         e = MovingOrBlocked2Element(ex, ey);
10806
10807       is_empty = (IS_FREE(ex, ey) ||
10808                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10809
10810       is_walkable     = (is_empty || IS_WALKABLE(e));
10811       is_diggable     = (is_empty || IS_DIGGABLE(e));
10812       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10813       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10814       is_removable    = (is_diggable || is_collectible);
10815
10816       can_replace[xx][yy] =
10817         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10818           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10819           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10820           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10821           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10822           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10823          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10824
10825       if (!can_replace[xx][yy])
10826         complete_replace = FALSE;
10827     }
10828
10829     if (!change->only_if_complete || complete_replace)
10830     {
10831       boolean something_has_changed = FALSE;
10832
10833       if (change->only_if_complete && change->use_random_replace &&
10834           RND(100) < change->random_percentage)
10835         return FALSE;
10836
10837       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10838       {
10839         int ex = x + xx - 1;
10840         int ey = y + yy - 1;
10841         int content_element;
10842
10843         if (can_replace[xx][yy] && (!change->use_random_replace ||
10844                                     RND(100) < change->random_percentage))
10845         {
10846           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10847             RemoveMovingField(ex, ey);
10848
10849           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10850
10851           content_element = change->target_content.e[xx][yy];
10852           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10853                                               ce_value, ce_score);
10854
10855           CreateElementFromChange(ex, ey, target_element);
10856
10857           something_has_changed = TRUE;
10858
10859           // for symmetry reasons, freeze newly created border elements
10860           if (ex != x || ey != y)
10861             Stop[ex][ey] = TRUE;        // no more moving in this frame
10862         }
10863       }
10864
10865       if (something_has_changed)
10866       {
10867         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10868         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10869       }
10870     }
10871   }
10872   else
10873   {
10874     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10875                                         ce_value, ce_score);
10876
10877     if (element == EL_DIAGONAL_GROWING ||
10878         element == EL_DIAGONAL_SHRINKING)
10879     {
10880       target_element = Store[x][y];
10881
10882       Store[x][y] = EL_EMPTY;
10883     }
10884
10885     // special case: element changes to player (and may be kept if walkable)
10886     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10887       CreateElementFromChange(x, y, EL_EMPTY);
10888
10889     CreateElementFromChange(x, y, target_element);
10890
10891     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10892     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10893   }
10894
10895   // this uses direct change before indirect change
10896   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10897
10898   return TRUE;
10899 }
10900
10901 static void HandleElementChange(int x, int y, int page)
10902 {
10903   int element = MovingOrBlocked2Element(x, y);
10904   struct ElementInfo *ei = &element_info[element];
10905   struct ElementChangeInfo *change = &ei->change_page[page];
10906   boolean handle_action_before_change = FALSE;
10907
10908 #ifdef DEBUG
10909   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10910       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10911   {
10912     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10913           x, y, element, element_info[element].token_name);
10914     Debug("game:playing:HandleElementChange", "This should never happen!");
10915   }
10916 #endif
10917
10918   // this can happen with classic bombs on walkable, changing elements
10919   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10920   {
10921     return;
10922   }
10923
10924   if (ChangeDelay[x][y] == 0)           // initialize element change
10925   {
10926     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10927
10928     if (change->can_change)
10929     {
10930       // !!! not clear why graphic animation should be reset at all here !!!
10931       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10932       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10933
10934       /*
10935         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10936
10937         When using an animation frame delay of 1 (this only happens with
10938         "sp_zonk.moving.left/right" in the classic graphics), the default
10939         (non-moving) animation shows wrong animation frames (while the
10940         moving animation, like "sp_zonk.moving.left/right", is correct,
10941         so this graphical bug never shows up with the classic graphics).
10942         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10943         be drawn instead of the correct frames 0,1,2,3. This is caused by
10944         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10945         an element change: First when the change delay ("ChangeDelay[][]")
10946         counter has reached zero after decrementing, then a second time in
10947         the next frame (after "GfxFrame[][]" was already incremented) when
10948         "ChangeDelay[][]" is reset to the initial delay value again.
10949
10950         This causes frame 0 to be drawn twice, while the last frame won't
10951         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10952
10953         As some animations may already be cleverly designed around this bug
10954         (at least the "Snake Bite" snake tail animation does this), it cannot
10955         simply be fixed here without breaking such existing animations.
10956         Unfortunately, it cannot easily be detected if a graphics set was
10957         designed "before" or "after" the bug was fixed. As a workaround,
10958         a new graphics set option "game.graphics_engine_version" was added
10959         to be able to specify the game's major release version for which the
10960         graphics set was designed, which can then be used to decide if the
10961         bugfix should be used (version 4 and above) or not (version 3 or
10962         below, or if no version was specified at all, as with old sets).
10963
10964         (The wrong/fixed animation frames can be tested with the test level set
10965         "test_gfxframe" and level "000", which contains a specially prepared
10966         custom element at level position (x/y) == (11/9) which uses the zonk
10967         animation mentioned above. Using "game.graphics_engine_version: 4"
10968         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10969         This can also be seen from the debug output for this test element.)
10970       */
10971
10972       // when a custom element is about to change (for example by change delay),
10973       // do not reset graphic animation when the custom element is moving
10974       if (game.graphics_engine_version < 4 &&
10975           !IS_MOVING(x, y))
10976       {
10977         ResetGfxAnimation(x, y);
10978         ResetRandomAnimationValue(x, y);
10979       }
10980
10981       if (change->pre_change_function)
10982         change->pre_change_function(x, y);
10983     }
10984   }
10985
10986   ChangeDelay[x][y]--;
10987
10988   if (ChangeDelay[x][y] != 0)           // continue element change
10989   {
10990     if (change->can_change)
10991     {
10992       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10993
10994       if (IS_ANIMATED(graphic))
10995         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10996
10997       if (change->change_function)
10998         change->change_function(x, y);
10999     }
11000   }
11001   else                                  // finish element change
11002   {
11003     if (ChangePage[x][y] != -1)         // remember page from delayed change
11004     {
11005       page = ChangePage[x][y];
11006       ChangePage[x][y] = -1;
11007
11008       change = &ei->change_page[page];
11009     }
11010
11011     if (IS_MOVING(x, y))                // never change a running system ;-)
11012     {
11013       ChangeDelay[x][y] = 1;            // try change after next move step
11014       ChangePage[x][y] = page;          // remember page to use for change
11015
11016       return;
11017     }
11018
11019     // special case: set new level random seed before changing element
11020     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11021       handle_action_before_change = TRUE;
11022
11023     if (change->has_action && handle_action_before_change)
11024       ExecuteCustomElementAction(x, y, element, page);
11025
11026     if (change->can_change)
11027     {
11028       if (ChangeElement(x, y, element, page))
11029       {
11030         if (change->post_change_function)
11031           change->post_change_function(x, y);
11032       }
11033     }
11034
11035     if (change->has_action && !handle_action_before_change)
11036       ExecuteCustomElementAction(x, y, element, page);
11037   }
11038 }
11039
11040 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11041                                               int trigger_element,
11042                                               int trigger_event,
11043                                               int trigger_player,
11044                                               int trigger_side,
11045                                               int trigger_page)
11046 {
11047   boolean change_done_any = FALSE;
11048   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11049   int i;
11050
11051   if (!(trigger_events[trigger_element][trigger_event]))
11052     return FALSE;
11053
11054   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11055
11056   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11057   {
11058     int element = EL_CUSTOM_START + i;
11059     boolean change_done = FALSE;
11060     int p;
11061
11062     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11063         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11064       continue;
11065
11066     for (p = 0; p < element_info[element].num_change_pages; p++)
11067     {
11068       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11069
11070       if (change->can_change_or_has_action &&
11071           change->has_event[trigger_event] &&
11072           change->trigger_side & trigger_side &&
11073           change->trigger_player & trigger_player &&
11074           change->trigger_page & trigger_page_bits &&
11075           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11076       {
11077         change->actual_trigger_element = trigger_element;
11078         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11079         change->actual_trigger_player_bits = trigger_player;
11080         change->actual_trigger_side = trigger_side;
11081         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11082         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11083
11084         if ((change->can_change && !change_done) || change->has_action)
11085         {
11086           int x, y;
11087
11088           SCAN_PLAYFIELD(x, y)
11089           {
11090             if (Tile[x][y] == element)
11091             {
11092               if (change->can_change && !change_done)
11093               {
11094                 // if element already changed in this frame, not only prevent
11095                 // another element change (checked in ChangeElement()), but
11096                 // also prevent additional element actions for this element
11097
11098                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11099                     !level.use_action_after_change_bug)
11100                   continue;
11101
11102                 ChangeDelay[x][y] = 1;
11103                 ChangeEvent[x][y] = trigger_event;
11104
11105                 HandleElementChange(x, y, p);
11106               }
11107               else if (change->has_action)
11108               {
11109                 // if element already changed in this frame, not only prevent
11110                 // another element change (checked in ChangeElement()), but
11111                 // also prevent additional element actions for this element
11112
11113                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11114                     !level.use_action_after_change_bug)
11115                   continue;
11116
11117                 ExecuteCustomElementAction(x, y, element, p);
11118                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11119               }
11120             }
11121           }
11122
11123           if (change->can_change)
11124           {
11125             change_done = TRUE;
11126             change_done_any = TRUE;
11127           }
11128         }
11129       }
11130     }
11131   }
11132
11133   RECURSION_LOOP_DETECTION_END();
11134
11135   return change_done_any;
11136 }
11137
11138 static boolean CheckElementChangeExt(int x, int y,
11139                                      int element,
11140                                      int trigger_element,
11141                                      int trigger_event,
11142                                      int trigger_player,
11143                                      int trigger_side)
11144 {
11145   boolean change_done = FALSE;
11146   int p;
11147
11148   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11149       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11150     return FALSE;
11151
11152   if (Tile[x][y] == EL_BLOCKED)
11153   {
11154     Blocked2Moving(x, y, &x, &y);
11155     element = Tile[x][y];
11156   }
11157
11158   // check if element has already changed or is about to change after moving
11159   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11160        Tile[x][y] != element) ||
11161
11162       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11163        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11164         ChangePage[x][y] != -1)))
11165     return FALSE;
11166
11167   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11168
11169   for (p = 0; p < element_info[element].num_change_pages; p++)
11170   {
11171     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11172
11173     /* check trigger element for all events where the element that is checked
11174        for changing interacts with a directly adjacent element -- this is
11175        different to element changes that affect other elements to change on the
11176        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11177     boolean check_trigger_element =
11178       (trigger_event == CE_NEXT_TO_X ||
11179        trigger_event == CE_TOUCHING_X ||
11180        trigger_event == CE_HITTING_X ||
11181        trigger_event == CE_HIT_BY_X ||
11182        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11183
11184     if (change->can_change_or_has_action &&
11185         change->has_event[trigger_event] &&
11186         change->trigger_side & trigger_side &&
11187         change->trigger_player & trigger_player &&
11188         (!check_trigger_element ||
11189          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11190     {
11191       change->actual_trigger_element = trigger_element;
11192       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11193       change->actual_trigger_player_bits = trigger_player;
11194       change->actual_trigger_side = trigger_side;
11195       change->actual_trigger_ce_value = CustomValue[x][y];
11196       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11197
11198       // special case: trigger element not at (x,y) position for some events
11199       if (check_trigger_element)
11200       {
11201         static struct
11202         {
11203           int dx, dy;
11204         } move_xy[] =
11205           {
11206             {  0,  0 },
11207             { -1,  0 },
11208             { +1,  0 },
11209             {  0,  0 },
11210             {  0, -1 },
11211             {  0,  0 }, { 0, 0 }, { 0, 0 },
11212             {  0, +1 }
11213           };
11214
11215         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11216         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11217
11218         change->actual_trigger_ce_value = CustomValue[xx][yy];
11219         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11220       }
11221
11222       if (change->can_change && !change_done)
11223       {
11224         ChangeDelay[x][y] = 1;
11225         ChangeEvent[x][y] = trigger_event;
11226
11227         HandleElementChange(x, y, p);
11228
11229         change_done = TRUE;
11230       }
11231       else if (change->has_action)
11232       {
11233         ExecuteCustomElementAction(x, y, element, p);
11234         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11235       }
11236     }
11237   }
11238
11239   RECURSION_LOOP_DETECTION_END();
11240
11241   return change_done;
11242 }
11243
11244 static void PlayPlayerSound(struct PlayerInfo *player)
11245 {
11246   int jx = player->jx, jy = player->jy;
11247   int sound_element = player->artwork_element;
11248   int last_action = player->last_action_waiting;
11249   int action = player->action_waiting;
11250
11251   if (player->is_waiting)
11252   {
11253     if (action != last_action)
11254       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11255     else
11256       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11257   }
11258   else
11259   {
11260     if (action != last_action)
11261       StopSound(element_info[sound_element].sound[last_action]);
11262
11263     if (last_action == ACTION_SLEEPING)
11264       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11265   }
11266 }
11267
11268 static void PlayAllPlayersSound(void)
11269 {
11270   int i;
11271
11272   for (i = 0; i < MAX_PLAYERS; i++)
11273     if (stored_player[i].active)
11274       PlayPlayerSound(&stored_player[i]);
11275 }
11276
11277 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11278 {
11279   boolean last_waiting = player->is_waiting;
11280   int move_dir = player->MovDir;
11281
11282   player->dir_waiting = move_dir;
11283   player->last_action_waiting = player->action_waiting;
11284
11285   if (is_waiting)
11286   {
11287     if (!last_waiting)          // not waiting -> waiting
11288     {
11289       player->is_waiting = TRUE;
11290
11291       player->frame_counter_bored =
11292         FrameCounter +
11293         game.player_boring_delay_fixed +
11294         GetSimpleRandom(game.player_boring_delay_random);
11295       player->frame_counter_sleeping =
11296         FrameCounter +
11297         game.player_sleeping_delay_fixed +
11298         GetSimpleRandom(game.player_sleeping_delay_random);
11299
11300       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11301     }
11302
11303     if (game.player_sleeping_delay_fixed +
11304         game.player_sleeping_delay_random > 0 &&
11305         player->anim_delay_counter == 0 &&
11306         player->post_delay_counter == 0 &&
11307         FrameCounter >= player->frame_counter_sleeping)
11308       player->is_sleeping = TRUE;
11309     else if (game.player_boring_delay_fixed +
11310              game.player_boring_delay_random > 0 &&
11311              FrameCounter >= player->frame_counter_bored)
11312       player->is_bored = TRUE;
11313
11314     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11315                               player->is_bored ? ACTION_BORING :
11316                               ACTION_WAITING);
11317
11318     if (player->is_sleeping && player->use_murphy)
11319     {
11320       // special case for sleeping Murphy when leaning against non-free tile
11321
11322       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_LEFT;
11326       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11327                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11328                 !IS_MOVING(player->jx + 1, player->jy)))
11329         move_dir = MV_RIGHT;
11330       else
11331         player->is_sleeping = FALSE;
11332
11333       player->dir_waiting = move_dir;
11334     }
11335
11336     if (player->is_sleeping)
11337     {
11338       if (player->num_special_action_sleeping > 0)
11339       {
11340         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11341         {
11342           int last_special_action = player->special_action_sleeping;
11343           int num_special_action = player->num_special_action_sleeping;
11344           int special_action =
11345             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11346              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11347              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11348              last_special_action + 1 : ACTION_SLEEPING);
11349           int special_graphic =
11350             el_act_dir2img(player->artwork_element, special_action, move_dir);
11351
11352           player->anim_delay_counter =
11353             graphic_info[special_graphic].anim_delay_fixed +
11354             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11355           player->post_delay_counter =
11356             graphic_info[special_graphic].post_delay_fixed +
11357             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11358
11359           player->special_action_sleeping = special_action;
11360         }
11361
11362         if (player->anim_delay_counter > 0)
11363         {
11364           player->action_waiting = player->special_action_sleeping;
11365           player->anim_delay_counter--;
11366         }
11367         else if (player->post_delay_counter > 0)
11368         {
11369           player->post_delay_counter--;
11370         }
11371       }
11372     }
11373     else if (player->is_bored)
11374     {
11375       if (player->num_special_action_bored > 0)
11376       {
11377         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11378         {
11379           int special_action =
11380             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11381           int special_graphic =
11382             el_act_dir2img(player->artwork_element, special_action, move_dir);
11383
11384           player->anim_delay_counter =
11385             graphic_info[special_graphic].anim_delay_fixed +
11386             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11387           player->post_delay_counter =
11388             graphic_info[special_graphic].post_delay_fixed +
11389             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11390
11391           player->special_action_bored = special_action;
11392         }
11393
11394         if (player->anim_delay_counter > 0)
11395         {
11396           player->action_waiting = player->special_action_bored;
11397           player->anim_delay_counter--;
11398         }
11399         else if (player->post_delay_counter > 0)
11400         {
11401           player->post_delay_counter--;
11402         }
11403       }
11404     }
11405   }
11406   else if (last_waiting)        // waiting -> not waiting
11407   {
11408     player->is_waiting = FALSE;
11409     player->is_bored = FALSE;
11410     player->is_sleeping = FALSE;
11411
11412     player->frame_counter_bored = -1;
11413     player->frame_counter_sleeping = -1;
11414
11415     player->anim_delay_counter = 0;
11416     player->post_delay_counter = 0;
11417
11418     player->dir_waiting = player->MovDir;
11419     player->action_waiting = ACTION_DEFAULT;
11420
11421     player->special_action_bored = ACTION_DEFAULT;
11422     player->special_action_sleeping = ACTION_DEFAULT;
11423   }
11424 }
11425
11426 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11427 {
11428   if ((!player->is_moving  && player->was_moving) ||
11429       (player->MovPos == 0 && player->was_moving) ||
11430       (player->is_snapping && !player->was_snapping) ||
11431       (player->is_dropping && !player->was_dropping))
11432   {
11433     if (!CheckSaveEngineSnapshotToList())
11434       return;
11435
11436     player->was_moving = FALSE;
11437     player->was_snapping = TRUE;
11438     player->was_dropping = TRUE;
11439   }
11440   else
11441   {
11442     if (player->is_moving)
11443       player->was_moving = TRUE;
11444
11445     if (!player->is_snapping)
11446       player->was_snapping = FALSE;
11447
11448     if (!player->is_dropping)
11449       player->was_dropping = FALSE;
11450   }
11451
11452   static struct MouseActionInfo mouse_action_last = { 0 };
11453   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11454   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11455
11456   if (new_released)
11457     CheckSaveEngineSnapshotToList();
11458
11459   mouse_action_last = mouse_action;
11460 }
11461
11462 static void CheckSingleStepMode(struct PlayerInfo *player)
11463 {
11464   if (tape.single_step && tape.recording && !tape.pausing)
11465   {
11466     // as it is called "single step mode", just return to pause mode when the
11467     // player stopped moving after one tile (or never starts moving at all)
11468     // (reverse logic needed here in case single step mode used in team mode)
11469     if (player->is_moving ||
11470         player->is_pushing ||
11471         player->is_dropping_pressed ||
11472         player->effective_mouse_action.button)
11473       game.enter_single_step_mode = FALSE;
11474   }
11475
11476   CheckSaveEngineSnapshot(player);
11477 }
11478
11479 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11480 {
11481   int left      = player_action & JOY_LEFT;
11482   int right     = player_action & JOY_RIGHT;
11483   int up        = player_action & JOY_UP;
11484   int down      = player_action & JOY_DOWN;
11485   int button1   = player_action & JOY_BUTTON_1;
11486   int button2   = player_action & JOY_BUTTON_2;
11487   int dx        = (left ? -1 : right ? 1 : 0);
11488   int dy        = (up   ? -1 : down  ? 1 : 0);
11489
11490   if (!player->active || tape.pausing)
11491     return 0;
11492
11493   if (player_action)
11494   {
11495     if (button1)
11496       SnapField(player, dx, dy);
11497     else
11498     {
11499       if (button2)
11500         DropElement(player);
11501
11502       MovePlayer(player, dx, dy);
11503     }
11504
11505     CheckSingleStepMode(player);
11506
11507     SetPlayerWaiting(player, FALSE);
11508
11509     return player_action;
11510   }
11511   else
11512   {
11513     // no actions for this player (no input at player's configured device)
11514
11515     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11516     SnapField(player, 0, 0);
11517     CheckGravityMovementWhenNotMoving(player);
11518
11519     if (player->MovPos == 0)
11520       SetPlayerWaiting(player, TRUE);
11521
11522     if (player->MovPos == 0)    // needed for tape.playing
11523       player->is_moving = FALSE;
11524
11525     player->is_dropping = FALSE;
11526     player->is_dropping_pressed = FALSE;
11527     player->drop_pressed_delay = 0;
11528
11529     CheckSingleStepMode(player);
11530
11531     return 0;
11532   }
11533 }
11534
11535 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11536                                          byte *tape_action)
11537 {
11538   if (!tape.use_mouse_actions)
11539     return;
11540
11541   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11542   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11543   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11544 }
11545
11546 static void SetTapeActionFromMouseAction(byte *tape_action,
11547                                          struct MouseActionInfo *mouse_action)
11548 {
11549   if (!tape.use_mouse_actions)
11550     return;
11551
11552   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11553   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11554   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11555 }
11556
11557 static void CheckLevelSolved(void)
11558 {
11559   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11560   {
11561     if (game_em.level_solved &&
11562         !game_em.game_over)                             // game won
11563     {
11564       LevelSolved();
11565
11566       game_em.game_over = TRUE;
11567
11568       game.all_players_gone = TRUE;
11569     }
11570
11571     if (game_em.game_over)                              // game lost
11572       game.all_players_gone = TRUE;
11573   }
11574   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11575   {
11576     if (game_sp.level_solved &&
11577         !game_sp.game_over)                             // game won
11578     {
11579       LevelSolved();
11580
11581       game_sp.game_over = TRUE;
11582
11583       game.all_players_gone = TRUE;
11584     }
11585
11586     if (game_sp.game_over)                              // game lost
11587       game.all_players_gone = TRUE;
11588   }
11589   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11590   {
11591     if (game_mm.level_solved &&
11592         !game_mm.game_over)                             // game won
11593     {
11594       LevelSolved();
11595
11596       game_mm.game_over = TRUE;
11597
11598       game.all_players_gone = TRUE;
11599     }
11600
11601     if (game_mm.game_over)                              // game lost
11602       game.all_players_gone = TRUE;
11603   }
11604 }
11605
11606 static void CheckLevelTime_StepCounter(void)
11607 {
11608   int i;
11609
11610   TimePlayed++;
11611
11612   if (TimeLeft > 0)
11613   {
11614     TimeLeft--;
11615
11616     if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
11617       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11618
11619     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11620
11621     DisplayGameControlValues();
11622
11623     if (!TimeLeft && setup.time_limit && !game.LevelSolved)
11624       for (i = 0; i < MAX_PLAYERS; i++)
11625         KillPlayer(&stored_player[i]);
11626   }
11627   else if (game.no_time_limit && !game.all_players_gone)
11628   {
11629     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11630
11631     DisplayGameControlValues();
11632   }
11633 }
11634
11635 static void CheckLevelTime(void)
11636 {
11637   int i;
11638
11639   if (TimeFrames >= FRAMES_PER_SECOND)
11640   {
11641     TimeFrames = 0;
11642     TapeTime++;
11643
11644     for (i = 0; i < MAX_PLAYERS; i++)
11645     {
11646       struct PlayerInfo *player = &stored_player[i];
11647
11648       if (SHIELD_ON(player))
11649       {
11650         player->shield_normal_time_left--;
11651
11652         if (player->shield_deadly_time_left > 0)
11653           player->shield_deadly_time_left--;
11654       }
11655     }
11656
11657     if (!game.LevelSolved && !level.use_step_counter)
11658     {
11659       TimePlayed++;
11660
11661       if (TimeLeft > 0)
11662       {
11663         TimeLeft--;
11664
11665         if (TimeLeft <= 10 && setup.time_limit)
11666           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11667
11668         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11669            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11670
11671         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11672
11673         if (!TimeLeft && setup.time_limit)
11674         {
11675           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11676             game_em.lev->killed_out_of_time = TRUE;
11677           else
11678             for (i = 0; i < MAX_PLAYERS; i++)
11679               KillPlayer(&stored_player[i]);
11680         }
11681       }
11682       else if (game.no_time_limit && !game.all_players_gone)
11683       {
11684         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11685       }
11686
11687       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11688     }
11689
11690     if (tape.recording || tape.playing)
11691       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11692   }
11693
11694   if (tape.recording || tape.playing)
11695     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11696
11697   UpdateAndDisplayGameControlValues();
11698 }
11699
11700 void AdvanceFrameAndPlayerCounters(int player_nr)
11701 {
11702   int i;
11703
11704   // advance frame counters (global frame counter and time frame counter)
11705   FrameCounter++;
11706   TimeFrames++;
11707
11708   // advance player counters (counters for move delay, move animation etc.)
11709   for (i = 0; i < MAX_PLAYERS; i++)
11710   {
11711     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11712     int move_delay_value = stored_player[i].move_delay_value;
11713     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11714
11715     if (!advance_player_counters)       // not all players may be affected
11716       continue;
11717
11718     if (move_frames == 0)       // less than one move per game frame
11719     {
11720       int stepsize = TILEX / move_delay_value;
11721       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11722       int count = (stored_player[i].is_moving ?
11723                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11724
11725       if (count % delay == 0)
11726         move_frames = 1;
11727     }
11728
11729     stored_player[i].Frame += move_frames;
11730
11731     if (stored_player[i].MovPos != 0)
11732       stored_player[i].StepFrame += move_frames;
11733
11734     if (stored_player[i].move_delay > 0)
11735       stored_player[i].move_delay--;
11736
11737     // due to bugs in previous versions, counter must count up, not down
11738     if (stored_player[i].push_delay != -1)
11739       stored_player[i].push_delay++;
11740
11741     if (stored_player[i].drop_delay > 0)
11742       stored_player[i].drop_delay--;
11743
11744     if (stored_player[i].is_dropping_pressed)
11745       stored_player[i].drop_pressed_delay++;
11746   }
11747 }
11748
11749 void StartGameActions(boolean init_network_game, boolean record_tape,
11750                       int random_seed)
11751 {
11752   unsigned int new_random_seed = InitRND(random_seed);
11753
11754   if (record_tape)
11755     TapeStartRecording(new_random_seed);
11756
11757   if (init_network_game)
11758   {
11759     SendToServer_LevelFile();
11760     SendToServer_StartPlaying();
11761
11762     return;
11763   }
11764
11765   InitGame();
11766 }
11767
11768 static void GameActionsExt(void)
11769 {
11770 #if 0
11771   static unsigned int game_frame_delay = 0;
11772 #endif
11773   unsigned int game_frame_delay_value;
11774   byte *recorded_player_action;
11775   byte summarized_player_action = 0;
11776   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11777   int i;
11778
11779   // detect endless loops, caused by custom element programming
11780   if (recursion_loop_detected && recursion_loop_depth == 0)
11781   {
11782     char *message = getStringCat3("Internal Error! Element ",
11783                                   EL_NAME(recursion_loop_element),
11784                                   " caused endless loop! Quit the game?");
11785
11786     Warn("element '%s' caused endless loop in game engine",
11787          EL_NAME(recursion_loop_element));
11788
11789     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11790
11791     recursion_loop_detected = FALSE;    // if game should be continued
11792
11793     free(message);
11794
11795     return;
11796   }
11797
11798   if (game.restart_level)
11799     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11800
11801   CheckLevelSolved();
11802
11803   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11804     GameWon();
11805
11806   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11807     TapeStop();
11808
11809   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11810     return;
11811
11812   game_frame_delay_value =
11813     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11814
11815   if (tape.playing && tape.warp_forward && !tape.pausing)
11816     game_frame_delay_value = 0;
11817
11818   SetVideoFrameDelay(game_frame_delay_value);
11819
11820   // (de)activate virtual buttons depending on current game status
11821   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11822   {
11823     if (game.all_players_gone)  // if no players there to be controlled anymore
11824       SetOverlayActive(FALSE);
11825     else if (!tape.playing)     // if game continues after tape stopped playing
11826       SetOverlayActive(TRUE);
11827   }
11828
11829 #if 0
11830 #if 0
11831   // ---------- main game synchronization point ----------
11832
11833   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11834
11835   Debug("game:playing:skip", "skip == %d", skip);
11836
11837 #else
11838   // ---------- main game synchronization point ----------
11839
11840   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11841 #endif
11842 #endif
11843
11844   if (network_playing && !network_player_action_received)
11845   {
11846     // try to get network player actions in time
11847
11848     // last chance to get network player actions without main loop delay
11849     HandleNetworking();
11850
11851     // game was quit by network peer
11852     if (game_status != GAME_MODE_PLAYING)
11853       return;
11854
11855     // check if network player actions still missing and game still running
11856     if (!network_player_action_received && !checkGameEnded())
11857       return;           // failed to get network player actions in time
11858
11859     // do not yet reset "network_player_action_received" (for tape.pausing)
11860   }
11861
11862   if (tape.pausing)
11863     return;
11864
11865   // at this point we know that we really continue executing the game
11866
11867   network_player_action_received = FALSE;
11868
11869   // when playing tape, read previously recorded player input from tape data
11870   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11871
11872   local_player->effective_mouse_action = local_player->mouse_action;
11873
11874   if (recorded_player_action != NULL)
11875     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11876                                  recorded_player_action);
11877
11878   // TapePlayAction() may return NULL when toggling to "pause before death"
11879   if (tape.pausing)
11880     return;
11881
11882   if (tape.set_centered_player)
11883   {
11884     game.centered_player_nr_next = tape.centered_player_nr_next;
11885     game.set_centered_player = TRUE;
11886   }
11887
11888   for (i = 0; i < MAX_PLAYERS; i++)
11889   {
11890     summarized_player_action |= stored_player[i].action;
11891
11892     if (!network_playing && (game.team_mode || tape.playing))
11893       stored_player[i].effective_action = stored_player[i].action;
11894   }
11895
11896   if (network_playing && !checkGameEnded())
11897     SendToServer_MovePlayer(summarized_player_action);
11898
11899   // summarize all actions at local players mapped input device position
11900   // (this allows using different input devices in single player mode)
11901   if (!network.enabled && !game.team_mode)
11902     stored_player[map_player_action[local_player->index_nr]].effective_action =
11903       summarized_player_action;
11904
11905   // summarize all actions at centered player in local team mode
11906   if (tape.recording &&
11907       setup.team_mode && !network.enabled &&
11908       setup.input_on_focus &&
11909       game.centered_player_nr != -1)
11910   {
11911     for (i = 0; i < MAX_PLAYERS; i++)
11912       stored_player[map_player_action[i]].effective_action =
11913         (i == game.centered_player_nr ? summarized_player_action : 0);
11914   }
11915
11916   if (recorded_player_action != NULL)
11917     for (i = 0; i < MAX_PLAYERS; i++)
11918       stored_player[i].effective_action = recorded_player_action[i];
11919
11920   for (i = 0; i < MAX_PLAYERS; i++)
11921   {
11922     tape_action[i] = stored_player[i].effective_action;
11923
11924     /* (this may happen in the RND game engine if a player was not present on
11925        the playfield on level start, but appeared later from a custom element */
11926     if (setup.team_mode &&
11927         tape.recording &&
11928         tape_action[i] &&
11929         !tape.player_participates[i])
11930       tape.player_participates[i] = TRUE;
11931   }
11932
11933   SetTapeActionFromMouseAction(tape_action,
11934                                &local_player->effective_mouse_action);
11935
11936   // only record actions from input devices, but not programmed actions
11937   if (tape.recording)
11938     TapeRecordAction(tape_action);
11939
11940   // remember if game was played (especially after tape stopped playing)
11941   if (!tape.playing && summarized_player_action)
11942     game.GamePlayed = TRUE;
11943
11944 #if USE_NEW_PLAYER_ASSIGNMENTS
11945   // !!! also map player actions in single player mode !!!
11946   // if (game.team_mode)
11947   if (1)
11948   {
11949     byte mapped_action[MAX_PLAYERS];
11950
11951 #if DEBUG_PLAYER_ACTIONS
11952     for (i = 0; i < MAX_PLAYERS; i++)
11953       DebugContinued("", "%d, ", stored_player[i].effective_action);
11954 #endif
11955
11956     for (i = 0; i < MAX_PLAYERS; i++)
11957       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11958
11959     for (i = 0; i < MAX_PLAYERS; i++)
11960       stored_player[i].effective_action = mapped_action[i];
11961
11962 #if DEBUG_PLAYER_ACTIONS
11963     DebugContinued("", "=> ");
11964     for (i = 0; i < MAX_PLAYERS; i++)
11965       DebugContinued("", "%d, ", stored_player[i].effective_action);
11966     DebugContinued("game:playing:player", "\n");
11967 #endif
11968   }
11969 #if DEBUG_PLAYER_ACTIONS
11970   else
11971   {
11972     for (i = 0; i < MAX_PLAYERS; i++)
11973       DebugContinued("", "%d, ", stored_player[i].effective_action);
11974     DebugContinued("game:playing:player", "\n");
11975   }
11976 #endif
11977 #endif
11978
11979   for (i = 0; i < MAX_PLAYERS; i++)
11980   {
11981     // allow engine snapshot in case of changed movement attempt
11982     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11983         (stored_player[i].effective_action & KEY_MOTION))
11984       game.snapshot.changed_action = TRUE;
11985
11986     // allow engine snapshot in case of snapping/dropping attempt
11987     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11988         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11989       game.snapshot.changed_action = TRUE;
11990
11991     game.snapshot.last_action[i] = stored_player[i].effective_action;
11992   }
11993
11994   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11995   {
11996     GameActions_EM_Main();
11997   }
11998   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11999   {
12000     GameActions_SP_Main();
12001   }
12002   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12003   {
12004     GameActions_MM_Main();
12005   }
12006   else
12007   {
12008     GameActions_RND_Main();
12009   }
12010
12011   BlitScreenToBitmap(backbuffer);
12012
12013   CheckLevelSolved();
12014   CheckLevelTime();
12015
12016   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12017
12018   if (global.show_frames_per_second)
12019   {
12020     static unsigned int fps_counter = 0;
12021     static int fps_frames = 0;
12022     unsigned int fps_delay_ms = Counter() - fps_counter;
12023
12024     fps_frames++;
12025
12026     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12027     {
12028       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12029
12030       fps_frames = 0;
12031       fps_counter = Counter();
12032
12033       // always draw FPS to screen after FPS value was updated
12034       redraw_mask |= REDRAW_FPS;
12035     }
12036
12037     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12038     if (GetDrawDeactivationMask() == REDRAW_NONE)
12039       redraw_mask |= REDRAW_FPS;
12040   }
12041 }
12042
12043 static void GameActions_CheckSaveEngineSnapshot(void)
12044 {
12045   if (!game.snapshot.save_snapshot)
12046     return;
12047
12048   // clear flag for saving snapshot _before_ saving snapshot
12049   game.snapshot.save_snapshot = FALSE;
12050
12051   SaveEngineSnapshotToList();
12052 }
12053
12054 void GameActions(void)
12055 {
12056   GameActionsExt();
12057
12058   GameActions_CheckSaveEngineSnapshot();
12059 }
12060
12061 void GameActions_EM_Main(void)
12062 {
12063   byte effective_action[MAX_PLAYERS];
12064   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12065   int i;
12066
12067   for (i = 0; i < MAX_PLAYERS; i++)
12068     effective_action[i] = stored_player[i].effective_action;
12069
12070   GameActions_EM(effective_action, warp_mode);
12071 }
12072
12073 void GameActions_SP_Main(void)
12074 {
12075   byte effective_action[MAX_PLAYERS];
12076   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12077   int i;
12078
12079   for (i = 0; i < MAX_PLAYERS; i++)
12080     effective_action[i] = stored_player[i].effective_action;
12081
12082   GameActions_SP(effective_action, warp_mode);
12083
12084   for (i = 0; i < MAX_PLAYERS; i++)
12085   {
12086     if (stored_player[i].force_dropping)
12087       stored_player[i].action |= KEY_BUTTON_DROP;
12088
12089     stored_player[i].force_dropping = FALSE;
12090   }
12091 }
12092
12093 void GameActions_MM_Main(void)
12094 {
12095   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12096
12097   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12098 }
12099
12100 void GameActions_RND_Main(void)
12101 {
12102   GameActions_RND();
12103 }
12104
12105 void GameActions_RND(void)
12106 {
12107   static struct MouseActionInfo mouse_action_last = { 0 };
12108   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12109   int magic_wall_x = 0, magic_wall_y = 0;
12110   int i, x, y, element, graphic, last_gfx_frame;
12111
12112   InitPlayfieldScanModeVars();
12113
12114   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12115   {
12116     SCAN_PLAYFIELD(x, y)
12117     {
12118       ChangeCount[x][y] = 0;
12119       ChangeEvent[x][y] = -1;
12120     }
12121   }
12122
12123   if (game.set_centered_player)
12124   {
12125     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12126
12127     // switching to "all players" only possible if all players fit to screen
12128     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12129     {
12130       game.centered_player_nr_next = game.centered_player_nr;
12131       game.set_centered_player = FALSE;
12132     }
12133
12134     // do not switch focus to non-existing (or non-active) player
12135     if (game.centered_player_nr_next >= 0 &&
12136         !stored_player[game.centered_player_nr_next].active)
12137     {
12138       game.centered_player_nr_next = game.centered_player_nr;
12139       game.set_centered_player = FALSE;
12140     }
12141   }
12142
12143   if (game.set_centered_player &&
12144       ScreenMovPos == 0)        // screen currently aligned at tile position
12145   {
12146     int sx, sy;
12147
12148     if (game.centered_player_nr_next == -1)
12149     {
12150       setScreenCenteredToAllPlayers(&sx, &sy);
12151     }
12152     else
12153     {
12154       sx = stored_player[game.centered_player_nr_next].jx;
12155       sy = stored_player[game.centered_player_nr_next].jy;
12156     }
12157
12158     game.centered_player_nr = game.centered_player_nr_next;
12159     game.set_centered_player = FALSE;
12160
12161     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12162     DrawGameDoorValues();
12163   }
12164
12165   // check single step mode (set flag and clear again if any player is active)
12166   game.enter_single_step_mode =
12167     (tape.single_step && tape.recording && !tape.pausing);
12168
12169   for (i = 0; i < MAX_PLAYERS; i++)
12170   {
12171     int actual_player_action = stored_player[i].effective_action;
12172
12173 #if 1
12174     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12175        - rnd_equinox_tetrachloride 048
12176        - rnd_equinox_tetrachloride_ii 096
12177        - rnd_emanuel_schmieg 002
12178        - doctor_sloan_ww 001, 020
12179     */
12180     if (stored_player[i].MovPos == 0)
12181       CheckGravityMovement(&stored_player[i]);
12182 #endif
12183
12184     // overwrite programmed action with tape action
12185     if (stored_player[i].programmed_action)
12186       actual_player_action = stored_player[i].programmed_action;
12187
12188     PlayerActions(&stored_player[i], actual_player_action);
12189
12190     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12191   }
12192
12193   // single step pause mode may already have been toggled by "ScrollPlayer()"
12194   if (game.enter_single_step_mode && !tape.pausing)
12195     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12196
12197   ScrollScreen(NULL, SCROLL_GO_ON);
12198
12199   /* for backwards compatibility, the following code emulates a fixed bug that
12200      occured when pushing elements (causing elements that just made their last
12201      pushing step to already (if possible) make their first falling step in the
12202      same game frame, which is bad); this code is also needed to use the famous
12203      "spring push bug" which is used in older levels and might be wanted to be
12204      used also in newer levels, but in this case the buggy pushing code is only
12205      affecting the "spring" element and no other elements */
12206
12207   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12208   {
12209     for (i = 0; i < MAX_PLAYERS; i++)
12210     {
12211       struct PlayerInfo *player = &stored_player[i];
12212       int x = player->jx;
12213       int y = player->jy;
12214
12215       if (player->active && player->is_pushing && player->is_moving &&
12216           IS_MOVING(x, y) &&
12217           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12218            Tile[x][y] == EL_SPRING))
12219       {
12220         ContinueMoving(x, y);
12221
12222         // continue moving after pushing (this is actually a bug)
12223         if (!IS_MOVING(x, y))
12224           Stop[x][y] = FALSE;
12225       }
12226     }
12227   }
12228
12229   SCAN_PLAYFIELD(x, y)
12230   {
12231     Last[x][y] = Tile[x][y];
12232
12233     ChangeCount[x][y] = 0;
12234     ChangeEvent[x][y] = -1;
12235
12236     // this must be handled before main playfield loop
12237     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12238     {
12239       MovDelay[x][y]--;
12240       if (MovDelay[x][y] <= 0)
12241         RemoveField(x, y);
12242     }
12243
12244     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12245     {
12246       MovDelay[x][y]--;
12247       if (MovDelay[x][y] <= 0)
12248       {
12249         int element = Store[x][y];
12250         int move_direction = MovDir[x][y];
12251         int player_index_bit = Store2[x][y];
12252
12253         Store[x][y] = 0;
12254         Store2[x][y] = 0;
12255
12256         RemoveField(x, y);
12257         TEST_DrawLevelField(x, y);
12258
12259         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12260
12261         if (IS_ENVELOPE(element))
12262           local_player->show_envelope = element;
12263       }
12264     }
12265
12266 #if DEBUG
12267     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12268     {
12269       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12270             x, y);
12271       Debug("game:playing:GameActions_RND", "This should never happen!");
12272
12273       ChangePage[x][y] = -1;
12274     }
12275 #endif
12276
12277     Stop[x][y] = FALSE;
12278     if (WasJustMoving[x][y] > 0)
12279       WasJustMoving[x][y]--;
12280     if (WasJustFalling[x][y] > 0)
12281       WasJustFalling[x][y]--;
12282     if (CheckCollision[x][y] > 0)
12283       CheckCollision[x][y]--;
12284     if (CheckImpact[x][y] > 0)
12285       CheckImpact[x][y]--;
12286
12287     GfxFrame[x][y]++;
12288
12289     /* reset finished pushing action (not done in ContinueMoving() to allow
12290        continuous pushing animation for elements with zero push delay) */
12291     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12292     {
12293       ResetGfxAnimation(x, y);
12294       TEST_DrawLevelField(x, y);
12295     }
12296
12297 #if DEBUG
12298     if (IS_BLOCKED(x, y))
12299     {
12300       int oldx, oldy;
12301
12302       Blocked2Moving(x, y, &oldx, &oldy);
12303       if (!IS_MOVING(oldx, oldy))
12304       {
12305         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12306         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12307         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12308         Debug("game:playing:GameActions_RND", "This should never happen!");
12309       }
12310     }
12311 #endif
12312   }
12313
12314   if (mouse_action.button)
12315   {
12316     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12317     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12318
12319     x = mouse_action.lx;
12320     y = mouse_action.ly;
12321     element = Tile[x][y];
12322
12323     if (new_button)
12324     {
12325       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12326       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12327                                          ch_button);
12328     }
12329
12330     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12331     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12332                                        ch_button);
12333
12334     if (level.use_step_counter)
12335     {
12336       boolean counted_click = FALSE;
12337
12338       // element clicked that can change when clicked/pressed
12339       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12340           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12341            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12342         counted_click = TRUE;
12343
12344       // element clicked that can trigger change when clicked/pressed
12345       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12346           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12347         counted_click = TRUE;
12348
12349       if (new_button && counted_click)
12350         CheckLevelTime_StepCounter();
12351     }
12352   }
12353
12354   SCAN_PLAYFIELD(x, y)
12355   {
12356     element = Tile[x][y];
12357     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12358     last_gfx_frame = GfxFrame[x][y];
12359
12360     if (element == EL_EMPTY)
12361       graphic = el2img(GfxElementEmpty[x][y]);
12362
12363     ResetGfxFrame(x, y);
12364
12365     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12366       DrawLevelGraphicAnimation(x, y, graphic);
12367
12368     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12369         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12370       ResetRandomAnimationValue(x, y);
12371
12372     SetRandomAnimationValue(x, y);
12373
12374     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12375
12376     if (IS_INACTIVE(element))
12377     {
12378       if (IS_ANIMATED(graphic))
12379         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12380
12381       continue;
12382     }
12383
12384     // this may take place after moving, so 'element' may have changed
12385     if (IS_CHANGING(x, y) &&
12386         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12387     {
12388       int page = element_info[element].event_page_nr[CE_DELAY];
12389
12390       HandleElementChange(x, y, page);
12391
12392       element = Tile[x][y];
12393       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12394     }
12395
12396     CheckNextToConditions(x, y);
12397
12398     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12399     {
12400       StartMoving(x, y);
12401
12402       element = Tile[x][y];
12403       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12404
12405       if (IS_ANIMATED(graphic) &&
12406           !IS_MOVING(x, y) &&
12407           !Stop[x][y])
12408         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12409
12410       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12411         TEST_DrawTwinkleOnField(x, y);
12412     }
12413     else if (element == EL_ACID)
12414     {
12415       if (!Stop[x][y])
12416         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12417     }
12418     else if ((element == EL_EXIT_OPEN ||
12419               element == EL_EM_EXIT_OPEN ||
12420               element == EL_SP_EXIT_OPEN ||
12421               element == EL_STEEL_EXIT_OPEN ||
12422               element == EL_EM_STEEL_EXIT_OPEN ||
12423               element == EL_SP_TERMINAL ||
12424               element == EL_SP_TERMINAL_ACTIVE ||
12425               element == EL_EXTRA_TIME ||
12426               element == EL_SHIELD_NORMAL ||
12427               element == EL_SHIELD_DEADLY) &&
12428              IS_ANIMATED(graphic))
12429       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12430     else if (IS_MOVING(x, y))
12431       ContinueMoving(x, y);
12432     else if (IS_ACTIVE_BOMB(element))
12433       CheckDynamite(x, y);
12434     else if (element == EL_AMOEBA_GROWING)
12435       AmoebaGrowing(x, y);
12436     else if (element == EL_AMOEBA_SHRINKING)
12437       AmoebaShrinking(x, y);
12438
12439 #if !USE_NEW_AMOEBA_CODE
12440     else if (IS_AMOEBALIVE(element))
12441       AmoebaReproduce(x, y);
12442 #endif
12443
12444     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12445       Life(x, y);
12446     else if (element == EL_EXIT_CLOSED)
12447       CheckExit(x, y);
12448     else if (element == EL_EM_EXIT_CLOSED)
12449       CheckExitEM(x, y);
12450     else if (element == EL_STEEL_EXIT_CLOSED)
12451       CheckExitSteel(x, y);
12452     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12453       CheckExitSteelEM(x, y);
12454     else if (element == EL_SP_EXIT_CLOSED)
12455       CheckExitSP(x, y);
12456     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12457              element == EL_EXPANDABLE_STEELWALL_GROWING)
12458       MauerWaechst(x, y);
12459     else if (element == EL_EXPANDABLE_WALL ||
12460              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12461              element == EL_EXPANDABLE_WALL_VERTICAL ||
12462              element == EL_EXPANDABLE_WALL_ANY ||
12463              element == EL_BD_EXPANDABLE_WALL)
12464       MauerAbleger(x, y);
12465     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12466              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12467              element == EL_EXPANDABLE_STEELWALL_ANY)
12468       MauerAblegerStahl(x, y);
12469     else if (element == EL_FLAMES)
12470       CheckForDragon(x, y);
12471     else if (element == EL_EXPLOSION)
12472       ; // drawing of correct explosion animation is handled separately
12473     else if (element == EL_ELEMENT_SNAPPING ||
12474              element == EL_DIAGONAL_SHRINKING ||
12475              element == EL_DIAGONAL_GROWING)
12476     {
12477       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12478
12479       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12480     }
12481     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12482       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12483
12484     if (IS_BELT_ACTIVE(element))
12485       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12486
12487     if (game.magic_wall_active)
12488     {
12489       int jx = local_player->jx, jy = local_player->jy;
12490
12491       // play the element sound at the position nearest to the player
12492       if ((element == EL_MAGIC_WALL_FULL ||
12493            element == EL_MAGIC_WALL_ACTIVE ||
12494            element == EL_MAGIC_WALL_EMPTYING ||
12495            element == EL_BD_MAGIC_WALL_FULL ||
12496            element == EL_BD_MAGIC_WALL_ACTIVE ||
12497            element == EL_BD_MAGIC_WALL_EMPTYING ||
12498            element == EL_DC_MAGIC_WALL_FULL ||
12499            element == EL_DC_MAGIC_WALL_ACTIVE ||
12500            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12501           ABS(x - jx) + ABS(y - jy) <
12502           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12503       {
12504         magic_wall_x = x;
12505         magic_wall_y = y;
12506       }
12507     }
12508   }
12509
12510 #if USE_NEW_AMOEBA_CODE
12511   // new experimental amoeba growth stuff
12512   if (!(FrameCounter % 8))
12513   {
12514     static unsigned int random = 1684108901;
12515
12516     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12517     {
12518       x = RND(lev_fieldx);
12519       y = RND(lev_fieldy);
12520       element = Tile[x][y];
12521
12522       if (!IS_PLAYER(x,y) &&
12523           (element == EL_EMPTY ||
12524            CAN_GROW_INTO(element) ||
12525            element == EL_QUICKSAND_EMPTY ||
12526            element == EL_QUICKSAND_FAST_EMPTY ||
12527            element == EL_ACID_SPLASH_LEFT ||
12528            element == EL_ACID_SPLASH_RIGHT))
12529       {
12530         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12531             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12532             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12533             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12534           Tile[x][y] = EL_AMOEBA_DROP;
12535       }
12536
12537       random = random * 129 + 1;
12538     }
12539   }
12540 #endif
12541
12542   game.explosions_delayed = FALSE;
12543
12544   SCAN_PLAYFIELD(x, y)
12545   {
12546     element = Tile[x][y];
12547
12548     if (ExplodeField[x][y])
12549       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12550     else if (element == EL_EXPLOSION)
12551       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12552
12553     ExplodeField[x][y] = EX_TYPE_NONE;
12554   }
12555
12556   game.explosions_delayed = TRUE;
12557
12558   if (game.magic_wall_active)
12559   {
12560     if (!(game.magic_wall_time_left % 4))
12561     {
12562       int element = Tile[magic_wall_x][magic_wall_y];
12563
12564       if (element == EL_BD_MAGIC_WALL_FULL ||
12565           element == EL_BD_MAGIC_WALL_ACTIVE ||
12566           element == EL_BD_MAGIC_WALL_EMPTYING)
12567         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12568       else if (element == EL_DC_MAGIC_WALL_FULL ||
12569                element == EL_DC_MAGIC_WALL_ACTIVE ||
12570                element == EL_DC_MAGIC_WALL_EMPTYING)
12571         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12572       else
12573         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12574     }
12575
12576     if (game.magic_wall_time_left > 0)
12577     {
12578       game.magic_wall_time_left--;
12579
12580       if (!game.magic_wall_time_left)
12581       {
12582         SCAN_PLAYFIELD(x, y)
12583         {
12584           element = Tile[x][y];
12585
12586           if (element == EL_MAGIC_WALL_ACTIVE ||
12587               element == EL_MAGIC_WALL_FULL)
12588           {
12589             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12590             TEST_DrawLevelField(x, y);
12591           }
12592           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12593                    element == EL_BD_MAGIC_WALL_FULL)
12594           {
12595             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12596             TEST_DrawLevelField(x, y);
12597           }
12598           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12599                    element == EL_DC_MAGIC_WALL_FULL)
12600           {
12601             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12602             TEST_DrawLevelField(x, y);
12603           }
12604         }
12605
12606         game.magic_wall_active = FALSE;
12607       }
12608     }
12609   }
12610
12611   if (game.light_time_left > 0)
12612   {
12613     game.light_time_left--;
12614
12615     if (game.light_time_left == 0)
12616       RedrawAllLightSwitchesAndInvisibleElements();
12617   }
12618
12619   if (game.timegate_time_left > 0)
12620   {
12621     game.timegate_time_left--;
12622
12623     if (game.timegate_time_left == 0)
12624       CloseAllOpenTimegates();
12625   }
12626
12627   if (game.lenses_time_left > 0)
12628   {
12629     game.lenses_time_left--;
12630
12631     if (game.lenses_time_left == 0)
12632       RedrawAllInvisibleElementsForLenses();
12633   }
12634
12635   if (game.magnify_time_left > 0)
12636   {
12637     game.magnify_time_left--;
12638
12639     if (game.magnify_time_left == 0)
12640       RedrawAllInvisibleElementsForMagnifier();
12641   }
12642
12643   for (i = 0; i < MAX_PLAYERS; i++)
12644   {
12645     struct PlayerInfo *player = &stored_player[i];
12646
12647     if (SHIELD_ON(player))
12648     {
12649       if (player->shield_deadly_time_left)
12650         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12651       else if (player->shield_normal_time_left)
12652         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12653     }
12654   }
12655
12656 #if USE_DELAYED_GFX_REDRAW
12657   SCAN_PLAYFIELD(x, y)
12658   {
12659     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12660     {
12661       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12662          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12663
12664       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12665         DrawLevelField(x, y);
12666
12667       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12668         DrawLevelFieldCrumbled(x, y);
12669
12670       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12671         DrawLevelFieldCrumbledNeighbours(x, y);
12672
12673       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12674         DrawTwinkleOnField(x, y);
12675     }
12676
12677     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12678   }
12679 #endif
12680
12681   DrawAllPlayers();
12682   PlayAllPlayersSound();
12683
12684   for (i = 0; i < MAX_PLAYERS; i++)
12685   {
12686     struct PlayerInfo *player = &stored_player[i];
12687
12688     if (player->show_envelope != 0 && (!player->active ||
12689                                        player->MovPos == 0))
12690     {
12691       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12692
12693       player->show_envelope = 0;
12694     }
12695   }
12696
12697   // use random number generator in every frame to make it less predictable
12698   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12699     RND(1);
12700
12701   mouse_action_last = mouse_action;
12702 }
12703
12704 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12705 {
12706   int min_x = x, min_y = y, max_x = x, max_y = y;
12707   int scr_fieldx = getScreenFieldSizeX();
12708   int scr_fieldy = getScreenFieldSizeY();
12709   int i;
12710
12711   for (i = 0; i < MAX_PLAYERS; i++)
12712   {
12713     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12714
12715     if (!stored_player[i].active || &stored_player[i] == player)
12716       continue;
12717
12718     min_x = MIN(min_x, jx);
12719     min_y = MIN(min_y, jy);
12720     max_x = MAX(max_x, jx);
12721     max_y = MAX(max_y, jy);
12722   }
12723
12724   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12725 }
12726
12727 static boolean AllPlayersInVisibleScreen(void)
12728 {
12729   int i;
12730
12731   for (i = 0; i < MAX_PLAYERS; i++)
12732   {
12733     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12734
12735     if (!stored_player[i].active)
12736       continue;
12737
12738     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12739       return FALSE;
12740   }
12741
12742   return TRUE;
12743 }
12744
12745 void ScrollLevel(int dx, int dy)
12746 {
12747   int scroll_offset = 2 * TILEX_VAR;
12748   int x, y;
12749
12750   BlitBitmap(drawto_field, drawto_field,
12751              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12752              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12753              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12754              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12755              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12756              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12757
12758   if (dx != 0)
12759   {
12760     x = (dx == 1 ? BX1 : BX2);
12761     for (y = BY1; y <= BY2; y++)
12762       DrawScreenField(x, y);
12763   }
12764
12765   if (dy != 0)
12766   {
12767     y = (dy == 1 ? BY1 : BY2);
12768     for (x = BX1; x <= BX2; x++)
12769       DrawScreenField(x, y);
12770   }
12771
12772   redraw_mask |= REDRAW_FIELD;
12773 }
12774
12775 static boolean canFallDown(struct PlayerInfo *player)
12776 {
12777   int jx = player->jx, jy = player->jy;
12778
12779   return (IN_LEV_FIELD(jx, jy + 1) &&
12780           (IS_FREE(jx, jy + 1) ||
12781            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12782           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12783           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12784 }
12785
12786 static boolean canPassField(int x, int y, int move_dir)
12787 {
12788   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12789   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12790   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12791   int nextx = x + dx;
12792   int nexty = y + dy;
12793   int element = Tile[x][y];
12794
12795   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12796           !CAN_MOVE(element) &&
12797           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12798           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12799           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12800 }
12801
12802 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12803 {
12804   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12805   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12806   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12807   int newx = x + dx;
12808   int newy = y + dy;
12809
12810   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12811           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12812           (IS_DIGGABLE(Tile[newx][newy]) ||
12813            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12814            canPassField(newx, newy, move_dir)));
12815 }
12816
12817 static void CheckGravityMovement(struct PlayerInfo *player)
12818 {
12819   if (player->gravity && !player->programmed_action)
12820   {
12821     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12822     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12823     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12824     int jx = player->jx, jy = player->jy;
12825     boolean player_is_moving_to_valid_field =
12826       (!player_is_snapping &&
12827        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12828         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12829     boolean player_can_fall_down = canFallDown(player);
12830
12831     if (player_can_fall_down &&
12832         !player_is_moving_to_valid_field)
12833       player->programmed_action = MV_DOWN;
12834   }
12835 }
12836
12837 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12838 {
12839   return CheckGravityMovement(player);
12840
12841   if (player->gravity && !player->programmed_action)
12842   {
12843     int jx = player->jx, jy = player->jy;
12844     boolean field_under_player_is_free =
12845       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12846     boolean player_is_standing_on_valid_field =
12847       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12848        (IS_WALKABLE(Tile[jx][jy]) &&
12849         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12850
12851     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12852       player->programmed_action = MV_DOWN;
12853   }
12854 }
12855
12856 /*
12857   MovePlayerOneStep()
12858   -----------------------------------------------------------------------------
12859   dx, dy:               direction (non-diagonal) to try to move the player to
12860   real_dx, real_dy:     direction as read from input device (can be diagonal)
12861 */
12862
12863 boolean MovePlayerOneStep(struct PlayerInfo *player,
12864                           int dx, int dy, int real_dx, int real_dy)
12865 {
12866   int jx = player->jx, jy = player->jy;
12867   int new_jx = jx + dx, new_jy = jy + dy;
12868   int can_move;
12869   boolean player_can_move = !player->cannot_move;
12870
12871   if (!player->active || (!dx && !dy))
12872     return MP_NO_ACTION;
12873
12874   player->MovDir = (dx < 0 ? MV_LEFT :
12875                     dx > 0 ? MV_RIGHT :
12876                     dy < 0 ? MV_UP :
12877                     dy > 0 ? MV_DOWN :  MV_NONE);
12878
12879   if (!IN_LEV_FIELD(new_jx, new_jy))
12880     return MP_NO_ACTION;
12881
12882   if (!player_can_move)
12883   {
12884     if (player->MovPos == 0)
12885     {
12886       player->is_moving = FALSE;
12887       player->is_digging = FALSE;
12888       player->is_collecting = FALSE;
12889       player->is_snapping = FALSE;
12890       player->is_pushing = FALSE;
12891     }
12892   }
12893
12894   if (!network.enabled && game.centered_player_nr == -1 &&
12895       !AllPlayersInSight(player, new_jx, new_jy))
12896     return MP_NO_ACTION;
12897
12898   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12899   if (can_move != MP_MOVING)
12900     return can_move;
12901
12902   // check if DigField() has caused relocation of the player
12903   if (player->jx != jx || player->jy != jy)
12904     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12905
12906   StorePlayer[jx][jy] = 0;
12907   player->last_jx = jx;
12908   player->last_jy = jy;
12909   player->jx = new_jx;
12910   player->jy = new_jy;
12911   StorePlayer[new_jx][new_jy] = player->element_nr;
12912
12913   if (player->move_delay_value_next != -1)
12914   {
12915     player->move_delay_value = player->move_delay_value_next;
12916     player->move_delay_value_next = -1;
12917   }
12918
12919   player->MovPos =
12920     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12921
12922   player->step_counter++;
12923
12924   PlayerVisit[jx][jy] = FrameCounter;
12925
12926   player->is_moving = TRUE;
12927
12928 #if 1
12929   // should better be called in MovePlayer(), but this breaks some tapes
12930   ScrollPlayer(player, SCROLL_INIT);
12931 #endif
12932
12933   return MP_MOVING;
12934 }
12935
12936 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12937 {
12938   int jx = player->jx, jy = player->jy;
12939   int old_jx = jx, old_jy = jy;
12940   int moved = MP_NO_ACTION;
12941
12942   if (!player->active)
12943     return FALSE;
12944
12945   if (!dx && !dy)
12946   {
12947     if (player->MovPos == 0)
12948     {
12949       player->is_moving = FALSE;
12950       player->is_digging = FALSE;
12951       player->is_collecting = FALSE;
12952       player->is_snapping = FALSE;
12953       player->is_pushing = FALSE;
12954     }
12955
12956     return FALSE;
12957   }
12958
12959   if (player->move_delay > 0)
12960     return FALSE;
12961
12962   player->move_delay = -1;              // set to "uninitialized" value
12963
12964   // store if player is automatically moved to next field
12965   player->is_auto_moving = (player->programmed_action != MV_NONE);
12966
12967   // remove the last programmed player action
12968   player->programmed_action = 0;
12969
12970   if (player->MovPos)
12971   {
12972     // should only happen if pre-1.2 tape recordings are played
12973     // this is only for backward compatibility
12974
12975     int original_move_delay_value = player->move_delay_value;
12976
12977 #if DEBUG
12978     Debug("game:playing:MovePlayer",
12979           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12980           tape.counter);
12981 #endif
12982
12983     // scroll remaining steps with finest movement resolution
12984     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12985
12986     while (player->MovPos)
12987     {
12988       ScrollPlayer(player, SCROLL_GO_ON);
12989       ScrollScreen(NULL, SCROLL_GO_ON);
12990
12991       AdvanceFrameAndPlayerCounters(player->index_nr);
12992
12993       DrawAllPlayers();
12994       BackToFront_WithFrameDelay(0);
12995     }
12996
12997     player->move_delay_value = original_move_delay_value;
12998   }
12999
13000   player->is_active = FALSE;
13001
13002   if (player->last_move_dir & MV_HORIZONTAL)
13003   {
13004     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13005       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13006   }
13007   else
13008   {
13009     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13010       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13011   }
13012
13013   if (!moved && !player->is_active)
13014   {
13015     player->is_moving = FALSE;
13016     player->is_digging = FALSE;
13017     player->is_collecting = FALSE;
13018     player->is_snapping = FALSE;
13019     player->is_pushing = FALSE;
13020   }
13021
13022   jx = player->jx;
13023   jy = player->jy;
13024
13025   if (moved & MP_MOVING && !ScreenMovPos &&
13026       (player->index_nr == game.centered_player_nr ||
13027        game.centered_player_nr == -1))
13028   {
13029     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13030
13031     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13032     {
13033       // actual player has left the screen -- scroll in that direction
13034       if (jx != old_jx)         // player has moved horizontally
13035         scroll_x += (jx - old_jx);
13036       else                      // player has moved vertically
13037         scroll_y += (jy - old_jy);
13038     }
13039     else
13040     {
13041       int offset_raw = game.scroll_delay_value;
13042
13043       if (jx != old_jx)         // player has moved horizontally
13044       {
13045         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13046         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13047         int new_scroll_x = jx - MIDPOSX + offset_x;
13048
13049         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13050             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13051           scroll_x = new_scroll_x;
13052
13053         // don't scroll over playfield boundaries
13054         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13055
13056         // don't scroll more than one field at a time
13057         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13058
13059         // don't scroll against the player's moving direction
13060         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13061             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13062           scroll_x = old_scroll_x;
13063       }
13064       else                      // player has moved vertically
13065       {
13066         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13067         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13068         int new_scroll_y = jy - MIDPOSY + offset_y;
13069
13070         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13071             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13072           scroll_y = new_scroll_y;
13073
13074         // don't scroll over playfield boundaries
13075         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13076
13077         // don't scroll more than one field at a time
13078         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13079
13080         // don't scroll against the player's moving direction
13081         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13082             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13083           scroll_y = old_scroll_y;
13084       }
13085     }
13086
13087     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13088     {
13089       if (!network.enabled && game.centered_player_nr == -1 &&
13090           !AllPlayersInVisibleScreen())
13091       {
13092         scroll_x = old_scroll_x;
13093         scroll_y = old_scroll_y;
13094       }
13095       else
13096       {
13097         ScrollScreen(player, SCROLL_INIT);
13098         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13099       }
13100     }
13101   }
13102
13103   player->StepFrame = 0;
13104
13105   if (moved & MP_MOVING)
13106   {
13107     if (old_jx != jx && old_jy == jy)
13108       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13109     else if (old_jx == jx && old_jy != jy)
13110       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13111
13112     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13113
13114     player->last_move_dir = player->MovDir;
13115     player->is_moving = TRUE;
13116     player->is_snapping = FALSE;
13117     player->is_switching = FALSE;
13118     player->is_dropping = FALSE;
13119     player->is_dropping_pressed = FALSE;
13120     player->drop_pressed_delay = 0;
13121
13122 #if 0
13123     // should better be called here than above, but this breaks some tapes
13124     ScrollPlayer(player, SCROLL_INIT);
13125 #endif
13126   }
13127   else
13128   {
13129     CheckGravityMovementWhenNotMoving(player);
13130
13131     player->is_moving = FALSE;
13132
13133     /* at this point, the player is allowed to move, but cannot move right now
13134        (e.g. because of something blocking the way) -- ensure that the player
13135        is also allowed to move in the next frame (in old versions before 3.1.1,
13136        the player was forced to wait again for eight frames before next try) */
13137
13138     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13139       player->move_delay = 0;   // allow direct movement in the next frame
13140   }
13141
13142   if (player->move_delay == -1)         // not yet initialized by DigField()
13143     player->move_delay = player->move_delay_value;
13144
13145   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13146   {
13147     TestIfPlayerTouchesBadThing(jx, jy);
13148     TestIfPlayerTouchesCustomElement(jx, jy);
13149   }
13150
13151   if (!player->active)
13152     RemovePlayer(player);
13153
13154   return moved;
13155 }
13156
13157 void ScrollPlayer(struct PlayerInfo *player, int mode)
13158 {
13159   int jx = player->jx, jy = player->jy;
13160   int last_jx = player->last_jx, last_jy = player->last_jy;
13161   int move_stepsize = TILEX / player->move_delay_value;
13162
13163   if (!player->active)
13164     return;
13165
13166   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13167     return;
13168
13169   if (mode == SCROLL_INIT)
13170   {
13171     player->actual_frame_counter = FrameCounter;
13172     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13173
13174     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13175         Tile[last_jx][last_jy] == EL_EMPTY)
13176     {
13177       int last_field_block_delay = 0;   // start with no blocking at all
13178       int block_delay_adjustment = player->block_delay_adjustment;
13179
13180       // if player blocks last field, add delay for exactly one move
13181       if (player->block_last_field)
13182       {
13183         last_field_block_delay += player->move_delay_value;
13184
13185         // when blocking enabled, prevent moving up despite gravity
13186         if (player->gravity && player->MovDir == MV_UP)
13187           block_delay_adjustment = -1;
13188       }
13189
13190       // add block delay adjustment (also possible when not blocking)
13191       last_field_block_delay += block_delay_adjustment;
13192
13193       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13194       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13195     }
13196
13197     if (player->MovPos != 0)    // player has not yet reached destination
13198       return;
13199   }
13200   else if (!FrameReached(&player->actual_frame_counter, 1))
13201     return;
13202
13203   if (player->MovPos != 0)
13204   {
13205     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13206     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13207
13208     // before DrawPlayer() to draw correct player graphic for this case
13209     if (player->MovPos == 0)
13210       CheckGravityMovement(player);
13211   }
13212
13213   if (player->MovPos == 0)      // player reached destination field
13214   {
13215     if (player->move_delay_reset_counter > 0)
13216     {
13217       player->move_delay_reset_counter--;
13218
13219       if (player->move_delay_reset_counter == 0)
13220       {
13221         // continue with normal speed after quickly moving through gate
13222         HALVE_PLAYER_SPEED(player);
13223
13224         // be able to make the next move without delay
13225         player->move_delay = 0;
13226       }
13227     }
13228
13229     player->last_jx = jx;
13230     player->last_jy = jy;
13231
13232     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13233         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13234         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13235         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13236         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13237         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13238         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13239         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13240     {
13241       ExitPlayer(player);
13242
13243       if (game.players_still_needed == 0 &&
13244           (game.friends_still_needed == 0 ||
13245            IS_SP_ELEMENT(Tile[jx][jy])))
13246         LevelSolved();
13247     }
13248
13249     // this breaks one level: "machine", level 000
13250     {
13251       int move_direction = player->MovDir;
13252       int enter_side = MV_DIR_OPPOSITE(move_direction);
13253       int leave_side = move_direction;
13254       int old_jx = last_jx;
13255       int old_jy = last_jy;
13256       int old_element = Tile[old_jx][old_jy];
13257       int new_element = Tile[jx][jy];
13258
13259       if (IS_CUSTOM_ELEMENT(old_element))
13260         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13261                                    CE_LEFT_BY_PLAYER,
13262                                    player->index_bit, leave_side);
13263
13264       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13265                                           CE_PLAYER_LEAVES_X,
13266                                           player->index_bit, leave_side);
13267
13268       if (IS_CUSTOM_ELEMENT(new_element))
13269         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13270                                    player->index_bit, enter_side);
13271
13272       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13273                                           CE_PLAYER_ENTERS_X,
13274                                           player->index_bit, enter_side);
13275
13276       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13277                                         CE_MOVE_OF_X, move_direction);
13278     }
13279
13280     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13281     {
13282       TestIfPlayerTouchesBadThing(jx, jy);
13283       TestIfPlayerTouchesCustomElement(jx, jy);
13284
13285       /* needed because pushed element has not yet reached its destination,
13286          so it would trigger a change event at its previous field location */
13287       if (!player->is_pushing)
13288         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13289
13290       if (level.finish_dig_collect &&
13291           (player->is_digging || player->is_collecting))
13292       {
13293         int last_element = player->last_removed_element;
13294         int move_direction = player->MovDir;
13295         int enter_side = MV_DIR_OPPOSITE(move_direction);
13296         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13297                             CE_PLAYER_COLLECTS_X);
13298
13299         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13300                                             player->index_bit, enter_side);
13301
13302         player->last_removed_element = EL_UNDEFINED;
13303       }
13304
13305       if (!player->active)
13306         RemovePlayer(player);
13307     }
13308
13309     if (level.use_step_counter)
13310       CheckLevelTime_StepCounter();
13311
13312     if (tape.single_step && tape.recording && !tape.pausing &&
13313         !player->programmed_action)
13314       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13315
13316     if (!player->programmed_action)
13317       CheckSaveEngineSnapshot(player);
13318   }
13319 }
13320
13321 void ScrollScreen(struct PlayerInfo *player, int mode)
13322 {
13323   static unsigned int screen_frame_counter = 0;
13324
13325   if (mode == SCROLL_INIT)
13326   {
13327     // set scrolling step size according to actual player's moving speed
13328     ScrollStepSize = TILEX / player->move_delay_value;
13329
13330     screen_frame_counter = FrameCounter;
13331     ScreenMovDir = player->MovDir;
13332     ScreenMovPos = player->MovPos;
13333     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13334     return;
13335   }
13336   else if (!FrameReached(&screen_frame_counter, 1))
13337     return;
13338
13339   if (ScreenMovPos)
13340   {
13341     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13342     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13343     redraw_mask |= REDRAW_FIELD;
13344   }
13345   else
13346     ScreenMovDir = MV_NONE;
13347 }
13348
13349 void CheckNextToConditions(int x, int y)
13350 {
13351   int element = Tile[x][y];
13352
13353   if (IS_PLAYER(x, y))
13354     TestIfPlayerNextToCustomElement(x, y);
13355
13356   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13357       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13358     TestIfElementNextToCustomElement(x, y);
13359 }
13360
13361 void TestIfPlayerNextToCustomElement(int x, int y)
13362 {
13363   static int xy[4][2] =
13364   {
13365     { 0, -1 },
13366     { -1, 0 },
13367     { +1, 0 },
13368     { 0, +1 }
13369   };
13370   static int trigger_sides[4][2] =
13371   {
13372     // center side       border side
13373     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13374     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13375     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13376     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13377   };
13378   int i;
13379
13380   if (!IS_PLAYER(x, y))
13381     return;
13382
13383   struct PlayerInfo *player = PLAYERINFO(x, y);
13384
13385   if (player->is_moving)
13386     return;
13387
13388   for (i = 0; i < NUM_DIRECTIONS; i++)
13389   {
13390     int xx = x + xy[i][0];
13391     int yy = y + xy[i][1];
13392     int border_side = trigger_sides[i][1];
13393     int border_element;
13394
13395     if (!IN_LEV_FIELD(xx, yy))
13396       continue;
13397
13398     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13399       continue;         // center and border element not connected
13400
13401     border_element = Tile[xx][yy];
13402
13403     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13404                                player->index_bit, border_side);
13405     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13406                                         CE_PLAYER_NEXT_TO_X,
13407                                         player->index_bit, border_side);
13408
13409     /* use player element that is initially defined in the level playfield,
13410        not the player element that corresponds to the runtime player number
13411        (example: a level that contains EL_PLAYER_3 as the only player would
13412        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13413
13414     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13415                              CE_NEXT_TO_X, border_side);
13416   }
13417 }
13418
13419 void TestIfPlayerTouchesCustomElement(int x, int y)
13420 {
13421   static int xy[4][2] =
13422   {
13423     { 0, -1 },
13424     { -1, 0 },
13425     { +1, 0 },
13426     { 0, +1 }
13427   };
13428   static int trigger_sides[4][2] =
13429   {
13430     // center side       border side
13431     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13432     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13433     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13434     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13435   };
13436   static int touch_dir[4] =
13437   {
13438     MV_LEFT | MV_RIGHT,
13439     MV_UP   | MV_DOWN,
13440     MV_UP   | MV_DOWN,
13441     MV_LEFT | MV_RIGHT
13442   };
13443   int center_element = Tile[x][y];      // should always be non-moving!
13444   int i;
13445
13446   for (i = 0; i < NUM_DIRECTIONS; i++)
13447   {
13448     int xx = x + xy[i][0];
13449     int yy = y + xy[i][1];
13450     int center_side = trigger_sides[i][0];
13451     int border_side = trigger_sides[i][1];
13452     int border_element;
13453
13454     if (!IN_LEV_FIELD(xx, yy))
13455       continue;
13456
13457     if (IS_PLAYER(x, y))                // player found at center element
13458     {
13459       struct PlayerInfo *player = PLAYERINFO(x, y);
13460
13461       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13462         border_element = Tile[xx][yy];          // may be moving!
13463       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13464         border_element = Tile[xx][yy];
13465       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13466         border_element = MovingOrBlocked2Element(xx, yy);
13467       else
13468         continue;               // center and border element do not touch
13469
13470       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13471                                  player->index_bit, border_side);
13472       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13473                                           CE_PLAYER_TOUCHES_X,
13474                                           player->index_bit, border_side);
13475
13476       {
13477         /* use player element that is initially defined in the level playfield,
13478            not the player element that corresponds to the runtime player number
13479            (example: a level that contains EL_PLAYER_3 as the only player would
13480            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13481         int player_element = PLAYERINFO(x, y)->initial_element;
13482
13483         CheckElementChangeBySide(xx, yy, border_element, player_element,
13484                                  CE_TOUCHING_X, border_side);
13485       }
13486     }
13487     else if (IS_PLAYER(xx, yy))         // player found at border element
13488     {
13489       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13490
13491       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13492       {
13493         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13494           continue;             // center and border element do not touch
13495       }
13496
13497       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13498                                  player->index_bit, center_side);
13499       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13500                                           CE_PLAYER_TOUCHES_X,
13501                                           player->index_bit, center_side);
13502
13503       {
13504         /* use player element that is initially defined in the level playfield,
13505            not the player element that corresponds to the runtime player number
13506            (example: a level that contains EL_PLAYER_3 as the only player would
13507            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13508         int player_element = PLAYERINFO(xx, yy)->initial_element;
13509
13510         CheckElementChangeBySide(x, y, center_element, player_element,
13511                                  CE_TOUCHING_X, center_side);
13512       }
13513
13514       break;
13515     }
13516   }
13517 }
13518
13519 void TestIfElementNextToCustomElement(int x, int y)
13520 {
13521   static int xy[4][2] =
13522   {
13523     { 0, -1 },
13524     { -1, 0 },
13525     { +1, 0 },
13526     { 0, +1 }
13527   };
13528   static int trigger_sides[4][2] =
13529   {
13530     // center side      border side
13531     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13532     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13533     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13534     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13535   };
13536   int center_element = Tile[x][y];      // should always be non-moving!
13537   int i;
13538
13539   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13540     return;
13541
13542   for (i = 0; i < NUM_DIRECTIONS; i++)
13543   {
13544     int xx = x + xy[i][0];
13545     int yy = y + xy[i][1];
13546     int border_side = trigger_sides[i][1];
13547     int border_element;
13548
13549     if (!IN_LEV_FIELD(xx, yy))
13550       continue;
13551
13552     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13553       continue;                 // center and border element not connected
13554
13555     border_element = Tile[xx][yy];
13556
13557     // check for change of center element (but change it only once)
13558     if (CheckElementChangeBySide(x, y, center_element, border_element,
13559                                  CE_NEXT_TO_X, border_side))
13560       break;
13561   }
13562 }
13563
13564 void TestIfElementTouchesCustomElement(int x, int y)
13565 {
13566   static int xy[4][2] =
13567   {
13568     { 0, -1 },
13569     { -1, 0 },
13570     { +1, 0 },
13571     { 0, +1 }
13572   };
13573   static int trigger_sides[4][2] =
13574   {
13575     // center side      border side
13576     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13577     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13578     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13579     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13580   };
13581   static int touch_dir[4] =
13582   {
13583     MV_LEFT | MV_RIGHT,
13584     MV_UP   | MV_DOWN,
13585     MV_UP   | MV_DOWN,
13586     MV_LEFT | MV_RIGHT
13587   };
13588   boolean change_center_element = FALSE;
13589   int center_element = Tile[x][y];      // should always be non-moving!
13590   int border_element_old[NUM_DIRECTIONS];
13591   int i;
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 border_element;
13598
13599     border_element_old[i] = -1;
13600
13601     if (!IN_LEV_FIELD(xx, yy))
13602       continue;
13603
13604     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13605       border_element = Tile[xx][yy];    // may be moving!
13606     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13607       border_element = Tile[xx][yy];
13608     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13609       border_element = MovingOrBlocked2Element(xx, yy);
13610     else
13611       continue;                 // center and border element do not touch
13612
13613     border_element_old[i] = border_element;
13614   }
13615
13616   for (i = 0; i < NUM_DIRECTIONS; i++)
13617   {
13618     int xx = x + xy[i][0];
13619     int yy = y + xy[i][1];
13620     int center_side = trigger_sides[i][0];
13621     int border_element = border_element_old[i];
13622
13623     if (border_element == -1)
13624       continue;
13625
13626     // check for change of border element
13627     CheckElementChangeBySide(xx, yy, border_element, center_element,
13628                              CE_TOUCHING_X, center_side);
13629
13630     // (center element cannot be player, so we dont have to check this here)
13631   }
13632
13633   for (i = 0; i < NUM_DIRECTIONS; i++)
13634   {
13635     int xx = x + xy[i][0];
13636     int yy = y + xy[i][1];
13637     int border_side = trigger_sides[i][1];
13638     int border_element = border_element_old[i];
13639
13640     if (border_element == -1)
13641       continue;
13642
13643     // check for change of center element (but change it only once)
13644     if (!change_center_element)
13645       change_center_element =
13646         CheckElementChangeBySide(x, y, center_element, border_element,
13647                                  CE_TOUCHING_X, border_side);
13648
13649     if (IS_PLAYER(xx, yy))
13650     {
13651       /* use player element that is initially defined in the level playfield,
13652          not the player element that corresponds to the runtime player number
13653          (example: a level that contains EL_PLAYER_3 as the only player would
13654          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13655       int player_element = PLAYERINFO(xx, yy)->initial_element;
13656
13657       CheckElementChangeBySide(x, y, center_element, player_element,
13658                                CE_TOUCHING_X, border_side);
13659     }
13660   }
13661 }
13662
13663 void TestIfElementHitsCustomElement(int x, int y, int direction)
13664 {
13665   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13666   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13667   int hitx = x + dx, hity = y + dy;
13668   int hitting_element = Tile[x][y];
13669   int touched_element;
13670
13671   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13672     return;
13673
13674   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13675                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13676
13677   if (IN_LEV_FIELD(hitx, hity))
13678   {
13679     int opposite_direction = MV_DIR_OPPOSITE(direction);
13680     int hitting_side = direction;
13681     int touched_side = opposite_direction;
13682     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13683                           MovDir[hitx][hity] != direction ||
13684                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13685
13686     object_hit = TRUE;
13687
13688     if (object_hit)
13689     {
13690       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13691                                CE_HITTING_X, touched_side);
13692
13693       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13694                                CE_HIT_BY_X, hitting_side);
13695
13696       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13697                                CE_HIT_BY_SOMETHING, opposite_direction);
13698
13699       if (IS_PLAYER(hitx, hity))
13700       {
13701         /* use player element that is initially defined in the level playfield,
13702            not the player element that corresponds to the runtime player number
13703            (example: a level that contains EL_PLAYER_3 as the only player would
13704            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13705         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13706
13707         CheckElementChangeBySide(x, y, hitting_element, player_element,
13708                                  CE_HITTING_X, touched_side);
13709       }
13710     }
13711   }
13712
13713   // "hitting something" is also true when hitting the playfield border
13714   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13715                            CE_HITTING_SOMETHING, direction);
13716 }
13717
13718 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13719 {
13720   int i, kill_x = -1, kill_y = -1;
13721
13722   int bad_element = -1;
13723   static int test_xy[4][2] =
13724   {
13725     { 0, -1 },
13726     { -1, 0 },
13727     { +1, 0 },
13728     { 0, +1 }
13729   };
13730   static int test_dir[4] =
13731   {
13732     MV_UP,
13733     MV_LEFT,
13734     MV_RIGHT,
13735     MV_DOWN
13736   };
13737
13738   for (i = 0; i < NUM_DIRECTIONS; i++)
13739   {
13740     int test_x, test_y, test_move_dir, test_element;
13741
13742     test_x = good_x + test_xy[i][0];
13743     test_y = good_y + test_xy[i][1];
13744
13745     if (!IN_LEV_FIELD(test_x, test_y))
13746       continue;
13747
13748     test_move_dir =
13749       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13750
13751     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13752
13753     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13754        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13755     */
13756     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13757         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13758     {
13759       kill_x = test_x;
13760       kill_y = test_y;
13761       bad_element = test_element;
13762
13763       break;
13764     }
13765   }
13766
13767   if (kill_x != -1 || kill_y != -1)
13768   {
13769     if (IS_PLAYER(good_x, good_y))
13770     {
13771       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13772
13773       if (player->shield_deadly_time_left > 0 &&
13774           !IS_INDESTRUCTIBLE(bad_element))
13775         Bang(kill_x, kill_y);
13776       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13777         KillPlayer(player);
13778     }
13779     else
13780       Bang(good_x, good_y);
13781   }
13782 }
13783
13784 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13785 {
13786   int i, kill_x = -1, kill_y = -1;
13787   int bad_element = Tile[bad_x][bad_y];
13788   static int test_xy[4][2] =
13789   {
13790     { 0, -1 },
13791     { -1, 0 },
13792     { +1, 0 },
13793     { 0, +1 }
13794   };
13795   static int touch_dir[4] =
13796   {
13797     MV_LEFT | MV_RIGHT,
13798     MV_UP   | MV_DOWN,
13799     MV_UP   | MV_DOWN,
13800     MV_LEFT | MV_RIGHT
13801   };
13802   static int test_dir[4] =
13803   {
13804     MV_UP,
13805     MV_LEFT,
13806     MV_RIGHT,
13807     MV_DOWN
13808   };
13809
13810   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13811     return;
13812
13813   for (i = 0; i < NUM_DIRECTIONS; i++)
13814   {
13815     int test_x, test_y, test_move_dir, test_element;
13816
13817     test_x = bad_x + test_xy[i][0];
13818     test_y = bad_y + test_xy[i][1];
13819
13820     if (!IN_LEV_FIELD(test_x, test_y))
13821       continue;
13822
13823     test_move_dir =
13824       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13825
13826     test_element = Tile[test_x][test_y];
13827
13828     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13829        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13830     */
13831     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13832         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13833     {
13834       // good thing is player or penguin that does not move away
13835       if (IS_PLAYER(test_x, test_y))
13836       {
13837         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13838
13839         if (bad_element == EL_ROBOT && player->is_moving)
13840           continue;     // robot does not kill player if he is moving
13841
13842         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13843         {
13844           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13845             continue;           // center and border element do not touch
13846         }
13847
13848         kill_x = test_x;
13849         kill_y = test_y;
13850
13851         break;
13852       }
13853       else if (test_element == EL_PENGUIN)
13854       {
13855         kill_x = test_x;
13856         kill_y = test_y;
13857
13858         break;
13859       }
13860     }
13861   }
13862
13863   if (kill_x != -1 || kill_y != -1)
13864   {
13865     if (IS_PLAYER(kill_x, kill_y))
13866     {
13867       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13868
13869       if (player->shield_deadly_time_left > 0 &&
13870           !IS_INDESTRUCTIBLE(bad_element))
13871         Bang(bad_x, bad_y);
13872       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13873         KillPlayer(player);
13874     }
13875     else
13876       Bang(kill_x, kill_y);
13877   }
13878 }
13879
13880 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13881 {
13882   int bad_element = Tile[bad_x][bad_y];
13883   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13884   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13885   int test_x = bad_x + dx, test_y = bad_y + dy;
13886   int test_move_dir, test_element;
13887   int kill_x = -1, kill_y = -1;
13888
13889   if (!IN_LEV_FIELD(test_x, test_y))
13890     return;
13891
13892   test_move_dir =
13893     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13894
13895   test_element = Tile[test_x][test_y];
13896
13897   if (test_move_dir != bad_move_dir)
13898   {
13899     // good thing can be player or penguin that does not move away
13900     if (IS_PLAYER(test_x, test_y))
13901     {
13902       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13903
13904       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13905          player as being hit when he is moving towards the bad thing, because
13906          the "get hit by" condition would be lost after the player stops) */
13907       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13908         return;         // player moves away from bad thing
13909
13910       kill_x = test_x;
13911       kill_y = test_y;
13912     }
13913     else if (test_element == EL_PENGUIN)
13914     {
13915       kill_x = test_x;
13916       kill_y = test_y;
13917     }
13918   }
13919
13920   if (kill_x != -1 || kill_y != -1)
13921   {
13922     if (IS_PLAYER(kill_x, kill_y))
13923     {
13924       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13925
13926       if (player->shield_deadly_time_left > 0 &&
13927           !IS_INDESTRUCTIBLE(bad_element))
13928         Bang(bad_x, bad_y);
13929       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13930         KillPlayer(player);
13931     }
13932     else
13933       Bang(kill_x, kill_y);
13934   }
13935 }
13936
13937 void TestIfPlayerTouchesBadThing(int x, int y)
13938 {
13939   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13940 }
13941
13942 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13943 {
13944   TestIfGoodThingHitsBadThing(x, y, move_dir);
13945 }
13946
13947 void TestIfBadThingTouchesPlayer(int x, int y)
13948 {
13949   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13950 }
13951
13952 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13953 {
13954   TestIfBadThingHitsGoodThing(x, y, move_dir);
13955 }
13956
13957 void TestIfFriendTouchesBadThing(int x, int y)
13958 {
13959   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13960 }
13961
13962 void TestIfBadThingTouchesFriend(int x, int y)
13963 {
13964   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13965 }
13966
13967 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13968 {
13969   int i, kill_x = bad_x, kill_y = bad_y;
13970   static int xy[4][2] =
13971   {
13972     { 0, -1 },
13973     { -1, 0 },
13974     { +1, 0 },
13975     { 0, +1 }
13976   };
13977
13978   for (i = 0; i < NUM_DIRECTIONS; i++)
13979   {
13980     int x, y, element;
13981
13982     x = bad_x + xy[i][0];
13983     y = bad_y + xy[i][1];
13984     if (!IN_LEV_FIELD(x, y))
13985       continue;
13986
13987     element = Tile[x][y];
13988     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13989         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13990     {
13991       kill_x = x;
13992       kill_y = y;
13993       break;
13994     }
13995   }
13996
13997   if (kill_x != bad_x || kill_y != bad_y)
13998     Bang(bad_x, bad_y);
13999 }
14000
14001 void KillPlayer(struct PlayerInfo *player)
14002 {
14003   int jx = player->jx, jy = player->jy;
14004
14005   if (!player->active)
14006     return;
14007
14008 #if 0
14009   Debug("game:playing:KillPlayer",
14010         "0: killed == %d, active == %d, reanimated == %d",
14011         player->killed, player->active, player->reanimated);
14012 #endif
14013
14014   /* the following code was introduced to prevent an infinite loop when calling
14015      -> Bang()
14016      -> CheckTriggeredElementChangeExt()
14017      -> ExecuteCustomElementAction()
14018      -> KillPlayer()
14019      -> (infinitely repeating the above sequence of function calls)
14020      which occurs when killing the player while having a CE with the setting
14021      "kill player X when explosion of <player X>"; the solution using a new
14022      field "player->killed" was chosen for backwards compatibility, although
14023      clever use of the fields "player->active" etc. would probably also work */
14024 #if 1
14025   if (player->killed)
14026     return;
14027 #endif
14028
14029   player->killed = TRUE;
14030
14031   // remove accessible field at the player's position
14032   Tile[jx][jy] = EL_EMPTY;
14033
14034   // deactivate shield (else Bang()/Explode() would not work right)
14035   player->shield_normal_time_left = 0;
14036   player->shield_deadly_time_left = 0;
14037
14038 #if 0
14039   Debug("game:playing:KillPlayer",
14040         "1: killed == %d, active == %d, reanimated == %d",
14041         player->killed, player->active, player->reanimated);
14042 #endif
14043
14044   Bang(jx, jy);
14045
14046 #if 0
14047   Debug("game:playing:KillPlayer",
14048         "2: killed == %d, active == %d, reanimated == %d",
14049         player->killed, player->active, player->reanimated);
14050 #endif
14051
14052   if (player->reanimated)       // killed player may have been reanimated
14053     player->killed = player->reanimated = FALSE;
14054   else
14055     BuryPlayer(player);
14056 }
14057
14058 static void KillPlayerUnlessEnemyProtected(int x, int y)
14059 {
14060   if (!PLAYER_ENEMY_PROTECTED(x, y))
14061     KillPlayer(PLAYERINFO(x, y));
14062 }
14063
14064 static void KillPlayerUnlessExplosionProtected(int x, int y)
14065 {
14066   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14067     KillPlayer(PLAYERINFO(x, y));
14068 }
14069
14070 void BuryPlayer(struct PlayerInfo *player)
14071 {
14072   int jx = player->jx, jy = player->jy;
14073
14074   if (!player->active)
14075     return;
14076
14077   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14078   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14079
14080   RemovePlayer(player);
14081
14082   player->buried = TRUE;
14083
14084   if (game.all_players_gone)
14085     game.GameOver = TRUE;
14086 }
14087
14088 void RemovePlayer(struct PlayerInfo *player)
14089 {
14090   int jx = player->jx, jy = player->jy;
14091   int i, found = FALSE;
14092
14093   player->present = FALSE;
14094   player->active = FALSE;
14095
14096   // required for some CE actions (even if the player is not active anymore)
14097   player->MovPos = 0;
14098
14099   if (!ExplodeField[jx][jy])
14100     StorePlayer[jx][jy] = 0;
14101
14102   if (player->is_moving)
14103     TEST_DrawLevelField(player->last_jx, player->last_jy);
14104
14105   for (i = 0; i < MAX_PLAYERS; i++)
14106     if (stored_player[i].active)
14107       found = TRUE;
14108
14109   if (!found)
14110   {
14111     game.all_players_gone = TRUE;
14112     game.GameOver = TRUE;
14113   }
14114
14115   game.exit_x = game.robot_wheel_x = jx;
14116   game.exit_y = game.robot_wheel_y = jy;
14117 }
14118
14119 void ExitPlayer(struct PlayerInfo *player)
14120 {
14121   DrawPlayer(player);   // needed here only to cleanup last field
14122   RemovePlayer(player);
14123
14124   if (game.players_still_needed > 0)
14125     game.players_still_needed--;
14126 }
14127
14128 static void SetFieldForSnapping(int x, int y, int element, int direction,
14129                                 int player_index_bit)
14130 {
14131   struct ElementInfo *ei = &element_info[element];
14132   int direction_bit = MV_DIR_TO_BIT(direction);
14133   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14134   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14135                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14136
14137   Tile[x][y] = EL_ELEMENT_SNAPPING;
14138   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14139   MovDir[x][y] = direction;
14140   Store[x][y] = element;
14141   Store2[x][y] = player_index_bit;
14142
14143   ResetGfxAnimation(x, y);
14144
14145   GfxElement[x][y] = element;
14146   GfxAction[x][y] = action;
14147   GfxDir[x][y] = direction;
14148   GfxFrame[x][y] = -1;
14149 }
14150
14151 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14152                                    int player_index_bit)
14153 {
14154   TestIfElementTouchesCustomElement(x, y);      // for empty space
14155
14156   if (level.finish_dig_collect)
14157   {
14158     int dig_side = MV_DIR_OPPOSITE(direction);
14159     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14160                         CE_PLAYER_COLLECTS_X);
14161
14162     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14163                                         player_index_bit, dig_side);
14164     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14165                                         player_index_bit, dig_side);
14166   }
14167 }
14168
14169 /*
14170   =============================================================================
14171   checkDiagonalPushing()
14172   -----------------------------------------------------------------------------
14173   check if diagonal input device direction results in pushing of object
14174   (by checking if the alternative direction is walkable, diggable, ...)
14175   =============================================================================
14176 */
14177
14178 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14179                                     int x, int y, int real_dx, int real_dy)
14180 {
14181   int jx, jy, dx, dy, xx, yy;
14182
14183   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14184     return TRUE;
14185
14186   // diagonal direction: check alternative direction
14187   jx = player->jx;
14188   jy = player->jy;
14189   dx = x - jx;
14190   dy = y - jy;
14191   xx = jx + (dx == 0 ? real_dx : 0);
14192   yy = jy + (dy == 0 ? real_dy : 0);
14193
14194   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14195 }
14196
14197 /*
14198   =============================================================================
14199   DigField()
14200   -----------------------------------------------------------------------------
14201   x, y:                 field next to player (non-diagonal) to try to dig to
14202   real_dx, real_dy:     direction as read from input device (can be diagonal)
14203   =============================================================================
14204 */
14205
14206 static int DigField(struct PlayerInfo *player,
14207                     int oldx, int oldy, int x, int y,
14208                     int real_dx, int real_dy, int mode)
14209 {
14210   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14211   boolean player_was_pushing = player->is_pushing;
14212   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14213   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14214   int jx = oldx, jy = oldy;
14215   int dx = x - jx, dy = y - jy;
14216   int nextx = x + dx, nexty = y + dy;
14217   int move_direction = (dx == -1 ? MV_LEFT  :
14218                         dx == +1 ? MV_RIGHT :
14219                         dy == -1 ? MV_UP    :
14220                         dy == +1 ? MV_DOWN  : MV_NONE);
14221   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14222   int dig_side = MV_DIR_OPPOSITE(move_direction);
14223   int old_element = Tile[jx][jy];
14224   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14225   int collect_count;
14226
14227   if (is_player)                // function can also be called by EL_PENGUIN
14228   {
14229     if (player->MovPos == 0)
14230     {
14231       player->is_digging = FALSE;
14232       player->is_collecting = FALSE;
14233     }
14234
14235     if (player->MovPos == 0)    // last pushing move finished
14236       player->is_pushing = FALSE;
14237
14238     if (mode == DF_NO_PUSH)     // player just stopped pushing
14239     {
14240       player->is_switching = FALSE;
14241       player->push_delay = -1;
14242
14243       return MP_NO_ACTION;
14244     }
14245   }
14246   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14247     old_element = Back[jx][jy];
14248
14249   // in case of element dropped at player position, check background
14250   else if (Back[jx][jy] != EL_EMPTY &&
14251            game.engine_version >= VERSION_IDENT(2,2,0,0))
14252     old_element = Back[jx][jy];
14253
14254   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14255     return MP_NO_ACTION;        // field has no opening in this direction
14256
14257   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14258     return MP_NO_ACTION;        // field has no opening in this direction
14259
14260   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14261   {
14262     SplashAcid(x, y);
14263
14264     Tile[jx][jy] = player->artwork_element;
14265     InitMovingField(jx, jy, MV_DOWN);
14266     Store[jx][jy] = EL_ACID;
14267     ContinueMoving(jx, jy);
14268     BuryPlayer(player);
14269
14270     return MP_DONT_RUN_INTO;
14271   }
14272
14273   if (player_can_move && DONT_RUN_INTO(element))
14274   {
14275     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14276
14277     return MP_DONT_RUN_INTO;
14278   }
14279
14280   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14281     return MP_NO_ACTION;
14282
14283   collect_count = element_info[element].collect_count_initial;
14284
14285   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14286     return MP_NO_ACTION;
14287
14288   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14289     player_can_move = player_can_move_or_snap;
14290
14291   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14292       game.engine_version >= VERSION_IDENT(2,2,0,0))
14293   {
14294     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14295                                player->index_bit, dig_side);
14296     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14297                                         player->index_bit, dig_side);
14298
14299     if (element == EL_DC_LANDMINE)
14300       Bang(x, y);
14301
14302     if (Tile[x][y] != element)          // field changed by snapping
14303       return MP_ACTION;
14304
14305     return MP_NO_ACTION;
14306   }
14307
14308   if (player->gravity && is_player && !player->is_auto_moving &&
14309       canFallDown(player) && move_direction != MV_DOWN &&
14310       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14311     return MP_NO_ACTION;        // player cannot walk here due to gravity
14312
14313   if (player_can_move &&
14314       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14315   {
14316     int sound_element = SND_ELEMENT(element);
14317     int sound_action = ACTION_WALKING;
14318
14319     if (IS_RND_GATE(element))
14320     {
14321       if (!player->key[RND_GATE_NR(element)])
14322         return MP_NO_ACTION;
14323     }
14324     else if (IS_RND_GATE_GRAY(element))
14325     {
14326       if (!player->key[RND_GATE_GRAY_NR(element)])
14327         return MP_NO_ACTION;
14328     }
14329     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14330     {
14331       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14332         return MP_NO_ACTION;
14333     }
14334     else if (element == EL_EXIT_OPEN ||
14335              element == EL_EM_EXIT_OPEN ||
14336              element == EL_EM_EXIT_OPENING ||
14337              element == EL_STEEL_EXIT_OPEN ||
14338              element == EL_EM_STEEL_EXIT_OPEN ||
14339              element == EL_EM_STEEL_EXIT_OPENING ||
14340              element == EL_SP_EXIT_OPEN ||
14341              element == EL_SP_EXIT_OPENING)
14342     {
14343       sound_action = ACTION_PASSING;    // player is passing exit
14344     }
14345     else if (element == EL_EMPTY)
14346     {
14347       sound_action = ACTION_MOVING;             // nothing to walk on
14348     }
14349
14350     // play sound from background or player, whatever is available
14351     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14352       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14353     else
14354       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14355   }
14356   else if (player_can_move &&
14357            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14358   {
14359     if (!ACCESS_FROM(element, opposite_direction))
14360       return MP_NO_ACTION;      // field not accessible from this direction
14361
14362     if (CAN_MOVE(element))      // only fixed elements can be passed!
14363       return MP_NO_ACTION;
14364
14365     if (IS_EM_GATE(element))
14366     {
14367       if (!player->key[EM_GATE_NR(element)])
14368         return MP_NO_ACTION;
14369     }
14370     else if (IS_EM_GATE_GRAY(element))
14371     {
14372       if (!player->key[EM_GATE_GRAY_NR(element)])
14373         return MP_NO_ACTION;
14374     }
14375     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14376     {
14377       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14378         return MP_NO_ACTION;
14379     }
14380     else if (IS_EMC_GATE(element))
14381     {
14382       if (!player->key[EMC_GATE_NR(element)])
14383         return MP_NO_ACTION;
14384     }
14385     else if (IS_EMC_GATE_GRAY(element))
14386     {
14387       if (!player->key[EMC_GATE_GRAY_NR(element)])
14388         return MP_NO_ACTION;
14389     }
14390     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14391     {
14392       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14393         return MP_NO_ACTION;
14394     }
14395     else if (element == EL_DC_GATE_WHITE ||
14396              element == EL_DC_GATE_WHITE_GRAY ||
14397              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14398     {
14399       if (player->num_white_keys == 0)
14400         return MP_NO_ACTION;
14401
14402       player->num_white_keys--;
14403     }
14404     else if (IS_SP_PORT(element))
14405     {
14406       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14407           element == EL_SP_GRAVITY_PORT_RIGHT ||
14408           element == EL_SP_GRAVITY_PORT_UP ||
14409           element == EL_SP_GRAVITY_PORT_DOWN)
14410         player->gravity = !player->gravity;
14411       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14412                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14413                element == EL_SP_GRAVITY_ON_PORT_UP ||
14414                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14415         player->gravity = TRUE;
14416       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14417                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14418                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14419                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14420         player->gravity = FALSE;
14421     }
14422
14423     // automatically move to the next field with double speed
14424     player->programmed_action = move_direction;
14425
14426     if (player->move_delay_reset_counter == 0)
14427     {
14428       player->move_delay_reset_counter = 2;     // two double speed steps
14429
14430       DOUBLE_PLAYER_SPEED(player);
14431     }
14432
14433     PlayLevelSoundAction(x, y, ACTION_PASSING);
14434   }
14435   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14436   {
14437     RemoveField(x, y);
14438
14439     if (mode != DF_SNAP)
14440     {
14441       GfxElement[x][y] = GFX_ELEMENT(element);
14442       player->is_digging = TRUE;
14443     }
14444
14445     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14446
14447     // use old behaviour for old levels (digging)
14448     if (!level.finish_dig_collect)
14449     {
14450       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14451                                           player->index_bit, dig_side);
14452
14453       // if digging triggered player relocation, finish digging tile
14454       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14455         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14456     }
14457
14458     if (mode == DF_SNAP)
14459     {
14460       if (level.block_snap_field)
14461         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14462       else
14463         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14464
14465       // use old behaviour for old levels (snapping)
14466       if (!level.finish_dig_collect)
14467         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14468                                             player->index_bit, dig_side);
14469     }
14470   }
14471   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14472   {
14473     RemoveField(x, y);
14474
14475     if (is_player && mode != DF_SNAP)
14476     {
14477       GfxElement[x][y] = element;
14478       player->is_collecting = TRUE;
14479     }
14480
14481     if (element == EL_SPEED_PILL)
14482     {
14483       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14484     }
14485     else if (element == EL_EXTRA_TIME && level.time > 0)
14486     {
14487       TimeLeft += level.extra_time;
14488
14489       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14490
14491       DisplayGameControlValues();
14492     }
14493     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14494     {
14495       player->shield_normal_time_left += level.shield_normal_time;
14496       if (element == EL_SHIELD_DEADLY)
14497         player->shield_deadly_time_left += level.shield_deadly_time;
14498     }
14499     else if (element == EL_DYNAMITE ||
14500              element == EL_EM_DYNAMITE ||
14501              element == EL_SP_DISK_RED)
14502     {
14503       if (player->inventory_size < MAX_INVENTORY_SIZE)
14504         player->inventory_element[player->inventory_size++] = element;
14505
14506       DrawGameDoorValues();
14507     }
14508     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14509     {
14510       player->dynabomb_count++;
14511       player->dynabombs_left++;
14512     }
14513     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14514     {
14515       player->dynabomb_size++;
14516     }
14517     else if (element == EL_DYNABOMB_INCREASE_POWER)
14518     {
14519       player->dynabomb_xl = TRUE;
14520     }
14521     else if (IS_KEY(element))
14522     {
14523       player->key[KEY_NR(element)] = TRUE;
14524
14525       DrawGameDoorValues();
14526     }
14527     else if (element == EL_DC_KEY_WHITE)
14528     {
14529       player->num_white_keys++;
14530
14531       // display white keys?
14532       // DrawGameDoorValues();
14533     }
14534     else if (IS_ENVELOPE(element))
14535     {
14536       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14537
14538       if (!wait_for_snapping)
14539         player->show_envelope = element;
14540     }
14541     else if (element == EL_EMC_LENSES)
14542     {
14543       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14544
14545       RedrawAllInvisibleElementsForLenses();
14546     }
14547     else if (element == EL_EMC_MAGNIFIER)
14548     {
14549       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14550
14551       RedrawAllInvisibleElementsForMagnifier();
14552     }
14553     else if (IS_DROPPABLE(element) ||
14554              IS_THROWABLE(element))     // can be collected and dropped
14555     {
14556       int i;
14557
14558       if (collect_count == 0)
14559         player->inventory_infinite_element = element;
14560       else
14561         for (i = 0; i < collect_count; i++)
14562           if (player->inventory_size < MAX_INVENTORY_SIZE)
14563             player->inventory_element[player->inventory_size++] = element;
14564
14565       DrawGameDoorValues();
14566     }
14567     else if (collect_count > 0)
14568     {
14569       game.gems_still_needed -= collect_count;
14570       if (game.gems_still_needed < 0)
14571         game.gems_still_needed = 0;
14572
14573       game.snapshot.collected_item = TRUE;
14574
14575       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14576
14577       DisplayGameControlValues();
14578     }
14579
14580     RaiseScoreElement(element);
14581     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14582
14583     // use old behaviour for old levels (collecting)
14584     if (!level.finish_dig_collect && is_player)
14585     {
14586       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14587                                           player->index_bit, dig_side);
14588
14589       // if collecting triggered player relocation, finish collecting tile
14590       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14591         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14592     }
14593
14594     if (mode == DF_SNAP)
14595     {
14596       if (level.block_snap_field)
14597         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14598       else
14599         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14600
14601       // use old behaviour for old levels (snapping)
14602       if (!level.finish_dig_collect)
14603         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14604                                             player->index_bit, dig_side);
14605     }
14606   }
14607   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14608   {
14609     if (mode == DF_SNAP && element != EL_BD_ROCK)
14610       return MP_NO_ACTION;
14611
14612     if (CAN_FALL(element) && dy)
14613       return MP_NO_ACTION;
14614
14615     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14616         !(element == EL_SPRING && level.use_spring_bug))
14617       return MP_NO_ACTION;
14618
14619     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14620         ((move_direction & MV_VERTICAL &&
14621           ((element_info[element].move_pattern & MV_LEFT &&
14622             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14623            (element_info[element].move_pattern & MV_RIGHT &&
14624             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14625          (move_direction & MV_HORIZONTAL &&
14626           ((element_info[element].move_pattern & MV_UP &&
14627             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14628            (element_info[element].move_pattern & MV_DOWN &&
14629             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14630       return MP_NO_ACTION;
14631
14632     // do not push elements already moving away faster than player
14633     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14634         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14635       return MP_NO_ACTION;
14636
14637     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14638     {
14639       if (player->push_delay_value == -1 || !player_was_pushing)
14640         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14641     }
14642     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14643     {
14644       if (player->push_delay_value == -1)
14645         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14646     }
14647     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14648     {
14649       if (!player->is_pushing)
14650         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14651     }
14652
14653     player->is_pushing = TRUE;
14654     player->is_active = TRUE;
14655
14656     if (!(IN_LEV_FIELD(nextx, nexty) &&
14657           (IS_FREE(nextx, nexty) ||
14658            (IS_SB_ELEMENT(element) &&
14659             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14660            (IS_CUSTOM_ELEMENT(element) &&
14661             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14662       return MP_NO_ACTION;
14663
14664     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14665       return MP_NO_ACTION;
14666
14667     if (player->push_delay == -1)       // new pushing; restart delay
14668       player->push_delay = 0;
14669
14670     if (player->push_delay < player->push_delay_value &&
14671         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14672         element != EL_SPRING && element != EL_BALLOON)
14673     {
14674       // make sure that there is no move delay before next try to push
14675       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14676         player->move_delay = 0;
14677
14678       return MP_NO_ACTION;
14679     }
14680
14681     if (IS_CUSTOM_ELEMENT(element) &&
14682         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14683     {
14684       if (!DigFieldByCE(nextx, nexty, element))
14685         return MP_NO_ACTION;
14686     }
14687
14688     if (IS_SB_ELEMENT(element))
14689     {
14690       boolean sokoban_task_solved = FALSE;
14691
14692       if (element == EL_SOKOBAN_FIELD_FULL)
14693       {
14694         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14695
14696         IncrementSokobanFieldsNeeded();
14697         IncrementSokobanObjectsNeeded();
14698       }
14699
14700       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14701       {
14702         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14703
14704         DecrementSokobanFieldsNeeded();
14705         DecrementSokobanObjectsNeeded();
14706
14707         // sokoban object was pushed from empty field to sokoban field
14708         if (Back[x][y] == EL_EMPTY)
14709           sokoban_task_solved = TRUE;
14710       }
14711
14712       Tile[x][y] = EL_SOKOBAN_OBJECT;
14713
14714       if (Back[x][y] == Back[nextx][nexty])
14715         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14716       else if (Back[x][y] != 0)
14717         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14718                                     ACTION_EMPTYING);
14719       else
14720         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14721                                     ACTION_FILLING);
14722
14723       if (sokoban_task_solved &&
14724           game.sokoban_fields_still_needed == 0 &&
14725           game.sokoban_objects_still_needed == 0 &&
14726           level.auto_exit_sokoban)
14727       {
14728         game.players_still_needed = 0;
14729
14730         LevelSolved();
14731
14732         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14733       }
14734     }
14735     else
14736       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14737
14738     InitMovingField(x, y, move_direction);
14739     GfxAction[x][y] = ACTION_PUSHING;
14740
14741     if (mode == DF_SNAP)
14742       ContinueMoving(x, y);
14743     else
14744       MovPos[x][y] = (dx != 0 ? dx : dy);
14745
14746     Pushed[x][y] = TRUE;
14747     Pushed[nextx][nexty] = TRUE;
14748
14749     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14750       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14751     else
14752       player->push_delay_value = -1;    // get new value later
14753
14754     // check for element change _after_ element has been pushed
14755     if (game.use_change_when_pushing_bug)
14756     {
14757       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14758                                  player->index_bit, dig_side);
14759       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14760                                           player->index_bit, dig_side);
14761     }
14762   }
14763   else if (IS_SWITCHABLE(element))
14764   {
14765     if (PLAYER_SWITCHING(player, x, y))
14766     {
14767       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14768                                           player->index_bit, dig_side);
14769
14770       return MP_ACTION;
14771     }
14772
14773     player->is_switching = TRUE;
14774     player->switch_x = x;
14775     player->switch_y = y;
14776
14777     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14778
14779     if (element == EL_ROBOT_WHEEL)
14780     {
14781       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14782
14783       game.robot_wheel_x = x;
14784       game.robot_wheel_y = y;
14785       game.robot_wheel_active = TRUE;
14786
14787       TEST_DrawLevelField(x, y);
14788     }
14789     else if (element == EL_SP_TERMINAL)
14790     {
14791       int xx, yy;
14792
14793       SCAN_PLAYFIELD(xx, yy)
14794       {
14795         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14796         {
14797           Bang(xx, yy);
14798         }
14799         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14800         {
14801           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14802
14803           ResetGfxAnimation(xx, yy);
14804           TEST_DrawLevelField(xx, yy);
14805         }
14806       }
14807     }
14808     else if (IS_BELT_SWITCH(element))
14809     {
14810       ToggleBeltSwitch(x, y);
14811     }
14812     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14813              element == EL_SWITCHGATE_SWITCH_DOWN ||
14814              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14815              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14816     {
14817       ToggleSwitchgateSwitch(x, y);
14818     }
14819     else if (element == EL_LIGHT_SWITCH ||
14820              element == EL_LIGHT_SWITCH_ACTIVE)
14821     {
14822       ToggleLightSwitch(x, y);
14823     }
14824     else if (element == EL_TIMEGATE_SWITCH ||
14825              element == EL_DC_TIMEGATE_SWITCH)
14826     {
14827       ActivateTimegateSwitch(x, y);
14828     }
14829     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14830              element == EL_BALLOON_SWITCH_RIGHT ||
14831              element == EL_BALLOON_SWITCH_UP    ||
14832              element == EL_BALLOON_SWITCH_DOWN  ||
14833              element == EL_BALLOON_SWITCH_NONE  ||
14834              element == EL_BALLOON_SWITCH_ANY)
14835     {
14836       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14837                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14838                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14839                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14840                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14841                              move_direction);
14842     }
14843     else if (element == EL_LAMP)
14844     {
14845       Tile[x][y] = EL_LAMP_ACTIVE;
14846       game.lights_still_needed--;
14847
14848       ResetGfxAnimation(x, y);
14849       TEST_DrawLevelField(x, y);
14850     }
14851     else if (element == EL_TIME_ORB_FULL)
14852     {
14853       Tile[x][y] = EL_TIME_ORB_EMPTY;
14854
14855       if (level.time > 0 || level.use_time_orb_bug)
14856       {
14857         TimeLeft += level.time_orb_time;
14858         game.no_time_limit = FALSE;
14859
14860         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14861
14862         DisplayGameControlValues();
14863       }
14864
14865       ResetGfxAnimation(x, y);
14866       TEST_DrawLevelField(x, y);
14867     }
14868     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14869              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14870     {
14871       int xx, yy;
14872
14873       game.ball_active = !game.ball_active;
14874
14875       SCAN_PLAYFIELD(xx, yy)
14876       {
14877         int e = Tile[xx][yy];
14878
14879         if (game.ball_active)
14880         {
14881           if (e == EL_EMC_MAGIC_BALL)
14882             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14883           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14884             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14885         }
14886         else
14887         {
14888           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14889             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14890           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14891             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14892         }
14893       }
14894     }
14895
14896     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14897                                         player->index_bit, dig_side);
14898
14899     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14900                                         player->index_bit, dig_side);
14901
14902     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14903                                         player->index_bit, dig_side);
14904
14905     return MP_ACTION;
14906   }
14907   else
14908   {
14909     if (!PLAYER_SWITCHING(player, x, y))
14910     {
14911       player->is_switching = TRUE;
14912       player->switch_x = x;
14913       player->switch_y = y;
14914
14915       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14916                                  player->index_bit, dig_side);
14917       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14918                                           player->index_bit, dig_side);
14919
14920       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14921                                  player->index_bit, dig_side);
14922       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14923                                           player->index_bit, dig_side);
14924     }
14925
14926     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14927                                player->index_bit, dig_side);
14928     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14929                                         player->index_bit, dig_side);
14930
14931     return MP_NO_ACTION;
14932   }
14933
14934   player->push_delay = -1;
14935
14936   if (is_player)                // function can also be called by EL_PENGUIN
14937   {
14938     if (Tile[x][y] != element)          // really digged/collected something
14939     {
14940       player->is_collecting = !player->is_digging;
14941       player->is_active = TRUE;
14942
14943       player->last_removed_element = element;
14944     }
14945   }
14946
14947   return MP_MOVING;
14948 }
14949
14950 static boolean DigFieldByCE(int x, int y, int digging_element)
14951 {
14952   int element = Tile[x][y];
14953
14954   if (!IS_FREE(x, y))
14955   {
14956     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14957                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14958                   ACTION_BREAKING);
14959
14960     // no element can dig solid indestructible elements
14961     if (IS_INDESTRUCTIBLE(element) &&
14962         !IS_DIGGABLE(element) &&
14963         !IS_COLLECTIBLE(element))
14964       return FALSE;
14965
14966     if (AmoebaNr[x][y] &&
14967         (element == EL_AMOEBA_FULL ||
14968          element == EL_BD_AMOEBA ||
14969          element == EL_AMOEBA_GROWING))
14970     {
14971       AmoebaCnt[AmoebaNr[x][y]]--;
14972       AmoebaCnt2[AmoebaNr[x][y]]--;
14973     }
14974
14975     if (IS_MOVING(x, y))
14976       RemoveMovingField(x, y);
14977     else
14978     {
14979       RemoveField(x, y);
14980       TEST_DrawLevelField(x, y);
14981     }
14982
14983     // if digged element was about to explode, prevent the explosion
14984     ExplodeField[x][y] = EX_TYPE_NONE;
14985
14986     PlayLevelSoundAction(x, y, action);
14987   }
14988
14989   Store[x][y] = EL_EMPTY;
14990
14991   // this makes it possible to leave the removed element again
14992   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14993     Store[x][y] = element;
14994
14995   return TRUE;
14996 }
14997
14998 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14999 {
15000   int jx = player->jx, jy = player->jy;
15001   int x = jx + dx, y = jy + dy;
15002   int snap_direction = (dx == -1 ? MV_LEFT  :
15003                         dx == +1 ? MV_RIGHT :
15004                         dy == -1 ? MV_UP    :
15005                         dy == +1 ? MV_DOWN  : MV_NONE);
15006   boolean can_continue_snapping = (level.continuous_snapping &&
15007                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15008
15009   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15010     return FALSE;
15011
15012   if (!player->active || !IN_LEV_FIELD(x, y))
15013     return FALSE;
15014
15015   if (dx && dy)
15016     return FALSE;
15017
15018   if (!dx && !dy)
15019   {
15020     if (player->MovPos == 0)
15021       player->is_pushing = FALSE;
15022
15023     player->is_snapping = FALSE;
15024
15025     if (player->MovPos == 0)
15026     {
15027       player->is_moving = FALSE;
15028       player->is_digging = FALSE;
15029       player->is_collecting = FALSE;
15030     }
15031
15032     return FALSE;
15033   }
15034
15035   // prevent snapping with already pressed snap key when not allowed
15036   if (player->is_snapping && !can_continue_snapping)
15037     return FALSE;
15038
15039   player->MovDir = snap_direction;
15040
15041   if (player->MovPos == 0)
15042   {
15043     player->is_moving = FALSE;
15044     player->is_digging = FALSE;
15045     player->is_collecting = FALSE;
15046   }
15047
15048   player->is_dropping = FALSE;
15049   player->is_dropping_pressed = FALSE;
15050   player->drop_pressed_delay = 0;
15051
15052   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15053     return FALSE;
15054
15055   player->is_snapping = TRUE;
15056   player->is_active = TRUE;
15057
15058   if (player->MovPos == 0)
15059   {
15060     player->is_moving = FALSE;
15061     player->is_digging = FALSE;
15062     player->is_collecting = FALSE;
15063   }
15064
15065   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15066     TEST_DrawLevelField(player->last_jx, player->last_jy);
15067
15068   TEST_DrawLevelField(x, y);
15069
15070   return TRUE;
15071 }
15072
15073 static boolean DropElement(struct PlayerInfo *player)
15074 {
15075   int old_element, new_element;
15076   int dropx = player->jx, dropy = player->jy;
15077   int drop_direction = player->MovDir;
15078   int drop_side = drop_direction;
15079   int drop_element = get_next_dropped_element(player);
15080
15081   /* do not drop an element on top of another element; when holding drop key
15082      pressed without moving, dropped element must move away before the next
15083      element can be dropped (this is especially important if the next element
15084      is dynamite, which can be placed on background for historical reasons) */
15085   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15086     return MP_ACTION;
15087
15088   if (IS_THROWABLE(drop_element))
15089   {
15090     dropx += GET_DX_FROM_DIR(drop_direction);
15091     dropy += GET_DY_FROM_DIR(drop_direction);
15092
15093     if (!IN_LEV_FIELD(dropx, dropy))
15094       return FALSE;
15095   }
15096
15097   old_element = Tile[dropx][dropy];     // old element at dropping position
15098   new_element = drop_element;           // default: no change when dropping
15099
15100   // check if player is active, not moving and ready to drop
15101   if (!player->active || player->MovPos || player->drop_delay > 0)
15102     return FALSE;
15103
15104   // check if player has anything that can be dropped
15105   if (new_element == EL_UNDEFINED)
15106     return FALSE;
15107
15108   // only set if player has anything that can be dropped
15109   player->is_dropping_pressed = TRUE;
15110
15111   // check if drop key was pressed long enough for EM style dynamite
15112   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15113     return FALSE;
15114
15115   // check if anything can be dropped at the current position
15116   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15117     return FALSE;
15118
15119   // collected custom elements can only be dropped on empty fields
15120   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15121     return FALSE;
15122
15123   if (old_element != EL_EMPTY)
15124     Back[dropx][dropy] = old_element;   // store old element on this field
15125
15126   ResetGfxAnimation(dropx, dropy);
15127   ResetRandomAnimationValue(dropx, dropy);
15128
15129   if (player->inventory_size > 0 ||
15130       player->inventory_infinite_element != EL_UNDEFINED)
15131   {
15132     if (player->inventory_size > 0)
15133     {
15134       player->inventory_size--;
15135
15136       DrawGameDoorValues();
15137
15138       if (new_element == EL_DYNAMITE)
15139         new_element = EL_DYNAMITE_ACTIVE;
15140       else if (new_element == EL_EM_DYNAMITE)
15141         new_element = EL_EM_DYNAMITE_ACTIVE;
15142       else if (new_element == EL_SP_DISK_RED)
15143         new_element = EL_SP_DISK_RED_ACTIVE;
15144     }
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     // needed if previous element just changed to "empty" in the last frame
15155     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15156
15157     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15158                                player->index_bit, drop_side);
15159     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15160                                         CE_PLAYER_DROPS_X,
15161                                         player->index_bit, drop_side);
15162
15163     TestIfElementTouchesCustomElement(dropx, dropy);
15164   }
15165   else          // player is dropping a dyna bomb
15166   {
15167     player->dynabombs_left--;
15168
15169     Tile[dropx][dropy] = new_element;
15170
15171     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15172       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15173                           el2img(Tile[dropx][dropy]), 0);
15174
15175     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15176   }
15177
15178   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15179     InitField_WithBug1(dropx, dropy, FALSE);
15180
15181   new_element = Tile[dropx][dropy];     // element might have changed
15182
15183   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15184       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15185   {
15186     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15187       MovDir[dropx][dropy] = drop_direction;
15188
15189     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15190
15191     // do not cause impact style collision by dropping elements that can fall
15192     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15193   }
15194
15195   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15196   player->is_dropping = TRUE;
15197
15198   player->drop_pressed_delay = 0;
15199   player->is_dropping_pressed = FALSE;
15200
15201   player->drop_x = dropx;
15202   player->drop_y = dropy;
15203
15204   return TRUE;
15205 }
15206
15207 // ----------------------------------------------------------------------------
15208 // game sound playing functions
15209 // ----------------------------------------------------------------------------
15210
15211 static int *loop_sound_frame = NULL;
15212 static int *loop_sound_volume = NULL;
15213
15214 void InitPlayLevelSound(void)
15215 {
15216   int num_sounds = getSoundListSize();
15217
15218   checked_free(loop_sound_frame);
15219   checked_free(loop_sound_volume);
15220
15221   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15222   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15223 }
15224
15225 static void PlayLevelSound(int x, int y, int nr)
15226 {
15227   int sx = SCREENX(x), sy = SCREENY(y);
15228   int volume, stereo_position;
15229   int max_distance = 8;
15230   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15231
15232   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15233       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15234     return;
15235
15236   if (!IN_LEV_FIELD(x, y) ||
15237       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15238       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15239     return;
15240
15241   volume = SOUND_MAX_VOLUME;
15242
15243   if (!IN_SCR_FIELD(sx, sy))
15244   {
15245     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15246     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15247
15248     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15249   }
15250
15251   stereo_position = (SOUND_MAX_LEFT +
15252                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15253                      (SCR_FIELDX + 2 * max_distance));
15254
15255   if (IS_LOOP_SOUND(nr))
15256   {
15257     /* This assures that quieter loop sounds do not overwrite louder ones,
15258        while restarting sound volume comparison with each new game frame. */
15259
15260     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15261       return;
15262
15263     loop_sound_volume[nr] = volume;
15264     loop_sound_frame[nr] = FrameCounter;
15265   }
15266
15267   PlaySoundExt(nr, volume, stereo_position, type);
15268 }
15269
15270 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15271 {
15272   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15273                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15274                  y < LEVELY(BY1) ? LEVELY(BY1) :
15275                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15276                  sound_action);
15277 }
15278
15279 static void PlayLevelSoundAction(int x, int y, int action)
15280 {
15281   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15282 }
15283
15284 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15285 {
15286   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15287
15288   if (sound_effect != SND_UNDEFINED)
15289     PlayLevelSound(x, y, sound_effect);
15290 }
15291
15292 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15293                                               int action)
15294 {
15295   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15296
15297   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15298     PlayLevelSound(x, y, sound_effect);
15299 }
15300
15301 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15302 {
15303   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15304
15305   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15306     PlayLevelSound(x, y, sound_effect);
15307 }
15308
15309 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15310 {
15311   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15312
15313   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15314     StopSound(sound_effect);
15315 }
15316
15317 static int getLevelMusicNr(void)
15318 {
15319   if (levelset.music[level_nr] != MUS_UNDEFINED)
15320     return levelset.music[level_nr];            // from config file
15321   else
15322     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15323 }
15324
15325 static void FadeLevelSounds(void)
15326 {
15327   FadeSounds();
15328 }
15329
15330 static void FadeLevelMusic(void)
15331 {
15332   int music_nr = getLevelMusicNr();
15333   char *curr_music = getCurrentlyPlayingMusicFilename();
15334   char *next_music = getMusicInfoEntryFilename(music_nr);
15335
15336   if (!strEqual(curr_music, next_music))
15337     FadeMusic();
15338 }
15339
15340 void FadeLevelSoundsAndMusic(void)
15341 {
15342   FadeLevelSounds();
15343   FadeLevelMusic();
15344 }
15345
15346 static void PlayLevelMusic(void)
15347 {
15348   int music_nr = getLevelMusicNr();
15349   char *curr_music = getCurrentlyPlayingMusicFilename();
15350   char *next_music = getMusicInfoEntryFilename(music_nr);
15351
15352   if (!strEqual(curr_music, next_music))
15353     PlayMusicLoop(music_nr);
15354 }
15355
15356 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15357 {
15358   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15359   int offset = 0;
15360   int x = xx - offset;
15361   int y = yy - offset;
15362
15363   switch (sample)
15364   {
15365     case SOUND_blank:
15366       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15367       break;
15368
15369     case SOUND_roll:
15370       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15371       break;
15372
15373     case SOUND_stone:
15374       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15375       break;
15376
15377     case SOUND_nut:
15378       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15379       break;
15380
15381     case SOUND_crack:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15383       break;
15384
15385     case SOUND_bug:
15386       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15387       break;
15388
15389     case SOUND_tank:
15390       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15391       break;
15392
15393     case SOUND_android_clone:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15395       break;
15396
15397     case SOUND_android_move:
15398       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15399       break;
15400
15401     case SOUND_spring:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15403       break;
15404
15405     case SOUND_slurp:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15407       break;
15408
15409     case SOUND_eater:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15411       break;
15412
15413     case SOUND_eater_eat:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15415       break;
15416
15417     case SOUND_alien:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15419       break;
15420
15421     case SOUND_collect:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15423       break;
15424
15425     case SOUND_diamond:
15426       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15427       break;
15428
15429     case SOUND_squash:
15430       // !!! CHECK THIS !!!
15431 #if 1
15432       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15433 #else
15434       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15435 #endif
15436       break;
15437
15438     case SOUND_wonderfall:
15439       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15440       break;
15441
15442     case SOUND_drip:
15443       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15444       break;
15445
15446     case SOUND_push:
15447       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15448       break;
15449
15450     case SOUND_dirt:
15451       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15452       break;
15453
15454     case SOUND_acid:
15455       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15456       break;
15457
15458     case SOUND_ball:
15459       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15460       break;
15461
15462     case SOUND_slide:
15463       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15464       break;
15465
15466     case SOUND_wonder:
15467       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15468       break;
15469
15470     case SOUND_door:
15471       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15472       break;
15473
15474     case SOUND_exit_open:
15475       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15476       break;
15477
15478     case SOUND_exit_leave:
15479       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15480       break;
15481
15482     case SOUND_dynamite:
15483       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15484       break;
15485
15486     case SOUND_tick:
15487       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15488       break;
15489
15490     case SOUND_press:
15491       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15492       break;
15493
15494     case SOUND_wheel:
15495       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15496       break;
15497
15498     case SOUND_boom:
15499       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15500       break;
15501
15502     case SOUND_die:
15503       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15504       break;
15505
15506     case SOUND_time:
15507       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15508       break;
15509
15510     default:
15511       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15512       break;
15513   }
15514 }
15515
15516 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15517 {
15518   int element = map_element_SP_to_RND(element_sp);
15519   int action = map_action_SP_to_RND(action_sp);
15520   int offset = (setup.sp_show_border_elements ? 0 : 1);
15521   int x = xx - offset;
15522   int y = yy - offset;
15523
15524   PlayLevelSoundElementAction(x, y, element, action);
15525 }
15526
15527 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15528 {
15529   int element = map_element_MM_to_RND(element_mm);
15530   int action = map_action_MM_to_RND(action_mm);
15531   int offset = 0;
15532   int x = xx - offset;
15533   int y = yy - offset;
15534
15535   if (!IS_MM_ELEMENT(element))
15536     element = EL_MM_DEFAULT;
15537
15538   PlayLevelSoundElementAction(x, y, element, action);
15539 }
15540
15541 void PlaySound_MM(int sound_mm)
15542 {
15543   int sound = map_sound_MM_to_RND(sound_mm);
15544
15545   if (sound == SND_UNDEFINED)
15546     return;
15547
15548   PlaySound(sound);
15549 }
15550
15551 void PlaySoundLoop_MM(int sound_mm)
15552 {
15553   int sound = map_sound_MM_to_RND(sound_mm);
15554
15555   if (sound == SND_UNDEFINED)
15556     return;
15557
15558   PlaySoundLoop(sound);
15559 }
15560
15561 void StopSound_MM(int sound_mm)
15562 {
15563   int sound = map_sound_MM_to_RND(sound_mm);
15564
15565   if (sound == SND_UNDEFINED)
15566     return;
15567
15568   StopSound(sound);
15569 }
15570
15571 void RaiseScore(int value)
15572 {
15573   game.score += value;
15574
15575   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15576
15577   DisplayGameControlValues();
15578 }
15579
15580 void RaiseScoreElement(int element)
15581 {
15582   switch (element)
15583   {
15584     case EL_EMERALD:
15585     case EL_BD_DIAMOND:
15586     case EL_EMERALD_YELLOW:
15587     case EL_EMERALD_RED:
15588     case EL_EMERALD_PURPLE:
15589     case EL_SP_INFOTRON:
15590       RaiseScore(level.score[SC_EMERALD]);
15591       break;
15592     case EL_DIAMOND:
15593       RaiseScore(level.score[SC_DIAMOND]);
15594       break;
15595     case EL_CRYSTAL:
15596       RaiseScore(level.score[SC_CRYSTAL]);
15597       break;
15598     case EL_PEARL:
15599       RaiseScore(level.score[SC_PEARL]);
15600       break;
15601     case EL_BUG:
15602     case EL_BD_BUTTERFLY:
15603     case EL_SP_ELECTRON:
15604       RaiseScore(level.score[SC_BUG]);
15605       break;
15606     case EL_SPACESHIP:
15607     case EL_BD_FIREFLY:
15608     case EL_SP_SNIKSNAK:
15609       RaiseScore(level.score[SC_SPACESHIP]);
15610       break;
15611     case EL_YAMYAM:
15612     case EL_DARK_YAMYAM:
15613       RaiseScore(level.score[SC_YAMYAM]);
15614       break;
15615     case EL_ROBOT:
15616       RaiseScore(level.score[SC_ROBOT]);
15617       break;
15618     case EL_PACMAN:
15619       RaiseScore(level.score[SC_PACMAN]);
15620       break;
15621     case EL_NUT:
15622       RaiseScore(level.score[SC_NUT]);
15623       break;
15624     case EL_DYNAMITE:
15625     case EL_EM_DYNAMITE:
15626     case EL_SP_DISK_RED:
15627     case EL_DYNABOMB_INCREASE_NUMBER:
15628     case EL_DYNABOMB_INCREASE_SIZE:
15629     case EL_DYNABOMB_INCREASE_POWER:
15630       RaiseScore(level.score[SC_DYNAMITE]);
15631       break;
15632     case EL_SHIELD_NORMAL:
15633     case EL_SHIELD_DEADLY:
15634       RaiseScore(level.score[SC_SHIELD]);
15635       break;
15636     case EL_EXTRA_TIME:
15637       RaiseScore(level.extra_time_score);
15638       break;
15639     case EL_KEY_1:
15640     case EL_KEY_2:
15641     case EL_KEY_3:
15642     case EL_KEY_4:
15643     case EL_EM_KEY_1:
15644     case EL_EM_KEY_2:
15645     case EL_EM_KEY_3:
15646     case EL_EM_KEY_4:
15647     case EL_EMC_KEY_5:
15648     case EL_EMC_KEY_6:
15649     case EL_EMC_KEY_7:
15650     case EL_EMC_KEY_8:
15651     case EL_DC_KEY_WHITE:
15652       RaiseScore(level.score[SC_KEY]);
15653       break;
15654     default:
15655       RaiseScore(element_info[element].collect_score);
15656       break;
15657   }
15658 }
15659
15660 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15661 {
15662   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15663   {
15664     if (!quick_quit)
15665     {
15666       // prevent short reactivation of overlay buttons while closing door
15667       SetOverlayActive(FALSE);
15668
15669       // door may still be open due to skipped or envelope style request
15670       CloseDoor(DOOR_CLOSE_1);
15671     }
15672
15673     if (network.enabled)
15674       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15675     else
15676     {
15677       if (quick_quit)
15678         FadeSkipNextFadeIn();
15679
15680       SetGameStatus(GAME_MODE_MAIN);
15681
15682       DrawMainMenu();
15683     }
15684   }
15685   else          // continue playing the game
15686   {
15687     if (tape.playing && tape.deactivate_display)
15688       TapeDeactivateDisplayOff(TRUE);
15689
15690     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15691
15692     if (tape.playing && tape.deactivate_display)
15693       TapeDeactivateDisplayOn();
15694   }
15695 }
15696
15697 void RequestQuitGame(boolean escape_key_pressed)
15698 {
15699   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15700   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15701                         level_editor_test_game);
15702   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15703                           quick_quit);
15704
15705   RequestQuitGameExt(skip_request, quick_quit,
15706                      "Do you really want to quit the game?");
15707 }
15708
15709 void RequestRestartGame(char *message)
15710 {
15711   game.restart_game_message = NULL;
15712
15713   boolean has_started_game = hasStartedNetworkGame();
15714   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15715
15716   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15717   {
15718     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15719   }
15720   else
15721   {
15722     // needed in case of envelope request to close game panel
15723     CloseDoor(DOOR_CLOSE_1);
15724
15725     SetGameStatus(GAME_MODE_MAIN);
15726
15727     DrawMainMenu();
15728   }
15729 }
15730
15731 void CheckGameOver(void)
15732 {
15733   static boolean last_game_over = FALSE;
15734   static int game_over_delay = 0;
15735   int game_over_delay_value = 50;
15736   boolean game_over = checkGameFailed();
15737
15738   // do not handle game over if request dialog is already active
15739   if (game.request_active)
15740     return;
15741
15742   // do not ask to play again if game was never actually played
15743   if (!game.GamePlayed)
15744     return;
15745
15746   if (!game_over)
15747   {
15748     last_game_over = FALSE;
15749     game_over_delay = game_over_delay_value;
15750
15751     return;
15752   }
15753
15754   if (game_over_delay > 0)
15755   {
15756     game_over_delay--;
15757
15758     return;
15759   }
15760
15761   if (last_game_over != game_over)
15762     game.restart_game_message = (hasStartedNetworkGame() ?
15763                                  "Game over! Play it again?" :
15764                                  "Game over!");
15765
15766   last_game_over = game_over;
15767 }
15768
15769 boolean checkGameSolved(void)
15770 {
15771   // set for all game engines if level was solved
15772   return game.LevelSolved_GameEnd;
15773 }
15774
15775 boolean checkGameFailed(void)
15776 {
15777   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15778     return (game_em.game_over && !game_em.level_solved);
15779   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15780     return (game_sp.game_over && !game_sp.level_solved);
15781   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15782     return (game_mm.game_over && !game_mm.level_solved);
15783   else                          // GAME_ENGINE_TYPE_RND
15784     return (game.GameOver && !game.LevelSolved);
15785 }
15786
15787 boolean checkGameEnded(void)
15788 {
15789   return (checkGameSolved() || checkGameFailed());
15790 }
15791
15792
15793 // ----------------------------------------------------------------------------
15794 // random generator functions
15795 // ----------------------------------------------------------------------------
15796
15797 unsigned int InitEngineRandom_RND(int seed)
15798 {
15799   game.num_random_calls = 0;
15800
15801   return InitEngineRandom(seed);
15802 }
15803
15804 unsigned int RND(int max)
15805 {
15806   if (max > 0)
15807   {
15808     game.num_random_calls++;
15809
15810     return GetEngineRandom(max);
15811   }
15812
15813   return 0;
15814 }
15815
15816
15817 // ----------------------------------------------------------------------------
15818 // game engine snapshot handling functions
15819 // ----------------------------------------------------------------------------
15820
15821 struct EngineSnapshotInfo
15822 {
15823   // runtime values for custom element collect score
15824   int collect_score[NUM_CUSTOM_ELEMENTS];
15825
15826   // runtime values for group element choice position
15827   int choice_pos[NUM_GROUP_ELEMENTS];
15828
15829   // runtime values for belt position animations
15830   int belt_graphic[4][NUM_BELT_PARTS];
15831   int belt_anim_mode[4][NUM_BELT_PARTS];
15832 };
15833
15834 static struct EngineSnapshotInfo engine_snapshot_rnd;
15835 static char *snapshot_level_identifier = NULL;
15836 static int snapshot_level_nr = -1;
15837
15838 static void SaveEngineSnapshotValues_RND(void)
15839 {
15840   static int belt_base_active_element[4] =
15841   {
15842     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15843     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15844     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15845     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15846   };
15847   int i, j;
15848
15849   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15850   {
15851     int element = EL_CUSTOM_START + i;
15852
15853     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15854   }
15855
15856   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15857   {
15858     int element = EL_GROUP_START + i;
15859
15860     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15861   }
15862
15863   for (i = 0; i < 4; i++)
15864   {
15865     for (j = 0; j < NUM_BELT_PARTS; j++)
15866     {
15867       int element = belt_base_active_element[i] + j;
15868       int graphic = el2img(element);
15869       int anim_mode = graphic_info[graphic].anim_mode;
15870
15871       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15872       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15873     }
15874   }
15875 }
15876
15877 static void LoadEngineSnapshotValues_RND(void)
15878 {
15879   unsigned int num_random_calls = game.num_random_calls;
15880   int i, j;
15881
15882   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15883   {
15884     int element = EL_CUSTOM_START + i;
15885
15886     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15887   }
15888
15889   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15890   {
15891     int element = EL_GROUP_START + i;
15892
15893     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15894   }
15895
15896   for (i = 0; i < 4; i++)
15897   {
15898     for (j = 0; j < NUM_BELT_PARTS; j++)
15899     {
15900       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15901       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15902
15903       graphic_info[graphic].anim_mode = anim_mode;
15904     }
15905   }
15906
15907   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15908   {
15909     InitRND(tape.random_seed);
15910     for (i = 0; i < num_random_calls; i++)
15911       RND(1);
15912   }
15913
15914   if (game.num_random_calls != num_random_calls)
15915   {
15916     Error("number of random calls out of sync");
15917     Error("number of random calls should be %d", num_random_calls);
15918     Error("number of random calls is %d", game.num_random_calls);
15919
15920     Fail("this should not happen -- please debug");
15921   }
15922 }
15923
15924 void FreeEngineSnapshotSingle(void)
15925 {
15926   FreeSnapshotSingle();
15927
15928   setString(&snapshot_level_identifier, NULL);
15929   snapshot_level_nr = -1;
15930 }
15931
15932 void FreeEngineSnapshotList(void)
15933 {
15934   FreeSnapshotList();
15935 }
15936
15937 static ListNode *SaveEngineSnapshotBuffers(void)
15938 {
15939   ListNode *buffers = NULL;
15940
15941   // copy some special values to a structure better suited for the snapshot
15942
15943   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15944     SaveEngineSnapshotValues_RND();
15945   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15946     SaveEngineSnapshotValues_EM();
15947   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15948     SaveEngineSnapshotValues_SP(&buffers);
15949   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15950     SaveEngineSnapshotValues_MM(&buffers);
15951
15952   // save values stored in special snapshot structure
15953
15954   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15955     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15956   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15957     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15958   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15959     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15960   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15961     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15962
15963   // save further RND engine values
15964
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15968
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15974
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15978
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15980
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15983
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16000   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16002
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16005
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16009
16010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16012
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16019
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16021   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16022
16023 #if 0
16024   ListNode *node = engine_snapshot_list_rnd;
16025   int num_bytes = 0;
16026
16027   while (node != NULL)
16028   {
16029     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16030
16031     node = node->next;
16032   }
16033
16034   Debug("game:playing:SaveEngineSnapshotBuffers",
16035         "size of engine snapshot: %d bytes", num_bytes);
16036 #endif
16037
16038   return buffers;
16039 }
16040
16041 void SaveEngineSnapshotSingle(void)
16042 {
16043   ListNode *buffers = SaveEngineSnapshotBuffers();
16044
16045   // finally save all snapshot buffers to single snapshot
16046   SaveSnapshotSingle(buffers);
16047
16048   // save level identification information
16049   setString(&snapshot_level_identifier, leveldir_current->identifier);
16050   snapshot_level_nr = level_nr;
16051 }
16052
16053 boolean CheckSaveEngineSnapshotToList(void)
16054 {
16055   boolean save_snapshot =
16056     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16057      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16058       game.snapshot.changed_action) ||
16059      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16060       game.snapshot.collected_item));
16061
16062   game.snapshot.changed_action = FALSE;
16063   game.snapshot.collected_item = FALSE;
16064   game.snapshot.save_snapshot = save_snapshot;
16065
16066   return save_snapshot;
16067 }
16068
16069 void SaveEngineSnapshotToList(void)
16070 {
16071   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16072       tape.quick_resume)
16073     return;
16074
16075   ListNode *buffers = SaveEngineSnapshotBuffers();
16076
16077   // finally save all snapshot buffers to snapshot list
16078   SaveSnapshotToList(buffers);
16079 }
16080
16081 void SaveEngineSnapshotToListInitial(void)
16082 {
16083   FreeEngineSnapshotList();
16084
16085   SaveEngineSnapshotToList();
16086 }
16087
16088 static void LoadEngineSnapshotValues(void)
16089 {
16090   // restore special values from snapshot structure
16091
16092   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16093     LoadEngineSnapshotValues_RND();
16094   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16095     LoadEngineSnapshotValues_EM();
16096   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16097     LoadEngineSnapshotValues_SP();
16098   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16099     LoadEngineSnapshotValues_MM();
16100 }
16101
16102 void LoadEngineSnapshotSingle(void)
16103 {
16104   LoadSnapshotSingle();
16105
16106   LoadEngineSnapshotValues();
16107 }
16108
16109 static void LoadEngineSnapshot_Undo(int steps)
16110 {
16111   LoadSnapshotFromList_Older(steps);
16112
16113   LoadEngineSnapshotValues();
16114 }
16115
16116 static void LoadEngineSnapshot_Redo(int steps)
16117 {
16118   LoadSnapshotFromList_Newer(steps);
16119
16120   LoadEngineSnapshotValues();
16121 }
16122
16123 boolean CheckEngineSnapshotSingle(void)
16124 {
16125   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16126           snapshot_level_nr == level_nr);
16127 }
16128
16129 boolean CheckEngineSnapshotList(void)
16130 {
16131   return CheckSnapshotList();
16132 }
16133
16134
16135 // ---------- new game button stuff -------------------------------------------
16136
16137 static struct
16138 {
16139   int graphic;
16140   struct XY *pos;
16141   int gadget_id;
16142   boolean *setup_value;
16143   boolean allowed_on_tape;
16144   boolean is_touch_button;
16145   char *infotext;
16146 } gamebutton_info[NUM_GAME_BUTTONS] =
16147 {
16148   {
16149     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16150     GAME_CTRL_ID_STOP,                          NULL,
16151     TRUE, FALSE,                                "stop game"
16152   },
16153   {
16154     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16155     GAME_CTRL_ID_PAUSE,                         NULL,
16156     TRUE, FALSE,                                "pause game"
16157   },
16158   {
16159     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16160     GAME_CTRL_ID_PLAY,                          NULL,
16161     TRUE, FALSE,                                "play game"
16162   },
16163   {
16164     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16165     GAME_CTRL_ID_UNDO,                          NULL,
16166     TRUE, FALSE,                                "undo step"
16167   },
16168   {
16169     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16170     GAME_CTRL_ID_REDO,                          NULL,
16171     TRUE, FALSE,                                "redo step"
16172   },
16173   {
16174     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16175     GAME_CTRL_ID_SAVE,                          NULL,
16176     TRUE, FALSE,                                "save game"
16177   },
16178   {
16179     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16180     GAME_CTRL_ID_PAUSE2,                        NULL,
16181     TRUE, FALSE,                                "pause game"
16182   },
16183   {
16184     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16185     GAME_CTRL_ID_LOAD,                          NULL,
16186     TRUE, FALSE,                                "load game"
16187   },
16188   {
16189     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16190     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16191     FALSE, FALSE,                               "stop game"
16192   },
16193   {
16194     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16195     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16196     FALSE, FALSE,                               "pause game"
16197   },
16198   {
16199     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16200     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16201     FALSE, FALSE,                               "play game"
16202   },
16203   {
16204     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16205     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16206     FALSE, TRUE,                                "stop game"
16207   },
16208   {
16209     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16210     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16211     FALSE, TRUE,                                "pause game"
16212   },
16213   {
16214     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16215     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16216     TRUE, FALSE,                                "background music on/off"
16217   },
16218   {
16219     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16220     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16221     TRUE, FALSE,                                "sound loops on/off"
16222   },
16223   {
16224     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16225     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16226     TRUE, FALSE,                                "normal sounds on/off"
16227   },
16228   {
16229     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16230     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16231     FALSE, FALSE,                               "background music on/off"
16232   },
16233   {
16234     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16235     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16236     FALSE, FALSE,                               "sound loops on/off"
16237   },
16238   {
16239     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16240     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16241     FALSE, FALSE,                               "normal sounds on/off"
16242   }
16243 };
16244
16245 void CreateGameButtons(void)
16246 {
16247   int i;
16248
16249   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16250   {
16251     int graphic = gamebutton_info[i].graphic;
16252     struct GraphicInfo *gfx = &graphic_info[graphic];
16253     struct XY *pos = gamebutton_info[i].pos;
16254     struct GadgetInfo *gi;
16255     int button_type;
16256     boolean checked;
16257     unsigned int event_mask;
16258     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16259     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16260     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16261     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16262     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16263     int gd_x   = gfx->src_x;
16264     int gd_y   = gfx->src_y;
16265     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16266     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16267     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16268     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16269     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16270     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16271     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16272     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16273     int id = i;
16274
16275     if (gfx->bitmap == NULL)
16276     {
16277       game_gadget[id] = NULL;
16278
16279       continue;
16280     }
16281
16282     if (id == GAME_CTRL_ID_STOP ||
16283         id == GAME_CTRL_ID_PANEL_STOP ||
16284         id == GAME_CTRL_ID_TOUCH_STOP ||
16285         id == GAME_CTRL_ID_PLAY ||
16286         id == GAME_CTRL_ID_PANEL_PLAY ||
16287         id == GAME_CTRL_ID_SAVE ||
16288         id == GAME_CTRL_ID_LOAD)
16289     {
16290       button_type = GD_TYPE_NORMAL_BUTTON;
16291       checked = FALSE;
16292       event_mask = GD_EVENT_RELEASED;
16293     }
16294     else if (id == GAME_CTRL_ID_UNDO ||
16295              id == GAME_CTRL_ID_REDO)
16296     {
16297       button_type = GD_TYPE_NORMAL_BUTTON;
16298       checked = FALSE;
16299       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16300     }
16301     else
16302     {
16303       button_type = GD_TYPE_CHECK_BUTTON;
16304       checked = (gamebutton_info[i].setup_value != NULL ?
16305                  *gamebutton_info[i].setup_value : FALSE);
16306       event_mask = GD_EVENT_PRESSED;
16307     }
16308
16309     gi = CreateGadget(GDI_CUSTOM_ID, id,
16310                       GDI_IMAGE_ID, graphic,
16311                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16312                       GDI_X, base_x + x,
16313                       GDI_Y, base_y + y,
16314                       GDI_WIDTH, gfx->width,
16315                       GDI_HEIGHT, gfx->height,
16316                       GDI_TYPE, button_type,
16317                       GDI_STATE, GD_BUTTON_UNPRESSED,
16318                       GDI_CHECKED, checked,
16319                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16320                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16321                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16322                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16323                       GDI_DIRECT_DRAW, FALSE,
16324                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16325                       GDI_EVENT_MASK, event_mask,
16326                       GDI_CALLBACK_ACTION, HandleGameButtons,
16327                       GDI_END);
16328
16329     if (gi == NULL)
16330       Fail("cannot create gadget");
16331
16332     game_gadget[id] = gi;
16333   }
16334 }
16335
16336 void FreeGameButtons(void)
16337 {
16338   int i;
16339
16340   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16341     FreeGadget(game_gadget[i]);
16342 }
16343
16344 static void UnmapGameButtonsAtSamePosition(int id)
16345 {
16346   int i;
16347
16348   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16349     if (i != id &&
16350         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16351         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16352       UnmapGadget(game_gadget[i]);
16353 }
16354
16355 static void UnmapGameButtonsAtSamePosition_All(void)
16356 {
16357   if (setup.show_load_save_buttons)
16358   {
16359     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16360     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16361     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16362   }
16363   else if (setup.show_undo_redo_buttons)
16364   {
16365     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16366     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16367     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16368   }
16369   else
16370   {
16371     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16372     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16373     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16374
16375     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16376     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16377     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16378   }
16379 }
16380
16381 void MapLoadSaveButtons(void)
16382 {
16383   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16384   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16385
16386   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16387   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16388 }
16389
16390 void MapUndoRedoButtons(void)
16391 {
16392   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16393   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16394
16395   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16396   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16397 }
16398
16399 void ModifyPauseButtons(void)
16400 {
16401   static int ids[] =
16402   {
16403     GAME_CTRL_ID_PAUSE,
16404     GAME_CTRL_ID_PAUSE2,
16405     GAME_CTRL_ID_PANEL_PAUSE,
16406     GAME_CTRL_ID_TOUCH_PAUSE,
16407     -1
16408   };
16409   int i;
16410
16411   for (i = 0; ids[i] > -1; i++)
16412     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16413 }
16414
16415 static void MapGameButtonsExt(boolean on_tape)
16416 {
16417   int i;
16418
16419   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16420     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16421       MapGadget(game_gadget[i]);
16422
16423   UnmapGameButtonsAtSamePosition_All();
16424
16425   RedrawGameButtons();
16426 }
16427
16428 static void UnmapGameButtonsExt(boolean on_tape)
16429 {
16430   int i;
16431
16432   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16433     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16434       UnmapGadget(game_gadget[i]);
16435 }
16436
16437 static void RedrawGameButtonsExt(boolean on_tape)
16438 {
16439   int i;
16440
16441   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16442     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16443       RedrawGadget(game_gadget[i]);
16444 }
16445
16446 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16447 {
16448   if (gi == NULL)
16449     return;
16450
16451   gi->checked = state;
16452 }
16453
16454 static void RedrawSoundButtonGadget(int id)
16455 {
16456   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16457              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16458              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16459              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16460              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16461              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16462              id);
16463
16464   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16465   RedrawGadget(game_gadget[id2]);
16466 }
16467
16468 void MapGameButtons(void)
16469 {
16470   MapGameButtonsExt(FALSE);
16471 }
16472
16473 void UnmapGameButtons(void)
16474 {
16475   UnmapGameButtonsExt(FALSE);
16476 }
16477
16478 void RedrawGameButtons(void)
16479 {
16480   RedrawGameButtonsExt(FALSE);
16481 }
16482
16483 void MapGameButtonsOnTape(void)
16484 {
16485   MapGameButtonsExt(TRUE);
16486 }
16487
16488 void UnmapGameButtonsOnTape(void)
16489 {
16490   UnmapGameButtonsExt(TRUE);
16491 }
16492
16493 void RedrawGameButtonsOnTape(void)
16494 {
16495   RedrawGameButtonsExt(TRUE);
16496 }
16497
16498 static void GameUndoRedoExt(void)
16499 {
16500   ClearPlayerAction();
16501
16502   tape.pausing = TRUE;
16503
16504   RedrawPlayfield();
16505   UpdateAndDisplayGameControlValues();
16506
16507   DrawCompleteVideoDisplay();
16508   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16509   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16510   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16511
16512   ModifyPauseButtons();
16513
16514   BackToFront();
16515 }
16516
16517 static void GameUndo(int steps)
16518 {
16519   if (!CheckEngineSnapshotList())
16520     return;
16521
16522   int tape_property_bits = tape.property_bits;
16523
16524   LoadEngineSnapshot_Undo(steps);
16525
16526   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16527
16528   GameUndoRedoExt();
16529 }
16530
16531 static void GameRedo(int steps)
16532 {
16533   if (!CheckEngineSnapshotList())
16534     return;
16535
16536   int tape_property_bits = tape.property_bits;
16537
16538   LoadEngineSnapshot_Redo(steps);
16539
16540   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16541
16542   GameUndoRedoExt();
16543 }
16544
16545 static void HandleGameButtonsExt(int id, int button)
16546 {
16547   static boolean game_undo_executed = FALSE;
16548   int steps = BUTTON_STEPSIZE(button);
16549   boolean handle_game_buttons =
16550     (game_status == GAME_MODE_PLAYING ||
16551      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16552
16553   if (!handle_game_buttons)
16554     return;
16555
16556   switch (id)
16557   {
16558     case GAME_CTRL_ID_STOP:
16559     case GAME_CTRL_ID_PANEL_STOP:
16560     case GAME_CTRL_ID_TOUCH_STOP:
16561       if (game_status == GAME_MODE_MAIN)
16562         break;
16563
16564       if (tape.playing)
16565         TapeStop();
16566       else
16567         RequestQuitGame(FALSE);
16568
16569       break;
16570
16571     case GAME_CTRL_ID_PAUSE:
16572     case GAME_CTRL_ID_PAUSE2:
16573     case GAME_CTRL_ID_PANEL_PAUSE:
16574     case GAME_CTRL_ID_TOUCH_PAUSE:
16575       if (network.enabled && game_status == GAME_MODE_PLAYING)
16576       {
16577         if (tape.pausing)
16578           SendToServer_ContinuePlaying();
16579         else
16580           SendToServer_PausePlaying();
16581       }
16582       else
16583         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16584
16585       game_undo_executed = FALSE;
16586
16587       break;
16588
16589     case GAME_CTRL_ID_PLAY:
16590     case GAME_CTRL_ID_PANEL_PLAY:
16591       if (game_status == GAME_MODE_MAIN)
16592       {
16593         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16594       }
16595       else if (tape.pausing)
16596       {
16597         if (network.enabled)
16598           SendToServer_ContinuePlaying();
16599         else
16600           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16601       }
16602       break;
16603
16604     case GAME_CTRL_ID_UNDO:
16605       // Important: When using "save snapshot when collecting an item" mode,
16606       // load last (current) snapshot for first "undo" after pressing "pause"
16607       // (else the last-but-one snapshot would be loaded, because the snapshot
16608       // pointer already points to the last snapshot when pressing "pause",
16609       // which is fine for "every step/move" mode, but not for "every collect")
16610       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16611           !game_undo_executed)
16612         steps--;
16613
16614       game_undo_executed = TRUE;
16615
16616       GameUndo(steps);
16617       break;
16618
16619     case GAME_CTRL_ID_REDO:
16620       GameRedo(steps);
16621       break;
16622
16623     case GAME_CTRL_ID_SAVE:
16624       TapeQuickSave();
16625       break;
16626
16627     case GAME_CTRL_ID_LOAD:
16628       TapeQuickLoad();
16629       break;
16630
16631     case SOUND_CTRL_ID_MUSIC:
16632     case SOUND_CTRL_ID_PANEL_MUSIC:
16633       if (setup.sound_music)
16634       { 
16635         setup.sound_music = FALSE;
16636
16637         FadeMusic();
16638       }
16639       else if (audio.music_available)
16640       { 
16641         setup.sound = setup.sound_music = TRUE;
16642
16643         SetAudioMode(setup.sound);
16644
16645         if (game_status == GAME_MODE_PLAYING)
16646           PlayLevelMusic();
16647       }
16648
16649       RedrawSoundButtonGadget(id);
16650
16651       break;
16652
16653     case SOUND_CTRL_ID_LOOPS:
16654     case SOUND_CTRL_ID_PANEL_LOOPS:
16655       if (setup.sound_loops)
16656         setup.sound_loops = FALSE;
16657       else if (audio.loops_available)
16658       {
16659         setup.sound = setup.sound_loops = TRUE;
16660
16661         SetAudioMode(setup.sound);
16662       }
16663
16664       RedrawSoundButtonGadget(id);
16665
16666       break;
16667
16668     case SOUND_CTRL_ID_SIMPLE:
16669     case SOUND_CTRL_ID_PANEL_SIMPLE:
16670       if (setup.sound_simple)
16671         setup.sound_simple = FALSE;
16672       else if (audio.sound_available)
16673       {
16674         setup.sound = setup.sound_simple = TRUE;
16675
16676         SetAudioMode(setup.sound);
16677       }
16678
16679       RedrawSoundButtonGadget(id);
16680
16681       break;
16682
16683     default:
16684       break;
16685   }
16686 }
16687
16688 static void HandleGameButtons(struct GadgetInfo *gi)
16689 {
16690   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16691 }
16692
16693 void HandleSoundButtonKeys(Key key)
16694 {
16695   if (key == setup.shortcut.sound_simple)
16696     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16697   else if (key == setup.shortcut.sound_loops)
16698     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16699   else if (key == setup.shortcut.sound_music)
16700     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16701 }