4b7533ad779cabe27e85c770da5dae961eaf2378
[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 struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame : INIT_GFX_RANDOM());
2570
2571         if (gpc->value != gpc->last_value)
2572         {
2573           gpc->gfx_frame = 0;
2574           gpc->gfx_random = init_gfx_random;
2575         }
2576         else
2577         {
2578           gpc->gfx_frame++;
2579
2580           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2581               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2582             gpc->gfx_random = init_gfx_random;
2583         }
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = gpc->gfx_random;
2587
2588         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2589           gpc->gfx_frame = element_info[element].collect_score;
2590
2591         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2592
2593         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2594           gfx.anim_random_frame = last_anim_random_frame;
2595       }
2596     }
2597     else if (gpc->type == TYPE_GRAPHIC)
2598     {
2599       if (gpc->graphic != IMG_UNDEFINED)
2600       {
2601         int last_anim_random_frame = gfx.anim_random_frame;
2602         int graphic = gpc->graphic;
2603         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2604                                sync_random_frame : INIT_GFX_RANDOM());
2605
2606         if (gpc->value != gpc->last_value)
2607         {
2608           gpc->gfx_frame = 0;
2609           gpc->gfx_random = init_gfx_random;
2610         }
2611         else
2612         {
2613           gpc->gfx_frame++;
2614
2615           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2616               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2617             gpc->gfx_random = init_gfx_random;
2618         }
2619
2620         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2621           gfx.anim_random_frame = gpc->gfx_random;
2622
2623         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2624
2625         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2626           gfx.anim_random_frame = last_anim_random_frame;
2627       }
2628     }
2629   }
2630 }
2631
2632 static void DisplayGameControlValues(void)
2633 {
2634   boolean redraw_panel = FALSE;
2635   int i;
2636
2637   for (i = 0; game_panel_controls[i].nr != -1; i++)
2638   {
2639     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2640
2641     if (PANEL_DEACTIVATED(gpc->pos))
2642       continue;
2643
2644     if (gpc->value == gpc->last_value &&
2645         gpc->frame == gpc->last_frame)
2646       continue;
2647
2648     redraw_panel = TRUE;
2649   }
2650
2651   if (!redraw_panel)
2652     return;
2653
2654   // copy default game door content to main double buffer
2655
2656   // !!! CHECK AGAIN !!!
2657   SetPanelBackground();
2658   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2659   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2660
2661   // redraw game control buttons
2662   RedrawGameButtons();
2663
2664   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2665
2666   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2667   {
2668     int nr = game_panel_order[i].nr;
2669     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2670     struct TextPosInfo *pos = gpc->pos;
2671     int type = gpc->type;
2672     int value = gpc->value;
2673     int frame = gpc->frame;
2674     int size = pos->size;
2675     int font = pos->font;
2676     boolean draw_masked = pos->draw_masked;
2677     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2678
2679     if (PANEL_DEACTIVATED(pos))
2680       continue;
2681
2682     if (pos->class == get_hash_from_key("extra_panel_items") &&
2683         !setup.prefer_extra_panel_items)
2684       continue;
2685
2686     gpc->last_value = value;
2687     gpc->last_frame = frame;
2688
2689     if (type == TYPE_INTEGER)
2690     {
2691       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2692           nr == GAME_PANEL_INVENTORY_COUNT ||
2693           nr == GAME_PANEL_SCORE ||
2694           nr == GAME_PANEL_HIGHSCORE ||
2695           nr == GAME_PANEL_TIME)
2696       {
2697         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2698
2699         if (use_dynamic_size)           // use dynamic number of digits
2700         {
2701           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2702                               nr == GAME_PANEL_INVENTORY_COUNT ||
2703                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2704           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2705                           nr == GAME_PANEL_INVENTORY_COUNT ||
2706                           nr == GAME_PANEL_TIME ? 1 : 2);
2707           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2708                        nr == GAME_PANEL_INVENTORY_COUNT ||
2709                        nr == GAME_PANEL_TIME ? 3 : 5);
2710           int size2 = size1 + size_add;
2711           int font1 = pos->font;
2712           int font2 = pos->font_alt;
2713
2714           size = (value < value_change ? size1 : size2);
2715           font = (value < value_change ? font1 : font2);
2716         }
2717       }
2718
2719       // correct text size if "digits" is zero or less
2720       if (size <= 0)
2721         size = strlen(int2str(value, size));
2722
2723       // dynamically correct text alignment
2724       pos->width = size * getFontWidth(font);
2725
2726       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2727                   int2str(value, size), font, mask_mode);
2728     }
2729     else if (type == TYPE_ELEMENT)
2730     {
2731       int element, graphic;
2732       Bitmap *src_bitmap;
2733       int src_x, src_y;
2734       int width, height;
2735       int dst_x = PANEL_XPOS(pos);
2736       int dst_y = PANEL_YPOS(pos);
2737
2738       if (value != EL_UNDEFINED && value != EL_EMPTY)
2739       {
2740         element = value;
2741         graphic = el2panelimg(value);
2742
2743 #if 0
2744         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2745               element, EL_NAME(element), size);
2746 #endif
2747
2748         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2749           size = TILESIZE;
2750
2751         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2752                               &src_x, &src_y);
2753
2754         width  = graphic_info[graphic].width  * size / TILESIZE;
2755         height = graphic_info[graphic].height * size / TILESIZE;
2756
2757         if (draw_masked)
2758           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2759                            dst_x, dst_y);
2760         else
2761           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2762                      dst_x, dst_y);
2763       }
2764     }
2765     else if (type == TYPE_GRAPHIC)
2766     {
2767       int graphic        = gpc->graphic;
2768       int graphic_active = gpc->graphic_active;
2769       Bitmap *src_bitmap;
2770       int src_x, src_y;
2771       int width, height;
2772       int dst_x = PANEL_XPOS(pos);
2773       int dst_y = PANEL_YPOS(pos);
2774       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2775                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2776
2777       if (graphic != IMG_UNDEFINED && !skip)
2778       {
2779         if (pos->style == STYLE_REVERSE)
2780           value = 100 - value;
2781
2782         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2783
2784         if (pos->direction & MV_HORIZONTAL)
2785         {
2786           width  = graphic_info[graphic_active].width * value / 100;
2787           height = graphic_info[graphic_active].height;
2788
2789           if (pos->direction == MV_LEFT)
2790           {
2791             src_x += graphic_info[graphic_active].width - width;
2792             dst_x += graphic_info[graphic_active].width - width;
2793           }
2794         }
2795         else
2796         {
2797           width  = graphic_info[graphic_active].width;
2798           height = graphic_info[graphic_active].height * value / 100;
2799
2800           if (pos->direction == MV_UP)
2801           {
2802             src_y += graphic_info[graphic_active].height - height;
2803             dst_y += graphic_info[graphic_active].height - height;
2804           }
2805         }
2806
2807         if (draw_masked)
2808           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2809                            dst_x, dst_y);
2810         else
2811           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2812                      dst_x, dst_y);
2813
2814         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2815
2816         if (pos->direction & MV_HORIZONTAL)
2817         {
2818           if (pos->direction == MV_RIGHT)
2819           {
2820             src_x += width;
2821             dst_x += width;
2822           }
2823           else
2824           {
2825             dst_x = PANEL_XPOS(pos);
2826           }
2827
2828           width = graphic_info[graphic].width - width;
2829         }
2830         else
2831         {
2832           if (pos->direction == MV_DOWN)
2833           {
2834             src_y += height;
2835             dst_y += height;
2836           }
2837           else
2838           {
2839             dst_y = PANEL_YPOS(pos);
2840           }
2841
2842           height = graphic_info[graphic].height - height;
2843         }
2844
2845         if (draw_masked)
2846           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2847                            dst_x, dst_y);
2848         else
2849           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2850                      dst_x, dst_y);
2851       }
2852     }
2853     else if (type == TYPE_STRING)
2854     {
2855       boolean active = (value != 0);
2856       char *state_normal = "off";
2857       char *state_active = "on";
2858       char *state = (active ? state_active : state_normal);
2859       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2860                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2861                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2862                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2863
2864       if (nr == GAME_PANEL_GRAVITY_STATE)
2865       {
2866         int font1 = pos->font;          // (used for normal state)
2867         int font2 = pos->font_alt;      // (used for active state)
2868
2869         font = (active ? font2 : font1);
2870       }
2871
2872       if (s != NULL)
2873       {
2874         char *s_cut;
2875
2876         if (size <= 0)
2877         {
2878           // don't truncate output if "chars" is zero or less
2879           size = strlen(s);
2880
2881           // dynamically correct text alignment
2882           pos->width = size * getFontWidth(font);
2883         }
2884
2885         s_cut = getStringCopyN(s, size);
2886
2887         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2888                     s_cut, font, mask_mode);
2889
2890         free(s_cut);
2891       }
2892     }
2893
2894     redraw_mask |= REDRAW_DOOR_1;
2895   }
2896
2897   SetGameStatus(GAME_MODE_PLAYING);
2898 }
2899
2900 void UpdateAndDisplayGameControlValues(void)
2901 {
2902   if (tape.deactivate_display)
2903     return;
2904
2905   UpdateGameControlValues();
2906   DisplayGameControlValues();
2907 }
2908
2909 void UpdateGameDoorValues(void)
2910 {
2911   UpdateGameControlValues();
2912 }
2913
2914 void DrawGameDoorValues(void)
2915 {
2916   DisplayGameControlValues();
2917 }
2918
2919
2920 // ============================================================================
2921 // InitGameEngine()
2922 // ----------------------------------------------------------------------------
2923 // initialize game engine due to level / tape version number
2924 // ============================================================================
2925
2926 static void InitGameEngine(void)
2927 {
2928   int i, j, k, l, x, y;
2929
2930   // set game engine from tape file when re-playing, else from level file
2931   game.engine_version = (tape.playing ? tape.engine_version :
2932                          level.game_version);
2933
2934   // set single or multi-player game mode (needed for re-playing tapes)
2935   game.team_mode = setup.team_mode;
2936
2937   if (tape.playing)
2938   {
2939     int num_players = 0;
2940
2941     for (i = 0; i < MAX_PLAYERS; i++)
2942       if (tape.player_participates[i])
2943         num_players++;
2944
2945     // multi-player tapes contain input data for more than one player
2946     game.team_mode = (num_players > 1);
2947   }
2948
2949 #if 0
2950   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2951         level.game_version);
2952   Debug("game:init:level", "          tape.file_version   == %06d",
2953         tape.file_version);
2954   Debug("game:init:level", "          tape.game_version   == %06d",
2955         tape.game_version);
2956   Debug("game:init:level", "          tape.engine_version == %06d",
2957         tape.engine_version);
2958   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2959         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2960 #endif
2961
2962   // --------------------------------------------------------------------------
2963   // set flags for bugs and changes according to active game engine version
2964   // --------------------------------------------------------------------------
2965
2966   /*
2967     Summary of bugfix:
2968     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2969
2970     Bug was introduced in version:
2971     2.0.1
2972
2973     Bug was fixed in version:
2974     4.2.0.0
2975
2976     Description:
2977     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2978     but the property "can fall" was missing, which caused some levels to be
2979     unsolvable. This was fixed in version 4.2.0.0.
2980
2981     Affected levels/tapes:
2982     An example for a tape that was fixed by this bugfix is tape 029 from the
2983     level set "rnd_sam_bateman".
2984     The wrong behaviour will still be used for all levels or tapes that were
2985     created/recorded with it. An example for this is tape 023 from the level
2986     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2987   */
2988
2989   boolean use_amoeba_dropping_cannot_fall_bug =
2990     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2991       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2992      (tape.playing &&
2993       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2994       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2995
2996   /*
2997     Summary of bugfix/change:
2998     Fixed move speed of elements entering or leaving magic wall.
2999
3000     Fixed/changed in version:
3001     2.0.1
3002
3003     Description:
3004     Before 2.0.1, move speed of elements entering or leaving magic wall was
3005     twice as fast as it is now.
3006     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3007
3008     Affected levels/tapes:
3009     The first condition is generally needed for all levels/tapes before version
3010     2.0.1, which might use the old behaviour before it was changed; known tapes
3011     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3012     The second condition is an exception from the above case and is needed for
3013     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3014     above, but before it was known that this change would break tapes like the
3015     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3016     although the engine version while recording maybe was before 2.0.1. There
3017     are a lot of tapes that are affected by this exception, like tape 006 from
3018     the level set "rnd_conor_mancone".
3019   */
3020
3021   boolean use_old_move_stepsize_for_magic_wall =
3022     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3023      !(tape.playing &&
3024        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3025        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3026
3027   /*
3028     Summary of bugfix/change:
3029     Fixed handling for custom elements that change when pushed by the player.
3030
3031     Fixed/changed in version:
3032     3.1.0
3033
3034     Description:
3035     Before 3.1.0, custom elements that "change when pushing" changed directly
3036     after the player started pushing them (until then handled in "DigField()").
3037     Since 3.1.0, these custom elements are not changed until the "pushing"
3038     move of the element is finished (now handled in "ContinueMoving()").
3039
3040     Affected levels/tapes:
3041     The first condition is generally needed for all levels/tapes before version
3042     3.1.0, which might use the old behaviour before it was changed; known tapes
3043     that are affected are some tapes from the level set "Walpurgis Gardens" by
3044     Jamie Cullen.
3045     The second condition is an exception from the above case and is needed for
3046     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3047     above (including some development versions of 3.1.0), but before it was
3048     known that this change would break tapes like the above and was fixed in
3049     3.1.1, so that the changed behaviour was active although the engine version
3050     while recording maybe was before 3.1.0. There is at least one tape that is
3051     affected by this exception, which is the tape for the one-level set "Bug
3052     Machine" by Juergen Bonhagen.
3053   */
3054
3055   game.use_change_when_pushing_bug =
3056     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3057      !(tape.playing &&
3058        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3059        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3060
3061   /*
3062     Summary of bugfix/change:
3063     Fixed handling for blocking the field the player leaves when moving.
3064
3065     Fixed/changed in version:
3066     3.1.1
3067
3068     Description:
3069     Before 3.1.1, when "block last field when moving" was enabled, the field
3070     the player is leaving when moving was blocked for the time of the move,
3071     and was directly unblocked afterwards. This resulted in the last field
3072     being blocked for exactly one less than the number of frames of one player
3073     move. Additionally, even when blocking was disabled, the last field was
3074     blocked for exactly one frame.
3075     Since 3.1.1, due to changes in player movement handling, the last field
3076     is not blocked at all when blocking is disabled. When blocking is enabled,
3077     the last field is blocked for exactly the number of frames of one player
3078     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3079     last field is blocked for exactly one more than the number of frames of
3080     one player move.
3081
3082     Affected levels/tapes:
3083     (!!! yet to be determined -- probably many !!!)
3084   */
3085
3086   game.use_block_last_field_bug =
3087     (game.engine_version < VERSION_IDENT(3,1,1,0));
3088
3089   /* various special flags and settings for native Emerald Mine game engine */
3090
3091   game_em.use_single_button =
3092     (game.engine_version > VERSION_IDENT(4,0,0,2));
3093
3094   game_em.use_snap_key_bug =
3095     (game.engine_version < VERSION_IDENT(4,0,1,0));
3096
3097   game_em.use_random_bug =
3098     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3099
3100   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3101
3102   game_em.use_old_explosions            = use_old_em_engine;
3103   game_em.use_old_android               = use_old_em_engine;
3104   game_em.use_old_push_elements         = use_old_em_engine;
3105   game_em.use_old_push_into_acid        = use_old_em_engine;
3106
3107   game_em.use_wrap_around               = !use_old_em_engine;
3108
3109   // --------------------------------------------------------------------------
3110
3111   // set maximal allowed number of custom element changes per game frame
3112   game.max_num_changes_per_frame = 1;
3113
3114   // default scan direction: scan playfield from top/left to bottom/right
3115   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3116
3117   // dynamically adjust element properties according to game engine version
3118   InitElementPropertiesEngine(game.engine_version);
3119
3120   // ---------- initialize special element properties -------------------------
3121
3122   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3123   if (use_amoeba_dropping_cannot_fall_bug)
3124     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3125
3126   // ---------- initialize player's initial move delay ------------------------
3127
3128   // dynamically adjust player properties according to level information
3129   for (i = 0; i < MAX_PLAYERS; i++)
3130     game.initial_move_delay_value[i] =
3131       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3132
3133   // dynamically adjust player properties according to game engine version
3134   for (i = 0; i < MAX_PLAYERS; i++)
3135     game.initial_move_delay[i] =
3136       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3137        game.initial_move_delay_value[i] : 0);
3138
3139   // ---------- initialize player's initial push delay ------------------------
3140
3141   // dynamically adjust player properties according to game engine version
3142   game.initial_push_delay_value =
3143     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3144
3145   // ---------- initialize changing elements ----------------------------------
3146
3147   // initialize changing elements information
3148   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3149   {
3150     struct ElementInfo *ei = &element_info[i];
3151
3152     // this pointer might have been changed in the level editor
3153     ei->change = &ei->change_page[0];
3154
3155     if (!IS_CUSTOM_ELEMENT(i))
3156     {
3157       ei->change->target_element = EL_EMPTY_SPACE;
3158       ei->change->delay_fixed = 0;
3159       ei->change->delay_random = 0;
3160       ei->change->delay_frames = 1;
3161     }
3162
3163     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3164     {
3165       ei->has_change_event[j] = FALSE;
3166
3167       ei->event_page_nr[j] = 0;
3168       ei->event_page[j] = &ei->change_page[0];
3169     }
3170   }
3171
3172   // add changing elements from pre-defined list
3173   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3174   {
3175     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3176     struct ElementInfo *ei = &element_info[ch_delay->element];
3177
3178     ei->change->target_element       = ch_delay->target_element;
3179     ei->change->delay_fixed          = ch_delay->change_delay;
3180
3181     ei->change->pre_change_function  = ch_delay->pre_change_function;
3182     ei->change->change_function      = ch_delay->change_function;
3183     ei->change->post_change_function = ch_delay->post_change_function;
3184
3185     ei->change->can_change = TRUE;
3186     ei->change->can_change_or_has_action = TRUE;
3187
3188     ei->has_change_event[CE_DELAY] = TRUE;
3189
3190     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3191     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3192   }
3193
3194   // ---------- initialize internal run-time variables ------------------------
3195
3196   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3197   {
3198     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3199
3200     for (j = 0; j < ei->num_change_pages; j++)
3201     {
3202       ei->change_page[j].can_change_or_has_action =
3203         (ei->change_page[j].can_change |
3204          ei->change_page[j].has_action);
3205     }
3206   }
3207
3208   // add change events from custom element configuration
3209   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3210   {
3211     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3212
3213     for (j = 0; j < ei->num_change_pages; j++)
3214     {
3215       if (!ei->change_page[j].can_change_or_has_action)
3216         continue;
3217
3218       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3219       {
3220         // only add event page for the first page found with this event
3221         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3222         {
3223           ei->has_change_event[k] = TRUE;
3224
3225           ei->event_page_nr[k] = j;
3226           ei->event_page[k] = &ei->change_page[j];
3227         }
3228       }
3229     }
3230   }
3231
3232   // ---------- initialize reference elements in change conditions ------------
3233
3234   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3235   {
3236     int element = EL_CUSTOM_START + i;
3237     struct ElementInfo *ei = &element_info[element];
3238
3239     for (j = 0; j < ei->num_change_pages; j++)
3240     {
3241       int trigger_element = ei->change_page[j].initial_trigger_element;
3242
3243       if (trigger_element >= EL_PREV_CE_8 &&
3244           trigger_element <= EL_NEXT_CE_8)
3245         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3246
3247       ei->change_page[j].trigger_element = trigger_element;
3248     }
3249   }
3250
3251   // ---------- initialize run-time trigger player and element ----------------
3252
3253   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3254   {
3255     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3256
3257     for (j = 0; j < ei->num_change_pages; j++)
3258     {
3259       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3260       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3261       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3262       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3263       ei->change_page[j].actual_trigger_ce_value = 0;
3264       ei->change_page[j].actual_trigger_ce_score = 0;
3265     }
3266   }
3267
3268   // ---------- initialize trigger events -------------------------------------
3269
3270   // initialize trigger events information
3271   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3272     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3273       trigger_events[i][j] = FALSE;
3274
3275   // add trigger events from element change event properties
3276   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3277   {
3278     struct ElementInfo *ei = &element_info[i];
3279
3280     for (j = 0; j < ei->num_change_pages; j++)
3281     {
3282       if (!ei->change_page[j].can_change_or_has_action)
3283         continue;
3284
3285       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3286       {
3287         int trigger_element = ei->change_page[j].trigger_element;
3288
3289         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3290         {
3291           if (ei->change_page[j].has_event[k])
3292           {
3293             if (IS_GROUP_ELEMENT(trigger_element))
3294             {
3295               struct ElementGroupInfo *group =
3296                 element_info[trigger_element].group;
3297
3298               for (l = 0; l < group->num_elements_resolved; l++)
3299                 trigger_events[group->element_resolved[l]][k] = TRUE;
3300             }
3301             else if (trigger_element == EL_ANY_ELEMENT)
3302               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3303                 trigger_events[l][k] = TRUE;
3304             else
3305               trigger_events[trigger_element][k] = TRUE;
3306           }
3307         }
3308       }
3309     }
3310   }
3311
3312   // ---------- initialize push delay -----------------------------------------
3313
3314   // initialize push delay values to default
3315   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3316   {
3317     if (!IS_CUSTOM_ELEMENT(i))
3318     {
3319       // set default push delay values (corrected since version 3.0.7-1)
3320       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3321       {
3322         element_info[i].push_delay_fixed = 2;
3323         element_info[i].push_delay_random = 8;
3324       }
3325       else
3326       {
3327         element_info[i].push_delay_fixed = 8;
3328         element_info[i].push_delay_random = 8;
3329       }
3330     }
3331   }
3332
3333   // set push delay value for certain elements from pre-defined list
3334   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3335   {
3336     int e = push_delay_list[i].element;
3337
3338     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3339     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3340   }
3341
3342   // set push delay value for Supaplex elements for newer engine versions
3343   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3344   {
3345     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3346     {
3347       if (IS_SP_ELEMENT(i))
3348       {
3349         // set SP push delay to just enough to push under a falling zonk
3350         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3351
3352         element_info[i].push_delay_fixed  = delay;
3353         element_info[i].push_delay_random = 0;
3354       }
3355     }
3356   }
3357
3358   // ---------- initialize move stepsize --------------------------------------
3359
3360   // initialize move stepsize values to default
3361   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3362     if (!IS_CUSTOM_ELEMENT(i))
3363       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3364
3365   // set move stepsize value for certain elements from pre-defined list
3366   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3367   {
3368     int e = move_stepsize_list[i].element;
3369
3370     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3371
3372     // set move stepsize value for certain elements for older engine versions
3373     if (use_old_move_stepsize_for_magic_wall)
3374     {
3375       if (e == EL_MAGIC_WALL_FILLING ||
3376           e == EL_MAGIC_WALL_EMPTYING ||
3377           e == EL_BD_MAGIC_WALL_FILLING ||
3378           e == EL_BD_MAGIC_WALL_EMPTYING)
3379         element_info[e].move_stepsize *= 2;
3380     }
3381   }
3382
3383   // ---------- initialize collect score --------------------------------------
3384
3385   // initialize collect score values for custom elements from initial value
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387     if (IS_CUSTOM_ELEMENT(i))
3388       element_info[i].collect_score = element_info[i].collect_score_initial;
3389
3390   // ---------- initialize collect count --------------------------------------
3391
3392   // initialize collect count values for non-custom elements
3393   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3394     if (!IS_CUSTOM_ELEMENT(i))
3395       element_info[i].collect_count_initial = 0;
3396
3397   // add collect count values for all elements from pre-defined list
3398   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3399     element_info[collect_count_list[i].element].collect_count_initial =
3400       collect_count_list[i].count;
3401
3402   // ---------- initialize access direction -----------------------------------
3403
3404   // initialize access direction values to default (access from every side)
3405   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3406     if (!IS_CUSTOM_ELEMENT(i))
3407       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3408
3409   // set access direction value for certain elements from pre-defined list
3410   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3411     element_info[access_direction_list[i].element].access_direction =
3412       access_direction_list[i].direction;
3413
3414   // ---------- initialize explosion content ----------------------------------
3415   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3416   {
3417     if (IS_CUSTOM_ELEMENT(i))
3418       continue;
3419
3420     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3421     {
3422       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3423
3424       element_info[i].content.e[x][y] =
3425         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3426          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3427          i == EL_PLAYER_3 ? EL_EMERALD :
3428          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3429          i == EL_MOLE ? EL_EMERALD_RED :
3430          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3431          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3432          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3433          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3434          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3435          i == EL_WALL_EMERALD ? EL_EMERALD :
3436          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3437          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3438          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3439          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3440          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3441          i == EL_WALL_PEARL ? EL_PEARL :
3442          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3443          EL_EMPTY);
3444     }
3445   }
3446
3447   // ---------- initialize recursion detection --------------------------------
3448   recursion_loop_depth = 0;
3449   recursion_loop_detected = FALSE;
3450   recursion_loop_element = EL_UNDEFINED;
3451
3452   // ---------- initialize graphics engine ------------------------------------
3453   game.scroll_delay_value =
3454     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3455      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3456      !setup.forced_scroll_delay           ? 0 :
3457      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3458   game.scroll_delay_value =
3459     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3460
3461   // ---------- initialize game engine snapshots ------------------------------
3462   for (i = 0; i < MAX_PLAYERS; i++)
3463     game.snapshot.last_action[i] = 0;
3464   game.snapshot.changed_action = FALSE;
3465   game.snapshot.collected_item = FALSE;
3466   game.snapshot.mode =
3467     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3468      SNAPSHOT_MODE_EVERY_STEP :
3469      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3470      SNAPSHOT_MODE_EVERY_MOVE :
3471      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3472      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3473   game.snapshot.save_snapshot = FALSE;
3474
3475   // ---------- initialize level time for Supaplex engine ---------------------
3476   // Supaplex levels with time limit currently unsupported -- should be added
3477   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3478     level.time = 0;
3479
3480   // ---------- initialize flags for handling game actions --------------------
3481
3482   // set flags for game actions to default values
3483   game.use_key_actions = TRUE;
3484   game.use_mouse_actions = FALSE;
3485
3486   // when using Mirror Magic game engine, handle mouse events only
3487   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3488   {
3489     game.use_key_actions = FALSE;
3490     game.use_mouse_actions = TRUE;
3491   }
3492
3493   // check for custom elements with mouse click events
3494   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3495   {
3496     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3497     {
3498       int element = EL_CUSTOM_START + i;
3499
3500       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3501           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3502           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3503           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3504         game.use_mouse_actions = TRUE;
3505     }
3506   }
3507 }
3508
3509 static int get_num_special_action(int element, int action_first,
3510                                   int action_last)
3511 {
3512   int num_special_action = 0;
3513   int i, j;
3514
3515   for (i = action_first; i <= action_last; i++)
3516   {
3517     boolean found = FALSE;
3518
3519     for (j = 0; j < NUM_DIRECTIONS; j++)
3520       if (el_act_dir2img(element, i, j) !=
3521           el_act_dir2img(element, ACTION_DEFAULT, j))
3522         found = TRUE;
3523
3524     if (found)
3525       num_special_action++;
3526     else
3527       break;
3528   }
3529
3530   return num_special_action;
3531 }
3532
3533
3534 // ============================================================================
3535 // InitGame()
3536 // ----------------------------------------------------------------------------
3537 // initialize and start new game
3538 // ============================================================================
3539
3540 #if DEBUG_INIT_PLAYER
3541 static void DebugPrintPlayerStatus(char *message)
3542 {
3543   int i;
3544
3545   if (!options.debug)
3546     return;
3547
3548   Debug("game:init:player", "%s:", message);
3549
3550   for (i = 0; i < MAX_PLAYERS; i++)
3551   {
3552     struct PlayerInfo *player = &stored_player[i];
3553
3554     Debug("game:init:player",
3555           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3556           i + 1,
3557           player->present,
3558           player->connected,
3559           player->connected_locally,
3560           player->connected_network,
3561           player->active,
3562           (local_player == player ? " (local player)" : ""));
3563   }
3564 }
3565 #endif
3566
3567 void InitGame(void)
3568 {
3569   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3570   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3571   int fade_mask = REDRAW_FIELD;
3572
3573   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3574   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3575   int initial_move_dir = MV_DOWN;
3576   int i, j, x, y;
3577
3578   // required here to update video display before fading (FIX THIS)
3579   DrawMaskedBorder(REDRAW_DOOR_2);
3580
3581   if (!game.restart_level)
3582     CloseDoor(DOOR_CLOSE_1);
3583
3584   SetGameStatus(GAME_MODE_PLAYING);
3585
3586   if (level_editor_test_game)
3587     FadeSkipNextFadeOut();
3588   else
3589     FadeSetEnterScreen();
3590
3591   if (CheckFadeAll())
3592     fade_mask = REDRAW_ALL;
3593
3594   FadeLevelSoundsAndMusic();
3595
3596   ExpireSoundLoops(TRUE);
3597
3598   FadeOut(fade_mask);
3599
3600   if (level_editor_test_game)
3601     FadeSkipNextFadeIn();
3602
3603   // needed if different viewport properties defined for playing
3604   ChangeViewportPropertiesIfNeeded();
3605
3606   ClearField();
3607
3608   DrawCompleteVideoDisplay();
3609
3610   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3611
3612   InitGameEngine();
3613   InitGameControlValues();
3614
3615   if (tape.recording)
3616   {
3617     // initialize tape actions from game when recording tape
3618     tape.use_key_actions   = game.use_key_actions;
3619     tape.use_mouse_actions = game.use_mouse_actions;
3620
3621     // initialize visible playfield size when recording tape (for team mode)
3622     tape.scr_fieldx = SCR_FIELDX;
3623     tape.scr_fieldy = SCR_FIELDY;
3624   }
3625
3626   // don't play tapes over network
3627   network_playing = (network.enabled && !tape.playing);
3628
3629   for (i = 0; i < MAX_PLAYERS; i++)
3630   {
3631     struct PlayerInfo *player = &stored_player[i];
3632
3633     player->index_nr = i;
3634     player->index_bit = (1 << i);
3635     player->element_nr = EL_PLAYER_1 + i;
3636
3637     player->present = FALSE;
3638     player->active = FALSE;
3639     player->mapped = FALSE;
3640
3641     player->killed = FALSE;
3642     player->reanimated = FALSE;
3643     player->buried = FALSE;
3644
3645     player->action = 0;
3646     player->effective_action = 0;
3647     player->programmed_action = 0;
3648     player->snap_action = 0;
3649
3650     player->mouse_action.lx = 0;
3651     player->mouse_action.ly = 0;
3652     player->mouse_action.button = 0;
3653     player->mouse_action.button_hint = 0;
3654
3655     player->effective_mouse_action.lx = 0;
3656     player->effective_mouse_action.ly = 0;
3657     player->effective_mouse_action.button = 0;
3658     player->effective_mouse_action.button_hint = 0;
3659
3660     for (j = 0; j < MAX_NUM_KEYS; j++)
3661       player->key[j] = FALSE;
3662
3663     player->num_white_keys = 0;
3664
3665     player->dynabomb_count = 0;
3666     player->dynabomb_size = 1;
3667     player->dynabombs_left = 0;
3668     player->dynabomb_xl = FALSE;
3669
3670     player->MovDir = initial_move_dir;
3671     player->MovPos = 0;
3672     player->GfxPos = 0;
3673     player->GfxDir = initial_move_dir;
3674     player->GfxAction = ACTION_DEFAULT;
3675     player->Frame = 0;
3676     player->StepFrame = 0;
3677
3678     player->initial_element = player->element_nr;
3679     player->artwork_element =
3680       (level.use_artwork_element[i] ? level.artwork_element[i] :
3681        player->element_nr);
3682     player->use_murphy = FALSE;
3683
3684     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3685     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3686
3687     player->gravity = level.initial_player_gravity[i];
3688
3689     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3690
3691     player->actual_frame_counter.count = 0;
3692     player->actual_frame_counter.value = 1;
3693
3694     player->step_counter = 0;
3695
3696     player->last_move_dir = initial_move_dir;
3697
3698     player->is_active = FALSE;
3699
3700     player->is_waiting = FALSE;
3701     player->is_moving = FALSE;
3702     player->is_auto_moving = FALSE;
3703     player->is_digging = FALSE;
3704     player->is_snapping = FALSE;
3705     player->is_collecting = FALSE;
3706     player->is_pushing = FALSE;
3707     player->is_switching = FALSE;
3708     player->is_dropping = FALSE;
3709     player->is_dropping_pressed = FALSE;
3710
3711     player->is_bored = FALSE;
3712     player->is_sleeping = FALSE;
3713
3714     player->was_waiting = TRUE;
3715     player->was_moving = FALSE;
3716     player->was_snapping = FALSE;
3717     player->was_dropping = FALSE;
3718
3719     player->force_dropping = FALSE;
3720
3721     player->frame_counter_bored = -1;
3722     player->frame_counter_sleeping = -1;
3723
3724     player->anim_delay_counter = 0;
3725     player->post_delay_counter = 0;
3726
3727     player->dir_waiting = initial_move_dir;
3728     player->action_waiting = ACTION_DEFAULT;
3729     player->last_action_waiting = ACTION_DEFAULT;
3730     player->special_action_bored = ACTION_DEFAULT;
3731     player->special_action_sleeping = ACTION_DEFAULT;
3732
3733     player->switch_x = -1;
3734     player->switch_y = -1;
3735
3736     player->drop_x = -1;
3737     player->drop_y = -1;
3738
3739     player->show_envelope = 0;
3740
3741     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3742
3743     player->push_delay       = -1;      // initialized when pushing starts
3744     player->push_delay_value = game.initial_push_delay_value;
3745
3746     player->drop_delay = 0;
3747     player->drop_pressed_delay = 0;
3748
3749     player->last_jx = -1;
3750     player->last_jy = -1;
3751     player->jx = -1;
3752     player->jy = -1;
3753
3754     player->shield_normal_time_left = 0;
3755     player->shield_deadly_time_left = 0;
3756
3757     player->last_removed_element = EL_UNDEFINED;
3758
3759     player->inventory_infinite_element = EL_UNDEFINED;
3760     player->inventory_size = 0;
3761
3762     if (level.use_initial_inventory[i])
3763     {
3764       for (j = 0; j < level.initial_inventory_size[i]; j++)
3765       {
3766         int element = level.initial_inventory_content[i][j];
3767         int collect_count = element_info[element].collect_count_initial;
3768         int k;
3769
3770         if (!IS_CUSTOM_ELEMENT(element))
3771           collect_count = 1;
3772
3773         if (collect_count == 0)
3774           player->inventory_infinite_element = element;
3775         else
3776           for (k = 0; k < collect_count; k++)
3777             if (player->inventory_size < MAX_INVENTORY_SIZE)
3778               player->inventory_element[player->inventory_size++] = element;
3779       }
3780     }
3781
3782     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3783     SnapField(player, 0, 0);
3784
3785     map_player_action[i] = i;
3786   }
3787
3788   network_player_action_received = FALSE;
3789
3790   // initial null action
3791   if (network_playing)
3792     SendToServer_MovePlayer(MV_NONE);
3793
3794   FrameCounter = 0;
3795   TimeFrames = 0;
3796   TimePlayed = 0;
3797   TimeLeft = level.time;
3798   TapeTime = 0;
3799
3800   ScreenMovDir = MV_NONE;
3801   ScreenMovPos = 0;
3802   ScreenGfxPos = 0;
3803
3804   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3805
3806   game.robot_wheel_x = -1;
3807   game.robot_wheel_y = -1;
3808
3809   game.exit_x = -1;
3810   game.exit_y = -1;
3811
3812   game.all_players_gone = FALSE;
3813
3814   game.LevelSolved = FALSE;
3815   game.GameOver = FALSE;
3816
3817   game.GamePlayed = !tape.playing;
3818
3819   game.LevelSolved_GameWon = FALSE;
3820   game.LevelSolved_GameEnd = FALSE;
3821   game.LevelSolved_SaveTape = FALSE;
3822   game.LevelSolved_SaveScore = FALSE;
3823
3824   game.LevelSolved_CountingTime = 0;
3825   game.LevelSolved_CountingScore = 0;
3826   game.LevelSolved_CountingHealth = 0;
3827
3828   game.panel.active = TRUE;
3829
3830   game.no_level_time_limit = (level.time == 0);
3831   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3832
3833   game.yamyam_content_nr = 0;
3834   game.robot_wheel_active = FALSE;
3835   game.magic_wall_active = FALSE;
3836   game.magic_wall_time_left = 0;
3837   game.light_time_left = 0;
3838   game.timegate_time_left = 0;
3839   game.switchgate_pos = 0;
3840   game.wind_direction = level.wind_direction_initial;
3841
3842   game.time_final = 0;
3843   game.score_time_final = 0;
3844
3845   game.score = 0;
3846   game.score_final = 0;
3847
3848   game.health = MAX_HEALTH;
3849   game.health_final = MAX_HEALTH;
3850
3851   game.gems_still_needed = level.gems_needed;
3852   game.sokoban_fields_still_needed = 0;
3853   game.sokoban_objects_still_needed = 0;
3854   game.lights_still_needed = 0;
3855   game.players_still_needed = 0;
3856   game.friends_still_needed = 0;
3857
3858   game.lenses_time_left = 0;
3859   game.magnify_time_left = 0;
3860
3861   game.ball_active = level.ball_active_initial;
3862   game.ball_content_nr = 0;
3863
3864   game.explosions_delayed = TRUE;
3865
3866   game.envelope_active = FALSE;
3867
3868   // special case: set custom artwork setting to initial value
3869   game.use_masked_elements = game.use_masked_elements_initial;
3870
3871   for (i = 0; i < NUM_BELTS; i++)
3872   {
3873     game.belt_dir[i] = MV_NONE;
3874     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3875   }
3876
3877   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3878     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3879
3880 #if DEBUG_INIT_PLAYER
3881   DebugPrintPlayerStatus("Player status at level initialization");
3882 #endif
3883
3884   SCAN_PLAYFIELD(x, y)
3885   {
3886     Tile[x][y] = Last[x][y] = level.field[x][y];
3887     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3888     ChangeDelay[x][y] = 0;
3889     ChangePage[x][y] = -1;
3890     CustomValue[x][y] = 0;              // initialized in InitField()
3891     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3892     AmoebaNr[x][y] = 0;
3893     WasJustMoving[x][y] = 0;
3894     WasJustFalling[x][y] = 0;
3895     CheckCollision[x][y] = 0;
3896     CheckImpact[x][y] = 0;
3897     Stop[x][y] = FALSE;
3898     Pushed[x][y] = FALSE;
3899
3900     ChangeCount[x][y] = 0;
3901     ChangeEvent[x][y] = -1;
3902
3903     ExplodePhase[x][y] = 0;
3904     ExplodeDelay[x][y] = 0;
3905     ExplodeField[x][y] = EX_TYPE_NONE;
3906
3907     RunnerVisit[x][y] = 0;
3908     PlayerVisit[x][y] = 0;
3909
3910     GfxFrame[x][y] = 0;
3911     GfxRandom[x][y] = INIT_GFX_RANDOM();
3912     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3913     GfxElement[x][y] = EL_UNDEFINED;
3914     GfxElementEmpty[x][y] = EL_EMPTY;
3915     GfxAction[x][y] = ACTION_DEFAULT;
3916     GfxDir[x][y] = MV_NONE;
3917     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3918   }
3919
3920   SCAN_PLAYFIELD(x, y)
3921   {
3922     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3923       emulate_bd = FALSE;
3924     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3925       emulate_sp = FALSE;
3926
3927     InitField(x, y, TRUE);
3928
3929     ResetGfxAnimation(x, y);
3930   }
3931
3932   InitBeltMovement();
3933
3934   for (i = 0; i < MAX_PLAYERS; i++)
3935   {
3936     struct PlayerInfo *player = &stored_player[i];
3937
3938     // set number of special actions for bored and sleeping animation
3939     player->num_special_action_bored =
3940       get_num_special_action(player->artwork_element,
3941                              ACTION_BORING_1, ACTION_BORING_LAST);
3942     player->num_special_action_sleeping =
3943       get_num_special_action(player->artwork_element,
3944                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3945   }
3946
3947   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3948                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3949
3950   // initialize type of slippery elements
3951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3952   {
3953     if (!IS_CUSTOM_ELEMENT(i))
3954     {
3955       // default: elements slip down either to the left or right randomly
3956       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3957
3958       // SP style elements prefer to slip down on the left side
3959       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3960         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3961
3962       // BD style elements prefer to slip down on the left side
3963       if (game.emulation == EMU_BOULDERDASH)
3964         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3965     }
3966   }
3967
3968   // initialize explosion and ignition delay
3969   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3970   {
3971     if (!IS_CUSTOM_ELEMENT(i))
3972     {
3973       int num_phase = 8;
3974       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3975                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3976                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3977       int last_phase = (num_phase + 1) * delay;
3978       int half_phase = (num_phase / 2) * delay;
3979
3980       element_info[i].explosion_delay = last_phase - 1;
3981       element_info[i].ignition_delay = half_phase;
3982
3983       if (i == EL_BLACK_ORB)
3984         element_info[i].ignition_delay = 1;
3985     }
3986   }
3987
3988   // correct non-moving belts to start moving left
3989   for (i = 0; i < NUM_BELTS; i++)
3990     if (game.belt_dir[i] == MV_NONE)
3991       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3992
3993 #if USE_NEW_PLAYER_ASSIGNMENTS
3994   // use preferred player also in local single-player mode
3995   if (!network.enabled && !game.team_mode)
3996   {
3997     int new_index_nr = setup.network_player_nr;
3998
3999     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4000     {
4001       for (i = 0; i < MAX_PLAYERS; i++)
4002         stored_player[i].connected_locally = FALSE;
4003
4004       stored_player[new_index_nr].connected_locally = TRUE;
4005     }
4006   }
4007
4008   for (i = 0; i < MAX_PLAYERS; i++)
4009   {
4010     stored_player[i].connected = FALSE;
4011
4012     // in network game mode, the local player might not be the first player
4013     if (stored_player[i].connected_locally)
4014       local_player = &stored_player[i];
4015   }
4016
4017   if (!network.enabled)
4018     local_player->connected = TRUE;
4019
4020   if (tape.playing)
4021   {
4022     for (i = 0; i < MAX_PLAYERS; i++)
4023       stored_player[i].connected = tape.player_participates[i];
4024   }
4025   else if (network.enabled)
4026   {
4027     // add team mode players connected over the network (needed for correct
4028     // assignment of player figures from level to locally playing players)
4029
4030     for (i = 0; i < MAX_PLAYERS; i++)
4031       if (stored_player[i].connected_network)
4032         stored_player[i].connected = TRUE;
4033   }
4034   else if (game.team_mode)
4035   {
4036     // try to guess locally connected team mode players (needed for correct
4037     // assignment of player figures from level to locally playing players)
4038
4039     for (i = 0; i < MAX_PLAYERS; i++)
4040       if (setup.input[i].use_joystick ||
4041           setup.input[i].key.left != KSYM_UNDEFINED)
4042         stored_player[i].connected = TRUE;
4043   }
4044
4045 #if DEBUG_INIT_PLAYER
4046   DebugPrintPlayerStatus("Player status after level initialization");
4047 #endif
4048
4049 #if DEBUG_INIT_PLAYER
4050   Debug("game:init:player", "Reassigning players ...");
4051 #endif
4052
4053   // check if any connected player was not found in playfield
4054   for (i = 0; i < MAX_PLAYERS; i++)
4055   {
4056     struct PlayerInfo *player = &stored_player[i];
4057
4058     if (player->connected && !player->present)
4059     {
4060       struct PlayerInfo *field_player = NULL;
4061
4062 #if DEBUG_INIT_PLAYER
4063       Debug("game:init:player",
4064             "- looking for field player for player %d ...", i + 1);
4065 #endif
4066
4067       // assign first free player found that is present in the playfield
4068
4069       // first try: look for unmapped playfield player that is not connected
4070       for (j = 0; j < MAX_PLAYERS; j++)
4071         if (field_player == NULL &&
4072             stored_player[j].present &&
4073             !stored_player[j].mapped &&
4074             !stored_player[j].connected)
4075           field_player = &stored_player[j];
4076
4077       // second try: look for *any* unmapped playfield player
4078       for (j = 0; j < MAX_PLAYERS; j++)
4079         if (field_player == NULL &&
4080             stored_player[j].present &&
4081             !stored_player[j].mapped)
4082           field_player = &stored_player[j];
4083
4084       if (field_player != NULL)
4085       {
4086         int jx = field_player->jx, jy = field_player->jy;
4087
4088 #if DEBUG_INIT_PLAYER
4089         Debug("game:init:player", "- found player %d",
4090               field_player->index_nr + 1);
4091 #endif
4092
4093         player->present = FALSE;
4094         player->active = FALSE;
4095
4096         field_player->present = TRUE;
4097         field_player->active = TRUE;
4098
4099         /*
4100         player->initial_element = field_player->initial_element;
4101         player->artwork_element = field_player->artwork_element;
4102
4103         player->block_last_field       = field_player->block_last_field;
4104         player->block_delay_adjustment = field_player->block_delay_adjustment;
4105         */
4106
4107         StorePlayer[jx][jy] = field_player->element_nr;
4108
4109         field_player->jx = field_player->last_jx = jx;
4110         field_player->jy = field_player->last_jy = jy;
4111
4112         if (local_player == player)
4113           local_player = field_player;
4114
4115         map_player_action[field_player->index_nr] = i;
4116
4117         field_player->mapped = TRUE;
4118
4119 #if DEBUG_INIT_PLAYER
4120         Debug("game:init:player", "- map_player_action[%d] == %d",
4121               field_player->index_nr + 1, i + 1);
4122 #endif
4123       }
4124     }
4125
4126     if (player->connected && player->present)
4127       player->mapped = TRUE;
4128   }
4129
4130 #if DEBUG_INIT_PLAYER
4131   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4132 #endif
4133
4134 #else
4135
4136   // check if any connected player was not found in playfield
4137   for (i = 0; i < MAX_PLAYERS; i++)
4138   {
4139     struct PlayerInfo *player = &stored_player[i];
4140
4141     if (player->connected && !player->present)
4142     {
4143       for (j = 0; j < MAX_PLAYERS; j++)
4144       {
4145         struct PlayerInfo *field_player = &stored_player[j];
4146         int jx = field_player->jx, jy = field_player->jy;
4147
4148         // assign first free player found that is present in the playfield
4149         if (field_player->present && !field_player->connected)
4150         {
4151           player->present = TRUE;
4152           player->active = TRUE;
4153
4154           field_player->present = FALSE;
4155           field_player->active = FALSE;
4156
4157           player->initial_element = field_player->initial_element;
4158           player->artwork_element = field_player->artwork_element;
4159
4160           player->block_last_field       = field_player->block_last_field;
4161           player->block_delay_adjustment = field_player->block_delay_adjustment;
4162
4163           StorePlayer[jx][jy] = player->element_nr;
4164
4165           player->jx = player->last_jx = jx;
4166           player->jy = player->last_jy = jy;
4167
4168           break;
4169         }
4170       }
4171     }
4172   }
4173 #endif
4174
4175 #if 0
4176   Debug("game:init:player", "local_player->present == %d",
4177         local_player->present);
4178 #endif
4179
4180   // set focus to local player for network games, else to all players
4181   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4182   game.centered_player_nr_next = game.centered_player_nr;
4183   game.set_centered_player = FALSE;
4184   game.set_centered_player_wrap = FALSE;
4185
4186   if (network_playing && tape.recording)
4187   {
4188     // store client dependent player focus when recording network games
4189     tape.centered_player_nr_next = game.centered_player_nr_next;
4190     tape.set_centered_player = TRUE;
4191   }
4192
4193   if (tape.playing)
4194   {
4195     // when playing a tape, eliminate all players who do not participate
4196
4197 #if USE_NEW_PLAYER_ASSIGNMENTS
4198
4199     if (!game.team_mode)
4200     {
4201       for (i = 0; i < MAX_PLAYERS; i++)
4202       {
4203         if (stored_player[i].active &&
4204             !tape.player_participates[map_player_action[i]])
4205         {
4206           struct PlayerInfo *player = &stored_player[i];
4207           int jx = player->jx, jy = player->jy;
4208
4209 #if DEBUG_INIT_PLAYER
4210           Debug("game:init:player", "Removing player %d at (%d, %d)",
4211                 i + 1, jx, jy);
4212 #endif
4213
4214           player->active = FALSE;
4215           StorePlayer[jx][jy] = 0;
4216           Tile[jx][jy] = EL_EMPTY;
4217         }
4218       }
4219     }
4220
4221 #else
4222
4223     for (i = 0; i < MAX_PLAYERS; i++)
4224     {
4225       if (stored_player[i].active &&
4226           !tape.player_participates[i])
4227       {
4228         struct PlayerInfo *player = &stored_player[i];
4229         int jx = player->jx, jy = player->jy;
4230
4231         player->active = FALSE;
4232         StorePlayer[jx][jy] = 0;
4233         Tile[jx][jy] = EL_EMPTY;
4234       }
4235     }
4236 #endif
4237   }
4238   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4239   {
4240     // when in single player mode, eliminate all but the local player
4241
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243     {
4244       struct PlayerInfo *player = &stored_player[i];
4245
4246       if (player->active && player != local_player)
4247       {
4248         int jx = player->jx, jy = player->jy;
4249
4250         player->active = FALSE;
4251         player->present = FALSE;
4252
4253         StorePlayer[jx][jy] = 0;
4254         Tile[jx][jy] = EL_EMPTY;
4255       }
4256     }
4257   }
4258
4259   for (i = 0; i < MAX_PLAYERS; i++)
4260     if (stored_player[i].active)
4261       game.players_still_needed++;
4262
4263   if (level.solved_by_one_player)
4264     game.players_still_needed = 1;
4265
4266   // when recording the game, store which players take part in the game
4267   if (tape.recording)
4268   {
4269 #if USE_NEW_PLAYER_ASSIGNMENTS
4270     for (i = 0; i < MAX_PLAYERS; i++)
4271       if (stored_player[i].connected)
4272         tape.player_participates[i] = TRUE;
4273 #else
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275       if (stored_player[i].active)
4276         tape.player_participates[i] = TRUE;
4277 #endif
4278   }
4279
4280 #if DEBUG_INIT_PLAYER
4281   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4282 #endif
4283
4284   if (BorderElement == EL_EMPTY)
4285   {
4286     SBX_Left = 0;
4287     SBX_Right = lev_fieldx - SCR_FIELDX;
4288     SBY_Upper = 0;
4289     SBY_Lower = lev_fieldy - SCR_FIELDY;
4290   }
4291   else
4292   {
4293     SBX_Left = -1;
4294     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4295     SBY_Upper = -1;
4296     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4297   }
4298
4299   if (full_lev_fieldx <= SCR_FIELDX)
4300     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4301   if (full_lev_fieldy <= SCR_FIELDY)
4302     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4303
4304   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4305     SBX_Left--;
4306   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4307     SBY_Upper--;
4308
4309   // if local player not found, look for custom element that might create
4310   // the player (make some assumptions about the right custom element)
4311   if (!local_player->present)
4312   {
4313     int start_x = 0, start_y = 0;
4314     int found_rating = 0;
4315     int found_element = EL_UNDEFINED;
4316     int player_nr = local_player->index_nr;
4317
4318     SCAN_PLAYFIELD(x, y)
4319     {
4320       int element = Tile[x][y];
4321       int content;
4322       int xx, yy;
4323       boolean is_player;
4324
4325       if (level.use_start_element[player_nr] &&
4326           level.start_element[player_nr] == element &&
4327           found_rating < 4)
4328       {
4329         start_x = x;
4330         start_y = y;
4331
4332         found_rating = 4;
4333         found_element = element;
4334       }
4335
4336       if (!IS_CUSTOM_ELEMENT(element))
4337         continue;
4338
4339       if (CAN_CHANGE(element))
4340       {
4341         for (i = 0; i < element_info[element].num_change_pages; i++)
4342         {
4343           // check for player created from custom element as single target
4344           content = element_info[element].change_page[i].target_element;
4345           is_player = IS_PLAYER_ELEMENT(content);
4346
4347           if (is_player && (found_rating < 3 ||
4348                             (found_rating == 3 && element < found_element)))
4349           {
4350             start_x = x;
4351             start_y = y;
4352
4353             found_rating = 3;
4354             found_element = element;
4355           }
4356         }
4357       }
4358
4359       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4360       {
4361         // check for player created from custom element as explosion content
4362         content = element_info[element].content.e[xx][yy];
4363         is_player = IS_PLAYER_ELEMENT(content);
4364
4365         if (is_player && (found_rating < 2 ||
4366                           (found_rating == 2 && element < found_element)))
4367         {
4368           start_x = x + xx - 1;
4369           start_y = y + yy - 1;
4370
4371           found_rating = 2;
4372           found_element = element;
4373         }
4374
4375         if (!CAN_CHANGE(element))
4376           continue;
4377
4378         for (i = 0; i < element_info[element].num_change_pages; i++)
4379         {
4380           // check for player created from custom element as extended target
4381           content =
4382             element_info[element].change_page[i].target_content.e[xx][yy];
4383
4384           is_player = IS_PLAYER_ELEMENT(content);
4385
4386           if (is_player && (found_rating < 1 ||
4387                             (found_rating == 1 && element < found_element)))
4388           {
4389             start_x = x + xx - 1;
4390             start_y = y + yy - 1;
4391
4392             found_rating = 1;
4393             found_element = element;
4394           }
4395         }
4396       }
4397     }
4398
4399     scroll_x = SCROLL_POSITION_X(start_x);
4400     scroll_y = SCROLL_POSITION_Y(start_y);
4401   }
4402   else
4403   {
4404     scroll_x = SCROLL_POSITION_X(local_player->jx);
4405     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4406   }
4407
4408   // !!! FIX THIS (START) !!!
4409   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4410   {
4411     InitGameEngine_EM();
4412   }
4413   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4414   {
4415     InitGameEngine_SP();
4416   }
4417   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4418   {
4419     InitGameEngine_MM();
4420   }
4421   else
4422   {
4423     DrawLevel(REDRAW_FIELD);
4424     DrawAllPlayers();
4425
4426     // after drawing the level, correct some elements
4427     if (game.timegate_time_left == 0)
4428       CloseAllOpenTimegates();
4429   }
4430
4431   // blit playfield from scroll buffer to normal back buffer for fading in
4432   BlitScreenToBitmap(backbuffer);
4433   // !!! FIX THIS (END) !!!
4434
4435   DrawMaskedBorder(fade_mask);
4436
4437   FadeIn(fade_mask);
4438
4439 #if 1
4440   // full screen redraw is required at this point in the following cases:
4441   // - special editor door undrawn when game was started from level editor
4442   // - drawing area (playfield) was changed and has to be removed completely
4443   redraw_mask = REDRAW_ALL;
4444   BackToFront();
4445 #endif
4446
4447   if (!game.restart_level)
4448   {
4449     // copy default game door content to main double buffer
4450
4451     // !!! CHECK AGAIN !!!
4452     SetPanelBackground();
4453     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4454     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4455   }
4456
4457   SetPanelBackground();
4458   SetDrawBackgroundMask(REDRAW_DOOR_1);
4459
4460   UpdateAndDisplayGameControlValues();
4461
4462   if (!game.restart_level)
4463   {
4464     UnmapGameButtons();
4465     UnmapTapeButtons();
4466
4467     FreeGameButtons();
4468     CreateGameButtons();
4469
4470     MapGameButtons();
4471     MapTapeButtons();
4472
4473     // copy actual game door content to door double buffer for OpenDoor()
4474     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4475
4476     OpenDoor(DOOR_OPEN_ALL);
4477
4478     KeyboardAutoRepeatOffUnlessAutoplay();
4479
4480 #if DEBUG_INIT_PLAYER
4481     DebugPrintPlayerStatus("Player status (final)");
4482 #endif
4483   }
4484
4485   UnmapAllGadgets();
4486
4487   MapGameButtons();
4488   MapTapeButtons();
4489
4490   if (!game.restart_level && !tape.playing)
4491   {
4492     LevelStats_incPlayed(level_nr);
4493
4494     SaveLevelSetup_SeriesInfo();
4495   }
4496
4497   game.restart_level = FALSE;
4498   game.restart_game_message = NULL;
4499
4500   game.request_active = FALSE;
4501   game.request_active_or_moving = FALSE;
4502
4503   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4504     InitGameActions_MM();
4505
4506   SaveEngineSnapshotToListInitial();
4507
4508   if (!game.restart_level)
4509   {
4510     PlaySound(SND_GAME_STARTING);
4511
4512     if (setup.sound_music)
4513       PlayLevelMusic();
4514   }
4515
4516   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4517 }
4518
4519 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4520                         int actual_player_x, int actual_player_y)
4521 {
4522   // this is used for non-R'n'D game engines to update certain engine values
4523
4524   // needed to determine if sounds are played within the visible screen area
4525   scroll_x = actual_scroll_x;
4526   scroll_y = actual_scroll_y;
4527
4528   // needed to get player position for "follow finger" playing input method
4529   local_player->jx = actual_player_x;
4530   local_player->jy = actual_player_y;
4531 }
4532
4533 void InitMovDir(int x, int y)
4534 {
4535   int i, element = Tile[x][y];
4536   static int xy[4][2] =
4537   {
4538     {  0, +1 },
4539     { +1,  0 },
4540     {  0, -1 },
4541     { -1,  0 }
4542   };
4543   static int direction[3][4] =
4544   {
4545     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4546     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4547     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4548   };
4549
4550   switch (element)
4551   {
4552     case EL_BUG_RIGHT:
4553     case EL_BUG_UP:
4554     case EL_BUG_LEFT:
4555     case EL_BUG_DOWN:
4556       Tile[x][y] = EL_BUG;
4557       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4558       break;
4559
4560     case EL_SPACESHIP_RIGHT:
4561     case EL_SPACESHIP_UP:
4562     case EL_SPACESHIP_LEFT:
4563     case EL_SPACESHIP_DOWN:
4564       Tile[x][y] = EL_SPACESHIP;
4565       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4566       break;
4567
4568     case EL_BD_BUTTERFLY_RIGHT:
4569     case EL_BD_BUTTERFLY_UP:
4570     case EL_BD_BUTTERFLY_LEFT:
4571     case EL_BD_BUTTERFLY_DOWN:
4572       Tile[x][y] = EL_BD_BUTTERFLY;
4573       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4574       break;
4575
4576     case EL_BD_FIREFLY_RIGHT:
4577     case EL_BD_FIREFLY_UP:
4578     case EL_BD_FIREFLY_LEFT:
4579     case EL_BD_FIREFLY_DOWN:
4580       Tile[x][y] = EL_BD_FIREFLY;
4581       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4582       break;
4583
4584     case EL_PACMAN_RIGHT:
4585     case EL_PACMAN_UP:
4586     case EL_PACMAN_LEFT:
4587     case EL_PACMAN_DOWN:
4588       Tile[x][y] = EL_PACMAN;
4589       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4590       break;
4591
4592     case EL_YAMYAM_LEFT:
4593     case EL_YAMYAM_RIGHT:
4594     case EL_YAMYAM_UP:
4595     case EL_YAMYAM_DOWN:
4596       Tile[x][y] = EL_YAMYAM;
4597       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4598       break;
4599
4600     case EL_SP_SNIKSNAK:
4601       MovDir[x][y] = MV_UP;
4602       break;
4603
4604     case EL_SP_ELECTRON:
4605       MovDir[x][y] = MV_LEFT;
4606       break;
4607
4608     case EL_MOLE_LEFT:
4609     case EL_MOLE_RIGHT:
4610     case EL_MOLE_UP:
4611     case EL_MOLE_DOWN:
4612       Tile[x][y] = EL_MOLE;
4613       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4614       break;
4615
4616     case EL_SPRING_LEFT:
4617     case EL_SPRING_RIGHT:
4618       Tile[x][y] = EL_SPRING;
4619       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4620       break;
4621
4622     default:
4623       if (IS_CUSTOM_ELEMENT(element))
4624       {
4625         struct ElementInfo *ei = &element_info[element];
4626         int move_direction_initial = ei->move_direction_initial;
4627         int move_pattern = ei->move_pattern;
4628
4629         if (move_direction_initial == MV_START_PREVIOUS)
4630         {
4631           if (MovDir[x][y] != MV_NONE)
4632             return;
4633
4634           move_direction_initial = MV_START_AUTOMATIC;
4635         }
4636
4637         if (move_direction_initial == MV_START_RANDOM)
4638           MovDir[x][y] = 1 << RND(4);
4639         else if (move_direction_initial & MV_ANY_DIRECTION)
4640           MovDir[x][y] = move_direction_initial;
4641         else if (move_pattern == MV_ALL_DIRECTIONS ||
4642                  move_pattern == MV_TURNING_LEFT ||
4643                  move_pattern == MV_TURNING_RIGHT ||
4644                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4645                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4646                  move_pattern == MV_TURNING_RANDOM)
4647           MovDir[x][y] = 1 << RND(4);
4648         else if (move_pattern == MV_HORIZONTAL)
4649           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4650         else if (move_pattern == MV_VERTICAL)
4651           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4652         else if (move_pattern & MV_ANY_DIRECTION)
4653           MovDir[x][y] = element_info[element].move_pattern;
4654         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4655                  move_pattern == MV_ALONG_RIGHT_SIDE)
4656         {
4657           // use random direction as default start direction
4658           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4659             MovDir[x][y] = 1 << RND(4);
4660
4661           for (i = 0; i < NUM_DIRECTIONS; i++)
4662           {
4663             int x1 = x + xy[i][0];
4664             int y1 = y + xy[i][1];
4665
4666             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4667             {
4668               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4669                 MovDir[x][y] = direction[0][i];
4670               else
4671                 MovDir[x][y] = direction[1][i];
4672
4673               break;
4674             }
4675           }
4676         }                
4677       }
4678       else
4679       {
4680         MovDir[x][y] = 1 << RND(4);
4681
4682         if (element != EL_BUG &&
4683             element != EL_SPACESHIP &&
4684             element != EL_BD_BUTTERFLY &&
4685             element != EL_BD_FIREFLY)
4686           break;
4687
4688         for (i = 0; i < NUM_DIRECTIONS; i++)
4689         {
4690           int x1 = x + xy[i][0];
4691           int y1 = y + xy[i][1];
4692
4693           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4694           {
4695             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4696             {
4697               MovDir[x][y] = direction[0][i];
4698               break;
4699             }
4700             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4701                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4702             {
4703               MovDir[x][y] = direction[1][i];
4704               break;
4705             }
4706           }
4707         }
4708       }
4709       break;
4710   }
4711
4712   GfxDir[x][y] = MovDir[x][y];
4713 }
4714
4715 void InitAmoebaNr(int x, int y)
4716 {
4717   int i;
4718   int group_nr = AmoebaNeighbourNr(x, y);
4719
4720   if (group_nr == 0)
4721   {
4722     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4723     {
4724       if (AmoebaCnt[i] == 0)
4725       {
4726         group_nr = i;
4727         break;
4728       }
4729     }
4730   }
4731
4732   AmoebaNr[x][y] = group_nr;
4733   AmoebaCnt[group_nr]++;
4734   AmoebaCnt2[group_nr]++;
4735 }
4736
4737 static void LevelSolved_SetFinalGameValues(void)
4738 {
4739   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4740   game.score_time_final = (level.use_step_counter ? TimePlayed :
4741                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4742
4743   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4744                       game_em.lev->score :
4745                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4746                       game_mm.score :
4747                       game.score);
4748
4749   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4750                        MM_HEALTH(game_mm.laser_overload_value) :
4751                        game.health);
4752
4753   game.LevelSolved_CountingTime = game.time_final;
4754   game.LevelSolved_CountingScore = game.score_final;
4755   game.LevelSolved_CountingHealth = game.health_final;
4756 }
4757
4758 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4759 {
4760   game.LevelSolved_CountingTime = time;
4761   game.LevelSolved_CountingScore = score;
4762   game.LevelSolved_CountingHealth = health;
4763
4764   game_panel_controls[GAME_PANEL_TIME].value = time;
4765   game_panel_controls[GAME_PANEL_SCORE].value = score;
4766   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4767
4768   DisplayGameControlValues();
4769 }
4770
4771 static void LevelSolved(void)
4772 {
4773   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4774       game.players_still_needed > 0)
4775     return;
4776
4777   game.LevelSolved = TRUE;
4778   game.GameOver = TRUE;
4779
4780   // needed here to display correct panel values while player walks into exit
4781   LevelSolved_SetFinalGameValues();
4782 }
4783
4784 void GameWon(void)
4785 {
4786   static int time_count_steps;
4787   static int time, time_final;
4788   static float score, score_final; // needed for time score < 10 for 10 seconds
4789   static int health, health_final;
4790   static int game_over_delay_1 = 0;
4791   static int game_over_delay_2 = 0;
4792   static int game_over_delay_3 = 0;
4793   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4794   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4795
4796   if (!game.LevelSolved_GameWon)
4797   {
4798     int i;
4799
4800     // do not start end game actions before the player stops moving (to exit)
4801     if (local_player->active && local_player->MovPos)
4802       return;
4803
4804     // calculate final game values after player finished walking into exit
4805     LevelSolved_SetFinalGameValues();
4806
4807     game.LevelSolved_GameWon = TRUE;
4808     game.LevelSolved_SaveTape = tape.recording;
4809     game.LevelSolved_SaveScore = !tape.playing;
4810
4811     if (!tape.playing)
4812     {
4813       LevelStats_incSolved(level_nr);
4814
4815       SaveLevelSetup_SeriesInfo();
4816     }
4817
4818     if (tape.auto_play)         // tape might already be stopped here
4819       tape.auto_play_level_solved = TRUE;
4820
4821     TapeStop();
4822
4823     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4824     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4825     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4826
4827     time = time_final = game.time_final;
4828     score = score_final = game.score_final;
4829     health = health_final = game.health_final;
4830
4831     // update game panel values before (delayed) counting of score (if any)
4832     LevelSolved_DisplayFinalGameValues(time, score, health);
4833
4834     // if level has time score defined, calculate new final game values
4835     if (time_score > 0)
4836     {
4837       int time_final_max = 999;
4838       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4839       int time_frames = 0;
4840       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4841       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4842
4843       if (TimeLeft > 0)
4844       {
4845         time_final = 0;
4846         time_frames = time_frames_left;
4847       }
4848       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4849       {
4850         time_final = time_final_max;
4851         time_frames = time_frames_final_max - time_frames_played;
4852       }
4853
4854       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4855
4856       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4857
4858       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4859       {
4860         health_final = 0;
4861         score_final += health * time_score;
4862       }
4863
4864       game.score_final = score_final;
4865       game.health_final = health_final;
4866     }
4867
4868     // if not counting score after game, immediately update game panel values
4869     if (level_editor_test_game || !setup.count_score_after_game)
4870     {
4871       time = time_final;
4872       score = score_final;
4873
4874       LevelSolved_DisplayFinalGameValues(time, score, health);
4875     }
4876
4877     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4878     {
4879       // check if last player has left the level
4880       if (game.exit_x >= 0 &&
4881           game.exit_y >= 0)
4882       {
4883         int x = game.exit_x;
4884         int y = game.exit_y;
4885         int element = Tile[x][y];
4886
4887         // close exit door after last player
4888         if ((game.all_players_gone &&
4889              (element == EL_EXIT_OPEN ||
4890               element == EL_SP_EXIT_OPEN ||
4891               element == EL_STEEL_EXIT_OPEN)) ||
4892             element == EL_EM_EXIT_OPEN ||
4893             element == EL_EM_STEEL_EXIT_OPEN)
4894         {
4895
4896           Tile[x][y] =
4897             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4898              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4899              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4900              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4901              EL_EM_STEEL_EXIT_CLOSING);
4902
4903           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4904         }
4905
4906         // player disappears
4907         DrawLevelField(x, y);
4908       }
4909
4910       for (i = 0; i < MAX_PLAYERS; i++)
4911       {
4912         struct PlayerInfo *player = &stored_player[i];
4913
4914         if (player->present)
4915         {
4916           RemovePlayer(player);
4917
4918           // player disappears
4919           DrawLevelField(player->jx, player->jy);
4920         }
4921       }
4922     }
4923
4924     PlaySound(SND_GAME_WINNING);
4925   }
4926
4927   if (setup.count_score_after_game)
4928   {
4929     if (time != time_final)
4930     {
4931       if (game_over_delay_1 > 0)
4932       {
4933         game_over_delay_1--;
4934
4935         return;
4936       }
4937
4938       int time_to_go = ABS(time_final - time);
4939       int time_count_dir = (time < time_final ? +1 : -1);
4940
4941       if (time_to_go < time_count_steps)
4942         time_count_steps = 1;
4943
4944       time  += time_count_steps * time_count_dir;
4945       score += time_count_steps * time_score;
4946
4947       // set final score to correct rounding differences after counting score
4948       if (time == time_final)
4949         score = score_final;
4950
4951       LevelSolved_DisplayFinalGameValues(time, score, health);
4952
4953       if (time == time_final)
4954         StopSound(SND_GAME_LEVELTIME_BONUS);
4955       else if (setup.sound_loops)
4956         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4957       else
4958         PlaySound(SND_GAME_LEVELTIME_BONUS);
4959
4960       return;
4961     }
4962
4963     if (health != health_final)
4964     {
4965       if (game_over_delay_2 > 0)
4966       {
4967         game_over_delay_2--;
4968
4969         return;
4970       }
4971
4972       int health_count_dir = (health < health_final ? +1 : -1);
4973
4974       health += health_count_dir;
4975       score  += time_score;
4976
4977       LevelSolved_DisplayFinalGameValues(time, score, health);
4978
4979       if (health == health_final)
4980         StopSound(SND_GAME_LEVELTIME_BONUS);
4981       else if (setup.sound_loops)
4982         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4983       else
4984         PlaySound(SND_GAME_LEVELTIME_BONUS);
4985
4986       return;
4987     }
4988   }
4989
4990   game.panel.active = FALSE;
4991
4992   if (game_over_delay_3 > 0)
4993   {
4994     game_over_delay_3--;
4995
4996     return;
4997   }
4998
4999   GameEnd();
5000 }
5001
5002 void GameEnd(void)
5003 {
5004   // used instead of "level_nr" (needed for network games)
5005   int last_level_nr = levelset.level_nr;
5006   boolean tape_saved = FALSE;
5007
5008   game.LevelSolved_GameEnd = TRUE;
5009
5010   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5011   {
5012     // make sure that request dialog to save tape does not open door again
5013     if (!global.use_envelope_request)
5014       CloseDoor(DOOR_CLOSE_1);
5015
5016     // ask to save tape
5017     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5018
5019     // set unique basename for score tape (also saved in high score table)
5020     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5021   }
5022
5023   // if no tape is to be saved, close both doors simultaneously
5024   CloseDoor(DOOR_CLOSE_ALL);
5025
5026   if (level_editor_test_game || score_info_tape_play)
5027   {
5028     SetGameStatus(GAME_MODE_MAIN);
5029
5030     DrawMainMenu();
5031
5032     return;
5033   }
5034
5035   if (!game.LevelSolved_SaveScore)
5036   {
5037     SetGameStatus(GAME_MODE_MAIN);
5038
5039     DrawMainMenu();
5040
5041     return;
5042   }
5043
5044   if (level_nr == leveldir_current->handicap_level)
5045   {
5046     leveldir_current->handicap_level++;
5047
5048     SaveLevelSetup_SeriesInfo();
5049   }
5050
5051   // save score and score tape before potentially erasing tape below
5052   NewHighScore(last_level_nr, tape_saved);
5053
5054   if (setup.increment_levels &&
5055       level_nr < leveldir_current->last_level &&
5056       !network_playing)
5057   {
5058     level_nr++;         // advance to next level
5059     TapeErase();        // start with empty tape
5060
5061     if (setup.auto_play_next_level)
5062     {
5063       scores.continue_playing = TRUE;
5064       scores.next_level_nr = level_nr;
5065
5066       LoadLevel(level_nr);
5067
5068       SaveLevelSetup_SeriesInfo();
5069     }
5070   }
5071
5072   if (scores.last_added >= 0 && setup.show_scores_after_game)
5073   {
5074     SetGameStatus(GAME_MODE_SCORES);
5075
5076     DrawHallOfFame(last_level_nr);
5077   }
5078   else if (scores.continue_playing)
5079   {
5080     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5081   }
5082   else
5083   {
5084     SetGameStatus(GAME_MODE_MAIN);
5085
5086     DrawMainMenu();
5087   }
5088 }
5089
5090 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5091                          boolean one_score_entry_per_name)
5092 {
5093   int i;
5094
5095   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5096     return -1;
5097
5098   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5099   {
5100     struct ScoreEntry *entry = &list->entry[i];
5101     boolean score_is_better = (new_entry->score >  entry->score);
5102     boolean score_is_equal  = (new_entry->score == entry->score);
5103     boolean time_is_better  = (new_entry->time  <  entry->time);
5104     boolean time_is_equal   = (new_entry->time  == entry->time);
5105     boolean better_by_score = (score_is_better ||
5106                                (score_is_equal && time_is_better));
5107     boolean better_by_time  = (time_is_better ||
5108                                (time_is_equal && score_is_better));
5109     boolean is_better = (level.rate_time_over_score ? better_by_time :
5110                          better_by_score);
5111     boolean entry_is_empty = (entry->score == 0 &&
5112                               entry->time == 0);
5113
5114     // prevent adding server score entries if also existing in local score file
5115     // (special case: historic score entries have an empty tape basename entry)
5116     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5117         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5118     {
5119       // special case: use server score instead of local score value if higher
5120       // (historic scores might have been truncated to 16-bit values locally)
5121       if (score_is_better)
5122         entry->score = new_entry->score;
5123
5124       return -1;
5125     }
5126
5127     if (is_better || entry_is_empty)
5128     {
5129       // player has made it to the hall of fame
5130
5131       if (i < MAX_SCORE_ENTRIES - 1)
5132       {
5133         int m = MAX_SCORE_ENTRIES - 1;
5134         int l;
5135
5136         if (one_score_entry_per_name)
5137         {
5138           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5139             if (strEqual(list->entry[l].name, new_entry->name))
5140               m = l;
5141
5142           if (m == i)   // player's new highscore overwrites his old one
5143             goto put_into_list;
5144         }
5145
5146         for (l = m; l > i; l--)
5147           list->entry[l] = list->entry[l - 1];
5148       }
5149
5150       put_into_list:
5151
5152       *entry = *new_entry;
5153
5154       return i;
5155     }
5156     else if (one_score_entry_per_name &&
5157              strEqual(entry->name, new_entry->name))
5158     {
5159       // player already in high score list with better score or time
5160
5161       return -1;
5162     }
5163   }
5164
5165   // special case: new score is beyond the last high score list position
5166   return MAX_SCORE_ENTRIES;
5167 }
5168
5169 void NewHighScore(int level_nr, boolean tape_saved)
5170 {
5171   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5172   boolean one_per_name = FALSE;
5173
5174   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5175   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5176
5177   new_entry.score = game.score_final;
5178   new_entry.time = game.score_time_final;
5179
5180   LoadScore(level_nr);
5181
5182   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5183
5184   if (scores.last_added >= MAX_SCORE_ENTRIES)
5185   {
5186     scores.last_added = MAX_SCORE_ENTRIES - 1;
5187     scores.force_last_added = TRUE;
5188
5189     scores.entry[scores.last_added] = new_entry;
5190
5191     // store last added local score entry (before merging server scores)
5192     scores.last_added_local = scores.last_added;
5193
5194     return;
5195   }
5196
5197   if (scores.last_added < 0)
5198     return;
5199
5200   SaveScore(level_nr);
5201
5202   // store last added local score entry (before merging server scores)
5203   scores.last_added_local = scores.last_added;
5204
5205   if (!game.LevelSolved_SaveTape)
5206     return;
5207
5208   SaveScoreTape(level_nr);
5209
5210   if (setup.ask_for_using_api_server)
5211   {
5212     setup.use_api_server =
5213       Request("Upload your score and tape to the high score server?", REQ_ASK);
5214
5215     if (!setup.use_api_server)
5216       Request("Not using high score server! Use setup menu to enable again!",
5217               REQ_CONFIRM);
5218
5219     runtime.use_api_server = setup.use_api_server;
5220
5221     // after asking for using API server once, do not ask again
5222     setup.ask_for_using_api_server = FALSE;
5223
5224     SaveSetup_ServerSetup();
5225   }
5226
5227   SaveServerScore(level_nr, tape_saved);
5228 }
5229
5230 void MergeServerScore(void)
5231 {
5232   struct ScoreEntry last_added_entry;
5233   boolean one_per_name = FALSE;
5234   int i;
5235
5236   if (scores.last_added >= 0)
5237     last_added_entry = scores.entry[scores.last_added];
5238
5239   for (i = 0; i < server_scores.num_entries; i++)
5240   {
5241     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5242
5243     if (pos >= 0 && pos <= scores.last_added)
5244       scores.last_added++;
5245   }
5246
5247   if (scores.last_added >= MAX_SCORE_ENTRIES)
5248   {
5249     scores.last_added = MAX_SCORE_ENTRIES - 1;
5250     scores.force_last_added = TRUE;
5251
5252     scores.entry[scores.last_added] = last_added_entry;
5253   }
5254 }
5255
5256 static int getElementMoveStepsizeExt(int x, int y, int direction)
5257 {
5258   int element = Tile[x][y];
5259   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5260   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5261   int horiz_move = (dx != 0);
5262   int sign = (horiz_move ? dx : dy);
5263   int step = sign * element_info[element].move_stepsize;
5264
5265   // special values for move stepsize for spring and things on conveyor belt
5266   if (horiz_move)
5267   {
5268     if (CAN_FALL(element) &&
5269         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5270       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5271     else if (element == EL_SPRING)
5272       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5273   }
5274
5275   return step;
5276 }
5277
5278 static int getElementMoveStepsize(int x, int y)
5279 {
5280   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5281 }
5282
5283 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5284 {
5285   if (player->GfxAction != action || player->GfxDir != dir)
5286   {
5287     player->GfxAction = action;
5288     player->GfxDir = dir;
5289     player->Frame = 0;
5290     player->StepFrame = 0;
5291   }
5292 }
5293
5294 static void ResetGfxFrame(int x, int y)
5295 {
5296   // profiling showed that "autotest" spends 10~20% of its time in this function
5297   if (DrawingDeactivatedField())
5298     return;
5299
5300   int element = Tile[x][y];
5301   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5302
5303   if (graphic_info[graphic].anim_global_sync)
5304     GfxFrame[x][y] = FrameCounter;
5305   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5306     GfxFrame[x][y] = CustomValue[x][y];
5307   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5308     GfxFrame[x][y] = element_info[element].collect_score;
5309   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5310     GfxFrame[x][y] = ChangeDelay[x][y];
5311 }
5312
5313 static void ResetGfxAnimation(int x, int y)
5314 {
5315   GfxAction[x][y] = ACTION_DEFAULT;
5316   GfxDir[x][y] = MovDir[x][y];
5317   GfxFrame[x][y] = 0;
5318
5319   ResetGfxFrame(x, y);
5320 }
5321
5322 static void ResetRandomAnimationValue(int x, int y)
5323 {
5324   GfxRandom[x][y] = INIT_GFX_RANDOM();
5325 }
5326
5327 static void InitMovingField(int x, int y, int direction)
5328 {
5329   int element = Tile[x][y];
5330   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5331   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5332   int newx = x + dx;
5333   int newy = y + dy;
5334   boolean is_moving_before, is_moving_after;
5335
5336   // check if element was/is moving or being moved before/after mode change
5337   is_moving_before = (WasJustMoving[x][y] != 0);
5338   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5339
5340   // reset animation only for moving elements which change direction of moving
5341   // or which just started or stopped moving
5342   // (else CEs with property "can move" / "not moving" are reset each frame)
5343   if (is_moving_before != is_moving_after ||
5344       direction != MovDir[x][y])
5345     ResetGfxAnimation(x, y);
5346
5347   MovDir[x][y] = direction;
5348   GfxDir[x][y] = direction;
5349
5350   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5351                      direction == MV_DOWN && CAN_FALL(element) ?
5352                      ACTION_FALLING : ACTION_MOVING);
5353
5354   // this is needed for CEs with property "can move" / "not moving"
5355
5356   if (is_moving_after)
5357   {
5358     if (Tile[newx][newy] == EL_EMPTY)
5359       Tile[newx][newy] = EL_BLOCKED;
5360
5361     MovDir[newx][newy] = MovDir[x][y];
5362
5363     CustomValue[newx][newy] = CustomValue[x][y];
5364
5365     GfxFrame[newx][newy] = GfxFrame[x][y];
5366     GfxRandom[newx][newy] = GfxRandom[x][y];
5367     GfxAction[newx][newy] = GfxAction[x][y];
5368     GfxDir[newx][newy] = GfxDir[x][y];
5369   }
5370 }
5371
5372 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5373 {
5374   int direction = MovDir[x][y];
5375   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5376   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5377
5378   *goes_to_x = newx;
5379   *goes_to_y = newy;
5380 }
5381
5382 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5383 {
5384   int oldx = x, oldy = y;
5385   int direction = MovDir[x][y];
5386
5387   if (direction == MV_LEFT)
5388     oldx++;
5389   else if (direction == MV_RIGHT)
5390     oldx--;
5391   else if (direction == MV_UP)
5392     oldy++;
5393   else if (direction == MV_DOWN)
5394     oldy--;
5395
5396   *comes_from_x = oldx;
5397   *comes_from_y = oldy;
5398 }
5399
5400 static int MovingOrBlocked2Element(int x, int y)
5401 {
5402   int element = Tile[x][y];
5403
5404   if (element == EL_BLOCKED)
5405   {
5406     int oldx, oldy;
5407
5408     Blocked2Moving(x, y, &oldx, &oldy);
5409     return Tile[oldx][oldy];
5410   }
5411   else
5412     return element;
5413 }
5414
5415 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5416 {
5417   // like MovingOrBlocked2Element(), but if element is moving
5418   // and (x,y) is the field the moving element is just leaving,
5419   // return EL_BLOCKED instead of the element value
5420   int element = Tile[x][y];
5421
5422   if (IS_MOVING(x, y))
5423   {
5424     if (element == EL_BLOCKED)
5425     {
5426       int oldx, oldy;
5427
5428       Blocked2Moving(x, y, &oldx, &oldy);
5429       return Tile[oldx][oldy];
5430     }
5431     else
5432       return EL_BLOCKED;
5433   }
5434   else
5435     return element;
5436 }
5437
5438 static void RemoveField(int x, int y)
5439 {
5440   Tile[x][y] = EL_EMPTY;
5441
5442   MovPos[x][y] = 0;
5443   MovDir[x][y] = 0;
5444   MovDelay[x][y] = 0;
5445
5446   CustomValue[x][y] = 0;
5447
5448   AmoebaNr[x][y] = 0;
5449   ChangeDelay[x][y] = 0;
5450   ChangePage[x][y] = -1;
5451   Pushed[x][y] = FALSE;
5452
5453   GfxElement[x][y] = EL_UNDEFINED;
5454   GfxAction[x][y] = ACTION_DEFAULT;
5455   GfxDir[x][y] = MV_NONE;
5456 }
5457
5458 static void RemoveMovingField(int x, int y)
5459 {
5460   int oldx = x, oldy = y, newx = x, newy = y;
5461   int element = Tile[x][y];
5462   int next_element = EL_UNDEFINED;
5463
5464   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5465     return;
5466
5467   if (IS_MOVING(x, y))
5468   {
5469     Moving2Blocked(x, y, &newx, &newy);
5470
5471     if (Tile[newx][newy] != EL_BLOCKED)
5472     {
5473       // element is moving, but target field is not free (blocked), but
5474       // already occupied by something different (example: acid pool);
5475       // in this case, only remove the moving field, but not the target
5476
5477       RemoveField(oldx, oldy);
5478
5479       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5480
5481       TEST_DrawLevelField(oldx, oldy);
5482
5483       return;
5484     }
5485   }
5486   else if (element == EL_BLOCKED)
5487   {
5488     Blocked2Moving(x, y, &oldx, &oldy);
5489     if (!IS_MOVING(oldx, oldy))
5490       return;
5491   }
5492
5493   if (element == EL_BLOCKED &&
5494       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5495        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5496        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5497        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5498        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5499        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5500     next_element = get_next_element(Tile[oldx][oldy]);
5501
5502   RemoveField(oldx, oldy);
5503   RemoveField(newx, newy);
5504
5505   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5506
5507   if (next_element != EL_UNDEFINED)
5508     Tile[oldx][oldy] = next_element;
5509
5510   TEST_DrawLevelField(oldx, oldy);
5511   TEST_DrawLevelField(newx, newy);
5512 }
5513
5514 void DrawDynamite(int x, int y)
5515 {
5516   int sx = SCREENX(x), sy = SCREENY(y);
5517   int graphic = el2img(Tile[x][y]);
5518   int frame;
5519
5520   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5521     return;
5522
5523   if (IS_WALKABLE_INSIDE(Back[x][y]))
5524     return;
5525
5526   if (Back[x][y])
5527     DrawLevelElement(x, y, Back[x][y]);
5528   else if (Store[x][y])
5529     DrawLevelElement(x, y, Store[x][y]);
5530   else if (game.use_masked_elements)
5531     DrawLevelElement(x, y, EL_EMPTY);
5532
5533   frame = getGraphicAnimationFrameXY(graphic, x, y);
5534
5535   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5536     DrawGraphicThruMask(sx, sy, graphic, frame);
5537   else
5538     DrawGraphic(sx, sy, graphic, frame);
5539 }
5540
5541 static void CheckDynamite(int x, int y)
5542 {
5543   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5544   {
5545     MovDelay[x][y]--;
5546
5547     if (MovDelay[x][y] != 0)
5548     {
5549       DrawDynamite(x, y);
5550       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5551
5552       return;
5553     }
5554   }
5555
5556   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5557
5558   Bang(x, y);
5559 }
5560
5561 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5562 {
5563   boolean num_checked_players = 0;
5564   int i;
5565
5566   for (i = 0; i < MAX_PLAYERS; i++)
5567   {
5568     if (stored_player[i].active)
5569     {
5570       int sx = stored_player[i].jx;
5571       int sy = stored_player[i].jy;
5572
5573       if (num_checked_players == 0)
5574       {
5575         *sx1 = *sx2 = sx;
5576         *sy1 = *sy2 = sy;
5577       }
5578       else
5579       {
5580         *sx1 = MIN(*sx1, sx);
5581         *sy1 = MIN(*sy1, sy);
5582         *sx2 = MAX(*sx2, sx);
5583         *sy2 = MAX(*sy2, sy);
5584       }
5585
5586       num_checked_players++;
5587     }
5588   }
5589 }
5590
5591 static boolean checkIfAllPlayersFitToScreen_RND(void)
5592 {
5593   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5594
5595   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5596
5597   return (sx2 - sx1 < SCR_FIELDX &&
5598           sy2 - sy1 < SCR_FIELDY);
5599 }
5600
5601 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5602 {
5603   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5604
5605   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5606
5607   *sx = (sx1 + sx2) / 2;
5608   *sy = (sy1 + sy2) / 2;
5609 }
5610
5611 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5612                                boolean center_screen, boolean quick_relocation)
5613 {
5614   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5615   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5616   boolean no_delay = (tape.warp_forward);
5617   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5618   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5619   int new_scroll_x, new_scroll_y;
5620
5621   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5622   {
5623     // case 1: quick relocation inside visible screen (without scrolling)
5624
5625     RedrawPlayfield();
5626
5627     return;
5628   }
5629
5630   if (!level.shifted_relocation || center_screen)
5631   {
5632     // relocation _with_ centering of screen
5633
5634     new_scroll_x = SCROLL_POSITION_X(x);
5635     new_scroll_y = SCROLL_POSITION_Y(y);
5636   }
5637   else
5638   {
5639     // relocation _without_ centering of screen
5640
5641     int center_scroll_x = SCROLL_POSITION_X(old_x);
5642     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5643     int offset_x = x + (scroll_x - center_scroll_x);
5644     int offset_y = y + (scroll_y - center_scroll_y);
5645
5646     // for new screen position, apply previous offset to center position
5647     new_scroll_x = SCROLL_POSITION_X(offset_x);
5648     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5649   }
5650
5651   if (quick_relocation)
5652   {
5653     // case 2: quick relocation (redraw without visible scrolling)
5654
5655     scroll_x = new_scroll_x;
5656     scroll_y = new_scroll_y;
5657
5658     RedrawPlayfield();
5659
5660     return;
5661   }
5662
5663   // case 3: visible relocation (with scrolling to new position)
5664
5665   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5666
5667   SetVideoFrameDelay(wait_delay_value);
5668
5669   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5670   {
5671     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5672     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5673
5674     if (dx == 0 && dy == 0)             // no scrolling needed at all
5675       break;
5676
5677     scroll_x -= dx;
5678     scroll_y -= dy;
5679
5680     // set values for horizontal/vertical screen scrolling (half tile size)
5681     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5682     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5683     int pos_x = dx * TILEX / 2;
5684     int pos_y = dy * TILEY / 2;
5685     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5686     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5687
5688     ScrollLevel(dx, dy);
5689     DrawAllPlayers();
5690
5691     // scroll in two steps of half tile size to make things smoother
5692     BlitScreenToBitmapExt_RND(window, fx, fy);
5693
5694     // scroll second step to align at full tile size
5695     BlitScreenToBitmap(window);
5696   }
5697
5698   DrawAllPlayers();
5699   BackToFront();
5700
5701   SetVideoFrameDelay(frame_delay_value_old);
5702 }
5703
5704 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5705 {
5706   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5707   int player_nr = GET_PLAYER_NR(el_player);
5708   struct PlayerInfo *player = &stored_player[player_nr];
5709   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5710   boolean no_delay = (tape.warp_forward);
5711   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5712   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5713   int old_jx = player->jx;
5714   int old_jy = player->jy;
5715   int old_element = Tile[old_jx][old_jy];
5716   int element = Tile[jx][jy];
5717   boolean player_relocated = (old_jx != jx || old_jy != jy);
5718
5719   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5720   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5721   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5722   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5723   int leave_side_horiz = move_dir_horiz;
5724   int leave_side_vert  = move_dir_vert;
5725   int enter_side = enter_side_horiz | enter_side_vert;
5726   int leave_side = leave_side_horiz | leave_side_vert;
5727
5728   if (player->buried)           // do not reanimate dead player
5729     return;
5730
5731   if (!player_relocated)        // no need to relocate the player
5732     return;
5733
5734   if (IS_PLAYER(jx, jy))        // player already placed at new position
5735   {
5736     RemoveField(jx, jy);        // temporarily remove newly placed player
5737     DrawLevelField(jx, jy);
5738   }
5739
5740   if (player->present)
5741   {
5742     while (player->MovPos)
5743     {
5744       ScrollPlayer(player, SCROLL_GO_ON);
5745       ScrollScreen(NULL, SCROLL_GO_ON);
5746
5747       AdvanceFrameAndPlayerCounters(player->index_nr);
5748
5749       DrawPlayer(player);
5750
5751       BackToFront_WithFrameDelay(wait_delay_value);
5752     }
5753
5754     DrawPlayer(player);         // needed here only to cleanup last field
5755     DrawLevelField(player->jx, player->jy);     // remove player graphic
5756
5757     player->is_moving = FALSE;
5758   }
5759
5760   if (IS_CUSTOM_ELEMENT(old_element))
5761     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5762                                CE_LEFT_BY_PLAYER,
5763                                player->index_bit, leave_side);
5764
5765   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5766                                       CE_PLAYER_LEAVES_X,
5767                                       player->index_bit, leave_side);
5768
5769   Tile[jx][jy] = el_player;
5770   InitPlayerField(jx, jy, el_player, TRUE);
5771
5772   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5773      possible that the relocation target field did not contain a player element,
5774      but a walkable element, to which the new player was relocated -- in this
5775      case, restore that (already initialized!) element on the player field */
5776   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5777   {
5778     Tile[jx][jy] = element;     // restore previously existing element
5779   }
5780
5781   // only visually relocate centered player
5782   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5783                      FALSE, level.instant_relocation);
5784
5785   TestIfPlayerTouchesBadThing(jx, jy);
5786   TestIfPlayerTouchesCustomElement(jx, jy);
5787
5788   if (IS_CUSTOM_ELEMENT(element))
5789     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5790                                player->index_bit, enter_side);
5791
5792   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5793                                       player->index_bit, enter_side);
5794
5795   if (player->is_switching)
5796   {
5797     /* ensure that relocation while still switching an element does not cause
5798        a new element to be treated as also switched directly after relocation
5799        (this is important for teleporter switches that teleport the player to
5800        a place where another teleporter switch is in the same direction, which
5801        would then incorrectly be treated as immediately switched before the
5802        direction key that caused the switch was released) */
5803
5804     player->switch_x += jx - old_jx;
5805     player->switch_y += jy - old_jy;
5806   }
5807 }
5808
5809 static void Explode(int ex, int ey, int phase, int mode)
5810 {
5811   int x, y;
5812   int last_phase;
5813   int border_element;
5814
5815   // !!! eliminate this variable !!!
5816   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5817
5818   if (game.explosions_delayed)
5819   {
5820     ExplodeField[ex][ey] = mode;
5821     return;
5822   }
5823
5824   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5825   {
5826     int center_element = Tile[ex][ey];
5827     int artwork_element, explosion_element;     // set these values later
5828
5829     // remove things displayed in background while burning dynamite
5830     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5831       Back[ex][ey] = 0;
5832
5833     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5834     {
5835       // put moving element to center field (and let it explode there)
5836       center_element = MovingOrBlocked2Element(ex, ey);
5837       RemoveMovingField(ex, ey);
5838       Tile[ex][ey] = center_element;
5839     }
5840
5841     // now "center_element" is finally determined -- set related values now
5842     artwork_element = center_element;           // for custom player artwork
5843     explosion_element = center_element;         // for custom player artwork
5844
5845     if (IS_PLAYER(ex, ey))
5846     {
5847       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5848
5849       artwork_element = stored_player[player_nr].artwork_element;
5850
5851       if (level.use_explosion_element[player_nr])
5852       {
5853         explosion_element = level.explosion_element[player_nr];
5854         artwork_element = explosion_element;
5855       }
5856     }
5857
5858     if (mode == EX_TYPE_NORMAL ||
5859         mode == EX_TYPE_CENTER ||
5860         mode == EX_TYPE_CROSS)
5861       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5862
5863     last_phase = element_info[explosion_element].explosion_delay + 1;
5864
5865     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5866     {
5867       int xx = x - ex + 1;
5868       int yy = y - ey + 1;
5869       int element;
5870
5871       if (!IN_LEV_FIELD(x, y) ||
5872           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5873           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5874         continue;
5875
5876       element = Tile[x][y];
5877
5878       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5879       {
5880         element = MovingOrBlocked2Element(x, y);
5881
5882         if (!IS_EXPLOSION_PROOF(element))
5883           RemoveMovingField(x, y);
5884       }
5885
5886       // indestructible elements can only explode in center (but not flames)
5887       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5888                                            mode == EX_TYPE_BORDER)) ||
5889           element == EL_FLAMES)
5890         continue;
5891
5892       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5893          behaviour, for example when touching a yamyam that explodes to rocks
5894          with active deadly shield, a rock is created under the player !!! */
5895       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5896 #if 0
5897       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5898           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5899            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5900 #else
5901       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5902 #endif
5903       {
5904         if (IS_ACTIVE_BOMB(element))
5905         {
5906           // re-activate things under the bomb like gate or penguin
5907           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5908           Back[x][y] = 0;
5909         }
5910
5911         continue;
5912       }
5913
5914       // save walkable background elements while explosion on same tile
5915       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5916           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5917         Back[x][y] = element;
5918
5919       // ignite explodable elements reached by other explosion
5920       if (element == EL_EXPLOSION)
5921         element = Store2[x][y];
5922
5923       if (AmoebaNr[x][y] &&
5924           (element == EL_AMOEBA_FULL ||
5925            element == EL_BD_AMOEBA ||
5926            element == EL_AMOEBA_GROWING))
5927       {
5928         AmoebaCnt[AmoebaNr[x][y]]--;
5929         AmoebaCnt2[AmoebaNr[x][y]]--;
5930       }
5931
5932       RemoveField(x, y);
5933
5934       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5935       {
5936         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5937
5938         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5939
5940         if (PLAYERINFO(ex, ey)->use_murphy)
5941           Store[x][y] = EL_EMPTY;
5942       }
5943
5944       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5945       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5946       else if (IS_PLAYER_ELEMENT(center_element))
5947         Store[x][y] = EL_EMPTY;
5948       else if (center_element == EL_YAMYAM)
5949         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5950       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5951         Store[x][y] = element_info[center_element].content.e[xx][yy];
5952 #if 1
5953       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5954       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5955       // otherwise) -- FIX THIS !!!
5956       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5957         Store[x][y] = element_info[element].content.e[1][1];
5958 #else
5959       else if (!CAN_EXPLODE(element))
5960         Store[x][y] = element_info[element].content.e[1][1];
5961 #endif
5962       else
5963         Store[x][y] = EL_EMPTY;
5964
5965       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5966           center_element == EL_AMOEBA_TO_DIAMOND)
5967         Store2[x][y] = element;
5968
5969       Tile[x][y] = EL_EXPLOSION;
5970       GfxElement[x][y] = artwork_element;
5971
5972       ExplodePhase[x][y] = 1;
5973       ExplodeDelay[x][y] = last_phase;
5974
5975       Stop[x][y] = TRUE;
5976     }
5977
5978     if (center_element == EL_YAMYAM)
5979       game.yamyam_content_nr =
5980         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5981
5982     return;
5983   }
5984
5985   if (Stop[ex][ey])
5986     return;
5987
5988   x = ex;
5989   y = ey;
5990
5991   if (phase == 1)
5992     GfxFrame[x][y] = 0;         // restart explosion animation
5993
5994   last_phase = ExplodeDelay[x][y];
5995
5996   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5997
5998   // this can happen if the player leaves an explosion just in time
5999   if (GfxElement[x][y] == EL_UNDEFINED)
6000     GfxElement[x][y] = EL_EMPTY;
6001
6002   border_element = Store2[x][y];
6003   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6004     border_element = StorePlayer[x][y];
6005
6006   if (phase == element_info[border_element].ignition_delay ||
6007       phase == last_phase)
6008   {
6009     boolean border_explosion = FALSE;
6010
6011     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6012         !PLAYER_EXPLOSION_PROTECTED(x, y))
6013     {
6014       KillPlayerUnlessExplosionProtected(x, y);
6015       border_explosion = TRUE;
6016     }
6017     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6018     {
6019       Tile[x][y] = Store2[x][y];
6020       Store2[x][y] = 0;
6021       Bang(x, y);
6022       border_explosion = TRUE;
6023     }
6024     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6025     {
6026       AmoebaToDiamond(x, y);
6027       Store2[x][y] = 0;
6028       border_explosion = TRUE;
6029     }
6030
6031     // if an element just explodes due to another explosion (chain-reaction),
6032     // do not immediately end the new explosion when it was the last frame of
6033     // the explosion (as it would be done in the following "if"-statement!)
6034     if (border_explosion && phase == last_phase)
6035       return;
6036   }
6037
6038   // this can happen if the player was just killed by an explosion
6039   if (GfxElement[x][y] == EL_UNDEFINED)
6040     GfxElement[x][y] = EL_EMPTY;
6041
6042   if (phase == last_phase)
6043   {
6044     int element;
6045
6046     element = Tile[x][y] = Store[x][y];
6047     Store[x][y] = Store2[x][y] = 0;
6048     GfxElement[x][y] = EL_UNDEFINED;
6049
6050     // player can escape from explosions and might therefore be still alive
6051     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6052         element <= EL_PLAYER_IS_EXPLODING_4)
6053     {
6054       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6055       int explosion_element = EL_PLAYER_1 + player_nr;
6056       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6057       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6058
6059       if (level.use_explosion_element[player_nr])
6060         explosion_element = level.explosion_element[player_nr];
6061
6062       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6063                     element_info[explosion_element].content.e[xx][yy]);
6064     }
6065
6066     // restore probably existing indestructible background element
6067     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6068       element = Tile[x][y] = Back[x][y];
6069     Back[x][y] = 0;
6070
6071     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6072     GfxDir[x][y] = MV_NONE;
6073     ChangeDelay[x][y] = 0;
6074     ChangePage[x][y] = -1;
6075
6076     CustomValue[x][y] = 0;
6077
6078     InitField_WithBug2(x, y, FALSE);
6079
6080     TEST_DrawLevelField(x, y);
6081
6082     TestIfElementTouchesCustomElement(x, y);
6083
6084     if (GFX_CRUMBLED(element))
6085       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6086
6087     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6088       StorePlayer[x][y] = 0;
6089
6090     if (IS_PLAYER_ELEMENT(element))
6091       RelocatePlayer(x, y, element);
6092   }
6093   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6094   {
6095     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6096     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6097
6098     if (phase == delay)
6099       TEST_DrawLevelFieldCrumbled(x, y);
6100
6101     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6102     {
6103       DrawLevelElement(x, y, Back[x][y]);
6104       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6105     }
6106     else if (IS_WALKABLE_UNDER(Back[x][y]))
6107     {
6108       DrawLevelGraphic(x, y, graphic, frame);
6109       DrawLevelElementThruMask(x, y, Back[x][y]);
6110     }
6111     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6112       DrawLevelGraphic(x, y, graphic, frame);
6113   }
6114 }
6115
6116 static void DynaExplode(int ex, int ey)
6117 {
6118   int i, j;
6119   int dynabomb_element = Tile[ex][ey];
6120   int dynabomb_size = 1;
6121   boolean dynabomb_xl = FALSE;
6122   struct PlayerInfo *player;
6123   struct XY *xy = xy_topdown;
6124
6125   if (IS_ACTIVE_BOMB(dynabomb_element))
6126   {
6127     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6128     dynabomb_size = player->dynabomb_size;
6129     dynabomb_xl = player->dynabomb_xl;
6130     player->dynabombs_left++;
6131   }
6132
6133   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6134
6135   for (i = 0; i < NUM_DIRECTIONS; i++)
6136   {
6137     for (j = 1; j <= dynabomb_size; j++)
6138     {
6139       int x = ex + j * xy[i].x;
6140       int y = ey + j * xy[i].y;
6141       int element;
6142
6143       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6144         break;
6145
6146       element = Tile[x][y];
6147
6148       // do not restart explosions of fields with active bombs
6149       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6150         continue;
6151
6152       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6153
6154       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6155           !IS_DIGGABLE(element) && !dynabomb_xl)
6156         break;
6157     }
6158   }
6159 }
6160
6161 void Bang(int x, int y)
6162 {
6163   int element = MovingOrBlocked2Element(x, y);
6164   int explosion_type = EX_TYPE_NORMAL;
6165
6166   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6167   {
6168     struct PlayerInfo *player = PLAYERINFO(x, y);
6169
6170     element = Tile[x][y] = player->initial_element;
6171
6172     if (level.use_explosion_element[player->index_nr])
6173     {
6174       int explosion_element = level.explosion_element[player->index_nr];
6175
6176       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6177         explosion_type = EX_TYPE_CROSS;
6178       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6179         explosion_type = EX_TYPE_CENTER;
6180     }
6181   }
6182
6183   switch (element)
6184   {
6185     case EL_BUG:
6186     case EL_SPACESHIP:
6187     case EL_BD_BUTTERFLY:
6188     case EL_BD_FIREFLY:
6189     case EL_YAMYAM:
6190     case EL_DARK_YAMYAM:
6191     case EL_ROBOT:
6192     case EL_PACMAN:
6193     case EL_MOLE:
6194       RaiseScoreElement(element);
6195       break;
6196
6197     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6198     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6199     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6200     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6201     case EL_DYNABOMB_INCREASE_NUMBER:
6202     case EL_DYNABOMB_INCREASE_SIZE:
6203     case EL_DYNABOMB_INCREASE_POWER:
6204       explosion_type = EX_TYPE_DYNA;
6205       break;
6206
6207     case EL_DC_LANDMINE:
6208       explosion_type = EX_TYPE_CENTER;
6209       break;
6210
6211     case EL_PENGUIN:
6212     case EL_LAMP:
6213     case EL_LAMP_ACTIVE:
6214     case EL_AMOEBA_TO_DIAMOND:
6215       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6216         explosion_type = EX_TYPE_CENTER;
6217       break;
6218
6219     default:
6220       if (element_info[element].explosion_type == EXPLODES_CROSS)
6221         explosion_type = EX_TYPE_CROSS;
6222       else if (element_info[element].explosion_type == EXPLODES_1X1)
6223         explosion_type = EX_TYPE_CENTER;
6224       break;
6225   }
6226
6227   if (explosion_type == EX_TYPE_DYNA)
6228     DynaExplode(x, y);
6229   else
6230     Explode(x, y, EX_PHASE_START, explosion_type);
6231
6232   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6233 }
6234
6235 static void SplashAcid(int x, int y)
6236 {
6237   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6238       (!IN_LEV_FIELD(x - 1, y - 2) ||
6239        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6240     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6241
6242   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6243       (!IN_LEV_FIELD(x + 1, y - 2) ||
6244        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6245     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6246
6247   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6248 }
6249
6250 static void InitBeltMovement(void)
6251 {
6252   static int belt_base_element[4] =
6253   {
6254     EL_CONVEYOR_BELT_1_LEFT,
6255     EL_CONVEYOR_BELT_2_LEFT,
6256     EL_CONVEYOR_BELT_3_LEFT,
6257     EL_CONVEYOR_BELT_4_LEFT
6258   };
6259   static int belt_base_active_element[4] =
6260   {
6261     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6262     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6263     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6264     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6265   };
6266
6267   int x, y, i, j;
6268
6269   // set frame order for belt animation graphic according to belt direction
6270   for (i = 0; i < NUM_BELTS; i++)
6271   {
6272     int belt_nr = i;
6273
6274     for (j = 0; j < NUM_BELT_PARTS; j++)
6275     {
6276       int element = belt_base_active_element[belt_nr] + j;
6277       int graphic_1 = el2img(element);
6278       int graphic_2 = el2panelimg(element);
6279
6280       if (game.belt_dir[i] == MV_LEFT)
6281       {
6282         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6283         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6284       }
6285       else
6286       {
6287         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6288         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6289       }
6290     }
6291   }
6292
6293   SCAN_PLAYFIELD(x, y)
6294   {
6295     int element = Tile[x][y];
6296
6297     for (i = 0; i < NUM_BELTS; i++)
6298     {
6299       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6300       {
6301         int e_belt_nr = getBeltNrFromBeltElement(element);
6302         int belt_nr = i;
6303
6304         if (e_belt_nr == belt_nr)
6305         {
6306           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6307
6308           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6309         }
6310       }
6311     }
6312   }
6313 }
6314
6315 static void ToggleBeltSwitch(int x, int y)
6316 {
6317   static int belt_base_element[4] =
6318   {
6319     EL_CONVEYOR_BELT_1_LEFT,
6320     EL_CONVEYOR_BELT_2_LEFT,
6321     EL_CONVEYOR_BELT_3_LEFT,
6322     EL_CONVEYOR_BELT_4_LEFT
6323   };
6324   static int belt_base_active_element[4] =
6325   {
6326     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6327     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6328     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6329     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6330   };
6331   static int belt_base_switch_element[4] =
6332   {
6333     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6334     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6335     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6336     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6337   };
6338   static int belt_move_dir[4] =
6339   {
6340     MV_LEFT,
6341     MV_NONE,
6342     MV_RIGHT,
6343     MV_NONE,
6344   };
6345
6346   int element = Tile[x][y];
6347   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6348   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6349   int belt_dir = belt_move_dir[belt_dir_nr];
6350   int xx, yy, i;
6351
6352   if (!IS_BELT_SWITCH(element))
6353     return;
6354
6355   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6356   game.belt_dir[belt_nr] = belt_dir;
6357
6358   if (belt_dir_nr == 3)
6359     belt_dir_nr = 1;
6360
6361   // set frame order for belt animation graphic according to belt direction
6362   for (i = 0; i < NUM_BELT_PARTS; i++)
6363   {
6364     int element = belt_base_active_element[belt_nr] + i;
6365     int graphic_1 = el2img(element);
6366     int graphic_2 = el2panelimg(element);
6367
6368     if (belt_dir == MV_LEFT)
6369     {
6370       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6371       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6372     }
6373     else
6374     {
6375       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6376       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6377     }
6378   }
6379
6380   SCAN_PLAYFIELD(xx, yy)
6381   {
6382     int element = Tile[xx][yy];
6383
6384     if (IS_BELT_SWITCH(element))
6385     {
6386       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6387
6388       if (e_belt_nr == belt_nr)
6389       {
6390         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6391         TEST_DrawLevelField(xx, yy);
6392       }
6393     }
6394     else if (IS_BELT(element) && belt_dir != MV_NONE)
6395     {
6396       int e_belt_nr = getBeltNrFromBeltElement(element);
6397
6398       if (e_belt_nr == belt_nr)
6399       {
6400         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6401
6402         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6403         TEST_DrawLevelField(xx, yy);
6404       }
6405     }
6406     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6407     {
6408       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6409
6410       if (e_belt_nr == belt_nr)
6411       {
6412         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6413
6414         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6415         TEST_DrawLevelField(xx, yy);
6416       }
6417     }
6418   }
6419 }
6420
6421 static void ToggleSwitchgateSwitch(int x, int y)
6422 {
6423   int xx, yy;
6424
6425   game.switchgate_pos = !game.switchgate_pos;
6426
6427   SCAN_PLAYFIELD(xx, yy)
6428   {
6429     int element = Tile[xx][yy];
6430
6431     if (element == EL_SWITCHGATE_SWITCH_UP)
6432     {
6433       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6434       TEST_DrawLevelField(xx, yy);
6435     }
6436     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6437     {
6438       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6439       TEST_DrawLevelField(xx, yy);
6440     }
6441     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6442     {
6443       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6444       TEST_DrawLevelField(xx, yy);
6445     }
6446     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6447     {
6448       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6449       TEST_DrawLevelField(xx, yy);
6450     }
6451     else if (element == EL_SWITCHGATE_OPEN ||
6452              element == EL_SWITCHGATE_OPENING)
6453     {
6454       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6455
6456       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6457     }
6458     else if (element == EL_SWITCHGATE_CLOSED ||
6459              element == EL_SWITCHGATE_CLOSING)
6460     {
6461       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6462
6463       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6464     }
6465   }
6466 }
6467
6468 static int getInvisibleActiveFromInvisibleElement(int element)
6469 {
6470   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6471           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6472           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6473           element);
6474 }
6475
6476 static int getInvisibleFromInvisibleActiveElement(int element)
6477 {
6478   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6479           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6480           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6481           element);
6482 }
6483
6484 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6485 {
6486   int x, y;
6487
6488   SCAN_PLAYFIELD(x, y)
6489   {
6490     int element = Tile[x][y];
6491
6492     if (element == EL_LIGHT_SWITCH &&
6493         game.light_time_left > 0)
6494     {
6495       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6496       TEST_DrawLevelField(x, y);
6497     }
6498     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6499              game.light_time_left == 0)
6500     {
6501       Tile[x][y] = EL_LIGHT_SWITCH;
6502       TEST_DrawLevelField(x, y);
6503     }
6504     else if (element == EL_EMC_DRIPPER &&
6505              game.light_time_left > 0)
6506     {
6507       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6508       TEST_DrawLevelField(x, y);
6509     }
6510     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6511              game.light_time_left == 0)
6512     {
6513       Tile[x][y] = EL_EMC_DRIPPER;
6514       TEST_DrawLevelField(x, y);
6515     }
6516     else if (element == EL_INVISIBLE_STEELWALL ||
6517              element == EL_INVISIBLE_WALL ||
6518              element == EL_INVISIBLE_SAND)
6519     {
6520       if (game.light_time_left > 0)
6521         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6522
6523       TEST_DrawLevelField(x, y);
6524
6525       // uncrumble neighbour fields, if needed
6526       if (element == EL_INVISIBLE_SAND)
6527         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6528     }
6529     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6530              element == EL_INVISIBLE_WALL_ACTIVE ||
6531              element == EL_INVISIBLE_SAND_ACTIVE)
6532     {
6533       if (game.light_time_left == 0)
6534         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6535
6536       TEST_DrawLevelField(x, y);
6537
6538       // re-crumble neighbour fields, if needed
6539       if (element == EL_INVISIBLE_SAND)
6540         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6541     }
6542   }
6543 }
6544
6545 static void RedrawAllInvisibleElementsForLenses(void)
6546 {
6547   int x, y;
6548
6549   SCAN_PLAYFIELD(x, y)
6550   {
6551     int element = Tile[x][y];
6552
6553     if (element == EL_EMC_DRIPPER &&
6554         game.lenses_time_left > 0)
6555     {
6556       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6557       TEST_DrawLevelField(x, y);
6558     }
6559     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6560              game.lenses_time_left == 0)
6561     {
6562       Tile[x][y] = EL_EMC_DRIPPER;
6563       TEST_DrawLevelField(x, y);
6564     }
6565     else if (element == EL_INVISIBLE_STEELWALL ||
6566              element == EL_INVISIBLE_WALL ||
6567              element == EL_INVISIBLE_SAND)
6568     {
6569       if (game.lenses_time_left > 0)
6570         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6571
6572       TEST_DrawLevelField(x, y);
6573
6574       // uncrumble neighbour fields, if needed
6575       if (element == EL_INVISIBLE_SAND)
6576         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6577     }
6578     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6579              element == EL_INVISIBLE_WALL_ACTIVE ||
6580              element == EL_INVISIBLE_SAND_ACTIVE)
6581     {
6582       if (game.lenses_time_left == 0)
6583         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6584
6585       TEST_DrawLevelField(x, y);
6586
6587       // re-crumble neighbour fields, if needed
6588       if (element == EL_INVISIBLE_SAND)
6589         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6590     }
6591   }
6592 }
6593
6594 static void RedrawAllInvisibleElementsForMagnifier(void)
6595 {
6596   int x, y;
6597
6598   SCAN_PLAYFIELD(x, y)
6599   {
6600     int element = Tile[x][y];
6601
6602     if (element == EL_EMC_FAKE_GRASS &&
6603         game.magnify_time_left > 0)
6604     {
6605       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6606       TEST_DrawLevelField(x, y);
6607     }
6608     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6609              game.magnify_time_left == 0)
6610     {
6611       Tile[x][y] = EL_EMC_FAKE_GRASS;
6612       TEST_DrawLevelField(x, y);
6613     }
6614     else if (IS_GATE_GRAY(element) &&
6615              game.magnify_time_left > 0)
6616     {
6617       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6618                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6619                     IS_EM_GATE_GRAY(element) ?
6620                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6621                     IS_EMC_GATE_GRAY(element) ?
6622                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6623                     IS_DC_GATE_GRAY(element) ?
6624                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6625                     element);
6626       TEST_DrawLevelField(x, y);
6627     }
6628     else if (IS_GATE_GRAY_ACTIVE(element) &&
6629              game.magnify_time_left == 0)
6630     {
6631       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6632                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6633                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6634                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6635                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6636                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6637                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6638                     EL_DC_GATE_WHITE_GRAY :
6639                     element);
6640       TEST_DrawLevelField(x, y);
6641     }
6642   }
6643 }
6644
6645 static void ToggleLightSwitch(int x, int y)
6646 {
6647   int element = Tile[x][y];
6648
6649   game.light_time_left =
6650     (element == EL_LIGHT_SWITCH ?
6651      level.time_light * FRAMES_PER_SECOND : 0);
6652
6653   RedrawAllLightSwitchesAndInvisibleElements();
6654 }
6655
6656 static void ActivateTimegateSwitch(int x, int y)
6657 {
6658   int xx, yy;
6659
6660   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6661
6662   SCAN_PLAYFIELD(xx, yy)
6663   {
6664     int element = Tile[xx][yy];
6665
6666     if (element == EL_TIMEGATE_CLOSED ||
6667         element == EL_TIMEGATE_CLOSING)
6668     {
6669       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6670       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6671     }
6672
6673     /*
6674     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6675     {
6676       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6677       TEST_DrawLevelField(xx, yy);
6678     }
6679     */
6680
6681   }
6682
6683   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6684                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6685 }
6686
6687 static void Impact(int x, int y)
6688 {
6689   boolean last_line = (y == lev_fieldy - 1);
6690   boolean object_hit = FALSE;
6691   boolean impact = (last_line || object_hit);
6692   int element = Tile[x][y];
6693   int smashed = EL_STEELWALL;
6694
6695   if (!last_line)       // check if element below was hit
6696   {
6697     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6698       return;
6699
6700     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6701                                          MovDir[x][y + 1] != MV_DOWN ||
6702                                          MovPos[x][y + 1] <= TILEY / 2));
6703
6704     // do not smash moving elements that left the smashed field in time
6705     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6706         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6707       object_hit = FALSE;
6708
6709 #if USE_QUICKSAND_IMPACT_BUGFIX
6710     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6711     {
6712       RemoveMovingField(x, y + 1);
6713       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6714       Tile[x][y + 2] = EL_ROCK;
6715       TEST_DrawLevelField(x, y + 2);
6716
6717       object_hit = TRUE;
6718     }
6719
6720     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6721     {
6722       RemoveMovingField(x, y + 1);
6723       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6724       Tile[x][y + 2] = EL_ROCK;
6725       TEST_DrawLevelField(x, y + 2);
6726
6727       object_hit = TRUE;
6728     }
6729 #endif
6730
6731     if (object_hit)
6732       smashed = MovingOrBlocked2Element(x, y + 1);
6733
6734     impact = (last_line || object_hit);
6735   }
6736
6737   if (!last_line && smashed == EL_ACID) // element falls into acid
6738   {
6739     SplashAcid(x, y + 1);
6740     return;
6741   }
6742
6743   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6744   // only reset graphic animation if graphic really changes after impact
6745   if (impact &&
6746       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6747   {
6748     ResetGfxAnimation(x, y);
6749     TEST_DrawLevelField(x, y);
6750   }
6751
6752   if (impact && CAN_EXPLODE_IMPACT(element))
6753   {
6754     Bang(x, y);
6755     return;
6756   }
6757   else if (impact && element == EL_PEARL &&
6758            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6759   {
6760     ResetGfxAnimation(x, y);
6761
6762     Tile[x][y] = EL_PEARL_BREAKING;
6763     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6764     return;
6765   }
6766   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6767   {
6768     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6769
6770     return;
6771   }
6772
6773   if (impact && element == EL_AMOEBA_DROP)
6774   {
6775     if (object_hit && IS_PLAYER(x, y + 1))
6776       KillPlayerUnlessEnemyProtected(x, y + 1);
6777     else if (object_hit && smashed == EL_PENGUIN)
6778       Bang(x, y + 1);
6779     else
6780     {
6781       Tile[x][y] = EL_AMOEBA_GROWING;
6782       Store[x][y] = EL_AMOEBA_WET;
6783
6784       ResetRandomAnimationValue(x, y);
6785     }
6786     return;
6787   }
6788
6789   if (object_hit)               // check which object was hit
6790   {
6791     if ((CAN_PASS_MAGIC_WALL(element) && 
6792          (smashed == EL_MAGIC_WALL ||
6793           smashed == EL_BD_MAGIC_WALL)) ||
6794         (CAN_PASS_DC_MAGIC_WALL(element) &&
6795          smashed == EL_DC_MAGIC_WALL))
6796     {
6797       int xx, yy;
6798       int activated_magic_wall =
6799         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6800          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6801          EL_DC_MAGIC_WALL_ACTIVE);
6802
6803       // activate magic wall / mill
6804       SCAN_PLAYFIELD(xx, yy)
6805       {
6806         if (Tile[xx][yy] == smashed)
6807           Tile[xx][yy] = activated_magic_wall;
6808       }
6809
6810       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6811       game.magic_wall_active = TRUE;
6812
6813       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6814                             SND_MAGIC_WALL_ACTIVATING :
6815                             smashed == EL_BD_MAGIC_WALL ?
6816                             SND_BD_MAGIC_WALL_ACTIVATING :
6817                             SND_DC_MAGIC_WALL_ACTIVATING));
6818     }
6819
6820     if (IS_PLAYER(x, y + 1))
6821     {
6822       if (CAN_SMASH_PLAYER(element))
6823       {
6824         KillPlayerUnlessEnemyProtected(x, y + 1);
6825         return;
6826       }
6827     }
6828     else if (smashed == EL_PENGUIN)
6829     {
6830       if (CAN_SMASH_PLAYER(element))
6831       {
6832         Bang(x, y + 1);
6833         return;
6834       }
6835     }
6836     else if (element == EL_BD_DIAMOND)
6837     {
6838       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6839       {
6840         Bang(x, y + 1);
6841         return;
6842       }
6843     }
6844     else if (((element == EL_SP_INFOTRON ||
6845                element == EL_SP_ZONK) &&
6846               (smashed == EL_SP_SNIKSNAK ||
6847                smashed == EL_SP_ELECTRON ||
6848                smashed == EL_SP_DISK_ORANGE)) ||
6849              (element == EL_SP_INFOTRON &&
6850               smashed == EL_SP_DISK_YELLOW))
6851     {
6852       Bang(x, y + 1);
6853       return;
6854     }
6855     else if (CAN_SMASH_EVERYTHING(element))
6856     {
6857       if (IS_CLASSIC_ENEMY(smashed) ||
6858           CAN_EXPLODE_SMASHED(smashed))
6859       {
6860         Bang(x, y + 1);
6861         return;
6862       }
6863       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6864       {
6865         if (smashed == EL_LAMP ||
6866             smashed == EL_LAMP_ACTIVE)
6867         {
6868           Bang(x, y + 1);
6869           return;
6870         }
6871         else if (smashed == EL_NUT)
6872         {
6873           Tile[x][y + 1] = EL_NUT_BREAKING;
6874           PlayLevelSound(x, y, SND_NUT_BREAKING);
6875           RaiseScoreElement(EL_NUT);
6876           return;
6877         }
6878         else if (smashed == EL_PEARL)
6879         {
6880           ResetGfxAnimation(x, y);
6881
6882           Tile[x][y + 1] = EL_PEARL_BREAKING;
6883           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6884           return;
6885         }
6886         else if (smashed == EL_DIAMOND)
6887         {
6888           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6889           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6890           return;
6891         }
6892         else if (IS_BELT_SWITCH(smashed))
6893         {
6894           ToggleBeltSwitch(x, y + 1);
6895         }
6896         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6897                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6898                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6899                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6900         {
6901           ToggleSwitchgateSwitch(x, y + 1);
6902         }
6903         else if (smashed == EL_LIGHT_SWITCH ||
6904                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6905         {
6906           ToggleLightSwitch(x, y + 1);
6907         }
6908         else
6909         {
6910           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6911
6912           CheckElementChangeBySide(x, y + 1, smashed, element,
6913                                    CE_SWITCHED, CH_SIDE_TOP);
6914           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6915                                             CH_SIDE_TOP);
6916         }
6917       }
6918       else
6919       {
6920         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6921       }
6922     }
6923   }
6924
6925   // play sound of magic wall / mill
6926   if (!last_line &&
6927       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6928        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6929        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6930   {
6931     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6932       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6933     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6934       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6935     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6936       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6937
6938     return;
6939   }
6940
6941   // play sound of object that hits the ground
6942   if (last_line || object_hit)
6943     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6944 }
6945
6946 static void TurnRoundExt(int x, int y)
6947 {
6948   static struct
6949   {
6950     int dx, dy;
6951   } move_xy[] =
6952   {
6953     {  0,  0 },
6954     { -1,  0 },
6955     { +1,  0 },
6956     {  0,  0 },
6957     {  0, -1 },
6958     {  0,  0 }, { 0, 0 }, { 0, 0 },
6959     {  0, +1 }
6960   };
6961   static struct
6962   {
6963     int left, right, back;
6964   } turn[] =
6965   {
6966     { 0,        0,              0        },
6967     { MV_DOWN,  MV_UP,          MV_RIGHT },
6968     { MV_UP,    MV_DOWN,        MV_LEFT  },
6969     { 0,        0,              0        },
6970     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6971     { 0,        0,              0        },
6972     { 0,        0,              0        },
6973     { 0,        0,              0        },
6974     { MV_RIGHT, MV_LEFT,        MV_UP    }
6975   };
6976
6977   int element = Tile[x][y];
6978   int move_pattern = element_info[element].move_pattern;
6979
6980   int old_move_dir = MovDir[x][y];
6981   int left_dir  = turn[old_move_dir].left;
6982   int right_dir = turn[old_move_dir].right;
6983   int back_dir  = turn[old_move_dir].back;
6984
6985   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6986   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6987   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6988   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6989
6990   int left_x  = x + left_dx,  left_y  = y + left_dy;
6991   int right_x = x + right_dx, right_y = y + right_dy;
6992   int move_x  = x + move_dx,  move_y  = y + move_dy;
6993
6994   int xx, yy;
6995
6996   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6997   {
6998     TestIfBadThingTouchesOtherBadThing(x, y);
6999
7000     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7001       MovDir[x][y] = right_dir;
7002     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7003       MovDir[x][y] = left_dir;
7004
7005     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7006       MovDelay[x][y] = 9;
7007     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7008       MovDelay[x][y] = 1;
7009   }
7010   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7011   {
7012     TestIfBadThingTouchesOtherBadThing(x, y);
7013
7014     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7015       MovDir[x][y] = left_dir;
7016     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7017       MovDir[x][y] = right_dir;
7018
7019     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7020       MovDelay[x][y] = 9;
7021     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7022       MovDelay[x][y] = 1;
7023   }
7024   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7025   {
7026     TestIfBadThingTouchesOtherBadThing(x, y);
7027
7028     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7029       MovDir[x][y] = left_dir;
7030     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7031       MovDir[x][y] = right_dir;
7032
7033     if (MovDir[x][y] != old_move_dir)
7034       MovDelay[x][y] = 9;
7035   }
7036   else if (element == EL_YAMYAM)
7037   {
7038     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7039     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7040
7041     if (can_turn_left && can_turn_right)
7042       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7043     else if (can_turn_left)
7044       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7045     else if (can_turn_right)
7046       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7047     else
7048       MovDir[x][y] = back_dir;
7049
7050     MovDelay[x][y] = 16 + 16 * RND(3);
7051   }
7052   else if (element == EL_DARK_YAMYAM)
7053   {
7054     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7055                                                          left_x, left_y);
7056     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7057                                                          right_x, right_y);
7058
7059     if (can_turn_left && can_turn_right)
7060       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7061     else if (can_turn_left)
7062       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7063     else if (can_turn_right)
7064       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7065     else
7066       MovDir[x][y] = back_dir;
7067
7068     MovDelay[x][y] = 16 + 16 * RND(3);
7069   }
7070   else if (element == EL_PACMAN)
7071   {
7072     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7073     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7074
7075     if (can_turn_left && can_turn_right)
7076       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7077     else if (can_turn_left)
7078       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7079     else if (can_turn_right)
7080       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7081     else
7082       MovDir[x][y] = back_dir;
7083
7084     MovDelay[x][y] = 6 + RND(40);
7085   }
7086   else if (element == EL_PIG)
7087   {
7088     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7089     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7090     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7091     boolean should_turn_left, should_turn_right, should_move_on;
7092     int rnd_value = 24;
7093     int rnd = RND(rnd_value);
7094
7095     should_turn_left = (can_turn_left &&
7096                         (!can_move_on ||
7097                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7098                                                    y + back_dy + left_dy)));
7099     should_turn_right = (can_turn_right &&
7100                          (!can_move_on ||
7101                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7102                                                     y + back_dy + right_dy)));
7103     should_move_on = (can_move_on &&
7104                       (!can_turn_left ||
7105                        !can_turn_right ||
7106                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7107                                                  y + move_dy + left_dy) ||
7108                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7109                                                  y + move_dy + right_dy)));
7110
7111     if (should_turn_left || should_turn_right || should_move_on)
7112     {
7113       if (should_turn_left && should_turn_right && should_move_on)
7114         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7115                         rnd < 2 * rnd_value / 3 ? right_dir :
7116                         old_move_dir);
7117       else if (should_turn_left && should_turn_right)
7118         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7119       else if (should_turn_left && should_move_on)
7120         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7121       else if (should_turn_right && should_move_on)
7122         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7123       else if (should_turn_left)
7124         MovDir[x][y] = left_dir;
7125       else if (should_turn_right)
7126         MovDir[x][y] = right_dir;
7127       else if (should_move_on)
7128         MovDir[x][y] = old_move_dir;
7129     }
7130     else if (can_move_on && rnd > rnd_value / 8)
7131       MovDir[x][y] = old_move_dir;
7132     else if (can_turn_left && can_turn_right)
7133       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7134     else if (can_turn_left && rnd > rnd_value / 8)
7135       MovDir[x][y] = left_dir;
7136     else if (can_turn_right && rnd > rnd_value/8)
7137       MovDir[x][y] = right_dir;
7138     else
7139       MovDir[x][y] = back_dir;
7140
7141     xx = x + move_xy[MovDir[x][y]].dx;
7142     yy = y + move_xy[MovDir[x][y]].dy;
7143
7144     if (!IN_LEV_FIELD(xx, yy) ||
7145         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7146       MovDir[x][y] = old_move_dir;
7147
7148     MovDelay[x][y] = 0;
7149   }
7150   else if (element == EL_DRAGON)
7151   {
7152     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7153     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7154     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7155     int rnd_value = 24;
7156     int rnd = RND(rnd_value);
7157
7158     if (can_move_on && rnd > rnd_value / 8)
7159       MovDir[x][y] = old_move_dir;
7160     else if (can_turn_left && can_turn_right)
7161       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7162     else if (can_turn_left && rnd > rnd_value / 8)
7163       MovDir[x][y] = left_dir;
7164     else if (can_turn_right && rnd > rnd_value / 8)
7165       MovDir[x][y] = right_dir;
7166     else
7167       MovDir[x][y] = back_dir;
7168
7169     xx = x + move_xy[MovDir[x][y]].dx;
7170     yy = y + move_xy[MovDir[x][y]].dy;
7171
7172     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7173       MovDir[x][y] = old_move_dir;
7174
7175     MovDelay[x][y] = 0;
7176   }
7177   else if (element == EL_MOLE)
7178   {
7179     boolean can_move_on =
7180       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7181                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7182                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7183     if (!can_move_on)
7184     {
7185       boolean can_turn_left =
7186         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7187                               IS_AMOEBOID(Tile[left_x][left_y])));
7188
7189       boolean can_turn_right =
7190         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7191                               IS_AMOEBOID(Tile[right_x][right_y])));
7192
7193       if (can_turn_left && can_turn_right)
7194         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7195       else if (can_turn_left)
7196         MovDir[x][y] = left_dir;
7197       else
7198         MovDir[x][y] = right_dir;
7199     }
7200
7201     if (MovDir[x][y] != old_move_dir)
7202       MovDelay[x][y] = 9;
7203   }
7204   else if (element == EL_BALLOON)
7205   {
7206     MovDir[x][y] = game.wind_direction;
7207     MovDelay[x][y] = 0;
7208   }
7209   else if (element == EL_SPRING)
7210   {
7211     if (MovDir[x][y] & MV_HORIZONTAL)
7212     {
7213       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7214           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7215       {
7216         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7217         ResetGfxAnimation(move_x, move_y);
7218         TEST_DrawLevelField(move_x, move_y);
7219
7220         MovDir[x][y] = back_dir;
7221       }
7222       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7223                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7224         MovDir[x][y] = MV_NONE;
7225     }
7226
7227     MovDelay[x][y] = 0;
7228   }
7229   else if (element == EL_ROBOT ||
7230            element == EL_SATELLITE ||
7231            element == EL_PENGUIN ||
7232            element == EL_EMC_ANDROID)
7233   {
7234     int attr_x = -1, attr_y = -1;
7235
7236     if (game.all_players_gone)
7237     {
7238       attr_x = game.exit_x;
7239       attr_y = game.exit_y;
7240     }
7241     else
7242     {
7243       int i;
7244
7245       for (i = 0; i < MAX_PLAYERS; i++)
7246       {
7247         struct PlayerInfo *player = &stored_player[i];
7248         int jx = player->jx, jy = player->jy;
7249
7250         if (!player->active)
7251           continue;
7252
7253         if (attr_x == -1 ||
7254             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7255         {
7256           attr_x = jx;
7257           attr_y = jy;
7258         }
7259       }
7260     }
7261
7262     if (element == EL_ROBOT &&
7263         game.robot_wheel_x >= 0 &&
7264         game.robot_wheel_y >= 0 &&
7265         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7266          game.engine_version < VERSION_IDENT(3,1,0,0)))
7267     {
7268       attr_x = game.robot_wheel_x;
7269       attr_y = game.robot_wheel_y;
7270     }
7271
7272     if (element == EL_PENGUIN)
7273     {
7274       int i;
7275       struct XY *xy = xy_topdown;
7276
7277       for (i = 0; i < NUM_DIRECTIONS; i++)
7278       {
7279         int ex = x + xy[i].x;
7280         int ey = y + xy[i].y;
7281
7282         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7283                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7284                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7285                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7286         {
7287           attr_x = ex;
7288           attr_y = ey;
7289           break;
7290         }
7291       }
7292     }
7293
7294     MovDir[x][y] = MV_NONE;
7295     if (attr_x < x)
7296       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7297     else if (attr_x > x)
7298       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7299     if (attr_y < y)
7300       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7301     else if (attr_y > y)
7302       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7303
7304     if (element == EL_ROBOT)
7305     {
7306       int newx, newy;
7307
7308       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7309         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7310       Moving2Blocked(x, y, &newx, &newy);
7311
7312       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7313         MovDelay[x][y] = 8 + 8 * !RND(3);
7314       else
7315         MovDelay[x][y] = 16;
7316     }
7317     else if (element == EL_PENGUIN)
7318     {
7319       int newx, newy;
7320
7321       MovDelay[x][y] = 1;
7322
7323       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7324       {
7325         boolean first_horiz = RND(2);
7326         int new_move_dir = MovDir[x][y];
7327
7328         MovDir[x][y] =
7329           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7330         Moving2Blocked(x, y, &newx, &newy);
7331
7332         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7333           return;
7334
7335         MovDir[x][y] =
7336           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7337         Moving2Blocked(x, y, &newx, &newy);
7338
7339         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7340           return;
7341
7342         MovDir[x][y] = old_move_dir;
7343         return;
7344       }
7345     }
7346     else if (element == EL_SATELLITE)
7347     {
7348       int newx, newy;
7349
7350       MovDelay[x][y] = 1;
7351
7352       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7353       {
7354         boolean first_horiz = RND(2);
7355         int new_move_dir = MovDir[x][y];
7356
7357         MovDir[x][y] =
7358           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7359         Moving2Blocked(x, y, &newx, &newy);
7360
7361         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7362           return;
7363
7364         MovDir[x][y] =
7365           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7366         Moving2Blocked(x, y, &newx, &newy);
7367
7368         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7369           return;
7370
7371         MovDir[x][y] = old_move_dir;
7372         return;
7373       }
7374     }
7375     else if (element == EL_EMC_ANDROID)
7376     {
7377       static int check_pos[16] =
7378       {
7379         -1,             //  0 => (invalid)
7380         7,              //  1 => MV_LEFT
7381         3,              //  2 => MV_RIGHT
7382         -1,             //  3 => (invalid)
7383         1,              //  4 =>            MV_UP
7384         0,              //  5 => MV_LEFT  | MV_UP
7385         2,              //  6 => MV_RIGHT | MV_UP
7386         -1,             //  7 => (invalid)
7387         5,              //  8 =>            MV_DOWN
7388         6,              //  9 => MV_LEFT  | MV_DOWN
7389         4,              // 10 => MV_RIGHT | MV_DOWN
7390         -1,             // 11 => (invalid)
7391         -1,             // 12 => (invalid)
7392         -1,             // 13 => (invalid)
7393         -1,             // 14 => (invalid)
7394         -1,             // 15 => (invalid)
7395       };
7396       static struct
7397       {
7398         int dx, dy;
7399         int dir;
7400       } check_xy[8] =
7401       {
7402         { -1, -1,       MV_LEFT  | MV_UP   },
7403         {  0, -1,                  MV_UP   },
7404         { +1, -1,       MV_RIGHT | MV_UP   },
7405         { +1,  0,       MV_RIGHT           },
7406         { +1, +1,       MV_RIGHT | MV_DOWN },
7407         {  0, +1,                  MV_DOWN },
7408         { -1, +1,       MV_LEFT  | MV_DOWN },
7409         { -1,  0,       MV_LEFT            },
7410       };
7411       int start_pos, check_order;
7412       boolean can_clone = FALSE;
7413       int i;
7414
7415       // check if there is any free field around current position
7416       for (i = 0; i < 8; i++)
7417       {
7418         int newx = x + check_xy[i].dx;
7419         int newy = y + check_xy[i].dy;
7420
7421         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7422         {
7423           can_clone = TRUE;
7424
7425           break;
7426         }
7427       }
7428
7429       if (can_clone)            // randomly find an element to clone
7430       {
7431         can_clone = FALSE;
7432
7433         start_pos = check_pos[RND(8)];
7434         check_order = (RND(2) ? -1 : +1);
7435
7436         for (i = 0; i < 8; i++)
7437         {
7438           int pos_raw = start_pos + i * check_order;
7439           int pos = (pos_raw + 8) % 8;
7440           int newx = x + check_xy[pos].dx;
7441           int newy = y + check_xy[pos].dy;
7442
7443           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7444           {
7445             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7446             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7447
7448             Store[x][y] = Tile[newx][newy];
7449
7450             can_clone = TRUE;
7451
7452             break;
7453           }
7454         }
7455       }
7456
7457       if (can_clone)            // randomly find a direction to move
7458       {
7459         can_clone = FALSE;
7460
7461         start_pos = check_pos[RND(8)];
7462         check_order = (RND(2) ? -1 : +1);
7463
7464         for (i = 0; i < 8; i++)
7465         {
7466           int pos_raw = start_pos + i * check_order;
7467           int pos = (pos_raw + 8) % 8;
7468           int newx = x + check_xy[pos].dx;
7469           int newy = y + check_xy[pos].dy;
7470           int new_move_dir = check_xy[pos].dir;
7471
7472           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7473           {
7474             MovDir[x][y] = new_move_dir;
7475             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7476
7477             can_clone = TRUE;
7478
7479             break;
7480           }
7481         }
7482       }
7483
7484       if (can_clone)            // cloning and moving successful
7485         return;
7486
7487       // cannot clone -- try to move towards player
7488
7489       start_pos = check_pos[MovDir[x][y] & 0x0f];
7490       check_order = (RND(2) ? -1 : +1);
7491
7492       for (i = 0; i < 3; i++)
7493       {
7494         // first check start_pos, then previous/next or (next/previous) pos
7495         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7496         int pos = (pos_raw + 8) % 8;
7497         int newx = x + check_xy[pos].dx;
7498         int newy = y + check_xy[pos].dy;
7499         int new_move_dir = check_xy[pos].dir;
7500
7501         if (IS_PLAYER(newx, newy))
7502           break;
7503
7504         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7505         {
7506           MovDir[x][y] = new_move_dir;
7507           MovDelay[x][y] = level.android_move_time * 8 + 1;
7508
7509           break;
7510         }
7511       }
7512     }
7513   }
7514   else if (move_pattern == MV_TURNING_LEFT ||
7515            move_pattern == MV_TURNING_RIGHT ||
7516            move_pattern == MV_TURNING_LEFT_RIGHT ||
7517            move_pattern == MV_TURNING_RIGHT_LEFT ||
7518            move_pattern == MV_TURNING_RANDOM ||
7519            move_pattern == MV_ALL_DIRECTIONS)
7520   {
7521     boolean can_turn_left =
7522       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7523     boolean can_turn_right =
7524       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7525
7526     if (element_info[element].move_stepsize == 0)       // "not moving"
7527       return;
7528
7529     if (move_pattern == MV_TURNING_LEFT)
7530       MovDir[x][y] = left_dir;
7531     else if (move_pattern == MV_TURNING_RIGHT)
7532       MovDir[x][y] = right_dir;
7533     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7534       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7535     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7536       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7537     else if (move_pattern == MV_TURNING_RANDOM)
7538       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7539                       can_turn_right && !can_turn_left ? right_dir :
7540                       RND(2) ? left_dir : right_dir);
7541     else if (can_turn_left && can_turn_right)
7542       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7543     else if (can_turn_left)
7544       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7545     else if (can_turn_right)
7546       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7547     else
7548       MovDir[x][y] = back_dir;
7549
7550     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7551   }
7552   else if (move_pattern == MV_HORIZONTAL ||
7553            move_pattern == MV_VERTICAL)
7554   {
7555     if (move_pattern & old_move_dir)
7556       MovDir[x][y] = back_dir;
7557     else if (move_pattern == MV_HORIZONTAL)
7558       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7559     else if (move_pattern == MV_VERTICAL)
7560       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7561
7562     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7563   }
7564   else if (move_pattern & MV_ANY_DIRECTION)
7565   {
7566     MovDir[x][y] = move_pattern;
7567     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7568   }
7569   else if (move_pattern & MV_WIND_DIRECTION)
7570   {
7571     MovDir[x][y] = game.wind_direction;
7572     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7573   }
7574   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7575   {
7576     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7577       MovDir[x][y] = left_dir;
7578     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7579       MovDir[x][y] = right_dir;
7580
7581     if (MovDir[x][y] != old_move_dir)
7582       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7583   }
7584   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7585   {
7586     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7587       MovDir[x][y] = right_dir;
7588     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7589       MovDir[x][y] = left_dir;
7590
7591     if (MovDir[x][y] != old_move_dir)
7592       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7593   }
7594   else if (move_pattern == MV_TOWARDS_PLAYER ||
7595            move_pattern == MV_AWAY_FROM_PLAYER)
7596   {
7597     int attr_x = -1, attr_y = -1;
7598     int newx, newy;
7599     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7600
7601     if (game.all_players_gone)
7602     {
7603       attr_x = game.exit_x;
7604       attr_y = game.exit_y;
7605     }
7606     else
7607     {
7608       int i;
7609
7610       for (i = 0; i < MAX_PLAYERS; i++)
7611       {
7612         struct PlayerInfo *player = &stored_player[i];
7613         int jx = player->jx, jy = player->jy;
7614
7615         if (!player->active)
7616           continue;
7617
7618         if (attr_x == -1 ||
7619             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7620         {
7621           attr_x = jx;
7622           attr_y = jy;
7623         }
7624       }
7625     }
7626
7627     MovDir[x][y] = MV_NONE;
7628     if (attr_x < x)
7629       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7630     else if (attr_x > x)
7631       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7632     if (attr_y < y)
7633       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7634     else if (attr_y > y)
7635       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7636
7637     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7638
7639     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7640     {
7641       boolean first_horiz = RND(2);
7642       int new_move_dir = MovDir[x][y];
7643
7644       if (element_info[element].move_stepsize == 0)     // "not moving"
7645       {
7646         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7647         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7648
7649         return;
7650       }
7651
7652       MovDir[x][y] =
7653         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7654       Moving2Blocked(x, y, &newx, &newy);
7655
7656       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7657         return;
7658
7659       MovDir[x][y] =
7660         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7661       Moving2Blocked(x, y, &newx, &newy);
7662
7663       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7664         return;
7665
7666       MovDir[x][y] = old_move_dir;
7667     }
7668   }
7669   else if (move_pattern == MV_WHEN_PUSHED ||
7670            move_pattern == MV_WHEN_DROPPED)
7671   {
7672     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7673       MovDir[x][y] = MV_NONE;
7674
7675     MovDelay[x][y] = 0;
7676   }
7677   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7678   {
7679     struct XY *test_xy = xy_topdown;
7680     static int test_dir[4] =
7681     {
7682       MV_UP,
7683       MV_LEFT,
7684       MV_RIGHT,
7685       MV_DOWN
7686     };
7687     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7688     int move_preference = -1000000;     // start with very low preference
7689     int new_move_dir = MV_NONE;
7690     int start_test = RND(4);
7691     int i;
7692
7693     for (i = 0; i < NUM_DIRECTIONS; i++)
7694     {
7695       int j = (start_test + i) % 4;
7696       int move_dir = test_dir[j];
7697       int move_dir_preference;
7698
7699       xx = x + test_xy[j].x;
7700       yy = y + test_xy[j].y;
7701
7702       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7703           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7704       {
7705         new_move_dir = move_dir;
7706
7707         break;
7708       }
7709
7710       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7711         continue;
7712
7713       move_dir_preference = -1 * RunnerVisit[xx][yy];
7714       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7715         move_dir_preference = PlayerVisit[xx][yy];
7716
7717       if (move_dir_preference > move_preference)
7718       {
7719         // prefer field that has not been visited for the longest time
7720         move_preference = move_dir_preference;
7721         new_move_dir = move_dir;
7722       }
7723       else if (move_dir_preference == move_preference &&
7724                move_dir == old_move_dir)
7725       {
7726         // prefer last direction when all directions are preferred equally
7727         move_preference = move_dir_preference;
7728         new_move_dir = move_dir;
7729       }
7730     }
7731
7732     MovDir[x][y] = new_move_dir;
7733     if (old_move_dir != new_move_dir)
7734       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7735   }
7736 }
7737
7738 static void TurnRound(int x, int y)
7739 {
7740   int direction = MovDir[x][y];
7741
7742   TurnRoundExt(x, y);
7743
7744   GfxDir[x][y] = MovDir[x][y];
7745
7746   if (direction != MovDir[x][y])
7747     GfxFrame[x][y] = 0;
7748
7749   if (MovDelay[x][y])
7750     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7751
7752   ResetGfxFrame(x, y);
7753 }
7754
7755 static boolean JustBeingPushed(int x, int y)
7756 {
7757   int i;
7758
7759   for (i = 0; i < MAX_PLAYERS; i++)
7760   {
7761     struct PlayerInfo *player = &stored_player[i];
7762
7763     if (player->active && player->is_pushing && player->MovPos)
7764     {
7765       int next_jx = player->jx + (player->jx - player->last_jx);
7766       int next_jy = player->jy + (player->jy - player->last_jy);
7767
7768       if (x == next_jx && y == next_jy)
7769         return TRUE;
7770     }
7771   }
7772
7773   return FALSE;
7774 }
7775
7776 static void StartMoving(int x, int y)
7777 {
7778   boolean started_moving = FALSE;       // some elements can fall _and_ move
7779   int element = Tile[x][y];
7780
7781   if (Stop[x][y])
7782     return;
7783
7784   if (MovDelay[x][y] == 0)
7785     GfxAction[x][y] = ACTION_DEFAULT;
7786
7787   if (CAN_FALL(element) && y < lev_fieldy - 1)
7788   {
7789     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7790         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7791       if (JustBeingPushed(x, y))
7792         return;
7793
7794     if (element == EL_QUICKSAND_FULL)
7795     {
7796       if (IS_FREE(x, y + 1))
7797       {
7798         InitMovingField(x, y, MV_DOWN);
7799         started_moving = TRUE;
7800
7801         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7802 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7803         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7804           Store[x][y] = EL_ROCK;
7805 #else
7806         Store[x][y] = EL_ROCK;
7807 #endif
7808
7809         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7810       }
7811       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7812       {
7813         if (!MovDelay[x][y])
7814         {
7815           MovDelay[x][y] = TILEY + 1;
7816
7817           ResetGfxAnimation(x, y);
7818           ResetGfxAnimation(x, y + 1);
7819         }
7820
7821         if (MovDelay[x][y])
7822         {
7823           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7824           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7825
7826           MovDelay[x][y]--;
7827           if (MovDelay[x][y])
7828             return;
7829         }
7830
7831         Tile[x][y] = EL_QUICKSAND_EMPTY;
7832         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7833         Store[x][y + 1] = Store[x][y];
7834         Store[x][y] = 0;
7835
7836         PlayLevelSoundAction(x, y, ACTION_FILLING);
7837       }
7838       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7839       {
7840         if (!MovDelay[x][y])
7841         {
7842           MovDelay[x][y] = TILEY + 1;
7843
7844           ResetGfxAnimation(x, y);
7845           ResetGfxAnimation(x, y + 1);
7846         }
7847
7848         if (MovDelay[x][y])
7849         {
7850           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7851           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7852
7853           MovDelay[x][y]--;
7854           if (MovDelay[x][y])
7855             return;
7856         }
7857
7858         Tile[x][y] = EL_QUICKSAND_EMPTY;
7859         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7860         Store[x][y + 1] = Store[x][y];
7861         Store[x][y] = 0;
7862
7863         PlayLevelSoundAction(x, y, ACTION_FILLING);
7864       }
7865     }
7866     else if (element == EL_QUICKSAND_FAST_FULL)
7867     {
7868       if (IS_FREE(x, y + 1))
7869       {
7870         InitMovingField(x, y, MV_DOWN);
7871         started_moving = TRUE;
7872
7873         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7874 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7875         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7876           Store[x][y] = EL_ROCK;
7877 #else
7878         Store[x][y] = EL_ROCK;
7879 #endif
7880
7881         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7882       }
7883       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7884       {
7885         if (!MovDelay[x][y])
7886         {
7887           MovDelay[x][y] = TILEY + 1;
7888
7889           ResetGfxAnimation(x, y);
7890           ResetGfxAnimation(x, y + 1);
7891         }
7892
7893         if (MovDelay[x][y])
7894         {
7895           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7896           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7897
7898           MovDelay[x][y]--;
7899           if (MovDelay[x][y])
7900             return;
7901         }
7902
7903         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7904         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7905         Store[x][y + 1] = Store[x][y];
7906         Store[x][y] = 0;
7907
7908         PlayLevelSoundAction(x, y, ACTION_FILLING);
7909       }
7910       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7911       {
7912         if (!MovDelay[x][y])
7913         {
7914           MovDelay[x][y] = TILEY + 1;
7915
7916           ResetGfxAnimation(x, y);
7917           ResetGfxAnimation(x, y + 1);
7918         }
7919
7920         if (MovDelay[x][y])
7921         {
7922           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7923           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7924
7925           MovDelay[x][y]--;
7926           if (MovDelay[x][y])
7927             return;
7928         }
7929
7930         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7931         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7932         Store[x][y + 1] = Store[x][y];
7933         Store[x][y] = 0;
7934
7935         PlayLevelSoundAction(x, y, ACTION_FILLING);
7936       }
7937     }
7938     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7939              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7940     {
7941       InitMovingField(x, y, MV_DOWN);
7942       started_moving = TRUE;
7943
7944       Tile[x][y] = EL_QUICKSAND_FILLING;
7945       Store[x][y] = element;
7946
7947       PlayLevelSoundAction(x, y, ACTION_FILLING);
7948     }
7949     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7950              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7951     {
7952       InitMovingField(x, y, MV_DOWN);
7953       started_moving = TRUE;
7954
7955       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7956       Store[x][y] = element;
7957
7958       PlayLevelSoundAction(x, y, ACTION_FILLING);
7959     }
7960     else if (element == EL_MAGIC_WALL_FULL)
7961     {
7962       if (IS_FREE(x, y + 1))
7963       {
7964         InitMovingField(x, y, MV_DOWN);
7965         started_moving = TRUE;
7966
7967         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7968         Store[x][y] = EL_CHANGED(Store[x][y]);
7969       }
7970       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7971       {
7972         if (!MovDelay[x][y])
7973           MovDelay[x][y] = TILEY / 4 + 1;
7974
7975         if (MovDelay[x][y])
7976         {
7977           MovDelay[x][y]--;
7978           if (MovDelay[x][y])
7979             return;
7980         }
7981
7982         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7983         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7984         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7985         Store[x][y] = 0;
7986       }
7987     }
7988     else if (element == EL_BD_MAGIC_WALL_FULL)
7989     {
7990       if (IS_FREE(x, y + 1))
7991       {
7992         InitMovingField(x, y, MV_DOWN);
7993         started_moving = TRUE;
7994
7995         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7996         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7997       }
7998       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7999       {
8000         if (!MovDelay[x][y])
8001           MovDelay[x][y] = TILEY / 4 + 1;
8002
8003         if (MovDelay[x][y])
8004         {
8005           MovDelay[x][y]--;
8006           if (MovDelay[x][y])
8007             return;
8008         }
8009
8010         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8011         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8012         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8013         Store[x][y] = 0;
8014       }
8015     }
8016     else if (element == EL_DC_MAGIC_WALL_FULL)
8017     {
8018       if (IS_FREE(x, y + 1))
8019       {
8020         InitMovingField(x, y, MV_DOWN);
8021         started_moving = TRUE;
8022
8023         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8024         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8025       }
8026       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8027       {
8028         if (!MovDelay[x][y])
8029           MovDelay[x][y] = TILEY / 4 + 1;
8030
8031         if (MovDelay[x][y])
8032         {
8033           MovDelay[x][y]--;
8034           if (MovDelay[x][y])
8035             return;
8036         }
8037
8038         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8039         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8040         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8041         Store[x][y] = 0;
8042       }
8043     }
8044     else if ((CAN_PASS_MAGIC_WALL(element) &&
8045               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8046                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8047              (CAN_PASS_DC_MAGIC_WALL(element) &&
8048               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8049
8050     {
8051       InitMovingField(x, y, MV_DOWN);
8052       started_moving = TRUE;
8053
8054       Tile[x][y] =
8055         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8056          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8057          EL_DC_MAGIC_WALL_FILLING);
8058       Store[x][y] = element;
8059     }
8060     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8061     {
8062       SplashAcid(x, y + 1);
8063
8064       InitMovingField(x, y, MV_DOWN);
8065       started_moving = TRUE;
8066
8067       Store[x][y] = EL_ACID;
8068     }
8069     else if (
8070              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8071               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8072              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8073               CAN_FALL(element) && WasJustFalling[x][y] &&
8074               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8075
8076              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8077               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8078               (Tile[x][y + 1] == EL_BLOCKED)))
8079     {
8080       /* this is needed for a special case not covered by calling "Impact()"
8081          from "ContinueMoving()": if an element moves to a tile directly below
8082          another element which was just falling on that tile (which was empty
8083          in the previous frame), the falling element above would just stop
8084          instead of smashing the element below (in previous version, the above
8085          element was just checked for "moving" instead of "falling", resulting
8086          in incorrect smashes caused by horizontal movement of the above
8087          element; also, the case of the player being the element to smash was
8088          simply not covered here... :-/ ) */
8089
8090       CheckCollision[x][y] = 0;
8091       CheckImpact[x][y] = 0;
8092
8093       Impact(x, y);
8094     }
8095     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8096     {
8097       if (MovDir[x][y] == MV_NONE)
8098       {
8099         InitMovingField(x, y, MV_DOWN);
8100         started_moving = TRUE;
8101       }
8102     }
8103     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8104     {
8105       if (WasJustFalling[x][y]) // prevent animation from being restarted
8106         MovDir[x][y] = MV_DOWN;
8107
8108       InitMovingField(x, y, MV_DOWN);
8109       started_moving = TRUE;
8110     }
8111     else if (element == EL_AMOEBA_DROP)
8112     {
8113       Tile[x][y] = EL_AMOEBA_GROWING;
8114       Store[x][y] = EL_AMOEBA_WET;
8115     }
8116     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8117               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8118              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8119              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8120     {
8121       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8122                                 (IS_FREE(x - 1, y + 1) ||
8123                                  Tile[x - 1][y + 1] == EL_ACID));
8124       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8125                                 (IS_FREE(x + 1, y + 1) ||
8126                                  Tile[x + 1][y + 1] == EL_ACID));
8127       boolean can_fall_any  = (can_fall_left || can_fall_right);
8128       boolean can_fall_both = (can_fall_left && can_fall_right);
8129       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8130
8131       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8132       {
8133         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8134           can_fall_right = FALSE;
8135         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8136           can_fall_left = FALSE;
8137         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8138           can_fall_right = FALSE;
8139         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8140           can_fall_left = FALSE;
8141
8142         can_fall_any  = (can_fall_left || can_fall_right);
8143         can_fall_both = FALSE;
8144       }
8145
8146       if (can_fall_both)
8147       {
8148         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8149           can_fall_right = FALSE;       // slip down on left side
8150         else
8151           can_fall_left = !(can_fall_right = RND(2));
8152
8153         can_fall_both = FALSE;
8154       }
8155
8156       if (can_fall_any)
8157       {
8158         // if not determined otherwise, prefer left side for slipping down
8159         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8160         started_moving = TRUE;
8161       }
8162     }
8163     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8164     {
8165       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8166       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8167       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8168       int belt_dir = game.belt_dir[belt_nr];
8169
8170       if ((belt_dir == MV_LEFT  && left_is_free) ||
8171           (belt_dir == MV_RIGHT && right_is_free))
8172       {
8173         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8174
8175         InitMovingField(x, y, belt_dir);
8176         started_moving = TRUE;
8177
8178         Pushed[x][y] = TRUE;
8179         Pushed[nextx][y] = TRUE;
8180
8181         GfxAction[x][y] = ACTION_DEFAULT;
8182       }
8183       else
8184       {
8185         MovDir[x][y] = 0;       // if element was moving, stop it
8186       }
8187     }
8188   }
8189
8190   // not "else if" because of elements that can fall and move (EL_SPRING)
8191   if (CAN_MOVE(element) && !started_moving)
8192   {
8193     int move_pattern = element_info[element].move_pattern;
8194     int newx, newy;
8195
8196     Moving2Blocked(x, y, &newx, &newy);
8197
8198     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8199       return;
8200
8201     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8202         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8203     {
8204       WasJustMoving[x][y] = 0;
8205       CheckCollision[x][y] = 0;
8206
8207       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8208
8209       if (Tile[x][y] != element)        // element has changed
8210         return;
8211     }
8212
8213     if (!MovDelay[x][y])        // start new movement phase
8214     {
8215       // all objects that can change their move direction after each step
8216       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8217
8218       if (element != EL_YAMYAM &&
8219           element != EL_DARK_YAMYAM &&
8220           element != EL_PACMAN &&
8221           !(move_pattern & MV_ANY_DIRECTION) &&
8222           move_pattern != MV_TURNING_LEFT &&
8223           move_pattern != MV_TURNING_RIGHT &&
8224           move_pattern != MV_TURNING_LEFT_RIGHT &&
8225           move_pattern != MV_TURNING_RIGHT_LEFT &&
8226           move_pattern != MV_TURNING_RANDOM)
8227       {
8228         TurnRound(x, y);
8229
8230         if (MovDelay[x][y] && (element == EL_BUG ||
8231                                element == EL_SPACESHIP ||
8232                                element == EL_SP_SNIKSNAK ||
8233                                element == EL_SP_ELECTRON ||
8234                                element == EL_MOLE))
8235           TEST_DrawLevelField(x, y);
8236       }
8237     }
8238
8239     if (MovDelay[x][y])         // wait some time before next movement
8240     {
8241       MovDelay[x][y]--;
8242
8243       if (element == EL_ROBOT ||
8244           element == EL_YAMYAM ||
8245           element == EL_DARK_YAMYAM)
8246       {
8247         DrawLevelElementAnimationIfNeeded(x, y, element);
8248         PlayLevelSoundAction(x, y, ACTION_WAITING);
8249       }
8250       else if (element == EL_SP_ELECTRON)
8251         DrawLevelElementAnimationIfNeeded(x, y, element);
8252       else if (element == EL_DRAGON)
8253       {
8254         int i;
8255         int dir = MovDir[x][y];
8256         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8257         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8258         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8259                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8260                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8261                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8262         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8263
8264         GfxAction[x][y] = ACTION_ATTACKING;
8265
8266         if (IS_PLAYER(x, y))
8267           DrawPlayerField(x, y);
8268         else
8269           TEST_DrawLevelField(x, y);
8270
8271         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8272
8273         for (i = 1; i <= 3; i++)
8274         {
8275           int xx = x + i * dx;
8276           int yy = y + i * dy;
8277           int sx = SCREENX(xx);
8278           int sy = SCREENY(yy);
8279           int flame_graphic = graphic + (i - 1);
8280
8281           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8282             break;
8283
8284           if (MovDelay[x][y])
8285           {
8286             int flamed = MovingOrBlocked2Element(xx, yy);
8287
8288             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8289               Bang(xx, yy);
8290             else
8291               RemoveMovingField(xx, yy);
8292
8293             ChangeDelay[xx][yy] = 0;
8294
8295             Tile[xx][yy] = EL_FLAMES;
8296
8297             if (IN_SCR_FIELD(sx, sy))
8298             {
8299               TEST_DrawLevelFieldCrumbled(xx, yy);
8300               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8301             }
8302           }
8303           else
8304           {
8305             if (Tile[xx][yy] == EL_FLAMES)
8306               Tile[xx][yy] = EL_EMPTY;
8307             TEST_DrawLevelField(xx, yy);
8308           }
8309         }
8310       }
8311
8312       if (MovDelay[x][y])       // element still has to wait some time
8313       {
8314         PlayLevelSoundAction(x, y, ACTION_WAITING);
8315
8316         return;
8317       }
8318     }
8319
8320     // now make next step
8321
8322     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8323
8324     if (DONT_COLLIDE_WITH(element) &&
8325         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8326         !PLAYER_ENEMY_PROTECTED(newx, newy))
8327     {
8328       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8329
8330       return;
8331     }
8332
8333     else if (CAN_MOVE_INTO_ACID(element) &&
8334              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8335              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8336              (MovDir[x][y] == MV_DOWN ||
8337               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8338     {
8339       SplashAcid(newx, newy);
8340       Store[x][y] = EL_ACID;
8341     }
8342     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8343     {
8344       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8345           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8346           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8347           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8348       {
8349         RemoveField(x, y);
8350         TEST_DrawLevelField(x, y);
8351
8352         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8353         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8354           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8355
8356         game.friends_still_needed--;
8357         if (!game.friends_still_needed &&
8358             !game.GameOver &&
8359             game.all_players_gone)
8360           LevelSolved();
8361
8362         return;
8363       }
8364       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8365       {
8366         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8367           TEST_DrawLevelField(newx, newy);
8368         else
8369           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8370       }
8371       else if (!IS_FREE(newx, newy))
8372       {
8373         GfxAction[x][y] = ACTION_WAITING;
8374
8375         if (IS_PLAYER(x, y))
8376           DrawPlayerField(x, y);
8377         else
8378           TEST_DrawLevelField(x, y);
8379
8380         return;
8381       }
8382     }
8383     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8384     {
8385       if (IS_FOOD_PIG(Tile[newx][newy]))
8386       {
8387         if (IS_MOVING(newx, newy))
8388           RemoveMovingField(newx, newy);
8389         else
8390         {
8391           Tile[newx][newy] = EL_EMPTY;
8392           TEST_DrawLevelField(newx, newy);
8393         }
8394
8395         PlayLevelSound(x, y, SND_PIG_DIGGING);
8396       }
8397       else if (!IS_FREE(newx, newy))
8398       {
8399         if (IS_PLAYER(x, y))
8400           DrawPlayerField(x, y);
8401         else
8402           TEST_DrawLevelField(x, y);
8403
8404         return;
8405       }
8406     }
8407     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8408     {
8409       if (Store[x][y] != EL_EMPTY)
8410       {
8411         boolean can_clone = FALSE;
8412         int xx, yy;
8413
8414         // check if element to clone is still there
8415         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8416         {
8417           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8418           {
8419             can_clone = TRUE;
8420
8421             break;
8422           }
8423         }
8424
8425         // cannot clone or target field not free anymore -- do not clone
8426         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8427           Store[x][y] = EL_EMPTY;
8428       }
8429
8430       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8431       {
8432         if (IS_MV_DIAGONAL(MovDir[x][y]))
8433         {
8434           int diagonal_move_dir = MovDir[x][y];
8435           int stored = Store[x][y];
8436           int change_delay = 8;
8437           int graphic;
8438
8439           // android is moving diagonally
8440
8441           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8442
8443           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8444           GfxElement[x][y] = EL_EMC_ANDROID;
8445           GfxAction[x][y] = ACTION_SHRINKING;
8446           GfxDir[x][y] = diagonal_move_dir;
8447           ChangeDelay[x][y] = change_delay;
8448
8449           if (Store[x][y] == EL_EMPTY)
8450             Store[x][y] = GfxElementEmpty[x][y];
8451
8452           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8453                                    GfxDir[x][y]);
8454
8455           DrawLevelGraphicAnimation(x, y, graphic);
8456           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8457
8458           if (Tile[newx][newy] == EL_ACID)
8459           {
8460             SplashAcid(newx, newy);
8461
8462             return;
8463           }
8464
8465           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8466
8467           Store[newx][newy] = EL_EMC_ANDROID;
8468           GfxElement[newx][newy] = EL_EMC_ANDROID;
8469           GfxAction[newx][newy] = ACTION_GROWING;
8470           GfxDir[newx][newy] = diagonal_move_dir;
8471           ChangeDelay[newx][newy] = change_delay;
8472
8473           graphic = el_act_dir2img(GfxElement[newx][newy],
8474                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8475
8476           DrawLevelGraphicAnimation(newx, newy, graphic);
8477           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8478
8479           return;
8480         }
8481         else
8482         {
8483           Tile[newx][newy] = EL_EMPTY;
8484           TEST_DrawLevelField(newx, newy);
8485
8486           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8487         }
8488       }
8489       else if (!IS_FREE(newx, newy))
8490       {
8491         return;
8492       }
8493     }
8494     else if (IS_CUSTOM_ELEMENT(element) &&
8495              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8496     {
8497       if (!DigFieldByCE(newx, newy, element))
8498         return;
8499
8500       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8501       {
8502         RunnerVisit[x][y] = FrameCounter;
8503         PlayerVisit[x][y] /= 8;         // expire player visit path
8504       }
8505     }
8506     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8507     {
8508       if (!IS_FREE(newx, newy))
8509       {
8510         if (IS_PLAYER(x, y))
8511           DrawPlayerField(x, y);
8512         else
8513           TEST_DrawLevelField(x, y);
8514
8515         return;
8516       }
8517       else
8518       {
8519         boolean wanna_flame = !RND(10);
8520         int dx = newx - x, dy = newy - y;
8521         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8522         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8523         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8524                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8525         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8526                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8527
8528         if ((wanna_flame ||
8529              IS_CLASSIC_ENEMY(element1) ||
8530              IS_CLASSIC_ENEMY(element2)) &&
8531             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8532             element1 != EL_FLAMES && element2 != EL_FLAMES)
8533         {
8534           ResetGfxAnimation(x, y);
8535           GfxAction[x][y] = ACTION_ATTACKING;
8536
8537           if (IS_PLAYER(x, y))
8538             DrawPlayerField(x, y);
8539           else
8540             TEST_DrawLevelField(x, y);
8541
8542           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8543
8544           MovDelay[x][y] = 50;
8545
8546           Tile[newx][newy] = EL_FLAMES;
8547           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8548             Tile[newx1][newy1] = EL_FLAMES;
8549           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8550             Tile[newx2][newy2] = EL_FLAMES;
8551
8552           return;
8553         }
8554       }
8555     }
8556     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8557              Tile[newx][newy] == EL_DIAMOND)
8558     {
8559       if (IS_MOVING(newx, newy))
8560         RemoveMovingField(newx, newy);
8561       else
8562       {
8563         Tile[newx][newy] = EL_EMPTY;
8564         TEST_DrawLevelField(newx, newy);
8565       }
8566
8567       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8568     }
8569     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8570              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8571     {
8572       if (AmoebaNr[newx][newy])
8573       {
8574         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8575         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8576             Tile[newx][newy] == EL_BD_AMOEBA)
8577           AmoebaCnt[AmoebaNr[newx][newy]]--;
8578       }
8579
8580       if (IS_MOVING(newx, newy))
8581       {
8582         RemoveMovingField(newx, newy);
8583       }
8584       else
8585       {
8586         Tile[newx][newy] = EL_EMPTY;
8587         TEST_DrawLevelField(newx, newy);
8588       }
8589
8590       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8591     }
8592     else if ((element == EL_PACMAN || element == EL_MOLE)
8593              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8594     {
8595       if (AmoebaNr[newx][newy])
8596       {
8597         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8598         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8599             Tile[newx][newy] == EL_BD_AMOEBA)
8600           AmoebaCnt[AmoebaNr[newx][newy]]--;
8601       }
8602
8603       if (element == EL_MOLE)
8604       {
8605         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8606         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8607
8608         ResetGfxAnimation(x, y);
8609         GfxAction[x][y] = ACTION_DIGGING;
8610         TEST_DrawLevelField(x, y);
8611
8612         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8613
8614         return;                         // wait for shrinking amoeba
8615       }
8616       else      // element == EL_PACMAN
8617       {
8618         Tile[newx][newy] = EL_EMPTY;
8619         TEST_DrawLevelField(newx, newy);
8620         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8621       }
8622     }
8623     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8624              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8625               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8626     {
8627       // wait for shrinking amoeba to completely disappear
8628       return;
8629     }
8630     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8631     {
8632       // object was running against a wall
8633
8634       TurnRound(x, y);
8635
8636       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8637         DrawLevelElementAnimation(x, y, element);
8638
8639       if (DONT_TOUCH(element))
8640         TestIfBadThingTouchesPlayer(x, y);
8641
8642       return;
8643     }
8644
8645     InitMovingField(x, y, MovDir[x][y]);
8646
8647     PlayLevelSoundAction(x, y, ACTION_MOVING);
8648   }
8649
8650   if (MovDir[x][y])
8651     ContinueMoving(x, y);
8652 }
8653
8654 void ContinueMoving(int x, int y)
8655 {
8656   int element = Tile[x][y];
8657   struct ElementInfo *ei = &element_info[element];
8658   int direction = MovDir[x][y];
8659   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8660   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8661   int newx = x + dx, newy = y + dy;
8662   int stored = Store[x][y];
8663   int stored_new = Store[newx][newy];
8664   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8665   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8666   boolean last_line = (newy == lev_fieldy - 1);
8667   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8668
8669   if (pushed_by_player)         // special case: moving object pushed by player
8670   {
8671     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8672   }
8673   else if (use_step_delay)      // special case: moving object has step delay
8674   {
8675     if (!MovDelay[x][y])
8676       MovPos[x][y] += getElementMoveStepsize(x, y);
8677
8678     if (MovDelay[x][y])
8679       MovDelay[x][y]--;
8680     else
8681       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8682
8683     if (MovDelay[x][y])
8684     {
8685       TEST_DrawLevelField(x, y);
8686
8687       return;   // element is still waiting
8688     }
8689   }
8690   else                          // normal case: generically moving object
8691   {
8692     MovPos[x][y] += getElementMoveStepsize(x, y);
8693   }
8694
8695   if (ABS(MovPos[x][y]) < TILEX)
8696   {
8697     TEST_DrawLevelField(x, y);
8698
8699     return;     // element is still moving
8700   }
8701
8702   // element reached destination field
8703
8704   Tile[x][y] = EL_EMPTY;
8705   Tile[newx][newy] = element;
8706   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8707
8708   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8709   {
8710     element = Tile[newx][newy] = EL_ACID;
8711   }
8712   else if (element == EL_MOLE)
8713   {
8714     Tile[x][y] = EL_SAND;
8715
8716     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8717   }
8718   else if (element == EL_QUICKSAND_FILLING)
8719   {
8720     element = Tile[newx][newy] = get_next_element(element);
8721     Store[newx][newy] = Store[x][y];
8722   }
8723   else if (element == EL_QUICKSAND_EMPTYING)
8724   {
8725     Tile[x][y] = get_next_element(element);
8726     element = Tile[newx][newy] = Store[x][y];
8727   }
8728   else if (element == EL_QUICKSAND_FAST_FILLING)
8729   {
8730     element = Tile[newx][newy] = get_next_element(element);
8731     Store[newx][newy] = Store[x][y];
8732   }
8733   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8734   {
8735     Tile[x][y] = get_next_element(element);
8736     element = Tile[newx][newy] = Store[x][y];
8737   }
8738   else if (element == EL_MAGIC_WALL_FILLING)
8739   {
8740     element = Tile[newx][newy] = get_next_element(element);
8741     if (!game.magic_wall_active)
8742       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8743     Store[newx][newy] = Store[x][y];
8744   }
8745   else if (element == EL_MAGIC_WALL_EMPTYING)
8746   {
8747     Tile[x][y] = get_next_element(element);
8748     if (!game.magic_wall_active)
8749       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8750     element = Tile[newx][newy] = Store[x][y];
8751
8752     InitField(newx, newy, FALSE);
8753   }
8754   else if (element == EL_BD_MAGIC_WALL_FILLING)
8755   {
8756     element = Tile[newx][newy] = get_next_element(element);
8757     if (!game.magic_wall_active)
8758       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8759     Store[newx][newy] = Store[x][y];
8760   }
8761   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8762   {
8763     Tile[x][y] = get_next_element(element);
8764     if (!game.magic_wall_active)
8765       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8766     element = Tile[newx][newy] = Store[x][y];
8767
8768     InitField(newx, newy, FALSE);
8769   }
8770   else if (element == EL_DC_MAGIC_WALL_FILLING)
8771   {
8772     element = Tile[newx][newy] = get_next_element(element);
8773     if (!game.magic_wall_active)
8774       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8775     Store[newx][newy] = Store[x][y];
8776   }
8777   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8778   {
8779     Tile[x][y] = get_next_element(element);
8780     if (!game.magic_wall_active)
8781       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8782     element = Tile[newx][newy] = Store[x][y];
8783
8784     InitField(newx, newy, FALSE);
8785   }
8786   else if (element == EL_AMOEBA_DROPPING)
8787   {
8788     Tile[x][y] = get_next_element(element);
8789     element = Tile[newx][newy] = Store[x][y];
8790   }
8791   else if (element == EL_SOKOBAN_OBJECT)
8792   {
8793     if (Back[x][y])
8794       Tile[x][y] = Back[x][y];
8795
8796     if (Back[newx][newy])
8797       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8798
8799     Back[x][y] = Back[newx][newy] = 0;
8800   }
8801
8802   Store[x][y] = EL_EMPTY;
8803   MovPos[x][y] = 0;
8804   MovDir[x][y] = 0;
8805   MovDelay[x][y] = 0;
8806
8807   MovDelay[newx][newy] = 0;
8808
8809   if (CAN_CHANGE_OR_HAS_ACTION(element))
8810   {
8811     // copy element change control values to new field
8812     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8813     ChangePage[newx][newy]  = ChangePage[x][y];
8814     ChangeCount[newx][newy] = ChangeCount[x][y];
8815     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8816   }
8817
8818   CustomValue[newx][newy] = CustomValue[x][y];
8819
8820   ChangeDelay[x][y] = 0;
8821   ChangePage[x][y] = -1;
8822   ChangeCount[x][y] = 0;
8823   ChangeEvent[x][y] = -1;
8824
8825   CustomValue[x][y] = 0;
8826
8827   // copy animation control values to new field
8828   GfxFrame[newx][newy]  = GfxFrame[x][y];
8829   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8830   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8831   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8832
8833   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8834
8835   // some elements can leave other elements behind after moving
8836   if (ei->move_leave_element != EL_EMPTY &&
8837       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8838       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8839   {
8840     int move_leave_element = ei->move_leave_element;
8841
8842     // this makes it possible to leave the removed element again
8843     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8844       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8845
8846     Tile[x][y] = move_leave_element;
8847
8848     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8849       MovDir[x][y] = direction;
8850
8851     InitField(x, y, FALSE);
8852
8853     if (GFX_CRUMBLED(Tile[x][y]))
8854       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8855
8856     if (IS_PLAYER_ELEMENT(move_leave_element))
8857       RelocatePlayer(x, y, move_leave_element);
8858   }
8859
8860   // do this after checking for left-behind element
8861   ResetGfxAnimation(x, y);      // reset animation values for old field
8862
8863   if (!CAN_MOVE(element) ||
8864       (CAN_FALL(element) && direction == MV_DOWN &&
8865        (element == EL_SPRING ||
8866         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8867         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8868     GfxDir[x][y] = MovDir[newx][newy] = 0;
8869
8870   TEST_DrawLevelField(x, y);
8871   TEST_DrawLevelField(newx, newy);
8872
8873   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8874
8875   // prevent pushed element from moving on in pushed direction
8876   if (pushed_by_player && CAN_MOVE(element) &&
8877       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8878       !(element_info[element].move_pattern & direction))
8879     TurnRound(newx, newy);
8880
8881   // prevent elements on conveyor belt from moving on in last direction
8882   if (pushed_by_conveyor && CAN_FALL(element) &&
8883       direction & MV_HORIZONTAL)
8884     MovDir[newx][newy] = 0;
8885
8886   if (!pushed_by_player)
8887   {
8888     int nextx = newx + dx, nexty = newy + dy;
8889     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8890
8891     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8892
8893     if (CAN_FALL(element) && direction == MV_DOWN)
8894       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8895
8896     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8897       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8898
8899     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8900       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8901   }
8902
8903   if (DONT_TOUCH(element))      // object may be nasty to player or others
8904   {
8905     TestIfBadThingTouchesPlayer(newx, newy);
8906     TestIfBadThingTouchesFriend(newx, newy);
8907
8908     if (!IS_CUSTOM_ELEMENT(element))
8909       TestIfBadThingTouchesOtherBadThing(newx, newy);
8910   }
8911   else if (element == EL_PENGUIN)
8912     TestIfFriendTouchesBadThing(newx, newy);
8913
8914   if (DONT_GET_HIT_BY(element))
8915   {
8916     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8917   }
8918
8919   // give the player one last chance (one more frame) to move away
8920   if (CAN_FALL(element) && direction == MV_DOWN &&
8921       (last_line || (!IS_FREE(x, newy + 1) &&
8922                      (!IS_PLAYER(x, newy + 1) ||
8923                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8924     Impact(x, newy);
8925
8926   if (pushed_by_player && !game.use_change_when_pushing_bug)
8927   {
8928     int push_side = MV_DIR_OPPOSITE(direction);
8929     struct PlayerInfo *player = PLAYERINFO(x, y);
8930
8931     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8932                                player->index_bit, push_side);
8933     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8934                                         player->index_bit, push_side);
8935   }
8936
8937   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8938     MovDelay[newx][newy] = 1;
8939
8940   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8941
8942   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8943   TestIfElementHitsCustomElement(newx, newy, direction);
8944   TestIfPlayerTouchesCustomElement(newx, newy);
8945   TestIfElementTouchesCustomElement(newx, newy);
8946
8947   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8948       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8949     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8950                              MV_DIR_OPPOSITE(direction));
8951 }
8952
8953 int AmoebaNeighbourNr(int ax, int ay)
8954 {
8955   int i;
8956   int element = Tile[ax][ay];
8957   int group_nr = 0;
8958   struct XY *xy = xy_topdown;
8959
8960   for (i = 0; i < NUM_DIRECTIONS; i++)
8961   {
8962     int x = ax + xy[i].x;
8963     int y = ay + xy[i].y;
8964
8965     if (!IN_LEV_FIELD(x, y))
8966       continue;
8967
8968     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8969       group_nr = AmoebaNr[x][y];
8970   }
8971
8972   return group_nr;
8973 }
8974
8975 static void AmoebaMerge(int ax, int ay)
8976 {
8977   int i, x, y, xx, yy;
8978   int new_group_nr = AmoebaNr[ax][ay];
8979   struct XY *xy = xy_topdown;
8980
8981   if (new_group_nr == 0)
8982     return;
8983
8984   for (i = 0; i < NUM_DIRECTIONS; i++)
8985   {
8986     x = ax + xy[i].x;
8987     y = ay + xy[i].y;
8988
8989     if (!IN_LEV_FIELD(x, y))
8990       continue;
8991
8992     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8993          Tile[x][y] == EL_BD_AMOEBA ||
8994          Tile[x][y] == EL_AMOEBA_DEAD) &&
8995         AmoebaNr[x][y] != new_group_nr)
8996     {
8997       int old_group_nr = AmoebaNr[x][y];
8998
8999       if (old_group_nr == 0)
9000         return;
9001
9002       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9003       AmoebaCnt[old_group_nr] = 0;
9004       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9005       AmoebaCnt2[old_group_nr] = 0;
9006
9007       SCAN_PLAYFIELD(xx, yy)
9008       {
9009         if (AmoebaNr[xx][yy] == old_group_nr)
9010           AmoebaNr[xx][yy] = new_group_nr;
9011       }
9012     }
9013   }
9014 }
9015
9016 void AmoebaToDiamond(int ax, int ay)
9017 {
9018   int i, x, y;
9019
9020   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9021   {
9022     int group_nr = AmoebaNr[ax][ay];
9023
9024 #ifdef DEBUG
9025     if (group_nr == 0)
9026     {
9027       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9028       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9029
9030       return;
9031     }
9032 #endif
9033
9034     SCAN_PLAYFIELD(x, y)
9035     {
9036       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9037       {
9038         AmoebaNr[x][y] = 0;
9039         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9040       }
9041     }
9042
9043     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9044                             SND_AMOEBA_TURNING_TO_GEM :
9045                             SND_AMOEBA_TURNING_TO_ROCK));
9046     Bang(ax, ay);
9047   }
9048   else
9049   {
9050     struct XY *xy = xy_topdown;
9051
9052     for (i = 0; i < NUM_DIRECTIONS; i++)
9053     {
9054       x = ax + xy[i].x;
9055       y = ay + xy[i].y;
9056
9057       if (!IN_LEV_FIELD(x, y))
9058         continue;
9059
9060       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9061       {
9062         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9063                               SND_AMOEBA_TURNING_TO_GEM :
9064                               SND_AMOEBA_TURNING_TO_ROCK));
9065         Bang(x, y);
9066       }
9067     }
9068   }
9069 }
9070
9071 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9072 {
9073   int x, y;
9074   int group_nr = AmoebaNr[ax][ay];
9075   boolean done = FALSE;
9076
9077 #ifdef DEBUG
9078   if (group_nr == 0)
9079   {
9080     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9081     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9082
9083     return;
9084   }
9085 #endif
9086
9087   SCAN_PLAYFIELD(x, y)
9088   {
9089     if (AmoebaNr[x][y] == group_nr &&
9090         (Tile[x][y] == EL_AMOEBA_DEAD ||
9091          Tile[x][y] == EL_BD_AMOEBA ||
9092          Tile[x][y] == EL_AMOEBA_GROWING))
9093     {
9094       AmoebaNr[x][y] = 0;
9095       Tile[x][y] = new_element;
9096       InitField(x, y, FALSE);
9097       TEST_DrawLevelField(x, y);
9098       done = TRUE;
9099     }
9100   }
9101
9102   if (done)
9103     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9104                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9105                             SND_BD_AMOEBA_TURNING_TO_GEM));
9106 }
9107
9108 static void AmoebaGrowing(int x, int y)
9109 {
9110   static DelayCounter sound_delay = { 0 };
9111
9112   if (!MovDelay[x][y])          // start new growing cycle
9113   {
9114     MovDelay[x][y] = 7;
9115
9116     if (DelayReached(&sound_delay))
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       DrawLevelGraphic(x, 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 DelayCounter sound_delay = { 0 };
9146
9147   if (!MovDelay[x][y])          // start new shrinking cycle
9148   {
9149     MovDelay[x][y] = 7;
9150
9151     if (DelayReached(&sound_delay))
9152       sound_delay.value = 30;
9153   }
9154
9155   if (MovDelay[x][y])           // wait some time before shrinking
9156   {
9157     MovDelay[x][y]--;
9158     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9159     {
9160       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9161                                            6 - MovDelay[x][y]);
9162
9163       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9164     }
9165
9166     if (!MovDelay[x][y])
9167     {
9168       Tile[x][y] = EL_EMPTY;
9169       TEST_DrawLevelField(x, y);
9170
9171       // don't let mole enter this field in this cycle;
9172       // (give priority to objects falling to this field from above)
9173       Stop[x][y] = TRUE;
9174     }
9175   }
9176 }
9177
9178 static void AmoebaReproduce(int ax, int ay)
9179 {
9180   int i;
9181   int element = Tile[ax][ay];
9182   int graphic = el2img(element);
9183   int newax = ax, neway = ay;
9184   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9185   struct XY *xy = xy_topdown;
9186
9187   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9188   {
9189     Tile[ax][ay] = EL_AMOEBA_DEAD;
9190     TEST_DrawLevelField(ax, ay);
9191     return;
9192   }
9193
9194   if (IS_ANIMATED(graphic))
9195     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9196
9197   if (!MovDelay[ax][ay])        // start making new amoeba field
9198     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9199
9200   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9201   {
9202     MovDelay[ax][ay]--;
9203     if (MovDelay[ax][ay])
9204       return;
9205   }
9206
9207   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9208   {
9209     int start = RND(4);
9210     int x = ax + xy[start].x;
9211     int y = ay + xy[start].y;
9212
9213     if (!IN_LEV_FIELD(x, y))
9214       return;
9215
9216     if (IS_FREE(x, y) ||
9217         CAN_GROW_INTO(Tile[x][y]) ||
9218         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9219         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9220     {
9221       newax = x;
9222       neway = y;
9223     }
9224
9225     if (newax == ax && neway == ay)
9226       return;
9227   }
9228   else                          // normal or "filled" (BD style) amoeba
9229   {
9230     int start = RND(4);
9231     boolean waiting_for_player = FALSE;
9232
9233     for (i = 0; i < NUM_DIRECTIONS; i++)
9234     {
9235       int j = (start + i) % 4;
9236       int x = ax + xy[j].x;
9237       int y = ay + xy[j].y;
9238
9239       if (!IN_LEV_FIELD(x, y))
9240         continue;
9241
9242       if (IS_FREE(x, y) ||
9243           CAN_GROW_INTO(Tile[x][y]) ||
9244           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9245           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9246       {
9247         newax = x;
9248         neway = y;
9249         break;
9250       }
9251       else if (IS_PLAYER(x, y))
9252         waiting_for_player = TRUE;
9253     }
9254
9255     if (newax == ax && neway == ay)             // amoeba cannot grow
9256     {
9257       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9258       {
9259         Tile[ax][ay] = EL_AMOEBA_DEAD;
9260         TEST_DrawLevelField(ax, ay);
9261         AmoebaCnt[AmoebaNr[ax][ay]]--;
9262
9263         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9264         {
9265           if (element == EL_AMOEBA_FULL)
9266             AmoebaToDiamond(ax, ay);
9267           else if (element == EL_BD_AMOEBA)
9268             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9269         }
9270       }
9271       return;
9272     }
9273     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9274     {
9275       // amoeba gets larger by growing in some direction
9276
9277       int new_group_nr = AmoebaNr[ax][ay];
9278
9279 #ifdef DEBUG
9280   if (new_group_nr == 0)
9281   {
9282     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9283           newax, neway);
9284     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9285
9286     return;
9287   }
9288 #endif
9289
9290       AmoebaNr[newax][neway] = new_group_nr;
9291       AmoebaCnt[new_group_nr]++;
9292       AmoebaCnt2[new_group_nr]++;
9293
9294       // if amoeba touches other amoeba(s) after growing, unify them
9295       AmoebaMerge(newax, neway);
9296
9297       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9298       {
9299         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9300         return;
9301       }
9302     }
9303   }
9304
9305   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9306       (neway == lev_fieldy - 1 && newax != ax))
9307   {
9308     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9309     Store[newax][neway] = element;
9310   }
9311   else if (neway == ay || element == EL_EMC_DRIPPER)
9312   {
9313     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9314
9315     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9316   }
9317   else
9318   {
9319     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9320     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9321     Store[ax][ay] = EL_AMOEBA_DROP;
9322     ContinueMoving(ax, ay);
9323     return;
9324   }
9325
9326   TEST_DrawLevelField(newax, neway);
9327 }
9328
9329 static void Life(int ax, int ay)
9330 {
9331   int x1, y1, x2, y2;
9332   int life_time = 40;
9333   int element = Tile[ax][ay];
9334   int graphic = el2img(element);
9335   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9336                          level.biomaze);
9337   boolean changed = FALSE;
9338
9339   if (IS_ANIMATED(graphic))
9340     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9341
9342   if (Stop[ax][ay])
9343     return;
9344
9345   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9346     MovDelay[ax][ay] = life_time;
9347
9348   if (MovDelay[ax][ay])         // wait some time before next cycle
9349   {
9350     MovDelay[ax][ay]--;
9351     if (MovDelay[ax][ay])
9352       return;
9353   }
9354
9355   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9356   {
9357     int xx = ax+x1, yy = ay+y1;
9358     int old_element = Tile[xx][yy];
9359     int num_neighbours = 0;
9360
9361     if (!IN_LEV_FIELD(xx, yy))
9362       continue;
9363
9364     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9365     {
9366       int x = xx+x2, y = yy+y2;
9367
9368       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9369         continue;
9370
9371       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9372       boolean is_neighbour = FALSE;
9373
9374       if (level.use_life_bugs)
9375         is_neighbour =
9376           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9377            (IS_FREE(x, y)                             &&  Stop[x][y]));
9378       else
9379         is_neighbour =
9380           (Last[x][y] == element || is_player_cell);
9381
9382       if (is_neighbour)
9383         num_neighbours++;
9384     }
9385
9386     boolean is_free = FALSE;
9387
9388     if (level.use_life_bugs)
9389       is_free = (IS_FREE(xx, yy));
9390     else
9391       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9392
9393     if (xx == ax && yy == ay)           // field in the middle
9394     {
9395       if (num_neighbours < life_parameter[0] ||
9396           num_neighbours > life_parameter[1])
9397       {
9398         Tile[xx][yy] = EL_EMPTY;
9399         if (Tile[xx][yy] != old_element)
9400           TEST_DrawLevelField(xx, yy);
9401         Stop[xx][yy] = TRUE;
9402         changed = TRUE;
9403       }
9404     }
9405     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9406     {                                   // free border field
9407       if (num_neighbours >= life_parameter[2] &&
9408           num_neighbours <= life_parameter[3])
9409       {
9410         Tile[xx][yy] = element;
9411         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9412         if (Tile[xx][yy] != old_element)
9413           TEST_DrawLevelField(xx, yy);
9414         Stop[xx][yy] = TRUE;
9415         changed = TRUE;
9416       }
9417     }
9418   }
9419
9420   if (changed)
9421     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9422                    SND_GAME_OF_LIFE_GROWING);
9423 }
9424
9425 static void InitRobotWheel(int x, int y)
9426 {
9427   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9428 }
9429
9430 static void RunRobotWheel(int x, int y)
9431 {
9432   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9433 }
9434
9435 static void StopRobotWheel(int x, int y)
9436 {
9437   if (game.robot_wheel_x == x &&
9438       game.robot_wheel_y == y)
9439   {
9440     game.robot_wheel_x = -1;
9441     game.robot_wheel_y = -1;
9442     game.robot_wheel_active = FALSE;
9443   }
9444 }
9445
9446 static void InitTimegateWheel(int x, int y)
9447 {
9448   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9449 }
9450
9451 static void RunTimegateWheel(int x, int y)
9452 {
9453   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9454 }
9455
9456 static void InitMagicBallDelay(int x, int y)
9457 {
9458   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9459 }
9460
9461 static void ActivateMagicBall(int bx, int by)
9462 {
9463   int x, y;
9464
9465   if (level.ball_random)
9466   {
9467     int pos_border = RND(8);    // select one of the eight border elements
9468     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9469     int xx = pos_content % 3;
9470     int yy = pos_content / 3;
9471
9472     x = bx - 1 + xx;
9473     y = by - 1 + yy;
9474
9475     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9476       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9477   }
9478   else
9479   {
9480     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9481     {
9482       int xx = x - bx + 1;
9483       int yy = y - by + 1;
9484
9485       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9486         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9487     }
9488   }
9489
9490   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9491 }
9492
9493 static void CheckExit(int x, int y)
9494 {
9495   if (game.gems_still_needed > 0 ||
9496       game.sokoban_fields_still_needed > 0 ||
9497       game.sokoban_objects_still_needed > 0 ||
9498       game.lights_still_needed > 0)
9499   {
9500     int element = Tile[x][y];
9501     int graphic = el2img(element);
9502
9503     if (IS_ANIMATED(graphic))
9504       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9505
9506     return;
9507   }
9508
9509   // do not re-open exit door closed after last player
9510   if (game.all_players_gone)
9511     return;
9512
9513   Tile[x][y] = EL_EXIT_OPENING;
9514
9515   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9516 }
9517
9518 static void CheckExitEM(int x, int y)
9519 {
9520   if (game.gems_still_needed > 0 ||
9521       game.sokoban_fields_still_needed > 0 ||
9522       game.sokoban_objects_still_needed > 0 ||
9523       game.lights_still_needed > 0)
9524   {
9525     int element = Tile[x][y];
9526     int graphic = el2img(element);
9527
9528     if (IS_ANIMATED(graphic))
9529       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9530
9531     return;
9532   }
9533
9534   // do not re-open exit door closed after last player
9535   if (game.all_players_gone)
9536     return;
9537
9538   Tile[x][y] = EL_EM_EXIT_OPENING;
9539
9540   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9541 }
9542
9543 static void CheckExitSteel(int x, int y)
9544 {
9545   if (game.gems_still_needed > 0 ||
9546       game.sokoban_fields_still_needed > 0 ||
9547       game.sokoban_objects_still_needed > 0 ||
9548       game.lights_still_needed > 0)
9549   {
9550     int element = Tile[x][y];
9551     int graphic = el2img(element);
9552
9553     if (IS_ANIMATED(graphic))
9554       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9555
9556     return;
9557   }
9558
9559   // do not re-open exit door closed after last player
9560   if (game.all_players_gone)
9561     return;
9562
9563   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9564
9565   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9566 }
9567
9568 static void CheckExitSteelEM(int x, int y)
9569 {
9570   if (game.gems_still_needed > 0 ||
9571       game.sokoban_fields_still_needed > 0 ||
9572       game.sokoban_objects_still_needed > 0 ||
9573       game.lights_still_needed > 0)
9574   {
9575     int element = Tile[x][y];
9576     int graphic = el2img(element);
9577
9578     if (IS_ANIMATED(graphic))
9579       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9580
9581     return;
9582   }
9583
9584   // do not re-open exit door closed after last player
9585   if (game.all_players_gone)
9586     return;
9587
9588   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9589
9590   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9591 }
9592
9593 static void CheckExitSP(int x, int y)
9594 {
9595   if (game.gems_still_needed > 0)
9596   {
9597     int element = Tile[x][y];
9598     int graphic = el2img(element);
9599
9600     if (IS_ANIMATED(graphic))
9601       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9602
9603     return;
9604   }
9605
9606   // do not re-open exit door closed after last player
9607   if (game.all_players_gone)
9608     return;
9609
9610   Tile[x][y] = EL_SP_EXIT_OPENING;
9611
9612   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9613 }
9614
9615 static void CloseAllOpenTimegates(void)
9616 {
9617   int x, y;
9618
9619   SCAN_PLAYFIELD(x, y)
9620   {
9621     int element = Tile[x][y];
9622
9623     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9624     {
9625       Tile[x][y] = EL_TIMEGATE_CLOSING;
9626
9627       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9628     }
9629   }
9630 }
9631
9632 static void DrawTwinkleOnField(int x, int y)
9633 {
9634   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9635     return;
9636
9637   if (Tile[x][y] == EL_BD_DIAMOND)
9638     return;
9639
9640   if (MovDelay[x][y] == 0)      // next animation frame
9641     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9642
9643   if (MovDelay[x][y] != 0)      // wait some time before next frame
9644   {
9645     MovDelay[x][y]--;
9646
9647     DrawLevelElementAnimation(x, y, Tile[x][y]);
9648
9649     if (MovDelay[x][y] != 0)
9650     {
9651       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9652                                            10 - MovDelay[x][y]);
9653
9654       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9655     }
9656   }
9657 }
9658
9659 static void MauerWaechst(int x, int y)
9660 {
9661   int delay = 6;
9662
9663   if (!MovDelay[x][y])          // next animation frame
9664     MovDelay[x][y] = 3 * delay;
9665
9666   if (MovDelay[x][y])           // wait some time before next frame
9667   {
9668     MovDelay[x][y]--;
9669
9670     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9671     {
9672       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9673       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9674
9675       DrawLevelGraphic(x, y, graphic, frame);
9676     }
9677
9678     if (!MovDelay[x][y])
9679     {
9680       if (MovDir[x][y] == MV_LEFT)
9681       {
9682         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9683           TEST_DrawLevelField(x - 1, y);
9684       }
9685       else if (MovDir[x][y] == MV_RIGHT)
9686       {
9687         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9688           TEST_DrawLevelField(x + 1, y);
9689       }
9690       else if (MovDir[x][y] == MV_UP)
9691       {
9692         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9693           TEST_DrawLevelField(x, y - 1);
9694       }
9695       else
9696       {
9697         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9698           TEST_DrawLevelField(x, y + 1);
9699       }
9700
9701       Tile[x][y] = Store[x][y];
9702       Store[x][y] = 0;
9703       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9704       TEST_DrawLevelField(x, y);
9705     }
9706   }
9707 }
9708
9709 static void MauerAbleger(int ax, int ay)
9710 {
9711   int element = Tile[ax][ay];
9712   int graphic = el2img(element);
9713   boolean oben_frei = FALSE, unten_frei = FALSE;
9714   boolean links_frei = FALSE, rechts_frei = FALSE;
9715   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9716   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9717   boolean new_wall = FALSE;
9718
9719   if (IS_ANIMATED(graphic))
9720     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9721
9722   if (!MovDelay[ax][ay])        // start building new wall
9723     MovDelay[ax][ay] = 6;
9724
9725   if (MovDelay[ax][ay])         // wait some time before building new wall
9726   {
9727     MovDelay[ax][ay]--;
9728     if (MovDelay[ax][ay])
9729       return;
9730   }
9731
9732   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9733     oben_frei = TRUE;
9734   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9735     unten_frei = TRUE;
9736   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9737     links_frei = TRUE;
9738   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9739     rechts_frei = TRUE;
9740
9741   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9742       element == EL_EXPANDABLE_WALL_ANY)
9743   {
9744     if (oben_frei)
9745     {
9746       Tile[ax][ay - 1] = EL_EXPANDABLE_WALL_GROWING;
9747       Store[ax][ay - 1] = element;
9748       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9749       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9750         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9751       new_wall = TRUE;
9752     }
9753     if (unten_frei)
9754     {
9755       Tile[ax][ay + 1] = EL_EXPANDABLE_WALL_GROWING;
9756       Store[ax][ay + 1] = element;
9757       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9758       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9759         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9760       new_wall = TRUE;
9761     }
9762   }
9763
9764   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9765       element == EL_EXPANDABLE_WALL_ANY ||
9766       element == EL_EXPANDABLE_WALL ||
9767       element == EL_BD_EXPANDABLE_WALL)
9768   {
9769     if (links_frei)
9770     {
9771       Tile[ax - 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9772       Store[ax - 1][ay] = element;
9773       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9774       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9775         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9776       new_wall = TRUE;
9777     }
9778
9779     if (rechts_frei)
9780     {
9781       Tile[ax + 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9782       Store[ax + 1][ay] = element;
9783       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9784       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9785         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9786       new_wall = TRUE;
9787     }
9788   }
9789
9790   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9791     TEST_DrawLevelField(ax, ay);
9792
9793   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9794     oben_massiv = TRUE;
9795   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9796     unten_massiv = TRUE;
9797   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9798     links_massiv = TRUE;
9799   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9800     rechts_massiv = TRUE;
9801
9802   if (((oben_massiv && unten_massiv) ||
9803        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9804        element == EL_EXPANDABLE_WALL) &&
9805       ((links_massiv && rechts_massiv) ||
9806        element == EL_EXPANDABLE_WALL_VERTICAL))
9807     Tile[ax][ay] = EL_WALL;
9808
9809   if (new_wall)
9810     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9811 }
9812
9813 static void MauerAblegerStahl(int ax, int ay)
9814 {
9815   int element = Tile[ax][ay];
9816   int graphic = el2img(element);
9817   boolean oben_frei = FALSE, unten_frei = FALSE;
9818   boolean links_frei = FALSE, rechts_frei = FALSE;
9819   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9820   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9821   boolean new_wall = FALSE;
9822
9823   if (IS_ANIMATED(graphic))
9824     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9825
9826   if (!MovDelay[ax][ay])        // start building new wall
9827     MovDelay[ax][ay] = 6;
9828
9829   if (MovDelay[ax][ay])         // wait some time before building new wall
9830   {
9831     MovDelay[ax][ay]--;
9832     if (MovDelay[ax][ay])
9833       return;
9834   }
9835
9836   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9837     oben_frei = TRUE;
9838   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9839     unten_frei = TRUE;
9840   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9841     links_frei = TRUE;
9842   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9843     rechts_frei = TRUE;
9844
9845   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9846       element == EL_EXPANDABLE_STEELWALL_ANY)
9847   {
9848     if (oben_frei)
9849     {
9850       Tile[ax][ay - 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9851       Store[ax][ay - 1] = element;
9852       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9853       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9854         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9855       new_wall = TRUE;
9856     }
9857     if (unten_frei)
9858     {
9859       Tile[ax][ay + 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9860       Store[ax][ay + 1] = element;
9861       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9862       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9863         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9864       new_wall = TRUE;
9865     }
9866   }
9867
9868   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9869       element == EL_EXPANDABLE_STEELWALL_ANY)
9870   {
9871     if (links_frei)
9872     {
9873       Tile[ax - 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9874       Store[ax - 1][ay] = element;
9875       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9876       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9877         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9878       new_wall = TRUE;
9879     }
9880
9881     if (rechts_frei)
9882     {
9883       Tile[ax + 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9884       Store[ax + 1][ay] = element;
9885       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9886       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9887         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9888       new_wall = TRUE;
9889     }
9890   }
9891
9892   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9893     oben_massiv = TRUE;
9894   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9895     unten_massiv = TRUE;
9896   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9897     links_massiv = TRUE;
9898   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9899     rechts_massiv = TRUE;
9900
9901   if (((oben_massiv && unten_massiv) ||
9902        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9903       ((links_massiv && rechts_massiv) ||
9904        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9905     Tile[ax][ay] = EL_STEELWALL;
9906
9907   if (new_wall)
9908     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9909 }
9910
9911 static void CheckForDragon(int x, int y)
9912 {
9913   int i, j;
9914   boolean dragon_found = FALSE;
9915   struct XY *xy = xy_topdown;
9916
9917   for (i = 0; i < NUM_DIRECTIONS; i++)
9918   {
9919     for (j = 0; j < 4; j++)
9920     {
9921       int xx = x + j * xy[i].x;
9922       int yy = y + j * xy[i].y;
9923
9924       if (IN_LEV_FIELD(xx, yy) &&
9925           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9926       {
9927         if (Tile[xx][yy] == EL_DRAGON)
9928           dragon_found = TRUE;
9929       }
9930       else
9931         break;
9932     }
9933   }
9934
9935   if (!dragon_found)
9936   {
9937     for (i = 0; i < NUM_DIRECTIONS; i++)
9938     {
9939       for (j = 0; j < 3; j++)
9940       {
9941         int xx = x + j * xy[i].x;
9942         int yy = y + j * xy[i].y;
9943
9944         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9945         {
9946           Tile[xx][yy] = EL_EMPTY;
9947           TEST_DrawLevelField(xx, yy);
9948         }
9949         else
9950           break;
9951       }
9952     }
9953   }
9954 }
9955
9956 static void InitBuggyBase(int x, int y)
9957 {
9958   int element = Tile[x][y];
9959   int activating_delay = FRAMES_PER_SECOND / 4;
9960
9961   ChangeDelay[x][y] =
9962     (element == EL_SP_BUGGY_BASE ?
9963      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9964      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9965      activating_delay :
9966      element == EL_SP_BUGGY_BASE_ACTIVE ?
9967      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9968 }
9969
9970 static void WarnBuggyBase(int x, int y)
9971 {
9972   int i;
9973   struct XY *xy = xy_topdown;
9974
9975   for (i = 0; i < NUM_DIRECTIONS; i++)
9976   {
9977     int xx = x + xy[i].x;
9978     int yy = y + xy[i].y;
9979
9980     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9981     {
9982       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9983
9984       break;
9985     }
9986   }
9987 }
9988
9989 static void InitTrap(int x, int y)
9990 {
9991   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9992 }
9993
9994 static void ActivateTrap(int x, int y)
9995 {
9996   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9997 }
9998
9999 static void ChangeActiveTrap(int x, int y)
10000 {
10001   int graphic = IMG_TRAP_ACTIVE;
10002
10003   // if new animation frame was drawn, correct crumbled sand border
10004   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10005     TEST_DrawLevelFieldCrumbled(x, y);
10006 }
10007
10008 static int getSpecialActionElement(int element, int number, int base_element)
10009 {
10010   return (element != EL_EMPTY ? element :
10011           number != -1 ? base_element + number - 1 :
10012           EL_EMPTY);
10013 }
10014
10015 static int getModifiedActionNumber(int value_old, int operator, int operand,
10016                                    int value_min, int value_max)
10017 {
10018   int value_new = (operator == CA_MODE_SET      ? operand :
10019                    operator == CA_MODE_ADD      ? value_old + operand :
10020                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10021                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10022                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10023                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10024                    value_old);
10025
10026   return (value_new < value_min ? value_min :
10027           value_new > value_max ? value_max :
10028           value_new);
10029 }
10030
10031 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10032 {
10033   struct ElementInfo *ei = &element_info[element];
10034   struct ElementChangeInfo *change = &ei->change_page[page];
10035   int target_element = change->target_element;
10036   int action_type = change->action_type;
10037   int action_mode = change->action_mode;
10038   int action_arg = change->action_arg;
10039   int action_element = change->action_element;
10040   int i;
10041
10042   if (!change->has_action)
10043     return;
10044
10045   // ---------- determine action paramater values -----------------------------
10046
10047   int level_time_value =
10048     (level.time > 0 ? TimeLeft :
10049      TimePlayed);
10050
10051   int action_arg_element_raw =
10052     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10053      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10054      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10055      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10056      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10057      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10058      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10059      EL_EMPTY);
10060   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10061
10062   int action_arg_direction =
10063     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10064      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10065      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10066      change->actual_trigger_side :
10067      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10068      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10069      MV_NONE);
10070
10071   int action_arg_number_min =
10072     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10073      CA_ARG_MIN);
10074
10075   int action_arg_number_max =
10076     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10077      action_type == CA_SET_LEVEL_GEMS ? 999 :
10078      action_type == CA_SET_LEVEL_TIME ? 9999 :
10079      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10080      action_type == CA_SET_CE_VALUE ? 9999 :
10081      action_type == CA_SET_CE_SCORE ? 9999 :
10082      CA_ARG_MAX);
10083
10084   int action_arg_number_reset =
10085     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10086      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10087      action_type == CA_SET_LEVEL_TIME ? level.time :
10088      action_type == CA_SET_LEVEL_SCORE ? 0 :
10089      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10090      action_type == CA_SET_CE_SCORE ? 0 :
10091      0);
10092
10093   int action_arg_number =
10094     (action_arg <= CA_ARG_MAX ? action_arg :
10095      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10096      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10097      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10098      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10099      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10100      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10101      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10102      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10103      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10104      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10105      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10106      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10107      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10108      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10109      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10110      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10111      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10112      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10113      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10114      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10115      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10116      -1);
10117
10118   int action_arg_number_old =
10119     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10120      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10121      action_type == CA_SET_LEVEL_SCORE ? game.score :
10122      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10123      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10124      0);
10125
10126   int action_arg_number_new =
10127     getModifiedActionNumber(action_arg_number_old,
10128                             action_mode, action_arg_number,
10129                             action_arg_number_min, action_arg_number_max);
10130
10131   int trigger_player_bits =
10132     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10133      change->actual_trigger_player_bits : change->trigger_player);
10134
10135   int action_arg_player_bits =
10136     (action_arg >= CA_ARG_PLAYER_1 &&
10137      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10138      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10139      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10140      PLAYER_BITS_ANY);
10141
10142   // ---------- execute action  -----------------------------------------------
10143
10144   switch (action_type)
10145   {
10146     case CA_NO_ACTION:
10147     {
10148       return;
10149     }
10150
10151     // ---------- level actions  ----------------------------------------------
10152
10153     case CA_RESTART_LEVEL:
10154     {
10155       game.restart_level = TRUE;
10156
10157       break;
10158     }
10159
10160     case CA_SHOW_ENVELOPE:
10161     {
10162       int element = getSpecialActionElement(action_arg_element,
10163                                             action_arg_number, EL_ENVELOPE_1);
10164
10165       if (IS_ENVELOPE(element))
10166         local_player->show_envelope = element;
10167
10168       break;
10169     }
10170
10171     case CA_SET_LEVEL_TIME:
10172     {
10173       if (level.time > 0)       // only modify limited time value
10174       {
10175         TimeLeft = action_arg_number_new;
10176
10177         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10178
10179         DisplayGameControlValues();
10180
10181         if (!TimeLeft && game.time_limit)
10182           for (i = 0; i < MAX_PLAYERS; i++)
10183             KillPlayer(&stored_player[i]);
10184       }
10185
10186       break;
10187     }
10188
10189     case CA_SET_LEVEL_SCORE:
10190     {
10191       game.score = action_arg_number_new;
10192
10193       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10194
10195       DisplayGameControlValues();
10196
10197       break;
10198     }
10199
10200     case CA_SET_LEVEL_GEMS:
10201     {
10202       game.gems_still_needed = action_arg_number_new;
10203
10204       game.snapshot.collected_item = TRUE;
10205
10206       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10207
10208       DisplayGameControlValues();
10209
10210       break;
10211     }
10212
10213     case CA_SET_LEVEL_WIND:
10214     {
10215       game.wind_direction = action_arg_direction;
10216
10217       break;
10218     }
10219
10220     case CA_SET_LEVEL_RANDOM_SEED:
10221     {
10222       // ensure that setting a new random seed while playing is predictable
10223       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10224
10225       break;
10226     }
10227
10228     // ---------- player actions  ---------------------------------------------
10229
10230     case CA_MOVE_PLAYER:
10231     case CA_MOVE_PLAYER_NEW:
10232     {
10233       // automatically move to the next field in specified direction
10234       for (i = 0; i < MAX_PLAYERS; i++)
10235         if (trigger_player_bits & (1 << i))
10236           if (action_type == CA_MOVE_PLAYER ||
10237               stored_player[i].MovPos == 0)
10238             stored_player[i].programmed_action = action_arg_direction;
10239
10240       break;
10241     }
10242
10243     case CA_EXIT_PLAYER:
10244     {
10245       for (i = 0; i < MAX_PLAYERS; i++)
10246         if (action_arg_player_bits & (1 << i))
10247           ExitPlayer(&stored_player[i]);
10248
10249       if (game.players_still_needed == 0)
10250         LevelSolved();
10251
10252       break;
10253     }
10254
10255     case CA_KILL_PLAYER:
10256     {
10257       for (i = 0; i < MAX_PLAYERS; i++)
10258         if (action_arg_player_bits & (1 << i))
10259           KillPlayer(&stored_player[i]);
10260
10261       break;
10262     }
10263
10264     case CA_SET_PLAYER_KEYS:
10265     {
10266       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10267       int element = getSpecialActionElement(action_arg_element,
10268                                             action_arg_number, EL_KEY_1);
10269
10270       if (IS_KEY(element))
10271       {
10272         for (i = 0; i < MAX_PLAYERS; i++)
10273         {
10274           if (trigger_player_bits & (1 << i))
10275           {
10276             stored_player[i].key[KEY_NR(element)] = key_state;
10277
10278             DrawGameDoorValues();
10279           }
10280         }
10281       }
10282
10283       break;
10284     }
10285
10286     case CA_SET_PLAYER_SPEED:
10287     {
10288       for (i = 0; i < MAX_PLAYERS; i++)
10289       {
10290         if (trigger_player_bits & (1 << i))
10291         {
10292           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10293
10294           if (action_arg == CA_ARG_SPEED_FASTER &&
10295               stored_player[i].cannot_move)
10296           {
10297             action_arg_number = STEPSIZE_VERY_SLOW;
10298           }
10299           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10300                    action_arg == CA_ARG_SPEED_FASTER)
10301           {
10302             action_arg_number = 2;
10303             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10304                            CA_MODE_MULTIPLY);
10305           }
10306           else if (action_arg == CA_ARG_NUMBER_RESET)
10307           {
10308             action_arg_number = level.initial_player_stepsize[i];
10309           }
10310
10311           move_stepsize =
10312             getModifiedActionNumber(move_stepsize,
10313                                     action_mode,
10314                                     action_arg_number,
10315                                     action_arg_number_min,
10316                                     action_arg_number_max);
10317
10318           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10319         }
10320       }
10321
10322       break;
10323     }
10324
10325     case CA_SET_PLAYER_SHIELD:
10326     {
10327       for (i = 0; i < MAX_PLAYERS; i++)
10328       {
10329         if (trigger_player_bits & (1 << i))
10330         {
10331           if (action_arg == CA_ARG_SHIELD_OFF)
10332           {
10333             stored_player[i].shield_normal_time_left = 0;
10334             stored_player[i].shield_deadly_time_left = 0;
10335           }
10336           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10337           {
10338             stored_player[i].shield_normal_time_left = 999999;
10339           }
10340           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10341           {
10342             stored_player[i].shield_normal_time_left = 999999;
10343             stored_player[i].shield_deadly_time_left = 999999;
10344           }
10345         }
10346       }
10347
10348       break;
10349     }
10350
10351     case CA_SET_PLAYER_GRAVITY:
10352     {
10353       for (i = 0; i < MAX_PLAYERS; i++)
10354       {
10355         if (trigger_player_bits & (1 << i))
10356         {
10357           stored_player[i].gravity =
10358             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10359              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10360              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10361              stored_player[i].gravity);
10362         }
10363       }
10364
10365       break;
10366     }
10367
10368     case CA_SET_PLAYER_ARTWORK:
10369     {
10370       for (i = 0; i < MAX_PLAYERS; i++)
10371       {
10372         if (trigger_player_bits & (1 << i))
10373         {
10374           int artwork_element = action_arg_element;
10375
10376           if (action_arg == CA_ARG_ELEMENT_RESET)
10377             artwork_element =
10378               (level.use_artwork_element[i] ? level.artwork_element[i] :
10379                stored_player[i].element_nr);
10380
10381           if (stored_player[i].artwork_element != artwork_element)
10382             stored_player[i].Frame = 0;
10383
10384           stored_player[i].artwork_element = artwork_element;
10385
10386           SetPlayerWaiting(&stored_player[i], FALSE);
10387
10388           // set number of special actions for bored and sleeping animation
10389           stored_player[i].num_special_action_bored =
10390             get_num_special_action(artwork_element,
10391                                    ACTION_BORING_1, ACTION_BORING_LAST);
10392           stored_player[i].num_special_action_sleeping =
10393             get_num_special_action(artwork_element,
10394                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10395         }
10396       }
10397
10398       break;
10399     }
10400
10401     case CA_SET_PLAYER_INVENTORY:
10402     {
10403       for (i = 0; i < MAX_PLAYERS; i++)
10404       {
10405         struct PlayerInfo *player = &stored_player[i];
10406         int j, k;
10407
10408         if (trigger_player_bits & (1 << i))
10409         {
10410           int inventory_element = action_arg_element;
10411
10412           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10413               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10414               action_arg == CA_ARG_ELEMENT_ACTION)
10415           {
10416             int element = inventory_element;
10417             int collect_count = element_info[element].collect_count_initial;
10418
10419             if (!IS_CUSTOM_ELEMENT(element))
10420               collect_count = 1;
10421
10422             if (collect_count == 0)
10423               player->inventory_infinite_element = element;
10424             else
10425               for (k = 0; k < collect_count; k++)
10426                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10427                   player->inventory_element[player->inventory_size++] =
10428                     element;
10429           }
10430           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10431                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10432                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10433           {
10434             if (player->inventory_infinite_element != EL_UNDEFINED &&
10435                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10436                                      action_arg_element_raw))
10437               player->inventory_infinite_element = EL_UNDEFINED;
10438
10439             for (k = 0, j = 0; j < player->inventory_size; j++)
10440             {
10441               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10442                                         action_arg_element_raw))
10443                 player->inventory_element[k++] = player->inventory_element[j];
10444             }
10445
10446             player->inventory_size = k;
10447           }
10448           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10449           {
10450             if (player->inventory_size > 0)
10451             {
10452               for (j = 0; j < player->inventory_size - 1; j++)
10453                 player->inventory_element[j] = player->inventory_element[j + 1];
10454
10455               player->inventory_size--;
10456             }
10457           }
10458           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10459           {
10460             if (player->inventory_size > 0)
10461               player->inventory_size--;
10462           }
10463           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10464           {
10465             player->inventory_infinite_element = EL_UNDEFINED;
10466             player->inventory_size = 0;
10467           }
10468           else if (action_arg == CA_ARG_INVENTORY_RESET)
10469           {
10470             player->inventory_infinite_element = EL_UNDEFINED;
10471             player->inventory_size = 0;
10472
10473             if (level.use_initial_inventory[i])
10474             {
10475               for (j = 0; j < level.initial_inventory_size[i]; j++)
10476               {
10477                 int element = level.initial_inventory_content[i][j];
10478                 int collect_count = element_info[element].collect_count_initial;
10479
10480                 if (!IS_CUSTOM_ELEMENT(element))
10481                   collect_count = 1;
10482
10483                 if (collect_count == 0)
10484                   player->inventory_infinite_element = element;
10485                 else
10486                   for (k = 0; k < collect_count; k++)
10487                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10488                       player->inventory_element[player->inventory_size++] =
10489                         element;
10490               }
10491             }
10492           }
10493         }
10494       }
10495
10496       break;
10497     }
10498
10499     // ---------- CE actions  -------------------------------------------------
10500
10501     case CA_SET_CE_VALUE:
10502     {
10503       int last_ce_value = CustomValue[x][y];
10504
10505       CustomValue[x][y] = action_arg_number_new;
10506
10507       if (CustomValue[x][y] != last_ce_value)
10508       {
10509         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10510         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10511
10512         if (CustomValue[x][y] == 0)
10513         {
10514           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10515           ChangeCount[x][y] = 0;        // allow at least one more change
10516
10517           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10518           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10519         }
10520       }
10521
10522       break;
10523     }
10524
10525     case CA_SET_CE_SCORE:
10526     {
10527       int last_ce_score = ei->collect_score;
10528
10529       ei->collect_score = action_arg_number_new;
10530
10531       if (ei->collect_score != last_ce_score)
10532       {
10533         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10534         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10535
10536         if (ei->collect_score == 0)
10537         {
10538           int xx, yy;
10539
10540           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10541           ChangeCount[x][y] = 0;        // allow at least one more change
10542
10543           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10544           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10545
10546           /*
10547             This is a very special case that seems to be a mixture between
10548             CheckElementChange() and CheckTriggeredElementChange(): while
10549             the first one only affects single elements that are triggered
10550             directly, the second one affects multiple elements in the playfield
10551             that are triggered indirectly by another element. This is a third
10552             case: Changing the CE score always affects multiple identical CEs,
10553             so every affected CE must be checked, not only the single CE for
10554             which the CE score was changed in the first place (as every instance
10555             of that CE shares the same CE score, and therefore also can change)!
10556           */
10557           SCAN_PLAYFIELD(xx, yy)
10558           {
10559             if (Tile[xx][yy] == element)
10560               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10561                                  CE_SCORE_GETS_ZERO);
10562           }
10563         }
10564       }
10565
10566       break;
10567     }
10568
10569     case CA_SET_CE_ARTWORK:
10570     {
10571       int artwork_element = action_arg_element;
10572       boolean reset_frame = FALSE;
10573       int xx, yy;
10574
10575       if (action_arg == CA_ARG_ELEMENT_RESET)
10576         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10577                            element);
10578
10579       if (ei->gfx_element != artwork_element)
10580         reset_frame = TRUE;
10581
10582       ei->gfx_element = artwork_element;
10583
10584       SCAN_PLAYFIELD(xx, yy)
10585       {
10586         if (Tile[xx][yy] == element)
10587         {
10588           if (reset_frame)
10589           {
10590             ResetGfxAnimation(xx, yy);
10591             ResetRandomAnimationValue(xx, yy);
10592           }
10593
10594           TEST_DrawLevelField(xx, yy);
10595         }
10596       }
10597
10598       break;
10599     }
10600
10601     // ---------- engine actions  ---------------------------------------------
10602
10603     case CA_SET_ENGINE_SCAN_MODE:
10604     {
10605       InitPlayfieldScanMode(action_arg);
10606
10607       break;
10608     }
10609
10610     default:
10611       break;
10612   }
10613 }
10614
10615 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10616 {
10617   int old_element = Tile[x][y];
10618   int new_element = GetElementFromGroupElement(element);
10619   int previous_move_direction = MovDir[x][y];
10620   int last_ce_value = CustomValue[x][y];
10621   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10622   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10623   boolean add_player_onto_element = (new_element_is_player &&
10624                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10625                                      IS_WALKABLE(old_element));
10626
10627   if (!add_player_onto_element)
10628   {
10629     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10630       RemoveMovingField(x, y);
10631     else
10632       RemoveField(x, y);
10633
10634     Tile[x][y] = new_element;
10635
10636     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10637       MovDir[x][y] = previous_move_direction;
10638
10639     if (element_info[new_element].use_last_ce_value)
10640       CustomValue[x][y] = last_ce_value;
10641
10642     InitField_WithBug1(x, y, FALSE);
10643
10644     new_element = Tile[x][y];   // element may have changed
10645
10646     ResetGfxAnimation(x, y);
10647     ResetRandomAnimationValue(x, y);
10648
10649     TEST_DrawLevelField(x, y);
10650
10651     if (GFX_CRUMBLED(new_element))
10652       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10653   }
10654
10655   // check if element under the player changes from accessible to unaccessible
10656   // (needed for special case of dropping element which then changes)
10657   // (must be checked after creating new element for walkable group elements)
10658   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10659       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10660   {
10661     Bang(x, y);
10662
10663     return;
10664   }
10665
10666   // "ChangeCount" not set yet to allow "entered by player" change one time
10667   if (new_element_is_player)
10668     RelocatePlayer(x, y, new_element);
10669
10670   if (is_change)
10671     ChangeCount[x][y]++;        // count number of changes in the same frame
10672
10673   TestIfBadThingTouchesPlayer(x, y);
10674   TestIfPlayerTouchesCustomElement(x, y);
10675   TestIfElementTouchesCustomElement(x, y);
10676 }
10677
10678 static void CreateField(int x, int y, int element)
10679 {
10680   CreateFieldExt(x, y, element, FALSE);
10681 }
10682
10683 static void CreateElementFromChange(int x, int y, int element)
10684 {
10685   element = GET_VALID_RUNTIME_ELEMENT(element);
10686
10687   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10688   {
10689     int old_element = Tile[x][y];
10690
10691     // prevent changed element from moving in same engine frame
10692     // unless both old and new element can either fall or move
10693     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10694         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10695       Stop[x][y] = TRUE;
10696   }
10697
10698   CreateFieldExt(x, y, element, TRUE);
10699 }
10700
10701 static boolean ChangeElement(int x, int y, int element, int page)
10702 {
10703   struct ElementInfo *ei = &element_info[element];
10704   struct ElementChangeInfo *change = &ei->change_page[page];
10705   int ce_value = CustomValue[x][y];
10706   int ce_score = ei->collect_score;
10707   int target_element;
10708   int old_element = Tile[x][y];
10709
10710   // always use default change event to prevent running into a loop
10711   if (ChangeEvent[x][y] == -1)
10712     ChangeEvent[x][y] = CE_DELAY;
10713
10714   if (ChangeEvent[x][y] == CE_DELAY)
10715   {
10716     // reset actual trigger element, trigger player and action element
10717     change->actual_trigger_element = EL_EMPTY;
10718     change->actual_trigger_player = EL_EMPTY;
10719     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10720     change->actual_trigger_side = CH_SIDE_NONE;
10721     change->actual_trigger_ce_value = 0;
10722     change->actual_trigger_ce_score = 0;
10723   }
10724
10725   // do not change elements more than a specified maximum number of changes
10726   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10727     return FALSE;
10728
10729   ChangeCount[x][y]++;          // count number of changes in the same frame
10730
10731   if (change->explode)
10732   {
10733     Bang(x, y);
10734
10735     return TRUE;
10736   }
10737
10738   if (change->use_target_content)
10739   {
10740     boolean complete_replace = TRUE;
10741     boolean can_replace[3][3];
10742     int xx, yy;
10743
10744     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10745     {
10746       boolean is_empty;
10747       boolean is_walkable;
10748       boolean is_diggable;
10749       boolean is_collectible;
10750       boolean is_removable;
10751       boolean is_destructible;
10752       int ex = x + xx - 1;
10753       int ey = y + yy - 1;
10754       int content_element = change->target_content.e[xx][yy];
10755       int e;
10756
10757       can_replace[xx][yy] = TRUE;
10758
10759       if (ex == x && ey == y)   // do not check changing element itself
10760         continue;
10761
10762       if (content_element == EL_EMPTY_SPACE)
10763       {
10764         can_replace[xx][yy] = FALSE;    // do not replace border with space
10765
10766         continue;
10767       }
10768
10769       if (!IN_LEV_FIELD(ex, ey))
10770       {
10771         can_replace[xx][yy] = FALSE;
10772         complete_replace = FALSE;
10773
10774         continue;
10775       }
10776
10777       e = Tile[ex][ey];
10778
10779       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10780         e = MovingOrBlocked2Element(ex, ey);
10781
10782       is_empty = (IS_FREE(ex, ey) ||
10783                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10784
10785       is_walkable     = (is_empty || IS_WALKABLE(e));
10786       is_diggable     = (is_empty || IS_DIGGABLE(e));
10787       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10788       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10789       is_removable    = (is_diggable || is_collectible);
10790
10791       can_replace[xx][yy] =
10792         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10793           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10794           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10795           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10796           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10797           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10798          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10799
10800       if (!can_replace[xx][yy])
10801         complete_replace = FALSE;
10802     }
10803
10804     if (!change->only_if_complete || complete_replace)
10805     {
10806       boolean something_has_changed = FALSE;
10807
10808       if (change->only_if_complete && change->use_random_replace &&
10809           RND(100) < change->random_percentage)
10810         return FALSE;
10811
10812       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10813       {
10814         int ex = x + xx - 1;
10815         int ey = y + yy - 1;
10816         int content_element;
10817
10818         if (can_replace[xx][yy] && (!change->use_random_replace ||
10819                                     RND(100) < change->random_percentage))
10820         {
10821           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10822             RemoveMovingField(ex, ey);
10823
10824           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10825
10826           content_element = change->target_content.e[xx][yy];
10827           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10828                                               ce_value, ce_score);
10829
10830           CreateElementFromChange(ex, ey, target_element);
10831
10832           something_has_changed = TRUE;
10833
10834           // for symmetry reasons, freeze newly created border elements
10835           if (ex != x || ey != y)
10836             Stop[ex][ey] = TRUE;        // no more moving in this frame
10837         }
10838       }
10839
10840       if (something_has_changed)
10841       {
10842         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10843         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10844       }
10845     }
10846   }
10847   else
10848   {
10849     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10850                                         ce_value, ce_score);
10851
10852     if (element == EL_DIAGONAL_GROWING ||
10853         element == EL_DIAGONAL_SHRINKING)
10854     {
10855       target_element = Store[x][y];
10856
10857       Store[x][y] = EL_EMPTY;
10858     }
10859
10860     // special case: element changes to player (and may be kept if walkable)
10861     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10862       CreateElementFromChange(x, y, EL_EMPTY);
10863
10864     CreateElementFromChange(x, y, target_element);
10865
10866     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10867     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10868   }
10869
10870   // this uses direct change before indirect change
10871   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10872
10873   return TRUE;
10874 }
10875
10876 static void HandleElementChange(int x, int y, int page)
10877 {
10878   int element = MovingOrBlocked2Element(x, y);
10879   struct ElementInfo *ei = &element_info[element];
10880   struct ElementChangeInfo *change = &ei->change_page[page];
10881   boolean handle_action_before_change = FALSE;
10882
10883 #ifdef DEBUG
10884   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10885       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10886   {
10887     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10888           x, y, element, element_info[element].token_name);
10889     Debug("game:playing:HandleElementChange", "This should never happen!");
10890   }
10891 #endif
10892
10893   // this can happen with classic bombs on walkable, changing elements
10894   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10895   {
10896     return;
10897   }
10898
10899   if (ChangeDelay[x][y] == 0)           // initialize element change
10900   {
10901     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10902
10903     if (change->can_change)
10904     {
10905       // !!! not clear why graphic animation should be reset at all here !!!
10906       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10907       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10908
10909       /*
10910         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10911
10912         When using an animation frame delay of 1 (this only happens with
10913         "sp_zonk.moving.left/right" in the classic graphics), the default
10914         (non-moving) animation shows wrong animation frames (while the
10915         moving animation, like "sp_zonk.moving.left/right", is correct,
10916         so this graphical bug never shows up with the classic graphics).
10917         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10918         be drawn instead of the correct frames 0,1,2,3. This is caused by
10919         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10920         an element change: First when the change delay ("ChangeDelay[][]")
10921         counter has reached zero after decrementing, then a second time in
10922         the next frame (after "GfxFrame[][]" was already incremented) when
10923         "ChangeDelay[][]" is reset to the initial delay value again.
10924
10925         This causes frame 0 to be drawn twice, while the last frame won't
10926         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10927
10928         As some animations may already be cleverly designed around this bug
10929         (at least the "Snake Bite" snake tail animation does this), it cannot
10930         simply be fixed here without breaking such existing animations.
10931         Unfortunately, it cannot easily be detected if a graphics set was
10932         designed "before" or "after" the bug was fixed. As a workaround,
10933         a new graphics set option "game.graphics_engine_version" was added
10934         to be able to specify the game's major release version for which the
10935         graphics set was designed, which can then be used to decide if the
10936         bugfix should be used (version 4 and above) or not (version 3 or
10937         below, or if no version was specified at all, as with old sets).
10938
10939         (The wrong/fixed animation frames can be tested with the test level set
10940         "test_gfxframe" and level "000", which contains a specially prepared
10941         custom element at level position (x/y) == (11/9) which uses the zonk
10942         animation mentioned above. Using "game.graphics_engine_version: 4"
10943         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10944         This can also be seen from the debug output for this test element.)
10945       */
10946
10947       // when a custom element is about to change (for example by change delay),
10948       // do not reset graphic animation when the custom element is moving
10949       if (game.graphics_engine_version < 4 &&
10950           !IS_MOVING(x, y))
10951       {
10952         ResetGfxAnimation(x, y);
10953         ResetRandomAnimationValue(x, y);
10954       }
10955
10956       if (change->pre_change_function)
10957         change->pre_change_function(x, y);
10958     }
10959   }
10960
10961   ChangeDelay[x][y]--;
10962
10963   if (ChangeDelay[x][y] != 0)           // continue element change
10964   {
10965     if (change->can_change)
10966     {
10967       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10968
10969       if (IS_ANIMATED(graphic))
10970         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10971
10972       if (change->change_function)
10973         change->change_function(x, y);
10974     }
10975   }
10976   else                                  // finish element change
10977   {
10978     if (ChangePage[x][y] != -1)         // remember page from delayed change
10979     {
10980       page = ChangePage[x][y];
10981       ChangePage[x][y] = -1;
10982
10983       change = &ei->change_page[page];
10984     }
10985
10986     if (IS_MOVING(x, y))                // never change a running system ;-)
10987     {
10988       ChangeDelay[x][y] = 1;            // try change after next move step
10989       ChangePage[x][y] = page;          // remember page to use for change
10990
10991       return;
10992     }
10993
10994     // special case: set new level random seed before changing element
10995     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10996       handle_action_before_change = TRUE;
10997
10998     if (change->has_action && handle_action_before_change)
10999       ExecuteCustomElementAction(x, y, element, page);
11000
11001     if (change->can_change)
11002     {
11003       if (ChangeElement(x, y, element, page))
11004       {
11005         if (change->post_change_function)
11006           change->post_change_function(x, y);
11007       }
11008     }
11009
11010     if (change->has_action && !handle_action_before_change)
11011       ExecuteCustomElementAction(x, y, element, page);
11012   }
11013 }
11014
11015 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11016                                               int trigger_element,
11017                                               int trigger_event,
11018                                               int trigger_player,
11019                                               int trigger_side,
11020                                               int trigger_page)
11021 {
11022   boolean change_done_any = FALSE;
11023   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11024   int i;
11025
11026   if (!(trigger_events[trigger_element][trigger_event]))
11027     return FALSE;
11028
11029   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11030
11031   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11032   {
11033     int element = EL_CUSTOM_START + i;
11034     boolean change_done = FALSE;
11035     int p;
11036
11037     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11038         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11039       continue;
11040
11041     for (p = 0; p < element_info[element].num_change_pages; p++)
11042     {
11043       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11044
11045       if (change->can_change_or_has_action &&
11046           change->has_event[trigger_event] &&
11047           change->trigger_side & trigger_side &&
11048           change->trigger_player & trigger_player &&
11049           change->trigger_page & trigger_page_bits &&
11050           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11051       {
11052         change->actual_trigger_element = trigger_element;
11053         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11054         change->actual_trigger_player_bits = trigger_player;
11055         change->actual_trigger_side = trigger_side;
11056         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11057         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11058
11059         if ((change->can_change && !change_done) || change->has_action)
11060         {
11061           int x, y;
11062
11063           SCAN_PLAYFIELD(x, y)
11064           {
11065             if (Tile[x][y] == element)
11066             {
11067               if (change->can_change && !change_done)
11068               {
11069                 // if element already changed in this frame, not only prevent
11070                 // another element change (checked in ChangeElement()), but
11071                 // also prevent additional element actions for this element
11072
11073                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11074                     !level.use_action_after_change_bug)
11075                   continue;
11076
11077                 ChangeDelay[x][y] = 1;
11078                 ChangeEvent[x][y] = trigger_event;
11079
11080                 HandleElementChange(x, y, p);
11081               }
11082               else if (change->has_action)
11083               {
11084                 // if element already changed in this frame, not only prevent
11085                 // another element change (checked in ChangeElement()), but
11086                 // also prevent additional element actions for this element
11087
11088                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11089                     !level.use_action_after_change_bug)
11090                   continue;
11091
11092                 ExecuteCustomElementAction(x, y, element, p);
11093                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11094               }
11095             }
11096           }
11097
11098           if (change->can_change)
11099           {
11100             change_done = TRUE;
11101             change_done_any = TRUE;
11102           }
11103         }
11104       }
11105     }
11106   }
11107
11108   RECURSION_LOOP_DETECTION_END();
11109
11110   return change_done_any;
11111 }
11112
11113 static boolean CheckElementChangeExt(int x, int y,
11114                                      int element,
11115                                      int trigger_element,
11116                                      int trigger_event,
11117                                      int trigger_player,
11118                                      int trigger_side)
11119 {
11120   boolean change_done = FALSE;
11121   int p;
11122
11123   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11124       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11125     return FALSE;
11126
11127   if (Tile[x][y] == EL_BLOCKED)
11128   {
11129     Blocked2Moving(x, y, &x, &y);
11130     element = Tile[x][y];
11131   }
11132
11133   // check if element has already changed or is about to change after moving
11134   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11135        Tile[x][y] != element) ||
11136
11137       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11138        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11139         ChangePage[x][y] != -1)))
11140     return FALSE;
11141
11142   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11143
11144   for (p = 0; p < element_info[element].num_change_pages; p++)
11145   {
11146     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11147
11148     /* check trigger element for all events where the element that is checked
11149        for changing interacts with a directly adjacent element -- this is
11150        different to element changes that affect other elements to change on the
11151        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11152     boolean check_trigger_element =
11153       (trigger_event == CE_NEXT_TO_X ||
11154        trigger_event == CE_TOUCHING_X ||
11155        trigger_event == CE_HITTING_X ||
11156        trigger_event == CE_HIT_BY_X ||
11157        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11158
11159     if (change->can_change_or_has_action &&
11160         change->has_event[trigger_event] &&
11161         change->trigger_side & trigger_side &&
11162         change->trigger_player & trigger_player &&
11163         (!check_trigger_element ||
11164          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11165     {
11166       change->actual_trigger_element = trigger_element;
11167       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11168       change->actual_trigger_player_bits = trigger_player;
11169       change->actual_trigger_side = trigger_side;
11170       change->actual_trigger_ce_value = CustomValue[x][y];
11171       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11172
11173       // special case: trigger element not at (x,y) position for some events
11174       if (check_trigger_element)
11175       {
11176         static struct
11177         {
11178           int dx, dy;
11179         } move_xy[] =
11180           {
11181             {  0,  0 },
11182             { -1,  0 },
11183             { +1,  0 },
11184             {  0,  0 },
11185             {  0, -1 },
11186             {  0,  0 }, { 0, 0 }, { 0, 0 },
11187             {  0, +1 }
11188           };
11189
11190         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11191         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11192
11193         change->actual_trigger_ce_value = CustomValue[xx][yy];
11194         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11195       }
11196
11197       if (change->can_change && !change_done)
11198       {
11199         ChangeDelay[x][y] = 1;
11200         ChangeEvent[x][y] = trigger_event;
11201
11202         HandleElementChange(x, y, p);
11203
11204         change_done = TRUE;
11205       }
11206       else if (change->has_action)
11207       {
11208         ExecuteCustomElementAction(x, y, element, p);
11209         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11210       }
11211     }
11212   }
11213
11214   RECURSION_LOOP_DETECTION_END();
11215
11216   return change_done;
11217 }
11218
11219 static void PlayPlayerSound(struct PlayerInfo *player)
11220 {
11221   int jx = player->jx, jy = player->jy;
11222   int sound_element = player->artwork_element;
11223   int last_action = player->last_action_waiting;
11224   int action = player->action_waiting;
11225
11226   if (player->is_waiting)
11227   {
11228     if (action != last_action)
11229       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11230     else
11231       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11232   }
11233   else
11234   {
11235     if (action != last_action)
11236       StopSound(element_info[sound_element].sound[last_action]);
11237
11238     if (last_action == ACTION_SLEEPING)
11239       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11240   }
11241 }
11242
11243 static void PlayAllPlayersSound(void)
11244 {
11245   int i;
11246
11247   for (i = 0; i < MAX_PLAYERS; i++)
11248     if (stored_player[i].active)
11249       PlayPlayerSound(&stored_player[i]);
11250 }
11251
11252 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11253 {
11254   boolean last_waiting = player->is_waiting;
11255   int move_dir = player->MovDir;
11256
11257   player->dir_waiting = move_dir;
11258   player->last_action_waiting = player->action_waiting;
11259
11260   if (is_waiting)
11261   {
11262     if (!last_waiting)          // not waiting -> waiting
11263     {
11264       player->is_waiting = TRUE;
11265
11266       player->frame_counter_bored =
11267         FrameCounter +
11268         game.player_boring_delay_fixed +
11269         GetSimpleRandom(game.player_boring_delay_random);
11270       player->frame_counter_sleeping =
11271         FrameCounter +
11272         game.player_sleeping_delay_fixed +
11273         GetSimpleRandom(game.player_sleeping_delay_random);
11274
11275       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11276     }
11277
11278     if (game.player_sleeping_delay_fixed +
11279         game.player_sleeping_delay_random > 0 &&
11280         player->anim_delay_counter == 0 &&
11281         player->post_delay_counter == 0 &&
11282         FrameCounter >= player->frame_counter_sleeping)
11283       player->is_sleeping = TRUE;
11284     else if (game.player_boring_delay_fixed +
11285              game.player_boring_delay_random > 0 &&
11286              FrameCounter >= player->frame_counter_bored)
11287       player->is_bored = TRUE;
11288
11289     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11290                               player->is_bored ? ACTION_BORING :
11291                               ACTION_WAITING);
11292
11293     if (player->is_sleeping && player->use_murphy)
11294     {
11295       // special case for sleeping Murphy when leaning against non-free tile
11296
11297       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11298           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11299            !IS_MOVING(player->jx - 1, player->jy)))
11300         move_dir = MV_LEFT;
11301       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11302                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11303                 !IS_MOVING(player->jx + 1, player->jy)))
11304         move_dir = MV_RIGHT;
11305       else
11306         player->is_sleeping = FALSE;
11307
11308       player->dir_waiting = move_dir;
11309     }
11310
11311     if (player->is_sleeping)
11312     {
11313       if (player->num_special_action_sleeping > 0)
11314       {
11315         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11316         {
11317           int last_special_action = player->special_action_sleeping;
11318           int num_special_action = player->num_special_action_sleeping;
11319           int special_action =
11320             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11321              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11322              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11323              last_special_action + 1 : ACTION_SLEEPING);
11324           int special_graphic =
11325             el_act_dir2img(player->artwork_element, special_action, move_dir);
11326
11327           player->anim_delay_counter =
11328             graphic_info[special_graphic].anim_delay_fixed +
11329             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11330           player->post_delay_counter =
11331             graphic_info[special_graphic].post_delay_fixed +
11332             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11333
11334           player->special_action_sleeping = special_action;
11335         }
11336
11337         if (player->anim_delay_counter > 0)
11338         {
11339           player->action_waiting = player->special_action_sleeping;
11340           player->anim_delay_counter--;
11341         }
11342         else if (player->post_delay_counter > 0)
11343         {
11344           player->post_delay_counter--;
11345         }
11346       }
11347     }
11348     else if (player->is_bored)
11349     {
11350       if (player->num_special_action_bored > 0)
11351       {
11352         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11353         {
11354           int special_action =
11355             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11356           int special_graphic =
11357             el_act_dir2img(player->artwork_element, special_action, move_dir);
11358
11359           player->anim_delay_counter =
11360             graphic_info[special_graphic].anim_delay_fixed +
11361             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11362           player->post_delay_counter =
11363             graphic_info[special_graphic].post_delay_fixed +
11364             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11365
11366           player->special_action_bored = special_action;
11367         }
11368
11369         if (player->anim_delay_counter > 0)
11370         {
11371           player->action_waiting = player->special_action_bored;
11372           player->anim_delay_counter--;
11373         }
11374         else if (player->post_delay_counter > 0)
11375         {
11376           player->post_delay_counter--;
11377         }
11378       }
11379     }
11380   }
11381   else if (last_waiting)        // waiting -> not waiting
11382   {
11383     player->is_waiting = FALSE;
11384     player->is_bored = FALSE;
11385     player->is_sleeping = FALSE;
11386
11387     player->frame_counter_bored = -1;
11388     player->frame_counter_sleeping = -1;
11389
11390     player->anim_delay_counter = 0;
11391     player->post_delay_counter = 0;
11392
11393     player->dir_waiting = player->MovDir;
11394     player->action_waiting = ACTION_DEFAULT;
11395
11396     player->special_action_bored = ACTION_DEFAULT;
11397     player->special_action_sleeping = ACTION_DEFAULT;
11398   }
11399 }
11400
11401 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11402 {
11403   if ((!player->is_moving  && player->was_moving) ||
11404       (player->MovPos == 0 && player->was_moving) ||
11405       (player->is_snapping && !player->was_snapping) ||
11406       (player->is_dropping && !player->was_dropping))
11407   {
11408     if (!CheckSaveEngineSnapshotToList())
11409       return;
11410
11411     player->was_moving = FALSE;
11412     player->was_snapping = TRUE;
11413     player->was_dropping = TRUE;
11414   }
11415   else
11416   {
11417     if (player->is_moving)
11418       player->was_moving = TRUE;
11419
11420     if (!player->is_snapping)
11421       player->was_snapping = FALSE;
11422
11423     if (!player->is_dropping)
11424       player->was_dropping = FALSE;
11425   }
11426
11427   static struct MouseActionInfo mouse_action_last = { 0 };
11428   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11429   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11430
11431   if (new_released)
11432     CheckSaveEngineSnapshotToList();
11433
11434   mouse_action_last = mouse_action;
11435 }
11436
11437 static void CheckSingleStepMode(struct PlayerInfo *player)
11438 {
11439   if (tape.single_step && tape.recording && !tape.pausing)
11440   {
11441     // as it is called "single step mode", just return to pause mode when the
11442     // player stopped moving after one tile (or never starts moving at all)
11443     // (reverse logic needed here in case single step mode used in team mode)
11444     if (player->is_moving ||
11445         player->is_pushing ||
11446         player->is_dropping_pressed ||
11447         player->effective_mouse_action.button)
11448       game.enter_single_step_mode = FALSE;
11449   }
11450
11451   CheckSaveEngineSnapshot(player);
11452 }
11453
11454 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11455 {
11456   int left      = player_action & JOY_LEFT;
11457   int right     = player_action & JOY_RIGHT;
11458   int up        = player_action & JOY_UP;
11459   int down      = player_action & JOY_DOWN;
11460   int button1   = player_action & JOY_BUTTON_1;
11461   int button2   = player_action & JOY_BUTTON_2;
11462   int dx        = (left ? -1 : right ? 1 : 0);
11463   int dy        = (up   ? -1 : down  ? 1 : 0);
11464
11465   if (!player->active || tape.pausing)
11466     return 0;
11467
11468   if (player_action)
11469   {
11470     if (button1)
11471       SnapField(player, dx, dy);
11472     else
11473     {
11474       if (button2)
11475         DropElement(player);
11476
11477       MovePlayer(player, dx, dy);
11478     }
11479
11480     CheckSingleStepMode(player);
11481
11482     SetPlayerWaiting(player, FALSE);
11483
11484     return player_action;
11485   }
11486   else
11487   {
11488     // no actions for this player (no input at player's configured device)
11489
11490     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11491     SnapField(player, 0, 0);
11492     CheckGravityMovementWhenNotMoving(player);
11493
11494     if (player->MovPos == 0)
11495       SetPlayerWaiting(player, TRUE);
11496
11497     if (player->MovPos == 0)    // needed for tape.playing
11498       player->is_moving = FALSE;
11499
11500     player->is_dropping = FALSE;
11501     player->is_dropping_pressed = FALSE;
11502     player->drop_pressed_delay = 0;
11503
11504     CheckSingleStepMode(player);
11505
11506     return 0;
11507   }
11508 }
11509
11510 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11511                                          byte *tape_action)
11512 {
11513   if (!tape.use_mouse_actions)
11514     return;
11515
11516   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11517   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11518   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11519 }
11520
11521 static void SetTapeActionFromMouseAction(byte *tape_action,
11522                                          struct MouseActionInfo *mouse_action)
11523 {
11524   if (!tape.use_mouse_actions)
11525     return;
11526
11527   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11528   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11529   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11530 }
11531
11532 static void CheckLevelSolved(void)
11533 {
11534   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11535   {
11536     if (game_em.level_solved &&
11537         !game_em.game_over)                             // game won
11538     {
11539       LevelSolved();
11540
11541       game_em.game_over = TRUE;
11542
11543       game.all_players_gone = TRUE;
11544     }
11545
11546     if (game_em.game_over)                              // game lost
11547       game.all_players_gone = TRUE;
11548   }
11549   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11550   {
11551     if (game_sp.level_solved &&
11552         !game_sp.game_over)                             // game won
11553     {
11554       LevelSolved();
11555
11556       game_sp.game_over = TRUE;
11557
11558       game.all_players_gone = TRUE;
11559     }
11560
11561     if (game_sp.game_over)                              // game lost
11562       game.all_players_gone = TRUE;
11563   }
11564   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11565   {
11566     if (game_mm.level_solved &&
11567         !game_mm.game_over)                             // game won
11568     {
11569       LevelSolved();
11570
11571       game_mm.game_over = TRUE;
11572
11573       game.all_players_gone = TRUE;
11574     }
11575
11576     if (game_mm.game_over)                              // game lost
11577       game.all_players_gone = TRUE;
11578   }
11579 }
11580
11581 static void CheckLevelTime_StepCounter(void)
11582 {
11583   int i;
11584
11585   TimePlayed++;
11586
11587   if (TimeLeft > 0)
11588   {
11589     TimeLeft--;
11590
11591     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11592       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11593
11594     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11595
11596     DisplayGameControlValues();
11597
11598     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11599       for (i = 0; i < MAX_PLAYERS; i++)
11600         KillPlayer(&stored_player[i]);
11601   }
11602   else if (game.no_level_time_limit && !game.all_players_gone)
11603   {
11604     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11605
11606     DisplayGameControlValues();
11607   }
11608 }
11609
11610 static void CheckLevelTime(void)
11611 {
11612   int i;
11613
11614   if (TimeFrames >= FRAMES_PER_SECOND)
11615   {
11616     TimeFrames = 0;
11617     TapeTime++;
11618
11619     for (i = 0; i < MAX_PLAYERS; i++)
11620     {
11621       struct PlayerInfo *player = &stored_player[i];
11622
11623       if (SHIELD_ON(player))
11624       {
11625         player->shield_normal_time_left--;
11626
11627         if (player->shield_deadly_time_left > 0)
11628           player->shield_deadly_time_left--;
11629       }
11630     }
11631
11632     if (!game.LevelSolved && !level.use_step_counter)
11633     {
11634       TimePlayed++;
11635
11636       if (TimeLeft > 0)
11637       {
11638         TimeLeft--;
11639
11640         if (TimeLeft <= 10 && game.time_limit)
11641           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11642
11643         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11644            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11645
11646         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11647
11648         if (!TimeLeft && game.time_limit)
11649         {
11650           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11651             game_em.lev->killed_out_of_time = TRUE;
11652           else
11653             for (i = 0; i < MAX_PLAYERS; i++)
11654               KillPlayer(&stored_player[i]);
11655         }
11656       }
11657       else if (game.no_level_time_limit && !game.all_players_gone)
11658       {
11659         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11660       }
11661
11662       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11663     }
11664
11665     if (tape.recording || tape.playing)
11666       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11667   }
11668
11669   if (tape.recording || tape.playing)
11670     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11671
11672   UpdateAndDisplayGameControlValues();
11673 }
11674
11675 void AdvanceFrameAndPlayerCounters(int player_nr)
11676 {
11677   int i;
11678
11679   // advance frame counters (global frame counter and time frame counter)
11680   FrameCounter++;
11681   TimeFrames++;
11682
11683   // advance player counters (counters for move delay, move animation etc.)
11684   for (i = 0; i < MAX_PLAYERS; i++)
11685   {
11686     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11687     int move_delay_value = stored_player[i].move_delay_value;
11688     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11689
11690     if (!advance_player_counters)       // not all players may be affected
11691       continue;
11692
11693     if (move_frames == 0)       // less than one move per game frame
11694     {
11695       int stepsize = TILEX / move_delay_value;
11696       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11697       int count = (stored_player[i].is_moving ?
11698                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11699
11700       if (count % delay == 0)
11701         move_frames = 1;
11702     }
11703
11704     stored_player[i].Frame += move_frames;
11705
11706     if (stored_player[i].MovPos != 0)
11707       stored_player[i].StepFrame += move_frames;
11708
11709     if (stored_player[i].move_delay > 0)
11710       stored_player[i].move_delay--;
11711
11712     // due to bugs in previous versions, counter must count up, not down
11713     if (stored_player[i].push_delay != -1)
11714       stored_player[i].push_delay++;
11715
11716     if (stored_player[i].drop_delay > 0)
11717       stored_player[i].drop_delay--;
11718
11719     if (stored_player[i].is_dropping_pressed)
11720       stored_player[i].drop_pressed_delay++;
11721   }
11722 }
11723
11724 void StartGameActions(boolean init_network_game, boolean record_tape,
11725                       int random_seed)
11726 {
11727   unsigned int new_random_seed = InitRND(random_seed);
11728
11729   if (record_tape)
11730     TapeStartRecording(new_random_seed);
11731
11732   if (setup.auto_pause_on_start && !tape.pausing)
11733     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11734
11735   if (init_network_game)
11736   {
11737     SendToServer_LevelFile();
11738     SendToServer_StartPlaying();
11739
11740     return;
11741   }
11742
11743   InitGame();
11744 }
11745
11746 static void GameActionsExt(void)
11747 {
11748 #if 0
11749   static unsigned int game_frame_delay = 0;
11750 #endif
11751   unsigned int game_frame_delay_value;
11752   byte *recorded_player_action;
11753   byte summarized_player_action = 0;
11754   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11755   int i;
11756
11757   // detect endless loops, caused by custom element programming
11758   if (recursion_loop_detected && recursion_loop_depth == 0)
11759   {
11760     char *message = getStringCat3("Internal Error! Element ",
11761                                   EL_NAME(recursion_loop_element),
11762                                   " caused endless loop! Quit the game?");
11763
11764     Warn("element '%s' caused endless loop in game engine",
11765          EL_NAME(recursion_loop_element));
11766
11767     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11768
11769     recursion_loop_detected = FALSE;    // if game should be continued
11770
11771     free(message);
11772
11773     return;
11774   }
11775
11776   if (game.restart_level)
11777     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11778
11779   CheckLevelSolved();
11780
11781   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11782     GameWon();
11783
11784   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11785     TapeStop();
11786
11787   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11788     return;
11789
11790   game_frame_delay_value =
11791     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11792
11793   if (tape.playing && tape.warp_forward && !tape.pausing)
11794     game_frame_delay_value = 0;
11795
11796   SetVideoFrameDelay(game_frame_delay_value);
11797
11798   // (de)activate virtual buttons depending on current game status
11799   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11800   {
11801     if (game.all_players_gone)  // if no players there to be controlled anymore
11802       SetOverlayActive(FALSE);
11803     else if (!tape.playing)     // if game continues after tape stopped playing
11804       SetOverlayActive(TRUE);
11805   }
11806
11807 #if 0
11808 #if 0
11809   // ---------- main game synchronization point ----------
11810
11811   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11812
11813   Debug("game:playing:skip", "skip == %d", skip);
11814
11815 #else
11816   // ---------- main game synchronization point ----------
11817
11818   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11819 #endif
11820 #endif
11821
11822   if (network_playing && !network_player_action_received)
11823   {
11824     // try to get network player actions in time
11825
11826     // last chance to get network player actions without main loop delay
11827     HandleNetworking();
11828
11829     // game was quit by network peer
11830     if (game_status != GAME_MODE_PLAYING)
11831       return;
11832
11833     // check if network player actions still missing and game still running
11834     if (!network_player_action_received && !checkGameEnded())
11835       return;           // failed to get network player actions in time
11836
11837     // do not yet reset "network_player_action_received" (for tape.pausing)
11838   }
11839
11840   if (tape.pausing)
11841     return;
11842
11843   // at this point we know that we really continue executing the game
11844
11845   network_player_action_received = FALSE;
11846
11847   // when playing tape, read previously recorded player input from tape data
11848   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11849
11850   local_player->effective_mouse_action = local_player->mouse_action;
11851
11852   if (recorded_player_action != NULL)
11853     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11854                                  recorded_player_action);
11855
11856   // TapePlayAction() may return NULL when toggling to "pause before death"
11857   if (tape.pausing)
11858     return;
11859
11860   if (tape.set_centered_player)
11861   {
11862     game.centered_player_nr_next = tape.centered_player_nr_next;
11863     game.set_centered_player = TRUE;
11864   }
11865
11866   for (i = 0; i < MAX_PLAYERS; i++)
11867   {
11868     summarized_player_action |= stored_player[i].action;
11869
11870     if (!network_playing && (game.team_mode || tape.playing))
11871       stored_player[i].effective_action = stored_player[i].action;
11872   }
11873
11874   if (network_playing && !checkGameEnded())
11875     SendToServer_MovePlayer(summarized_player_action);
11876
11877   // summarize all actions at local players mapped input device position
11878   // (this allows using different input devices in single player mode)
11879   if (!network.enabled && !game.team_mode)
11880     stored_player[map_player_action[local_player->index_nr]].effective_action =
11881       summarized_player_action;
11882
11883   // summarize all actions at centered player in local team mode
11884   if (tape.recording &&
11885       setup.team_mode && !network.enabled &&
11886       setup.input_on_focus &&
11887       game.centered_player_nr != -1)
11888   {
11889     for (i = 0; i < MAX_PLAYERS; i++)
11890       stored_player[map_player_action[i]].effective_action =
11891         (i == game.centered_player_nr ? summarized_player_action : 0);
11892   }
11893
11894   if (recorded_player_action != NULL)
11895     for (i = 0; i < MAX_PLAYERS; i++)
11896       stored_player[i].effective_action = recorded_player_action[i];
11897
11898   for (i = 0; i < MAX_PLAYERS; i++)
11899   {
11900     tape_action[i] = stored_player[i].effective_action;
11901
11902     /* (this may happen in the RND game engine if a player was not present on
11903        the playfield on level start, but appeared later from a custom element */
11904     if (setup.team_mode &&
11905         tape.recording &&
11906         tape_action[i] &&
11907         !tape.player_participates[i])
11908       tape.player_participates[i] = TRUE;
11909   }
11910
11911   SetTapeActionFromMouseAction(tape_action,
11912                                &local_player->effective_mouse_action);
11913
11914   // only record actions from input devices, but not programmed actions
11915   if (tape.recording)
11916     TapeRecordAction(tape_action);
11917
11918   // remember if game was played (especially after tape stopped playing)
11919   if (!tape.playing && summarized_player_action)
11920     game.GamePlayed = TRUE;
11921
11922 #if USE_NEW_PLAYER_ASSIGNMENTS
11923   // !!! also map player actions in single player mode !!!
11924   // if (game.team_mode)
11925   if (1)
11926   {
11927     byte mapped_action[MAX_PLAYERS];
11928
11929 #if DEBUG_PLAYER_ACTIONS
11930     for (i = 0; i < MAX_PLAYERS; i++)
11931       DebugContinued("", "%d, ", stored_player[i].effective_action);
11932 #endif
11933
11934     for (i = 0; i < MAX_PLAYERS; i++)
11935       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11936
11937     for (i = 0; i < MAX_PLAYERS; i++)
11938       stored_player[i].effective_action = mapped_action[i];
11939
11940 #if DEBUG_PLAYER_ACTIONS
11941     DebugContinued("", "=> ");
11942     for (i = 0; i < MAX_PLAYERS; i++)
11943       DebugContinued("", "%d, ", stored_player[i].effective_action);
11944     DebugContinued("game:playing:player", "\n");
11945 #endif
11946   }
11947 #if DEBUG_PLAYER_ACTIONS
11948   else
11949   {
11950     for (i = 0; i < MAX_PLAYERS; i++)
11951       DebugContinued("", "%d, ", stored_player[i].effective_action);
11952     DebugContinued("game:playing:player", "\n");
11953   }
11954 #endif
11955 #endif
11956
11957   for (i = 0; i < MAX_PLAYERS; i++)
11958   {
11959     // allow engine snapshot in case of changed movement attempt
11960     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11961         (stored_player[i].effective_action & KEY_MOTION))
11962       game.snapshot.changed_action = TRUE;
11963
11964     // allow engine snapshot in case of snapping/dropping attempt
11965     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11966         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11967       game.snapshot.changed_action = TRUE;
11968
11969     game.snapshot.last_action[i] = stored_player[i].effective_action;
11970   }
11971
11972   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11973   {
11974     GameActions_EM_Main();
11975   }
11976   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11977   {
11978     GameActions_SP_Main();
11979   }
11980   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11981   {
11982     GameActions_MM_Main();
11983   }
11984   else
11985   {
11986     GameActions_RND_Main();
11987   }
11988
11989   BlitScreenToBitmap(backbuffer);
11990
11991   CheckLevelSolved();
11992   CheckLevelTime();
11993
11994   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11995
11996   if (global.show_frames_per_second)
11997   {
11998     static unsigned int fps_counter = 0;
11999     static int fps_frames = 0;
12000     unsigned int fps_delay_ms = Counter() - fps_counter;
12001
12002     fps_frames++;
12003
12004     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12005     {
12006       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12007
12008       fps_frames = 0;
12009       fps_counter = Counter();
12010
12011       // always draw FPS to screen after FPS value was updated
12012       redraw_mask |= REDRAW_FPS;
12013     }
12014
12015     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12016     if (GetDrawDeactivationMask() == REDRAW_NONE)
12017       redraw_mask |= REDRAW_FPS;
12018   }
12019 }
12020
12021 static void GameActions_CheckSaveEngineSnapshot(void)
12022 {
12023   if (!game.snapshot.save_snapshot)
12024     return;
12025
12026   // clear flag for saving snapshot _before_ saving snapshot
12027   game.snapshot.save_snapshot = FALSE;
12028
12029   SaveEngineSnapshotToList();
12030 }
12031
12032 void GameActions(void)
12033 {
12034   GameActionsExt();
12035
12036   GameActions_CheckSaveEngineSnapshot();
12037 }
12038
12039 void GameActions_EM_Main(void)
12040 {
12041   byte effective_action[MAX_PLAYERS];
12042   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12043   int i;
12044
12045   for (i = 0; i < MAX_PLAYERS; i++)
12046     effective_action[i] = stored_player[i].effective_action;
12047
12048   GameActions_EM(effective_action, warp_mode);
12049 }
12050
12051 void GameActions_SP_Main(void)
12052 {
12053   byte effective_action[MAX_PLAYERS];
12054   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12055   int i;
12056
12057   for (i = 0; i < MAX_PLAYERS; i++)
12058     effective_action[i] = stored_player[i].effective_action;
12059
12060   GameActions_SP(effective_action, warp_mode);
12061
12062   for (i = 0; i < MAX_PLAYERS; i++)
12063   {
12064     if (stored_player[i].force_dropping)
12065       stored_player[i].action |= KEY_BUTTON_DROP;
12066
12067     stored_player[i].force_dropping = FALSE;
12068   }
12069 }
12070
12071 void GameActions_MM_Main(void)
12072 {
12073   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12074
12075   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12076 }
12077
12078 void GameActions_RND_Main(void)
12079 {
12080   GameActions_RND();
12081 }
12082
12083 void GameActions_RND(void)
12084 {
12085   static struct MouseActionInfo mouse_action_last = { 0 };
12086   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12087   int magic_wall_x = 0, magic_wall_y = 0;
12088   int i, x, y, element, graphic, last_gfx_frame;
12089
12090   InitPlayfieldScanModeVars();
12091
12092   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12093   {
12094     SCAN_PLAYFIELD(x, y)
12095     {
12096       ChangeCount[x][y] = 0;
12097       ChangeEvent[x][y] = -1;
12098     }
12099   }
12100
12101   if (game.set_centered_player)
12102   {
12103     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12104
12105     // switching to "all players" only possible if all players fit to screen
12106     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12107     {
12108       game.centered_player_nr_next = game.centered_player_nr;
12109       game.set_centered_player = FALSE;
12110     }
12111
12112     // do not switch focus to non-existing (or non-active) player
12113     if (game.centered_player_nr_next >= 0 &&
12114         !stored_player[game.centered_player_nr_next].active)
12115     {
12116       game.centered_player_nr_next = game.centered_player_nr;
12117       game.set_centered_player = FALSE;
12118     }
12119   }
12120
12121   if (game.set_centered_player &&
12122       ScreenMovPos == 0)        // screen currently aligned at tile position
12123   {
12124     int sx, sy;
12125
12126     if (game.centered_player_nr_next == -1)
12127     {
12128       setScreenCenteredToAllPlayers(&sx, &sy);
12129     }
12130     else
12131     {
12132       sx = stored_player[game.centered_player_nr_next].jx;
12133       sy = stored_player[game.centered_player_nr_next].jy;
12134     }
12135
12136     game.centered_player_nr = game.centered_player_nr_next;
12137     game.set_centered_player = FALSE;
12138
12139     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12140     DrawGameDoorValues();
12141   }
12142
12143   // check single step mode (set flag and clear again if any player is active)
12144   game.enter_single_step_mode =
12145     (tape.single_step && tape.recording && !tape.pausing);
12146
12147   for (i = 0; i < MAX_PLAYERS; i++)
12148   {
12149     int actual_player_action = stored_player[i].effective_action;
12150
12151 #if 1
12152     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12153        - rnd_equinox_tetrachloride 048
12154        - rnd_equinox_tetrachloride_ii 096
12155        - rnd_emanuel_schmieg 002
12156        - doctor_sloan_ww 001, 020
12157     */
12158     if (stored_player[i].MovPos == 0)
12159       CheckGravityMovement(&stored_player[i]);
12160 #endif
12161
12162     // overwrite programmed action with tape action
12163     if (stored_player[i].programmed_action)
12164       actual_player_action = stored_player[i].programmed_action;
12165
12166     PlayerActions(&stored_player[i], actual_player_action);
12167
12168     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12169   }
12170
12171   // single step pause mode may already have been toggled by "ScrollPlayer()"
12172   if (game.enter_single_step_mode && !tape.pausing)
12173     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12174
12175   ScrollScreen(NULL, SCROLL_GO_ON);
12176
12177   /* for backwards compatibility, the following code emulates a fixed bug that
12178      occured when pushing elements (causing elements that just made their last
12179      pushing step to already (if possible) make their first falling step in the
12180      same game frame, which is bad); this code is also needed to use the famous
12181      "spring push bug" which is used in older levels and might be wanted to be
12182      used also in newer levels, but in this case the buggy pushing code is only
12183      affecting the "spring" element and no other elements */
12184
12185   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12186   {
12187     for (i = 0; i < MAX_PLAYERS; i++)
12188     {
12189       struct PlayerInfo *player = &stored_player[i];
12190       int x = player->jx;
12191       int y = player->jy;
12192
12193       if (player->active && player->is_pushing && player->is_moving &&
12194           IS_MOVING(x, y) &&
12195           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12196            Tile[x][y] == EL_SPRING))
12197       {
12198         ContinueMoving(x, y);
12199
12200         // continue moving after pushing (this is actually a bug)
12201         if (!IS_MOVING(x, y))
12202           Stop[x][y] = FALSE;
12203       }
12204     }
12205   }
12206
12207   SCAN_PLAYFIELD(x, y)
12208   {
12209     Last[x][y] = Tile[x][y];
12210
12211     ChangeCount[x][y] = 0;
12212     ChangeEvent[x][y] = -1;
12213
12214     // this must be handled before main playfield loop
12215     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12216     {
12217       MovDelay[x][y]--;
12218       if (MovDelay[x][y] <= 0)
12219         RemoveField(x, y);
12220     }
12221
12222     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12223     {
12224       MovDelay[x][y]--;
12225       if (MovDelay[x][y] <= 0)
12226       {
12227         int element = Store[x][y];
12228         int move_direction = MovDir[x][y];
12229         int player_index_bit = Store2[x][y];
12230
12231         Store[x][y] = 0;
12232         Store2[x][y] = 0;
12233
12234         RemoveField(x, y);
12235         TEST_DrawLevelField(x, y);
12236
12237         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12238
12239         if (IS_ENVELOPE(element))
12240           local_player->show_envelope = element;
12241       }
12242     }
12243
12244 #if DEBUG
12245     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12246     {
12247       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12248             x, y);
12249       Debug("game:playing:GameActions_RND", "This should never happen!");
12250
12251       ChangePage[x][y] = -1;
12252     }
12253 #endif
12254
12255     Stop[x][y] = FALSE;
12256     if (WasJustMoving[x][y] > 0)
12257       WasJustMoving[x][y]--;
12258     if (WasJustFalling[x][y] > 0)
12259       WasJustFalling[x][y]--;
12260     if (CheckCollision[x][y] > 0)
12261       CheckCollision[x][y]--;
12262     if (CheckImpact[x][y] > 0)
12263       CheckImpact[x][y]--;
12264
12265     GfxFrame[x][y]++;
12266
12267     /* reset finished pushing action (not done in ContinueMoving() to allow
12268        continuous pushing animation for elements with zero push delay) */
12269     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12270     {
12271       ResetGfxAnimation(x, y);
12272       TEST_DrawLevelField(x, y);
12273     }
12274
12275 #if DEBUG
12276     if (IS_BLOCKED(x, y))
12277     {
12278       int oldx, oldy;
12279
12280       Blocked2Moving(x, y, &oldx, &oldy);
12281       if (!IS_MOVING(oldx, oldy))
12282       {
12283         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12284         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12285         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12286         Debug("game:playing:GameActions_RND", "This should never happen!");
12287       }
12288     }
12289 #endif
12290   }
12291
12292   if (mouse_action.button)
12293   {
12294     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12295     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12296
12297     x = mouse_action.lx;
12298     y = mouse_action.ly;
12299     element = Tile[x][y];
12300
12301     if (new_button)
12302     {
12303       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12304       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12305                                          ch_button);
12306     }
12307
12308     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12309     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12310                                        ch_button);
12311
12312     if (level.use_step_counter)
12313     {
12314       boolean counted_click = FALSE;
12315
12316       // element clicked that can change when clicked/pressed
12317       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12318           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12319            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12320         counted_click = TRUE;
12321
12322       // element clicked that can trigger change when clicked/pressed
12323       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12324           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12325         counted_click = TRUE;
12326
12327       if (new_button && counted_click)
12328         CheckLevelTime_StepCounter();
12329     }
12330   }
12331
12332   SCAN_PLAYFIELD(x, y)
12333   {
12334     element = Tile[x][y];
12335     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12336     last_gfx_frame = GfxFrame[x][y];
12337
12338     if (element == EL_EMPTY)
12339       graphic = el2img(GfxElementEmpty[x][y]);
12340
12341     ResetGfxFrame(x, y);
12342
12343     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12344       DrawLevelGraphicAnimation(x, y, graphic);
12345
12346     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12347         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12348       ResetRandomAnimationValue(x, y);
12349
12350     SetRandomAnimationValue(x, y);
12351
12352     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12353
12354     if (IS_INACTIVE(element))
12355     {
12356       if (IS_ANIMATED(graphic))
12357         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12358
12359       continue;
12360     }
12361
12362     // this may take place after moving, so 'element' may have changed
12363     if (IS_CHANGING(x, y) &&
12364         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12365     {
12366       int page = element_info[element].event_page_nr[CE_DELAY];
12367
12368       HandleElementChange(x, y, page);
12369
12370       element = Tile[x][y];
12371       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12372     }
12373
12374     CheckNextToConditions(x, y);
12375
12376     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12377     {
12378       StartMoving(x, y);
12379
12380       element = Tile[x][y];
12381       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12382
12383       if (IS_ANIMATED(graphic) &&
12384           !IS_MOVING(x, y) &&
12385           !Stop[x][y])
12386         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12387
12388       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12389         TEST_DrawTwinkleOnField(x, y);
12390     }
12391     else if (element == EL_ACID)
12392     {
12393       if (!Stop[x][y])
12394         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12395     }
12396     else if ((element == EL_EXIT_OPEN ||
12397               element == EL_EM_EXIT_OPEN ||
12398               element == EL_SP_EXIT_OPEN ||
12399               element == EL_STEEL_EXIT_OPEN ||
12400               element == EL_EM_STEEL_EXIT_OPEN ||
12401               element == EL_SP_TERMINAL ||
12402               element == EL_SP_TERMINAL_ACTIVE ||
12403               element == EL_EXTRA_TIME ||
12404               element == EL_SHIELD_NORMAL ||
12405               element == EL_SHIELD_DEADLY) &&
12406              IS_ANIMATED(graphic))
12407       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12408     else if (IS_MOVING(x, y))
12409       ContinueMoving(x, y);
12410     else if (IS_ACTIVE_BOMB(element))
12411       CheckDynamite(x, y);
12412     else if (element == EL_AMOEBA_GROWING)
12413       AmoebaGrowing(x, y);
12414     else if (element == EL_AMOEBA_SHRINKING)
12415       AmoebaShrinking(x, y);
12416
12417 #if !USE_NEW_AMOEBA_CODE
12418     else if (IS_AMOEBALIVE(element))
12419       AmoebaReproduce(x, y);
12420 #endif
12421
12422     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12423       Life(x, y);
12424     else if (element == EL_EXIT_CLOSED)
12425       CheckExit(x, y);
12426     else if (element == EL_EM_EXIT_CLOSED)
12427       CheckExitEM(x, y);
12428     else if (element == EL_STEEL_EXIT_CLOSED)
12429       CheckExitSteel(x, y);
12430     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12431       CheckExitSteelEM(x, y);
12432     else if (element == EL_SP_EXIT_CLOSED)
12433       CheckExitSP(x, y);
12434     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12435              element == EL_EXPANDABLE_STEELWALL_GROWING)
12436       MauerWaechst(x, y);
12437     else if (element == EL_EXPANDABLE_WALL ||
12438              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12439              element == EL_EXPANDABLE_WALL_VERTICAL ||
12440              element == EL_EXPANDABLE_WALL_ANY ||
12441              element == EL_BD_EXPANDABLE_WALL)
12442       MauerAbleger(x, y);
12443     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12444              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12445              element == EL_EXPANDABLE_STEELWALL_ANY)
12446       MauerAblegerStahl(x, y);
12447     else if (element == EL_FLAMES)
12448       CheckForDragon(x, y);
12449     else if (element == EL_EXPLOSION)
12450       ; // drawing of correct explosion animation is handled separately
12451     else if (element == EL_ELEMENT_SNAPPING ||
12452              element == EL_DIAGONAL_SHRINKING ||
12453              element == EL_DIAGONAL_GROWING)
12454     {
12455       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12456
12457       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12458     }
12459     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12460       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12461
12462     if (IS_BELT_ACTIVE(element))
12463       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12464
12465     if (game.magic_wall_active)
12466     {
12467       int jx = local_player->jx, jy = local_player->jy;
12468
12469       // play the element sound at the position nearest to the player
12470       if ((element == EL_MAGIC_WALL_FULL ||
12471            element == EL_MAGIC_WALL_ACTIVE ||
12472            element == EL_MAGIC_WALL_EMPTYING ||
12473            element == EL_BD_MAGIC_WALL_FULL ||
12474            element == EL_BD_MAGIC_WALL_ACTIVE ||
12475            element == EL_BD_MAGIC_WALL_EMPTYING ||
12476            element == EL_DC_MAGIC_WALL_FULL ||
12477            element == EL_DC_MAGIC_WALL_ACTIVE ||
12478            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12479           ABS(x - jx) + ABS(y - jy) <
12480           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12481       {
12482         magic_wall_x = x;
12483         magic_wall_y = y;
12484       }
12485     }
12486   }
12487
12488 #if USE_NEW_AMOEBA_CODE
12489   // new experimental amoeba growth stuff
12490   if (!(FrameCounter % 8))
12491   {
12492     static unsigned int random = 1684108901;
12493
12494     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12495     {
12496       x = RND(lev_fieldx);
12497       y = RND(lev_fieldy);
12498       element = Tile[x][y];
12499
12500       if (!IS_PLAYER(x,y) &&
12501           (element == EL_EMPTY ||
12502            CAN_GROW_INTO(element) ||
12503            element == EL_QUICKSAND_EMPTY ||
12504            element == EL_QUICKSAND_FAST_EMPTY ||
12505            element == EL_ACID_SPLASH_LEFT ||
12506            element == EL_ACID_SPLASH_RIGHT))
12507       {
12508         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12509             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12510             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12511             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12512           Tile[x][y] = EL_AMOEBA_DROP;
12513       }
12514
12515       random = random * 129 + 1;
12516     }
12517   }
12518 #endif
12519
12520   game.explosions_delayed = FALSE;
12521
12522   SCAN_PLAYFIELD(x, y)
12523   {
12524     element = Tile[x][y];
12525
12526     if (ExplodeField[x][y])
12527       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12528     else if (element == EL_EXPLOSION)
12529       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12530
12531     ExplodeField[x][y] = EX_TYPE_NONE;
12532   }
12533
12534   game.explosions_delayed = TRUE;
12535
12536   if (game.magic_wall_active)
12537   {
12538     if (!(game.magic_wall_time_left % 4))
12539     {
12540       int element = Tile[magic_wall_x][magic_wall_y];
12541
12542       if (element == EL_BD_MAGIC_WALL_FULL ||
12543           element == EL_BD_MAGIC_WALL_ACTIVE ||
12544           element == EL_BD_MAGIC_WALL_EMPTYING)
12545         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12546       else if (element == EL_DC_MAGIC_WALL_FULL ||
12547                element == EL_DC_MAGIC_WALL_ACTIVE ||
12548                element == EL_DC_MAGIC_WALL_EMPTYING)
12549         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12550       else
12551         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12552     }
12553
12554     if (game.magic_wall_time_left > 0)
12555     {
12556       game.magic_wall_time_left--;
12557
12558       if (!game.magic_wall_time_left)
12559       {
12560         SCAN_PLAYFIELD(x, y)
12561         {
12562           element = Tile[x][y];
12563
12564           if (element == EL_MAGIC_WALL_ACTIVE ||
12565               element == EL_MAGIC_WALL_FULL)
12566           {
12567             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12568             TEST_DrawLevelField(x, y);
12569           }
12570           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12571                    element == EL_BD_MAGIC_WALL_FULL)
12572           {
12573             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12574             TEST_DrawLevelField(x, y);
12575           }
12576           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12577                    element == EL_DC_MAGIC_WALL_FULL)
12578           {
12579             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12580             TEST_DrawLevelField(x, y);
12581           }
12582         }
12583
12584         game.magic_wall_active = FALSE;
12585       }
12586     }
12587   }
12588
12589   if (game.light_time_left > 0)
12590   {
12591     game.light_time_left--;
12592
12593     if (game.light_time_left == 0)
12594       RedrawAllLightSwitchesAndInvisibleElements();
12595   }
12596
12597   if (game.timegate_time_left > 0)
12598   {
12599     game.timegate_time_left--;
12600
12601     if (game.timegate_time_left == 0)
12602       CloseAllOpenTimegates();
12603   }
12604
12605   if (game.lenses_time_left > 0)
12606   {
12607     game.lenses_time_left--;
12608
12609     if (game.lenses_time_left == 0)
12610       RedrawAllInvisibleElementsForLenses();
12611   }
12612
12613   if (game.magnify_time_left > 0)
12614   {
12615     game.magnify_time_left--;
12616
12617     if (game.magnify_time_left == 0)
12618       RedrawAllInvisibleElementsForMagnifier();
12619   }
12620
12621   for (i = 0; i < MAX_PLAYERS; i++)
12622   {
12623     struct PlayerInfo *player = &stored_player[i];
12624
12625     if (SHIELD_ON(player))
12626     {
12627       if (player->shield_deadly_time_left)
12628         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12629       else if (player->shield_normal_time_left)
12630         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12631     }
12632   }
12633
12634 #if USE_DELAYED_GFX_REDRAW
12635   SCAN_PLAYFIELD(x, y)
12636   {
12637     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12638     {
12639       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12640          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12641
12642       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12643         DrawLevelField(x, y);
12644
12645       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12646         DrawLevelFieldCrumbled(x, y);
12647
12648       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12649         DrawLevelFieldCrumbledNeighbours(x, y);
12650
12651       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12652         DrawTwinkleOnField(x, y);
12653     }
12654
12655     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12656   }
12657 #endif
12658
12659   DrawAllPlayers();
12660   PlayAllPlayersSound();
12661
12662   for (i = 0; i < MAX_PLAYERS; i++)
12663   {
12664     struct PlayerInfo *player = &stored_player[i];
12665
12666     if (player->show_envelope != 0 && (!player->active ||
12667                                        player->MovPos == 0))
12668     {
12669       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12670
12671       player->show_envelope = 0;
12672     }
12673   }
12674
12675   // use random number generator in every frame to make it less predictable
12676   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12677     RND(1);
12678
12679   mouse_action_last = mouse_action;
12680 }
12681
12682 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12683 {
12684   int min_x = x, min_y = y, max_x = x, max_y = y;
12685   int scr_fieldx = getScreenFieldSizeX();
12686   int scr_fieldy = getScreenFieldSizeY();
12687   int i;
12688
12689   for (i = 0; i < MAX_PLAYERS; i++)
12690   {
12691     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12692
12693     if (!stored_player[i].active || &stored_player[i] == player)
12694       continue;
12695
12696     min_x = MIN(min_x, jx);
12697     min_y = MIN(min_y, jy);
12698     max_x = MAX(max_x, jx);
12699     max_y = MAX(max_y, jy);
12700   }
12701
12702   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12703 }
12704
12705 static boolean AllPlayersInVisibleScreen(void)
12706 {
12707   int i;
12708
12709   for (i = 0; i < MAX_PLAYERS; i++)
12710   {
12711     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12712
12713     if (!stored_player[i].active)
12714       continue;
12715
12716     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12717       return FALSE;
12718   }
12719
12720   return TRUE;
12721 }
12722
12723 void ScrollLevel(int dx, int dy)
12724 {
12725   int scroll_offset = 2 * TILEX_VAR;
12726   int x, y;
12727
12728   BlitBitmap(drawto_field, drawto_field,
12729              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12730              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12731              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12732              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12733              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12734              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12735
12736   if (dx != 0)
12737   {
12738     x = (dx == 1 ? BX1 : BX2);
12739     for (y = BY1; y <= BY2; y++)
12740       DrawScreenField(x, y);
12741   }
12742
12743   if (dy != 0)
12744   {
12745     y = (dy == 1 ? BY1 : BY2);
12746     for (x = BX1; x <= BX2; x++)
12747       DrawScreenField(x, y);
12748   }
12749
12750   redraw_mask |= REDRAW_FIELD;
12751 }
12752
12753 static boolean canFallDown(struct PlayerInfo *player)
12754 {
12755   int jx = player->jx, jy = player->jy;
12756
12757   return (IN_LEV_FIELD(jx, jy + 1) &&
12758           (IS_FREE(jx, jy + 1) ||
12759            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12760           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12761           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12762 }
12763
12764 static boolean canPassField(int x, int y, int move_dir)
12765 {
12766   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12767   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12768   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12769   int nextx = x + dx;
12770   int nexty = y + dy;
12771   int element = Tile[x][y];
12772
12773   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12774           !CAN_MOVE(element) &&
12775           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12776           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12777           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12778 }
12779
12780 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12781 {
12782   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12783   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12784   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12785   int newx = x + dx;
12786   int newy = y + dy;
12787
12788   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12789           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12790           (IS_DIGGABLE(Tile[newx][newy]) ||
12791            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12792            canPassField(newx, newy, move_dir)));
12793 }
12794
12795 static void CheckGravityMovement(struct PlayerInfo *player)
12796 {
12797   if (player->gravity && !player->programmed_action)
12798   {
12799     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12800     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12801     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12802     int jx = player->jx, jy = player->jy;
12803     boolean player_is_moving_to_valid_field =
12804       (!player_is_snapping &&
12805        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12806         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12807     boolean player_can_fall_down = canFallDown(player);
12808
12809     if (player_can_fall_down &&
12810         !player_is_moving_to_valid_field)
12811       player->programmed_action = MV_DOWN;
12812   }
12813 }
12814
12815 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12816 {
12817   return CheckGravityMovement(player);
12818
12819   if (player->gravity && !player->programmed_action)
12820   {
12821     int jx = player->jx, jy = player->jy;
12822     boolean field_under_player_is_free =
12823       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12824     boolean player_is_standing_on_valid_field =
12825       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12826        (IS_WALKABLE(Tile[jx][jy]) &&
12827         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12828
12829     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12830       player->programmed_action = MV_DOWN;
12831   }
12832 }
12833
12834 /*
12835   MovePlayerOneStep()
12836   -----------------------------------------------------------------------------
12837   dx, dy:               direction (non-diagonal) to try to move the player to
12838   real_dx, real_dy:     direction as read from input device (can be diagonal)
12839 */
12840
12841 boolean MovePlayerOneStep(struct PlayerInfo *player,
12842                           int dx, int dy, int real_dx, int real_dy)
12843 {
12844   int jx = player->jx, jy = player->jy;
12845   int new_jx = jx + dx, new_jy = jy + dy;
12846   int can_move;
12847   boolean player_can_move = !player->cannot_move;
12848
12849   if (!player->active || (!dx && !dy))
12850     return MP_NO_ACTION;
12851
12852   player->MovDir = (dx < 0 ? MV_LEFT :
12853                     dx > 0 ? MV_RIGHT :
12854                     dy < 0 ? MV_UP :
12855                     dy > 0 ? MV_DOWN :  MV_NONE);
12856
12857   if (!IN_LEV_FIELD(new_jx, new_jy))
12858     return MP_NO_ACTION;
12859
12860   if (!player_can_move)
12861   {
12862     if (player->MovPos == 0)
12863     {
12864       player->is_moving = FALSE;
12865       player->is_digging = FALSE;
12866       player->is_collecting = FALSE;
12867       player->is_snapping = FALSE;
12868       player->is_pushing = FALSE;
12869     }
12870   }
12871
12872   if (!network.enabled && game.centered_player_nr == -1 &&
12873       !AllPlayersInSight(player, new_jx, new_jy))
12874     return MP_NO_ACTION;
12875
12876   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12877   if (can_move != MP_MOVING)
12878     return can_move;
12879
12880   // check if DigField() has caused relocation of the player
12881   if (player->jx != jx || player->jy != jy)
12882     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12883
12884   StorePlayer[jx][jy] = 0;
12885   player->last_jx = jx;
12886   player->last_jy = jy;
12887   player->jx = new_jx;
12888   player->jy = new_jy;
12889   StorePlayer[new_jx][new_jy] = player->element_nr;
12890
12891   if (player->move_delay_value_next != -1)
12892   {
12893     player->move_delay_value = player->move_delay_value_next;
12894     player->move_delay_value_next = -1;
12895   }
12896
12897   player->MovPos =
12898     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12899
12900   player->step_counter++;
12901
12902   PlayerVisit[jx][jy] = FrameCounter;
12903
12904   player->is_moving = TRUE;
12905
12906 #if 1
12907   // should better be called in MovePlayer(), but this breaks some tapes
12908   ScrollPlayer(player, SCROLL_INIT);
12909 #endif
12910
12911   return MP_MOVING;
12912 }
12913
12914 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12915 {
12916   int jx = player->jx, jy = player->jy;
12917   int old_jx = jx, old_jy = jy;
12918   int moved = MP_NO_ACTION;
12919
12920   if (!player->active)
12921     return FALSE;
12922
12923   if (!dx && !dy)
12924   {
12925     if (player->MovPos == 0)
12926     {
12927       player->is_moving = FALSE;
12928       player->is_digging = FALSE;
12929       player->is_collecting = FALSE;
12930       player->is_snapping = FALSE;
12931       player->is_pushing = FALSE;
12932     }
12933
12934     return FALSE;
12935   }
12936
12937   if (player->move_delay > 0)
12938     return FALSE;
12939
12940   player->move_delay = -1;              // set to "uninitialized" value
12941
12942   // store if player is automatically moved to next field
12943   player->is_auto_moving = (player->programmed_action != MV_NONE);
12944
12945   // remove the last programmed player action
12946   player->programmed_action = 0;
12947
12948   if (player->MovPos)
12949   {
12950     // should only happen if pre-1.2 tape recordings are played
12951     // this is only for backward compatibility
12952
12953     int original_move_delay_value = player->move_delay_value;
12954
12955 #if DEBUG
12956     Debug("game:playing:MovePlayer",
12957           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12958           tape.counter);
12959 #endif
12960
12961     // scroll remaining steps with finest movement resolution
12962     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12963
12964     while (player->MovPos)
12965     {
12966       ScrollPlayer(player, SCROLL_GO_ON);
12967       ScrollScreen(NULL, SCROLL_GO_ON);
12968
12969       AdvanceFrameAndPlayerCounters(player->index_nr);
12970
12971       DrawAllPlayers();
12972       BackToFront_WithFrameDelay(0);
12973     }
12974
12975     player->move_delay_value = original_move_delay_value;
12976   }
12977
12978   player->is_active = FALSE;
12979
12980   if (player->last_move_dir & MV_HORIZONTAL)
12981   {
12982     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12983       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12984   }
12985   else
12986   {
12987     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12988       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12989   }
12990
12991   if (!moved && !player->is_active)
12992   {
12993     player->is_moving = FALSE;
12994     player->is_digging = FALSE;
12995     player->is_collecting = FALSE;
12996     player->is_snapping = FALSE;
12997     player->is_pushing = FALSE;
12998   }
12999
13000   jx = player->jx;
13001   jy = player->jy;
13002
13003   if (moved & MP_MOVING && !ScreenMovPos &&
13004       (player->index_nr == game.centered_player_nr ||
13005        game.centered_player_nr == -1))
13006   {
13007     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13008
13009     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13010     {
13011       // actual player has left the screen -- scroll in that direction
13012       if (jx != old_jx)         // player has moved horizontally
13013         scroll_x += (jx - old_jx);
13014       else                      // player has moved vertically
13015         scroll_y += (jy - old_jy);
13016     }
13017     else
13018     {
13019       int offset_raw = game.scroll_delay_value;
13020
13021       if (jx != old_jx)         // player has moved horizontally
13022       {
13023         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13024         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13025         int new_scroll_x = jx - MIDPOSX + offset_x;
13026
13027         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13028             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13029           scroll_x = new_scroll_x;
13030
13031         // don't scroll over playfield boundaries
13032         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13033
13034         // don't scroll more than one field at a time
13035         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13036
13037         // don't scroll against the player's moving direction
13038         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13039             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13040           scroll_x = old_scroll_x;
13041       }
13042       else                      // player has moved vertically
13043       {
13044         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13045         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13046         int new_scroll_y = jy - MIDPOSY + offset_y;
13047
13048         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13049             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13050           scroll_y = new_scroll_y;
13051
13052         // don't scroll over playfield boundaries
13053         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13054
13055         // don't scroll more than one field at a time
13056         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13057
13058         // don't scroll against the player's moving direction
13059         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13060             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13061           scroll_y = old_scroll_y;
13062       }
13063     }
13064
13065     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13066     {
13067       if (!network.enabled && game.centered_player_nr == -1 &&
13068           !AllPlayersInVisibleScreen())
13069       {
13070         scroll_x = old_scroll_x;
13071         scroll_y = old_scroll_y;
13072       }
13073       else
13074       {
13075         ScrollScreen(player, SCROLL_INIT);
13076         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13077       }
13078     }
13079   }
13080
13081   player->StepFrame = 0;
13082
13083   if (moved & MP_MOVING)
13084   {
13085     if (old_jx != jx && old_jy == jy)
13086       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13087     else if (old_jx == jx && old_jy != jy)
13088       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13089
13090     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13091
13092     player->last_move_dir = player->MovDir;
13093     player->is_moving = TRUE;
13094     player->is_snapping = FALSE;
13095     player->is_switching = FALSE;
13096     player->is_dropping = FALSE;
13097     player->is_dropping_pressed = FALSE;
13098     player->drop_pressed_delay = 0;
13099
13100 #if 0
13101     // should better be called here than above, but this breaks some tapes
13102     ScrollPlayer(player, SCROLL_INIT);
13103 #endif
13104   }
13105   else
13106   {
13107     CheckGravityMovementWhenNotMoving(player);
13108
13109     player->is_moving = FALSE;
13110
13111     /* at this point, the player is allowed to move, but cannot move right now
13112        (e.g. because of something blocking the way) -- ensure that the player
13113        is also allowed to move in the next frame (in old versions before 3.1.1,
13114        the player was forced to wait again for eight frames before next try) */
13115
13116     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13117       player->move_delay = 0;   // allow direct movement in the next frame
13118   }
13119
13120   if (player->move_delay == -1)         // not yet initialized by DigField()
13121     player->move_delay = player->move_delay_value;
13122
13123   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13124   {
13125     TestIfPlayerTouchesBadThing(jx, jy);
13126     TestIfPlayerTouchesCustomElement(jx, jy);
13127   }
13128
13129   if (!player->active)
13130     RemovePlayer(player);
13131
13132   return moved;
13133 }
13134
13135 void ScrollPlayer(struct PlayerInfo *player, int mode)
13136 {
13137   int jx = player->jx, jy = player->jy;
13138   int last_jx = player->last_jx, last_jy = player->last_jy;
13139   int move_stepsize = TILEX / player->move_delay_value;
13140
13141   if (!player->active)
13142     return;
13143
13144   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13145     return;
13146
13147   if (mode == SCROLL_INIT)
13148   {
13149     player->actual_frame_counter.count = FrameCounter;
13150     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13151
13152     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13153         Tile[last_jx][last_jy] == EL_EMPTY)
13154     {
13155       int last_field_block_delay = 0;   // start with no blocking at all
13156       int block_delay_adjustment = player->block_delay_adjustment;
13157
13158       // if player blocks last field, add delay for exactly one move
13159       if (player->block_last_field)
13160       {
13161         last_field_block_delay += player->move_delay_value;
13162
13163         // when blocking enabled, prevent moving up despite gravity
13164         if (player->gravity && player->MovDir == MV_UP)
13165           block_delay_adjustment = -1;
13166       }
13167
13168       // add block delay adjustment (also possible when not blocking)
13169       last_field_block_delay += block_delay_adjustment;
13170
13171       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13172       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13173     }
13174
13175     if (player->MovPos != 0)    // player has not yet reached destination
13176       return;
13177   }
13178   else if (!FrameReached(&player->actual_frame_counter))
13179     return;
13180
13181   if (player->MovPos != 0)
13182   {
13183     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13184     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13185
13186     // before DrawPlayer() to draw correct player graphic for this case
13187     if (player->MovPos == 0)
13188       CheckGravityMovement(player);
13189   }
13190
13191   if (player->MovPos == 0)      // player reached destination field
13192   {
13193     if (player->move_delay_reset_counter > 0)
13194     {
13195       player->move_delay_reset_counter--;
13196
13197       if (player->move_delay_reset_counter == 0)
13198       {
13199         // continue with normal speed after quickly moving through gate
13200         HALVE_PLAYER_SPEED(player);
13201
13202         // be able to make the next move without delay
13203         player->move_delay = 0;
13204       }
13205     }
13206
13207     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13208         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13209         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13210         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13211         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13212         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13213         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13214         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13215     {
13216       ExitPlayer(player);
13217
13218       if (game.players_still_needed == 0 &&
13219           (game.friends_still_needed == 0 ||
13220            IS_SP_ELEMENT(Tile[jx][jy])))
13221         LevelSolved();
13222     }
13223
13224     player->last_jx = jx;
13225     player->last_jy = jy;
13226
13227     // this breaks one level: "machine", level 000
13228     {
13229       int move_direction = player->MovDir;
13230       int enter_side = MV_DIR_OPPOSITE(move_direction);
13231       int leave_side = move_direction;
13232       int old_jx = last_jx;
13233       int old_jy = last_jy;
13234       int old_element = Tile[old_jx][old_jy];
13235       int new_element = Tile[jx][jy];
13236
13237       if (IS_CUSTOM_ELEMENT(old_element))
13238         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13239                                    CE_LEFT_BY_PLAYER,
13240                                    player->index_bit, leave_side);
13241
13242       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13243                                           CE_PLAYER_LEAVES_X,
13244                                           player->index_bit, leave_side);
13245
13246       if (IS_CUSTOM_ELEMENT(new_element))
13247         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13248                                    player->index_bit, enter_side);
13249
13250       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13251                                           CE_PLAYER_ENTERS_X,
13252                                           player->index_bit, enter_side);
13253
13254       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13255                                         CE_MOVE_OF_X, move_direction);
13256     }
13257
13258     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13259     {
13260       TestIfPlayerTouchesBadThing(jx, jy);
13261       TestIfPlayerTouchesCustomElement(jx, jy);
13262
13263       /* needed because pushed element has not yet reached its destination,
13264          so it would trigger a change event at its previous field location */
13265       if (!player->is_pushing)
13266         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13267
13268       if (level.finish_dig_collect &&
13269           (player->is_digging || player->is_collecting))
13270       {
13271         int last_element = player->last_removed_element;
13272         int move_direction = player->MovDir;
13273         int enter_side = MV_DIR_OPPOSITE(move_direction);
13274         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13275                             CE_PLAYER_COLLECTS_X);
13276
13277         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13278                                             player->index_bit, enter_side);
13279
13280         player->last_removed_element = EL_UNDEFINED;
13281       }
13282
13283       if (!player->active)
13284         RemovePlayer(player);
13285     }
13286
13287     if (level.use_step_counter)
13288       CheckLevelTime_StepCounter();
13289
13290     if (tape.single_step && tape.recording && !tape.pausing &&
13291         !player->programmed_action)
13292       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13293
13294     if (!player->programmed_action)
13295       CheckSaveEngineSnapshot(player);
13296   }
13297 }
13298
13299 void ScrollScreen(struct PlayerInfo *player, int mode)
13300 {
13301   static DelayCounter screen_frame_counter = { 0 };
13302
13303   if (mode == SCROLL_INIT)
13304   {
13305     // set scrolling step size according to actual player's moving speed
13306     ScrollStepSize = TILEX / player->move_delay_value;
13307
13308     screen_frame_counter.count = FrameCounter;
13309     screen_frame_counter.value = 1;
13310
13311     ScreenMovDir = player->MovDir;
13312     ScreenMovPos = player->MovPos;
13313     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13314     return;
13315   }
13316   else if (!FrameReached(&screen_frame_counter))
13317     return;
13318
13319   if (ScreenMovPos)
13320   {
13321     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13322     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13323     redraw_mask |= REDRAW_FIELD;
13324   }
13325   else
13326     ScreenMovDir = MV_NONE;
13327 }
13328
13329 void CheckNextToConditions(int x, int y)
13330 {
13331   int element = Tile[x][y];
13332
13333   if (IS_PLAYER(x, y))
13334     TestIfPlayerNextToCustomElement(x, y);
13335
13336   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13337       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13338     TestIfElementNextToCustomElement(x, y);
13339 }
13340
13341 void TestIfPlayerNextToCustomElement(int x, int y)
13342 {
13343   struct XY *xy = xy_topdown;
13344   static int trigger_sides[4][2] =
13345   {
13346     // center side       border side
13347     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13348     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13349     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13350     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13351   };
13352   int i;
13353
13354   if (!IS_PLAYER(x, y))
13355     return;
13356
13357   struct PlayerInfo *player = PLAYERINFO(x, y);
13358
13359   if (player->is_moving)
13360     return;
13361
13362   for (i = 0; i < NUM_DIRECTIONS; i++)
13363   {
13364     int xx = x + xy[i].x;
13365     int yy = y + xy[i].y;
13366     int border_side = trigger_sides[i][1];
13367     int border_element;
13368
13369     if (!IN_LEV_FIELD(xx, yy))
13370       continue;
13371
13372     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13373       continue;         // center and border element not connected
13374
13375     border_element = Tile[xx][yy];
13376
13377     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13378                                player->index_bit, border_side);
13379     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13380                                         CE_PLAYER_NEXT_TO_X,
13381                                         player->index_bit, border_side);
13382
13383     /* use player element that is initially defined in the level playfield,
13384        not the player element that corresponds to the runtime player number
13385        (example: a level that contains EL_PLAYER_3 as the only player would
13386        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13387
13388     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13389                              CE_NEXT_TO_X, border_side);
13390   }
13391 }
13392
13393 void TestIfPlayerTouchesCustomElement(int x, int y)
13394 {
13395   struct XY *xy = xy_topdown;
13396   static int trigger_sides[4][2] =
13397   {
13398     // center side       border side
13399     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13400     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13401     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13402     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13403   };
13404   static int touch_dir[4] =
13405   {
13406     MV_LEFT | MV_RIGHT,
13407     MV_UP   | MV_DOWN,
13408     MV_UP   | MV_DOWN,
13409     MV_LEFT | MV_RIGHT
13410   };
13411   int center_element = Tile[x][y];      // should always be non-moving!
13412   int i;
13413
13414   for (i = 0; i < NUM_DIRECTIONS; i++)
13415   {
13416     int xx = x + xy[i].x;
13417     int yy = y + xy[i].y;
13418     int center_side = trigger_sides[i][0];
13419     int border_side = trigger_sides[i][1];
13420     int border_element;
13421
13422     if (!IN_LEV_FIELD(xx, yy))
13423       continue;
13424
13425     if (IS_PLAYER(x, y))                // player found at center element
13426     {
13427       struct PlayerInfo *player = PLAYERINFO(x, y);
13428
13429       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13430         border_element = Tile[xx][yy];          // may be moving!
13431       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13432         border_element = Tile[xx][yy];
13433       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13434         border_element = MovingOrBlocked2Element(xx, yy);
13435       else
13436         continue;               // center and border element do not touch
13437
13438       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13439                                  player->index_bit, border_side);
13440       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13441                                           CE_PLAYER_TOUCHES_X,
13442                                           player->index_bit, border_side);
13443
13444       {
13445         /* use player element that is initially defined in the level playfield,
13446            not the player element that corresponds to the runtime player number
13447            (example: a level that contains EL_PLAYER_3 as the only player would
13448            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13449         int player_element = PLAYERINFO(x, y)->initial_element;
13450
13451         CheckElementChangeBySide(xx, yy, border_element, player_element,
13452                                  CE_TOUCHING_X, border_side);
13453       }
13454     }
13455     else if (IS_PLAYER(xx, yy))         // player found at border element
13456     {
13457       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13458
13459       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13460       {
13461         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13462           continue;             // center and border element do not touch
13463       }
13464
13465       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13466                                  player->index_bit, center_side);
13467       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13468                                           CE_PLAYER_TOUCHES_X,
13469                                           player->index_bit, center_side);
13470
13471       {
13472         /* use player element that is initially defined in the level playfield,
13473            not the player element that corresponds to the runtime player number
13474            (example: a level that contains EL_PLAYER_3 as the only player would
13475            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13476         int player_element = PLAYERINFO(xx, yy)->initial_element;
13477
13478         CheckElementChangeBySide(x, y, center_element, player_element,
13479                                  CE_TOUCHING_X, center_side);
13480       }
13481
13482       break;
13483     }
13484   }
13485 }
13486
13487 void TestIfElementNextToCustomElement(int x, int y)
13488 {
13489   struct XY *xy = xy_topdown;
13490   static int trigger_sides[4][2] =
13491   {
13492     // center side      border side
13493     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13494     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13495     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13496     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13497   };
13498   int center_element = Tile[x][y];      // should always be non-moving!
13499   int i;
13500
13501   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13502     return;
13503
13504   for (i = 0; i < NUM_DIRECTIONS; i++)
13505   {
13506     int xx = x + xy[i].x;
13507     int yy = y + xy[i].y;
13508     int border_side = trigger_sides[i][1];
13509     int border_element;
13510
13511     if (!IN_LEV_FIELD(xx, yy))
13512       continue;
13513
13514     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13515       continue;                 // center and border element not connected
13516
13517     border_element = Tile[xx][yy];
13518
13519     // check for change of center element (but change it only once)
13520     if (CheckElementChangeBySide(x, y, center_element, border_element,
13521                                  CE_NEXT_TO_X, border_side))
13522       break;
13523   }
13524 }
13525
13526 void TestIfElementTouchesCustomElement(int x, int y)
13527 {
13528   struct XY *xy = xy_topdown;
13529   static int trigger_sides[4][2] =
13530   {
13531     // center side      border side
13532     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13533     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13534     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13535     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13536   };
13537   static int touch_dir[4] =
13538   {
13539     MV_LEFT | MV_RIGHT,
13540     MV_UP   | MV_DOWN,
13541     MV_UP   | MV_DOWN,
13542     MV_LEFT | MV_RIGHT
13543   };
13544   boolean change_center_element = FALSE;
13545   int center_element = Tile[x][y];      // should always be non-moving!
13546   int border_element_old[NUM_DIRECTIONS];
13547   int i;
13548
13549   for (i = 0; i < NUM_DIRECTIONS; i++)
13550   {
13551     int xx = x + xy[i].x;
13552     int yy = y + xy[i].y;
13553     int border_element;
13554
13555     border_element_old[i] = -1;
13556
13557     if (!IN_LEV_FIELD(xx, yy))
13558       continue;
13559
13560     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13561       border_element = Tile[xx][yy];    // may be moving!
13562     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13563       border_element = Tile[xx][yy];
13564     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13565       border_element = MovingOrBlocked2Element(xx, yy);
13566     else
13567       continue;                 // center and border element do not touch
13568
13569     border_element_old[i] = border_element;
13570   }
13571
13572   for (i = 0; i < NUM_DIRECTIONS; i++)
13573   {
13574     int xx = x + xy[i].x;
13575     int yy = y + xy[i].y;
13576     int center_side = trigger_sides[i][0];
13577     int border_element = border_element_old[i];
13578
13579     if (border_element == -1)
13580       continue;
13581
13582     // check for change of border element
13583     CheckElementChangeBySide(xx, yy, border_element, center_element,
13584                              CE_TOUCHING_X, center_side);
13585
13586     // (center element cannot be player, so we dont have to check this here)
13587   }
13588
13589   for (i = 0; i < NUM_DIRECTIONS; i++)
13590   {
13591     int xx = x + xy[i].x;
13592     int yy = y + xy[i].y;
13593     int border_side = trigger_sides[i][1];
13594     int border_element = border_element_old[i];
13595
13596     if (border_element == -1)
13597       continue;
13598
13599     // check for change of center element (but change it only once)
13600     if (!change_center_element)
13601       change_center_element =
13602         CheckElementChangeBySide(x, y, center_element, border_element,
13603                                  CE_TOUCHING_X, border_side);
13604
13605     if (IS_PLAYER(xx, yy))
13606     {
13607       /* use player element that is initially defined in the level playfield,
13608          not the player element that corresponds to the runtime player number
13609          (example: a level that contains EL_PLAYER_3 as the only player would
13610          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13611       int player_element = PLAYERINFO(xx, yy)->initial_element;
13612
13613       CheckElementChangeBySide(x, y, center_element, player_element,
13614                                CE_TOUCHING_X, border_side);
13615     }
13616   }
13617 }
13618
13619 void TestIfElementHitsCustomElement(int x, int y, int direction)
13620 {
13621   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13622   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13623   int hitx = x + dx, hity = y + dy;
13624   int hitting_element = Tile[x][y];
13625   int touched_element;
13626
13627   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13628     return;
13629
13630   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13631                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13632
13633   if (IN_LEV_FIELD(hitx, hity))
13634   {
13635     int opposite_direction = MV_DIR_OPPOSITE(direction);
13636     int hitting_side = direction;
13637     int touched_side = opposite_direction;
13638     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13639                           MovDir[hitx][hity] != direction ||
13640                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13641
13642     object_hit = TRUE;
13643
13644     if (object_hit)
13645     {
13646       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13647                                CE_HITTING_X, touched_side);
13648
13649       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13650                                CE_HIT_BY_X, hitting_side);
13651
13652       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13653                                CE_HIT_BY_SOMETHING, opposite_direction);
13654
13655       if (IS_PLAYER(hitx, hity))
13656       {
13657         /* use player element that is initially defined in the level playfield,
13658            not the player element that corresponds to the runtime player number
13659            (example: a level that contains EL_PLAYER_3 as the only player would
13660            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13661         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13662
13663         CheckElementChangeBySide(x, y, hitting_element, player_element,
13664                                  CE_HITTING_X, touched_side);
13665       }
13666     }
13667   }
13668
13669   // "hitting something" is also true when hitting the playfield border
13670   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13671                            CE_HITTING_SOMETHING, direction);
13672 }
13673
13674 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13675 {
13676   int i, kill_x = -1, kill_y = -1;
13677
13678   int bad_element = -1;
13679   struct XY *test_xy = xy_topdown;
13680   static int test_dir[4] =
13681   {
13682     MV_UP,
13683     MV_LEFT,
13684     MV_RIGHT,
13685     MV_DOWN
13686   };
13687
13688   for (i = 0; i < NUM_DIRECTIONS; i++)
13689   {
13690     int test_x, test_y, test_move_dir, test_element;
13691
13692     test_x = good_x + test_xy[i].x;
13693     test_y = good_y + test_xy[i].y;
13694
13695     if (!IN_LEV_FIELD(test_x, test_y))
13696       continue;
13697
13698     test_move_dir =
13699       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13700
13701     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13702
13703     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13704        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13705     */
13706     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13707         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13708     {
13709       kill_x = test_x;
13710       kill_y = test_y;
13711       bad_element = test_element;
13712
13713       break;
13714     }
13715   }
13716
13717   if (kill_x != -1 || kill_y != -1)
13718   {
13719     if (IS_PLAYER(good_x, good_y))
13720     {
13721       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13722
13723       if (player->shield_deadly_time_left > 0 &&
13724           !IS_INDESTRUCTIBLE(bad_element))
13725         Bang(kill_x, kill_y);
13726       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13727         KillPlayer(player);
13728     }
13729     else
13730       Bang(good_x, good_y);
13731   }
13732 }
13733
13734 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13735 {
13736   int i, kill_x = -1, kill_y = -1;
13737   int bad_element = Tile[bad_x][bad_y];
13738   struct XY *test_xy = xy_topdown;
13739   static int touch_dir[4] =
13740   {
13741     MV_LEFT | MV_RIGHT,
13742     MV_UP   | MV_DOWN,
13743     MV_UP   | MV_DOWN,
13744     MV_LEFT | MV_RIGHT
13745   };
13746   static int test_dir[4] =
13747   {
13748     MV_UP,
13749     MV_LEFT,
13750     MV_RIGHT,
13751     MV_DOWN
13752   };
13753
13754   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13755     return;
13756
13757   for (i = 0; i < NUM_DIRECTIONS; i++)
13758   {
13759     int test_x, test_y, test_move_dir, test_element;
13760
13761     test_x = bad_x + test_xy[i].x;
13762     test_y = bad_y + test_xy[i].y;
13763
13764     if (!IN_LEV_FIELD(test_x, test_y))
13765       continue;
13766
13767     test_move_dir =
13768       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13769
13770     test_element = Tile[test_x][test_y];
13771
13772     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13773        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13774     */
13775     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13776         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13777     {
13778       // good thing is player or penguin that does not move away
13779       if (IS_PLAYER(test_x, test_y))
13780       {
13781         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13782
13783         if (bad_element == EL_ROBOT && player->is_moving)
13784           continue;     // robot does not kill player if he is moving
13785
13786         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13787         {
13788           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13789             continue;           // center and border element do not touch
13790         }
13791
13792         kill_x = test_x;
13793         kill_y = test_y;
13794
13795         break;
13796       }
13797       else if (test_element == EL_PENGUIN)
13798       {
13799         kill_x = test_x;
13800         kill_y = test_y;
13801
13802         break;
13803       }
13804     }
13805   }
13806
13807   if (kill_x != -1 || kill_y != -1)
13808   {
13809     if (IS_PLAYER(kill_x, kill_y))
13810     {
13811       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13812
13813       if (player->shield_deadly_time_left > 0 &&
13814           !IS_INDESTRUCTIBLE(bad_element))
13815         Bang(bad_x, bad_y);
13816       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13817         KillPlayer(player);
13818     }
13819     else
13820       Bang(kill_x, kill_y);
13821   }
13822 }
13823
13824 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13825 {
13826   int bad_element = Tile[bad_x][bad_y];
13827   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13828   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13829   int test_x = bad_x + dx, test_y = bad_y + dy;
13830   int test_move_dir, test_element;
13831   int kill_x = -1, kill_y = -1;
13832
13833   if (!IN_LEV_FIELD(test_x, test_y))
13834     return;
13835
13836   test_move_dir =
13837     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13838
13839   test_element = Tile[test_x][test_y];
13840
13841   if (test_move_dir != bad_move_dir)
13842   {
13843     // good thing can be player or penguin that does not move away
13844     if (IS_PLAYER(test_x, test_y))
13845     {
13846       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13847
13848       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13849          player as being hit when he is moving towards the bad thing, because
13850          the "get hit by" condition would be lost after the player stops) */
13851       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13852         return;         // player moves away from bad thing
13853
13854       kill_x = test_x;
13855       kill_y = test_y;
13856     }
13857     else if (test_element == EL_PENGUIN)
13858     {
13859       kill_x = test_x;
13860       kill_y = test_y;
13861     }
13862   }
13863
13864   if (kill_x != -1 || kill_y != -1)
13865   {
13866     if (IS_PLAYER(kill_x, kill_y))
13867     {
13868       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13869
13870       if (player->shield_deadly_time_left > 0 &&
13871           !IS_INDESTRUCTIBLE(bad_element))
13872         Bang(bad_x, bad_y);
13873       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13874         KillPlayer(player);
13875     }
13876     else
13877       Bang(kill_x, kill_y);
13878   }
13879 }
13880
13881 void TestIfPlayerTouchesBadThing(int x, int y)
13882 {
13883   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13884 }
13885
13886 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13887 {
13888   TestIfGoodThingHitsBadThing(x, y, move_dir);
13889 }
13890
13891 void TestIfBadThingTouchesPlayer(int x, int y)
13892 {
13893   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13894 }
13895
13896 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13897 {
13898   TestIfBadThingHitsGoodThing(x, y, move_dir);
13899 }
13900
13901 void TestIfFriendTouchesBadThing(int x, int y)
13902 {
13903   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13904 }
13905
13906 void TestIfBadThingTouchesFriend(int x, int y)
13907 {
13908   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13909 }
13910
13911 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13912 {
13913   int i, kill_x = bad_x, kill_y = bad_y;
13914   struct XY *xy = xy_topdown;
13915
13916   for (i = 0; i < NUM_DIRECTIONS; i++)
13917   {
13918     int x, y, element;
13919
13920     x = bad_x + xy[i].x;
13921     y = bad_y + xy[i].y;
13922     if (!IN_LEV_FIELD(x, y))
13923       continue;
13924
13925     element = Tile[x][y];
13926     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13927         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13928     {
13929       kill_x = x;
13930       kill_y = y;
13931       break;
13932     }
13933   }
13934
13935   if (kill_x != bad_x || kill_y != bad_y)
13936     Bang(bad_x, bad_y);
13937 }
13938
13939 void KillPlayer(struct PlayerInfo *player)
13940 {
13941   int jx = player->jx, jy = player->jy;
13942
13943   if (!player->active)
13944     return;
13945
13946 #if 0
13947   Debug("game:playing:KillPlayer",
13948         "0: killed == %d, active == %d, reanimated == %d",
13949         player->killed, player->active, player->reanimated);
13950 #endif
13951
13952   /* the following code was introduced to prevent an infinite loop when calling
13953      -> Bang()
13954      -> CheckTriggeredElementChangeExt()
13955      -> ExecuteCustomElementAction()
13956      -> KillPlayer()
13957      -> (infinitely repeating the above sequence of function calls)
13958      which occurs when killing the player while having a CE with the setting
13959      "kill player X when explosion of <player X>"; the solution using a new
13960      field "player->killed" was chosen for backwards compatibility, although
13961      clever use of the fields "player->active" etc. would probably also work */
13962 #if 1
13963   if (player->killed)
13964     return;
13965 #endif
13966
13967   player->killed = TRUE;
13968
13969   // remove accessible field at the player's position
13970   Tile[jx][jy] = EL_EMPTY;
13971
13972   // deactivate shield (else Bang()/Explode() would not work right)
13973   player->shield_normal_time_left = 0;
13974   player->shield_deadly_time_left = 0;
13975
13976 #if 0
13977   Debug("game:playing:KillPlayer",
13978         "1: killed == %d, active == %d, reanimated == %d",
13979         player->killed, player->active, player->reanimated);
13980 #endif
13981
13982   Bang(jx, jy);
13983
13984 #if 0
13985   Debug("game:playing:KillPlayer",
13986         "2: killed == %d, active == %d, reanimated == %d",
13987         player->killed, player->active, player->reanimated);
13988 #endif
13989
13990   if (player->reanimated)       // killed player may have been reanimated
13991     player->killed = player->reanimated = FALSE;
13992   else
13993     BuryPlayer(player);
13994 }
13995
13996 static void KillPlayerUnlessEnemyProtected(int x, int y)
13997 {
13998   if (!PLAYER_ENEMY_PROTECTED(x, y))
13999     KillPlayer(PLAYERINFO(x, y));
14000 }
14001
14002 static void KillPlayerUnlessExplosionProtected(int x, int y)
14003 {
14004   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14005     KillPlayer(PLAYERINFO(x, y));
14006 }
14007
14008 void BuryPlayer(struct PlayerInfo *player)
14009 {
14010   int jx = player->jx, jy = player->jy;
14011
14012   if (!player->active)
14013     return;
14014
14015   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14016   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14017
14018   RemovePlayer(player);
14019
14020   player->buried = TRUE;
14021
14022   if (game.all_players_gone)
14023     game.GameOver = TRUE;
14024 }
14025
14026 void RemovePlayer(struct PlayerInfo *player)
14027 {
14028   int jx = player->jx, jy = player->jy;
14029   int i, found = FALSE;
14030
14031   player->present = FALSE;
14032   player->active = FALSE;
14033
14034   // required for some CE actions (even if the player is not active anymore)
14035   player->MovPos = 0;
14036
14037   if (!ExplodeField[jx][jy])
14038     StorePlayer[jx][jy] = 0;
14039
14040   if (player->is_moving)
14041     TEST_DrawLevelField(player->last_jx, player->last_jy);
14042
14043   for (i = 0; i < MAX_PLAYERS; i++)
14044     if (stored_player[i].active)
14045       found = TRUE;
14046
14047   if (!found)
14048   {
14049     game.all_players_gone = TRUE;
14050     game.GameOver = TRUE;
14051   }
14052
14053   game.exit_x = game.robot_wheel_x = jx;
14054   game.exit_y = game.robot_wheel_y = jy;
14055 }
14056
14057 void ExitPlayer(struct PlayerInfo *player)
14058 {
14059   DrawPlayer(player);   // needed here only to cleanup last field
14060   RemovePlayer(player);
14061
14062   if (game.players_still_needed > 0)
14063     game.players_still_needed--;
14064 }
14065
14066 static void SetFieldForSnapping(int x, int y, int element, int direction,
14067                                 int player_index_bit)
14068 {
14069   struct ElementInfo *ei = &element_info[element];
14070   int direction_bit = MV_DIR_TO_BIT(direction);
14071   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14072   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14073                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14074
14075   Tile[x][y] = EL_ELEMENT_SNAPPING;
14076   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14077   MovDir[x][y] = direction;
14078   Store[x][y] = element;
14079   Store2[x][y] = player_index_bit;
14080
14081   ResetGfxAnimation(x, y);
14082
14083   GfxElement[x][y] = element;
14084   GfxAction[x][y] = action;
14085   GfxDir[x][y] = direction;
14086   GfxFrame[x][y] = -1;
14087 }
14088
14089 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14090                                    int player_index_bit)
14091 {
14092   TestIfElementTouchesCustomElement(x, y);      // for empty space
14093
14094   if (level.finish_dig_collect)
14095   {
14096     int dig_side = MV_DIR_OPPOSITE(direction);
14097     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14098                         CE_PLAYER_COLLECTS_X);
14099
14100     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14101                                         player_index_bit, dig_side);
14102     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14103                                         player_index_bit, dig_side);
14104   }
14105 }
14106
14107 /*
14108   =============================================================================
14109   checkDiagonalPushing()
14110   -----------------------------------------------------------------------------
14111   check if diagonal input device direction results in pushing of object
14112   (by checking if the alternative direction is walkable, diggable, ...)
14113   =============================================================================
14114 */
14115
14116 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14117                                     int x, int y, int real_dx, int real_dy)
14118 {
14119   int jx, jy, dx, dy, xx, yy;
14120
14121   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14122     return TRUE;
14123
14124   // diagonal direction: check alternative direction
14125   jx = player->jx;
14126   jy = player->jy;
14127   dx = x - jx;
14128   dy = y - jy;
14129   xx = jx + (dx == 0 ? real_dx : 0);
14130   yy = jy + (dy == 0 ? real_dy : 0);
14131
14132   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14133 }
14134
14135 /*
14136   =============================================================================
14137   DigField()
14138   -----------------------------------------------------------------------------
14139   x, y:                 field next to player (non-diagonal) to try to dig to
14140   real_dx, real_dy:     direction as read from input device (can be diagonal)
14141   =============================================================================
14142 */
14143
14144 static int DigField(struct PlayerInfo *player,
14145                     int oldx, int oldy, int x, int y,
14146                     int real_dx, int real_dy, int mode)
14147 {
14148   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14149   boolean player_was_pushing = player->is_pushing;
14150   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14151   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14152   int jx = oldx, jy = oldy;
14153   int dx = x - jx, dy = y - jy;
14154   int nextx = x + dx, nexty = y + dy;
14155   int move_direction = (dx == -1 ? MV_LEFT  :
14156                         dx == +1 ? MV_RIGHT :
14157                         dy == -1 ? MV_UP    :
14158                         dy == +1 ? MV_DOWN  : MV_NONE);
14159   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14160   int dig_side = MV_DIR_OPPOSITE(move_direction);
14161   int old_element = Tile[jx][jy];
14162   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14163   int collect_count;
14164
14165   if (is_player)                // function can also be called by EL_PENGUIN
14166   {
14167     if (player->MovPos == 0)
14168     {
14169       player->is_digging = FALSE;
14170       player->is_collecting = FALSE;
14171     }
14172
14173     if (player->MovPos == 0)    // last pushing move finished
14174       player->is_pushing = FALSE;
14175
14176     if (mode == DF_NO_PUSH)     // player just stopped pushing
14177     {
14178       player->is_switching = FALSE;
14179       player->push_delay = -1;
14180
14181       return MP_NO_ACTION;
14182     }
14183   }
14184   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14185     old_element = Back[jx][jy];
14186
14187   // in case of element dropped at player position, check background
14188   else if (Back[jx][jy] != EL_EMPTY &&
14189            game.engine_version >= VERSION_IDENT(2,2,0,0))
14190     old_element = Back[jx][jy];
14191
14192   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14193     return MP_NO_ACTION;        // field has no opening in this direction
14194
14195   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14196     return MP_NO_ACTION;        // field has no opening in this direction
14197
14198   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14199   {
14200     SplashAcid(x, y);
14201
14202     Tile[jx][jy] = player->artwork_element;
14203     InitMovingField(jx, jy, MV_DOWN);
14204     Store[jx][jy] = EL_ACID;
14205     ContinueMoving(jx, jy);
14206     BuryPlayer(player);
14207
14208     return MP_DONT_RUN_INTO;
14209   }
14210
14211   if (player_can_move && DONT_RUN_INTO(element))
14212   {
14213     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14214
14215     return MP_DONT_RUN_INTO;
14216   }
14217
14218   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14219     return MP_NO_ACTION;
14220
14221   collect_count = element_info[element].collect_count_initial;
14222
14223   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14224     return MP_NO_ACTION;
14225
14226   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14227     player_can_move = player_can_move_or_snap;
14228
14229   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14230       game.engine_version >= VERSION_IDENT(2,2,0,0))
14231   {
14232     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14233                                player->index_bit, dig_side);
14234     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14235                                         player->index_bit, dig_side);
14236
14237     if (element == EL_DC_LANDMINE)
14238       Bang(x, y);
14239
14240     if (Tile[x][y] != element)          // field changed by snapping
14241       return MP_ACTION;
14242
14243     return MP_NO_ACTION;
14244   }
14245
14246   if (player->gravity && is_player && !player->is_auto_moving &&
14247       canFallDown(player) && move_direction != MV_DOWN &&
14248       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14249     return MP_NO_ACTION;        // player cannot walk here due to gravity
14250
14251   if (player_can_move &&
14252       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14253   {
14254     int sound_element = SND_ELEMENT(element);
14255     int sound_action = ACTION_WALKING;
14256
14257     if (IS_RND_GATE(element))
14258     {
14259       if (!player->key[RND_GATE_NR(element)])
14260         return MP_NO_ACTION;
14261     }
14262     else if (IS_RND_GATE_GRAY(element))
14263     {
14264       if (!player->key[RND_GATE_GRAY_NR(element)])
14265         return MP_NO_ACTION;
14266     }
14267     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14268     {
14269       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14270         return MP_NO_ACTION;
14271     }
14272     else if (element == EL_EXIT_OPEN ||
14273              element == EL_EM_EXIT_OPEN ||
14274              element == EL_EM_EXIT_OPENING ||
14275              element == EL_STEEL_EXIT_OPEN ||
14276              element == EL_EM_STEEL_EXIT_OPEN ||
14277              element == EL_EM_STEEL_EXIT_OPENING ||
14278              element == EL_SP_EXIT_OPEN ||
14279              element == EL_SP_EXIT_OPENING)
14280     {
14281       sound_action = ACTION_PASSING;    // player is passing exit
14282     }
14283     else if (element == EL_EMPTY)
14284     {
14285       sound_action = ACTION_MOVING;             // nothing to walk on
14286     }
14287
14288     // play sound from background or player, whatever is available
14289     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14290       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14291     else
14292       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14293   }
14294   else if (player_can_move &&
14295            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14296   {
14297     if (!ACCESS_FROM(element, opposite_direction))
14298       return MP_NO_ACTION;      // field not accessible from this direction
14299
14300     if (CAN_MOVE(element))      // only fixed elements can be passed!
14301       return MP_NO_ACTION;
14302
14303     if (IS_EM_GATE(element))
14304     {
14305       if (!player->key[EM_GATE_NR(element)])
14306         return MP_NO_ACTION;
14307     }
14308     else if (IS_EM_GATE_GRAY(element))
14309     {
14310       if (!player->key[EM_GATE_GRAY_NR(element)])
14311         return MP_NO_ACTION;
14312     }
14313     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14314     {
14315       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14316         return MP_NO_ACTION;
14317     }
14318     else if (IS_EMC_GATE(element))
14319     {
14320       if (!player->key[EMC_GATE_NR(element)])
14321         return MP_NO_ACTION;
14322     }
14323     else if (IS_EMC_GATE_GRAY(element))
14324     {
14325       if (!player->key[EMC_GATE_GRAY_NR(element)])
14326         return MP_NO_ACTION;
14327     }
14328     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14329     {
14330       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14331         return MP_NO_ACTION;
14332     }
14333     else if (element == EL_DC_GATE_WHITE ||
14334              element == EL_DC_GATE_WHITE_GRAY ||
14335              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14336     {
14337       if (player->num_white_keys == 0)
14338         return MP_NO_ACTION;
14339
14340       player->num_white_keys--;
14341     }
14342     else if (IS_SP_PORT(element))
14343     {
14344       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14345           element == EL_SP_GRAVITY_PORT_RIGHT ||
14346           element == EL_SP_GRAVITY_PORT_UP ||
14347           element == EL_SP_GRAVITY_PORT_DOWN)
14348         player->gravity = !player->gravity;
14349       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14350                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14351                element == EL_SP_GRAVITY_ON_PORT_UP ||
14352                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14353         player->gravity = TRUE;
14354       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14355                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14356                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14357                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14358         player->gravity = FALSE;
14359     }
14360
14361     // automatically move to the next field with double speed
14362     player->programmed_action = move_direction;
14363
14364     if (player->move_delay_reset_counter == 0)
14365     {
14366       player->move_delay_reset_counter = 2;     // two double speed steps
14367
14368       DOUBLE_PLAYER_SPEED(player);
14369     }
14370
14371     PlayLevelSoundAction(x, y, ACTION_PASSING);
14372   }
14373   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14374   {
14375     RemoveField(x, y);
14376
14377     if (mode != DF_SNAP)
14378     {
14379       GfxElement[x][y] = GFX_ELEMENT(element);
14380       player->is_digging = TRUE;
14381     }
14382
14383     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14384
14385     // use old behaviour for old levels (digging)
14386     if (!level.finish_dig_collect)
14387     {
14388       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14389                                           player->index_bit, dig_side);
14390
14391       // if digging triggered player relocation, finish digging tile
14392       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14393         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14394     }
14395
14396     if (mode == DF_SNAP)
14397     {
14398       if (level.block_snap_field)
14399         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14400       else
14401         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14402
14403       // use old behaviour for old levels (snapping)
14404       if (!level.finish_dig_collect)
14405         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14406                                             player->index_bit, dig_side);
14407     }
14408   }
14409   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14410   {
14411     RemoveField(x, y);
14412
14413     if (is_player && mode != DF_SNAP)
14414     {
14415       GfxElement[x][y] = element;
14416       player->is_collecting = TRUE;
14417     }
14418
14419     if (element == EL_SPEED_PILL)
14420     {
14421       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14422     }
14423     else if (element == EL_EXTRA_TIME && level.time > 0)
14424     {
14425       TimeLeft += level.extra_time;
14426
14427       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14428
14429       DisplayGameControlValues();
14430     }
14431     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14432     {
14433       int shield_time = (element == EL_SHIELD_DEADLY ?
14434                          level.shield_deadly_time :
14435                          level.shield_normal_time);
14436
14437       player->shield_normal_time_left += shield_time;
14438       if (element == EL_SHIELD_DEADLY)
14439         player->shield_deadly_time_left += shield_time;
14440     }
14441     else if (element == EL_DYNAMITE ||
14442              element == EL_EM_DYNAMITE ||
14443              element == EL_SP_DISK_RED)
14444     {
14445       if (player->inventory_size < MAX_INVENTORY_SIZE)
14446         player->inventory_element[player->inventory_size++] = element;
14447
14448       DrawGameDoorValues();
14449     }
14450     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14451     {
14452       player->dynabomb_count++;
14453       player->dynabombs_left++;
14454     }
14455     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14456     {
14457       player->dynabomb_size++;
14458     }
14459     else if (element == EL_DYNABOMB_INCREASE_POWER)
14460     {
14461       player->dynabomb_xl = TRUE;
14462     }
14463     else if (IS_KEY(element))
14464     {
14465       player->key[KEY_NR(element)] = TRUE;
14466
14467       DrawGameDoorValues();
14468     }
14469     else if (element == EL_DC_KEY_WHITE)
14470     {
14471       player->num_white_keys++;
14472
14473       // display white keys?
14474       // DrawGameDoorValues();
14475     }
14476     else if (IS_ENVELOPE(element))
14477     {
14478       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14479
14480       if (!wait_for_snapping)
14481         player->show_envelope = element;
14482     }
14483     else if (element == EL_EMC_LENSES)
14484     {
14485       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14486
14487       RedrawAllInvisibleElementsForLenses();
14488     }
14489     else if (element == EL_EMC_MAGNIFIER)
14490     {
14491       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14492
14493       RedrawAllInvisibleElementsForMagnifier();
14494     }
14495     else if (IS_DROPPABLE(element) ||
14496              IS_THROWABLE(element))     // can be collected and dropped
14497     {
14498       int i;
14499
14500       if (collect_count == 0)
14501         player->inventory_infinite_element = element;
14502       else
14503         for (i = 0; i < collect_count; i++)
14504           if (player->inventory_size < MAX_INVENTORY_SIZE)
14505             player->inventory_element[player->inventory_size++] = element;
14506
14507       DrawGameDoorValues();
14508     }
14509     else if (collect_count > 0)
14510     {
14511       game.gems_still_needed -= collect_count;
14512       if (game.gems_still_needed < 0)
14513         game.gems_still_needed = 0;
14514
14515       game.snapshot.collected_item = TRUE;
14516
14517       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14518
14519       DisplayGameControlValues();
14520     }
14521
14522     RaiseScoreElement(element);
14523     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14524
14525     // use old behaviour for old levels (collecting)
14526     if (!level.finish_dig_collect && is_player)
14527     {
14528       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14529                                           player->index_bit, dig_side);
14530
14531       // if collecting triggered player relocation, finish collecting tile
14532       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14533         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14534     }
14535
14536     if (mode == DF_SNAP)
14537     {
14538       if (level.block_snap_field)
14539         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14540       else
14541         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14542
14543       // use old behaviour for old levels (snapping)
14544       if (!level.finish_dig_collect)
14545         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14546                                             player->index_bit, dig_side);
14547     }
14548   }
14549   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14550   {
14551     if (mode == DF_SNAP && element != EL_BD_ROCK)
14552       return MP_NO_ACTION;
14553
14554     if (CAN_FALL(element) && dy)
14555       return MP_NO_ACTION;
14556
14557     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14558         !(element == EL_SPRING && level.use_spring_bug))
14559       return MP_NO_ACTION;
14560
14561     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14562         ((move_direction & MV_VERTICAL &&
14563           ((element_info[element].move_pattern & MV_LEFT &&
14564             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14565            (element_info[element].move_pattern & MV_RIGHT &&
14566             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14567          (move_direction & MV_HORIZONTAL &&
14568           ((element_info[element].move_pattern & MV_UP &&
14569             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14570            (element_info[element].move_pattern & MV_DOWN &&
14571             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14572       return MP_NO_ACTION;
14573
14574     // do not push elements already moving away faster than player
14575     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14576         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14577       return MP_NO_ACTION;
14578
14579     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14580     {
14581       if (player->push_delay_value == -1 || !player_was_pushing)
14582         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14583     }
14584     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14585     {
14586       if (player->push_delay_value == -1)
14587         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14588     }
14589     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14590     {
14591       if (!player->is_pushing)
14592         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14593     }
14594
14595     player->is_pushing = TRUE;
14596     player->is_active = TRUE;
14597
14598     if (!(IN_LEV_FIELD(nextx, nexty) &&
14599           (IS_FREE(nextx, nexty) ||
14600            (IS_SB_ELEMENT(element) &&
14601             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14602            (IS_CUSTOM_ELEMENT(element) &&
14603             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14604       return MP_NO_ACTION;
14605
14606     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14607       return MP_NO_ACTION;
14608
14609     if (player->push_delay == -1)       // new pushing; restart delay
14610       player->push_delay = 0;
14611
14612     if (player->push_delay < player->push_delay_value &&
14613         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14614         element != EL_SPRING && element != EL_BALLOON)
14615     {
14616       // make sure that there is no move delay before next try to push
14617       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14618         player->move_delay = 0;
14619
14620       return MP_NO_ACTION;
14621     }
14622
14623     if (IS_CUSTOM_ELEMENT(element) &&
14624         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14625     {
14626       if (!DigFieldByCE(nextx, nexty, element))
14627         return MP_NO_ACTION;
14628     }
14629
14630     if (IS_SB_ELEMENT(element))
14631     {
14632       boolean sokoban_task_solved = FALSE;
14633
14634       if (element == EL_SOKOBAN_FIELD_FULL)
14635       {
14636         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14637
14638         IncrementSokobanFieldsNeeded();
14639         IncrementSokobanObjectsNeeded();
14640       }
14641
14642       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14643       {
14644         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14645
14646         DecrementSokobanFieldsNeeded();
14647         DecrementSokobanObjectsNeeded();
14648
14649         // sokoban object was pushed from empty field to sokoban field
14650         if (Back[x][y] == EL_EMPTY)
14651           sokoban_task_solved = TRUE;
14652       }
14653
14654       Tile[x][y] = EL_SOKOBAN_OBJECT;
14655
14656       if (Back[x][y] == Back[nextx][nexty])
14657         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14658       else if (Back[x][y] != 0)
14659         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14660                                     ACTION_EMPTYING);
14661       else
14662         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14663                                     ACTION_FILLING);
14664
14665       if (sokoban_task_solved &&
14666           game.sokoban_fields_still_needed == 0 &&
14667           game.sokoban_objects_still_needed == 0 &&
14668           level.auto_exit_sokoban)
14669       {
14670         game.players_still_needed = 0;
14671
14672         LevelSolved();
14673
14674         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14675       }
14676     }
14677     else
14678       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14679
14680     InitMovingField(x, y, move_direction);
14681     GfxAction[x][y] = ACTION_PUSHING;
14682
14683     if (mode == DF_SNAP)
14684       ContinueMoving(x, y);
14685     else
14686       MovPos[x][y] = (dx != 0 ? dx : dy);
14687
14688     Pushed[x][y] = TRUE;
14689     Pushed[nextx][nexty] = TRUE;
14690
14691     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14692       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14693     else
14694       player->push_delay_value = -1;    // get new value later
14695
14696     // check for element change _after_ element has been pushed
14697     if (game.use_change_when_pushing_bug)
14698     {
14699       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14700                                  player->index_bit, dig_side);
14701       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14702                                           player->index_bit, dig_side);
14703     }
14704   }
14705   else if (IS_SWITCHABLE(element))
14706   {
14707     if (PLAYER_SWITCHING(player, x, y))
14708     {
14709       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14710                                           player->index_bit, dig_side);
14711
14712       return MP_ACTION;
14713     }
14714
14715     player->is_switching = TRUE;
14716     player->switch_x = x;
14717     player->switch_y = y;
14718
14719     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14720
14721     if (element == EL_ROBOT_WHEEL)
14722     {
14723       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14724
14725       game.robot_wheel_x = x;
14726       game.robot_wheel_y = y;
14727       game.robot_wheel_active = TRUE;
14728
14729       TEST_DrawLevelField(x, y);
14730     }
14731     else if (element == EL_SP_TERMINAL)
14732     {
14733       int xx, yy;
14734
14735       SCAN_PLAYFIELD(xx, yy)
14736       {
14737         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14738         {
14739           Bang(xx, yy);
14740         }
14741         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14742         {
14743           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14744
14745           ResetGfxAnimation(xx, yy);
14746           TEST_DrawLevelField(xx, yy);
14747         }
14748       }
14749     }
14750     else if (IS_BELT_SWITCH(element))
14751     {
14752       ToggleBeltSwitch(x, y);
14753     }
14754     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14755              element == EL_SWITCHGATE_SWITCH_DOWN ||
14756              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14757              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14758     {
14759       ToggleSwitchgateSwitch(x, y);
14760     }
14761     else if (element == EL_LIGHT_SWITCH ||
14762              element == EL_LIGHT_SWITCH_ACTIVE)
14763     {
14764       ToggleLightSwitch(x, y);
14765     }
14766     else if (element == EL_TIMEGATE_SWITCH ||
14767              element == EL_DC_TIMEGATE_SWITCH)
14768     {
14769       ActivateTimegateSwitch(x, y);
14770     }
14771     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14772              element == EL_BALLOON_SWITCH_RIGHT ||
14773              element == EL_BALLOON_SWITCH_UP    ||
14774              element == EL_BALLOON_SWITCH_DOWN  ||
14775              element == EL_BALLOON_SWITCH_NONE  ||
14776              element == EL_BALLOON_SWITCH_ANY)
14777     {
14778       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14779                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14780                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14781                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14782                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14783                              move_direction);
14784     }
14785     else if (element == EL_LAMP)
14786     {
14787       Tile[x][y] = EL_LAMP_ACTIVE;
14788       game.lights_still_needed--;
14789
14790       ResetGfxAnimation(x, y);
14791       TEST_DrawLevelField(x, y);
14792     }
14793     else if (element == EL_TIME_ORB_FULL)
14794     {
14795       Tile[x][y] = EL_TIME_ORB_EMPTY;
14796
14797       if (level.time > 0 || level.use_time_orb_bug)
14798       {
14799         TimeLeft += level.time_orb_time;
14800         game.no_level_time_limit = FALSE;
14801
14802         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14803
14804         DisplayGameControlValues();
14805       }
14806
14807       ResetGfxAnimation(x, y);
14808       TEST_DrawLevelField(x, y);
14809     }
14810     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14811              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14812     {
14813       int xx, yy;
14814
14815       game.ball_active = !game.ball_active;
14816
14817       SCAN_PLAYFIELD(xx, yy)
14818       {
14819         int e = Tile[xx][yy];
14820
14821         if (game.ball_active)
14822         {
14823           if (e == EL_EMC_MAGIC_BALL)
14824             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14825           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14826             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14827         }
14828         else
14829         {
14830           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14831             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14832           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14833             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14834         }
14835       }
14836     }
14837
14838     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14839                                         player->index_bit, dig_side);
14840
14841     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14842                                         player->index_bit, dig_side);
14843
14844     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14845                                         player->index_bit, dig_side);
14846
14847     return MP_ACTION;
14848   }
14849   else
14850   {
14851     if (!PLAYER_SWITCHING(player, x, y))
14852     {
14853       player->is_switching = TRUE;
14854       player->switch_x = x;
14855       player->switch_y = y;
14856
14857       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14858                                  player->index_bit, dig_side);
14859       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14860                                           player->index_bit, dig_side);
14861
14862       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14863                                  player->index_bit, dig_side);
14864       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14865                                           player->index_bit, dig_side);
14866     }
14867
14868     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14869                                player->index_bit, dig_side);
14870     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14871                                         player->index_bit, dig_side);
14872
14873     return MP_NO_ACTION;
14874   }
14875
14876   player->push_delay = -1;
14877
14878   if (is_player)                // function can also be called by EL_PENGUIN
14879   {
14880     if (Tile[x][y] != element)          // really digged/collected something
14881     {
14882       player->is_collecting = !player->is_digging;
14883       player->is_active = TRUE;
14884
14885       player->last_removed_element = element;
14886     }
14887   }
14888
14889   return MP_MOVING;
14890 }
14891
14892 static boolean DigFieldByCE(int x, int y, int digging_element)
14893 {
14894   int element = Tile[x][y];
14895
14896   if (!IS_FREE(x, y))
14897   {
14898     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14899                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14900                   ACTION_BREAKING);
14901
14902     // no element can dig solid indestructible elements
14903     if (IS_INDESTRUCTIBLE(element) &&
14904         !IS_DIGGABLE(element) &&
14905         !IS_COLLECTIBLE(element))
14906       return FALSE;
14907
14908     if (AmoebaNr[x][y] &&
14909         (element == EL_AMOEBA_FULL ||
14910          element == EL_BD_AMOEBA ||
14911          element == EL_AMOEBA_GROWING))
14912     {
14913       AmoebaCnt[AmoebaNr[x][y]]--;
14914       AmoebaCnt2[AmoebaNr[x][y]]--;
14915     }
14916
14917     if (IS_MOVING(x, y))
14918       RemoveMovingField(x, y);
14919     else
14920     {
14921       RemoveField(x, y);
14922       TEST_DrawLevelField(x, y);
14923     }
14924
14925     // if digged element was about to explode, prevent the explosion
14926     ExplodeField[x][y] = EX_TYPE_NONE;
14927
14928     PlayLevelSoundAction(x, y, action);
14929   }
14930
14931   Store[x][y] = EL_EMPTY;
14932
14933   // this makes it possible to leave the removed element again
14934   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14935     Store[x][y] = element;
14936
14937   return TRUE;
14938 }
14939
14940 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14941 {
14942   int jx = player->jx, jy = player->jy;
14943   int x = jx + dx, y = jy + dy;
14944   int snap_direction = (dx == -1 ? MV_LEFT  :
14945                         dx == +1 ? MV_RIGHT :
14946                         dy == -1 ? MV_UP    :
14947                         dy == +1 ? MV_DOWN  : MV_NONE);
14948   boolean can_continue_snapping = (level.continuous_snapping &&
14949                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14950
14951   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14952     return FALSE;
14953
14954   if (!player->active || !IN_LEV_FIELD(x, y))
14955     return FALSE;
14956
14957   if (dx && dy)
14958     return FALSE;
14959
14960   if (!dx && !dy)
14961   {
14962     if (player->MovPos == 0)
14963       player->is_pushing = FALSE;
14964
14965     player->is_snapping = FALSE;
14966
14967     if (player->MovPos == 0)
14968     {
14969       player->is_moving = FALSE;
14970       player->is_digging = FALSE;
14971       player->is_collecting = FALSE;
14972     }
14973
14974     return FALSE;
14975   }
14976
14977   // prevent snapping with already pressed snap key when not allowed
14978   if (player->is_snapping && !can_continue_snapping)
14979     return FALSE;
14980
14981   player->MovDir = snap_direction;
14982
14983   if (player->MovPos == 0)
14984   {
14985     player->is_moving = FALSE;
14986     player->is_digging = FALSE;
14987     player->is_collecting = FALSE;
14988   }
14989
14990   player->is_dropping = FALSE;
14991   player->is_dropping_pressed = FALSE;
14992   player->drop_pressed_delay = 0;
14993
14994   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14995     return FALSE;
14996
14997   player->is_snapping = TRUE;
14998   player->is_active = TRUE;
14999
15000   if (player->MovPos == 0)
15001   {
15002     player->is_moving = FALSE;
15003     player->is_digging = FALSE;
15004     player->is_collecting = FALSE;
15005   }
15006
15007   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15008     TEST_DrawLevelField(player->last_jx, player->last_jy);
15009
15010   TEST_DrawLevelField(x, y);
15011
15012   return TRUE;
15013 }
15014
15015 static boolean DropElement(struct PlayerInfo *player)
15016 {
15017   int old_element, new_element;
15018   int dropx = player->jx, dropy = player->jy;
15019   int drop_direction = player->MovDir;
15020   int drop_side = drop_direction;
15021   int drop_element = get_next_dropped_element(player);
15022
15023   /* do not drop an element on top of another element; when holding drop key
15024      pressed without moving, dropped element must move away before the next
15025      element can be dropped (this is especially important if the next element
15026      is dynamite, which can be placed on background for historical reasons) */
15027   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15028     return MP_ACTION;
15029
15030   if (IS_THROWABLE(drop_element))
15031   {
15032     dropx += GET_DX_FROM_DIR(drop_direction);
15033     dropy += GET_DY_FROM_DIR(drop_direction);
15034
15035     if (!IN_LEV_FIELD(dropx, dropy))
15036       return FALSE;
15037   }
15038
15039   old_element = Tile[dropx][dropy];     // old element at dropping position
15040   new_element = drop_element;           // default: no change when dropping
15041
15042   // check if player is active, not moving and ready to drop
15043   if (!player->active || player->MovPos || player->drop_delay > 0)
15044     return FALSE;
15045
15046   // check if player has anything that can be dropped
15047   if (new_element == EL_UNDEFINED)
15048     return FALSE;
15049
15050   // only set if player has anything that can be dropped
15051   player->is_dropping_pressed = TRUE;
15052
15053   // check if drop key was pressed long enough for EM style dynamite
15054   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15055     return FALSE;
15056
15057   // check if anything can be dropped at the current position
15058   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15059     return FALSE;
15060
15061   // collected custom elements can only be dropped on empty fields
15062   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15063     return FALSE;
15064
15065   if (old_element != EL_EMPTY)
15066     Back[dropx][dropy] = old_element;   // store old element on this field
15067
15068   ResetGfxAnimation(dropx, dropy);
15069   ResetRandomAnimationValue(dropx, dropy);
15070
15071   if (player->inventory_size > 0 ||
15072       player->inventory_infinite_element != EL_UNDEFINED)
15073   {
15074     if (player->inventory_size > 0)
15075     {
15076       player->inventory_size--;
15077
15078       DrawGameDoorValues();
15079
15080       if (new_element == EL_DYNAMITE)
15081         new_element = EL_DYNAMITE_ACTIVE;
15082       else if (new_element == EL_EM_DYNAMITE)
15083         new_element = EL_EM_DYNAMITE_ACTIVE;
15084       else if (new_element == EL_SP_DISK_RED)
15085         new_element = EL_SP_DISK_RED_ACTIVE;
15086     }
15087
15088     Tile[dropx][dropy] = new_element;
15089
15090     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15091       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15092                           el2img(Tile[dropx][dropy]), 0);
15093
15094     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15095
15096     // needed if previous element just changed to "empty" in the last frame
15097     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15098
15099     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15100                                player->index_bit, drop_side);
15101     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15102                                         CE_PLAYER_DROPS_X,
15103                                         player->index_bit, drop_side);
15104
15105     TestIfElementTouchesCustomElement(dropx, dropy);
15106   }
15107   else          // player is dropping a dyna bomb
15108   {
15109     player->dynabombs_left--;
15110
15111     Tile[dropx][dropy] = new_element;
15112
15113     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15114       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15115                           el2img(Tile[dropx][dropy]), 0);
15116
15117     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15118   }
15119
15120   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15121     InitField_WithBug1(dropx, dropy, FALSE);
15122
15123   new_element = Tile[dropx][dropy];     // element might have changed
15124
15125   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15126       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15127   {
15128     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15129       MovDir[dropx][dropy] = drop_direction;
15130
15131     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15132
15133     // do not cause impact style collision by dropping elements that can fall
15134     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15135   }
15136
15137   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15138   player->is_dropping = TRUE;
15139
15140   player->drop_pressed_delay = 0;
15141   player->is_dropping_pressed = FALSE;
15142
15143   player->drop_x = dropx;
15144   player->drop_y = dropy;
15145
15146   return TRUE;
15147 }
15148
15149 // ----------------------------------------------------------------------------
15150 // game sound playing functions
15151 // ----------------------------------------------------------------------------
15152
15153 static int *loop_sound_frame = NULL;
15154 static int *loop_sound_volume = NULL;
15155
15156 void InitPlayLevelSound(void)
15157 {
15158   int num_sounds = getSoundListSize();
15159
15160   checked_free(loop_sound_frame);
15161   checked_free(loop_sound_volume);
15162
15163   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15164   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15165 }
15166
15167 static void PlayLevelSound(int x, int y, int nr)
15168 {
15169   int sx = SCREENX(x), sy = SCREENY(y);
15170   int volume, stereo_position;
15171   int max_distance = 8;
15172   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15173
15174   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15175       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15176     return;
15177
15178   if (!IN_LEV_FIELD(x, y) ||
15179       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15180       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15181     return;
15182
15183   volume = SOUND_MAX_VOLUME;
15184
15185   if (!IN_SCR_FIELD(sx, sy))
15186   {
15187     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15188     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15189
15190     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15191   }
15192
15193   stereo_position = (SOUND_MAX_LEFT +
15194                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15195                      (SCR_FIELDX + 2 * max_distance));
15196
15197   if (IS_LOOP_SOUND(nr))
15198   {
15199     /* This assures that quieter loop sounds do not overwrite louder ones,
15200        while restarting sound volume comparison with each new game frame. */
15201
15202     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15203       return;
15204
15205     loop_sound_volume[nr] = volume;
15206     loop_sound_frame[nr] = FrameCounter;
15207   }
15208
15209   PlaySoundExt(nr, volume, stereo_position, type);
15210 }
15211
15212 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15213 {
15214   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15215                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15216                  y < LEVELY(BY1) ? LEVELY(BY1) :
15217                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15218                  sound_action);
15219 }
15220
15221 static void PlayLevelSoundAction(int x, int y, int action)
15222 {
15223   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15224 }
15225
15226 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15227 {
15228   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15229
15230   if (sound_effect != SND_UNDEFINED)
15231     PlayLevelSound(x, y, sound_effect);
15232 }
15233
15234 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15235                                               int action)
15236 {
15237   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15238
15239   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15240     PlayLevelSound(x, y, sound_effect);
15241 }
15242
15243 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15244 {
15245   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15246
15247   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15248     PlayLevelSound(x, y, sound_effect);
15249 }
15250
15251 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15252 {
15253   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15254
15255   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15256     StopSound(sound_effect);
15257 }
15258
15259 static int getLevelMusicNr(void)
15260 {
15261   if (levelset.music[level_nr] != MUS_UNDEFINED)
15262     return levelset.music[level_nr];            // from config file
15263   else
15264     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15265 }
15266
15267 static void FadeLevelSounds(void)
15268 {
15269   FadeSounds();
15270 }
15271
15272 static void FadeLevelMusic(void)
15273 {
15274   int music_nr = getLevelMusicNr();
15275   char *curr_music = getCurrentlyPlayingMusicFilename();
15276   char *next_music = getMusicInfoEntryFilename(music_nr);
15277
15278   if (!strEqual(curr_music, next_music))
15279     FadeMusic();
15280 }
15281
15282 void FadeLevelSoundsAndMusic(void)
15283 {
15284   FadeLevelSounds();
15285   FadeLevelMusic();
15286 }
15287
15288 static void PlayLevelMusic(void)
15289 {
15290   int music_nr = getLevelMusicNr();
15291   char *curr_music = getCurrentlyPlayingMusicFilename();
15292   char *next_music = getMusicInfoEntryFilename(music_nr);
15293
15294   if (!strEqual(curr_music, next_music))
15295     PlayMusicLoop(music_nr);
15296 }
15297
15298 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15299 {
15300   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15301   int offset = 0;
15302   int x = xx - offset;
15303   int y = yy - offset;
15304
15305   switch (sample)
15306   {
15307     case SOUND_blank:
15308       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15309       break;
15310
15311     case SOUND_roll:
15312       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15313       break;
15314
15315     case SOUND_stone:
15316       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15317       break;
15318
15319     case SOUND_nut:
15320       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15321       break;
15322
15323     case SOUND_crack:
15324       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15325       break;
15326
15327     case SOUND_bug:
15328       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15329       break;
15330
15331     case SOUND_tank:
15332       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15333       break;
15334
15335     case SOUND_android_clone:
15336       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15337       break;
15338
15339     case SOUND_android_move:
15340       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15341       break;
15342
15343     case SOUND_spring:
15344       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15345       break;
15346
15347     case SOUND_slurp:
15348       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15349       break;
15350
15351     case SOUND_eater:
15352       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15353       break;
15354
15355     case SOUND_eater_eat:
15356       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15357       break;
15358
15359     case SOUND_alien:
15360       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15361       break;
15362
15363     case SOUND_collect:
15364       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15365       break;
15366
15367     case SOUND_diamond:
15368       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15369       break;
15370
15371     case SOUND_squash:
15372       // !!! CHECK THIS !!!
15373 #if 1
15374       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15375 #else
15376       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15377 #endif
15378       break;
15379
15380     case SOUND_wonderfall:
15381       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15382       break;
15383
15384     case SOUND_drip:
15385       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15386       break;
15387
15388     case SOUND_push:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15390       break;
15391
15392     case SOUND_dirt:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15394       break;
15395
15396     case SOUND_acid:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15398       break;
15399
15400     case SOUND_ball:
15401       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15402       break;
15403
15404     case SOUND_slide:
15405       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15406       break;
15407
15408     case SOUND_wonder:
15409       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15410       break;
15411
15412     case SOUND_door:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15414       break;
15415
15416     case SOUND_exit_open:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15418       break;
15419
15420     case SOUND_exit_leave:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15422       break;
15423
15424     case SOUND_dynamite:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15426       break;
15427
15428     case SOUND_tick:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15430       break;
15431
15432     case SOUND_press:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15434       break;
15435
15436     case SOUND_wheel:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15438       break;
15439
15440     case SOUND_boom:
15441       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15442       break;
15443
15444     case SOUND_die:
15445       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15446       break;
15447
15448     case SOUND_time:
15449       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15450       break;
15451
15452     default:
15453       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15454       break;
15455   }
15456 }
15457
15458 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15459 {
15460   int element = map_element_SP_to_RND(element_sp);
15461   int action = map_action_SP_to_RND(action_sp);
15462   int offset = (setup.sp_show_border_elements ? 0 : 1);
15463   int x = xx - offset;
15464   int y = yy - offset;
15465
15466   PlayLevelSoundElementAction(x, y, element, action);
15467 }
15468
15469 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15470 {
15471   int element = map_element_MM_to_RND(element_mm);
15472   int action = map_action_MM_to_RND(action_mm);
15473   int offset = 0;
15474   int x = xx - offset;
15475   int y = yy - offset;
15476
15477   if (!IS_MM_ELEMENT(element))
15478     element = EL_MM_DEFAULT;
15479
15480   PlayLevelSoundElementAction(x, y, element, action);
15481 }
15482
15483 void PlaySound_MM(int sound_mm)
15484 {
15485   int sound = map_sound_MM_to_RND(sound_mm);
15486
15487   if (sound == SND_UNDEFINED)
15488     return;
15489
15490   PlaySound(sound);
15491 }
15492
15493 void PlaySoundLoop_MM(int sound_mm)
15494 {
15495   int sound = map_sound_MM_to_RND(sound_mm);
15496
15497   if (sound == SND_UNDEFINED)
15498     return;
15499
15500   PlaySoundLoop(sound);
15501 }
15502
15503 void StopSound_MM(int sound_mm)
15504 {
15505   int sound = map_sound_MM_to_RND(sound_mm);
15506
15507   if (sound == SND_UNDEFINED)
15508     return;
15509
15510   StopSound(sound);
15511 }
15512
15513 void RaiseScore(int value)
15514 {
15515   game.score += value;
15516
15517   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15518
15519   DisplayGameControlValues();
15520 }
15521
15522 void RaiseScoreElement(int element)
15523 {
15524   switch (element)
15525   {
15526     case EL_EMERALD:
15527     case EL_BD_DIAMOND:
15528     case EL_EMERALD_YELLOW:
15529     case EL_EMERALD_RED:
15530     case EL_EMERALD_PURPLE:
15531     case EL_SP_INFOTRON:
15532       RaiseScore(level.score[SC_EMERALD]);
15533       break;
15534     case EL_DIAMOND:
15535       RaiseScore(level.score[SC_DIAMOND]);
15536       break;
15537     case EL_CRYSTAL:
15538       RaiseScore(level.score[SC_CRYSTAL]);
15539       break;
15540     case EL_PEARL:
15541       RaiseScore(level.score[SC_PEARL]);
15542       break;
15543     case EL_BUG:
15544     case EL_BD_BUTTERFLY:
15545     case EL_SP_ELECTRON:
15546       RaiseScore(level.score[SC_BUG]);
15547       break;
15548     case EL_SPACESHIP:
15549     case EL_BD_FIREFLY:
15550     case EL_SP_SNIKSNAK:
15551       RaiseScore(level.score[SC_SPACESHIP]);
15552       break;
15553     case EL_YAMYAM:
15554     case EL_DARK_YAMYAM:
15555       RaiseScore(level.score[SC_YAMYAM]);
15556       break;
15557     case EL_ROBOT:
15558       RaiseScore(level.score[SC_ROBOT]);
15559       break;
15560     case EL_PACMAN:
15561       RaiseScore(level.score[SC_PACMAN]);
15562       break;
15563     case EL_NUT:
15564       RaiseScore(level.score[SC_NUT]);
15565       break;
15566     case EL_DYNAMITE:
15567     case EL_EM_DYNAMITE:
15568     case EL_SP_DISK_RED:
15569     case EL_DYNABOMB_INCREASE_NUMBER:
15570     case EL_DYNABOMB_INCREASE_SIZE:
15571     case EL_DYNABOMB_INCREASE_POWER:
15572       RaiseScore(level.score[SC_DYNAMITE]);
15573       break;
15574     case EL_SHIELD_NORMAL:
15575     case EL_SHIELD_DEADLY:
15576       RaiseScore(level.score[SC_SHIELD]);
15577       break;
15578     case EL_EXTRA_TIME:
15579       RaiseScore(level.extra_time_score);
15580       break;
15581     case EL_KEY_1:
15582     case EL_KEY_2:
15583     case EL_KEY_3:
15584     case EL_KEY_4:
15585     case EL_EM_KEY_1:
15586     case EL_EM_KEY_2:
15587     case EL_EM_KEY_3:
15588     case EL_EM_KEY_4:
15589     case EL_EMC_KEY_5:
15590     case EL_EMC_KEY_6:
15591     case EL_EMC_KEY_7:
15592     case EL_EMC_KEY_8:
15593     case EL_DC_KEY_WHITE:
15594       RaiseScore(level.score[SC_KEY]);
15595       break;
15596     default:
15597       RaiseScore(element_info[element].collect_score);
15598       break;
15599   }
15600 }
15601
15602 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15603 {
15604   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15605   {
15606     if (!quick_quit)
15607     {
15608       // prevent short reactivation of overlay buttons while closing door
15609       SetOverlayActive(FALSE);
15610       UnmapGameButtons();
15611
15612       // door may still be open due to skipped or envelope style request
15613       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15614     }
15615
15616     if (network.enabled)
15617       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15618     else
15619     {
15620       if (quick_quit)
15621         FadeSkipNextFadeIn();
15622
15623       SetGameStatus(GAME_MODE_MAIN);
15624
15625       DrawMainMenu();
15626     }
15627   }
15628   else          // continue playing the game
15629   {
15630     if (tape.playing && tape.deactivate_display)
15631       TapeDeactivateDisplayOff(TRUE);
15632
15633     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15634
15635     if (tape.playing && tape.deactivate_display)
15636       TapeDeactivateDisplayOn();
15637   }
15638 }
15639
15640 void RequestQuitGame(boolean escape_key_pressed)
15641 {
15642   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15643   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15644                         level_editor_test_game);
15645   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15646                           quick_quit || score_info_tape_play);
15647
15648   RequestQuitGameExt(skip_request, quick_quit,
15649                      "Do you really want to quit the game?");
15650 }
15651
15652 void RequestRestartGame(char *message)
15653 {
15654   game.restart_game_message = NULL;
15655
15656   boolean has_started_game = hasStartedNetworkGame();
15657   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15658
15659   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15660   {
15661     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15662   }
15663   else
15664   {
15665     // needed in case of envelope request to close game panel
15666     CloseDoor(DOOR_CLOSE_1);
15667
15668     SetGameStatus(GAME_MODE_MAIN);
15669
15670     DrawMainMenu();
15671   }
15672 }
15673
15674 void CheckGameOver(void)
15675 {
15676   static boolean last_game_over = FALSE;
15677   static int game_over_delay = 0;
15678   int game_over_delay_value = 50;
15679   boolean game_over = checkGameFailed();
15680
15681   // do not handle game over if request dialog is already active
15682   if (game.request_active)
15683     return;
15684
15685   // do not ask to play again if game was never actually played
15686   if (!game.GamePlayed)
15687     return;
15688
15689   if (!game_over)
15690   {
15691     last_game_over = FALSE;
15692     game_over_delay = game_over_delay_value;
15693
15694     return;
15695   }
15696
15697   if (game_over_delay > 0)
15698   {
15699     game_over_delay--;
15700
15701     return;
15702   }
15703
15704   if (last_game_over != game_over)
15705     game.restart_game_message = (hasStartedNetworkGame() ?
15706                                  "Game over! Play it again?" :
15707                                  "Game over!");
15708
15709   last_game_over = game_over;
15710 }
15711
15712 boolean checkGameSolved(void)
15713 {
15714   // set for all game engines if level was solved
15715   return game.LevelSolved_GameEnd;
15716 }
15717
15718 boolean checkGameFailed(void)
15719 {
15720   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15721     return (game_em.game_over && !game_em.level_solved);
15722   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15723     return (game_sp.game_over && !game_sp.level_solved);
15724   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15725     return (game_mm.game_over && !game_mm.level_solved);
15726   else                          // GAME_ENGINE_TYPE_RND
15727     return (game.GameOver && !game.LevelSolved);
15728 }
15729
15730 boolean checkGameEnded(void)
15731 {
15732   return (checkGameSolved() || checkGameFailed());
15733 }
15734
15735
15736 // ----------------------------------------------------------------------------
15737 // random generator functions
15738 // ----------------------------------------------------------------------------
15739
15740 unsigned int InitEngineRandom_RND(int seed)
15741 {
15742   game.num_random_calls = 0;
15743
15744   return InitEngineRandom(seed);
15745 }
15746
15747 unsigned int RND(int max)
15748 {
15749   if (max > 0)
15750   {
15751     game.num_random_calls++;
15752
15753     return GetEngineRandom(max);
15754   }
15755
15756   return 0;
15757 }
15758
15759
15760 // ----------------------------------------------------------------------------
15761 // game engine snapshot handling functions
15762 // ----------------------------------------------------------------------------
15763
15764 struct EngineSnapshotInfo
15765 {
15766   // runtime values for custom element collect score
15767   int collect_score[NUM_CUSTOM_ELEMENTS];
15768
15769   // runtime values for group element choice position
15770   int choice_pos[NUM_GROUP_ELEMENTS];
15771
15772   // runtime values for belt position animations
15773   int belt_graphic[4][NUM_BELT_PARTS];
15774   int belt_anim_mode[4][NUM_BELT_PARTS];
15775 };
15776
15777 static struct EngineSnapshotInfo engine_snapshot_rnd;
15778 static char *snapshot_level_identifier = NULL;
15779 static int snapshot_level_nr = -1;
15780
15781 static void SaveEngineSnapshotValues_RND(void)
15782 {
15783   static int belt_base_active_element[4] =
15784   {
15785     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15786     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15787     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15788     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15789   };
15790   int i, j;
15791
15792   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15793   {
15794     int element = EL_CUSTOM_START + i;
15795
15796     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15797   }
15798
15799   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15800   {
15801     int element = EL_GROUP_START + i;
15802
15803     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15804   }
15805
15806   for (i = 0; i < 4; i++)
15807   {
15808     for (j = 0; j < NUM_BELT_PARTS; j++)
15809     {
15810       int element = belt_base_active_element[i] + j;
15811       int graphic = el2img(element);
15812       int anim_mode = graphic_info[graphic].anim_mode;
15813
15814       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15815       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15816     }
15817   }
15818 }
15819
15820 static void LoadEngineSnapshotValues_RND(void)
15821 {
15822   unsigned int num_random_calls = game.num_random_calls;
15823   int i, j;
15824
15825   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15826   {
15827     int element = EL_CUSTOM_START + i;
15828
15829     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15830   }
15831
15832   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15833   {
15834     int element = EL_GROUP_START + i;
15835
15836     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15837   }
15838
15839   for (i = 0; i < 4; i++)
15840   {
15841     for (j = 0; j < NUM_BELT_PARTS; j++)
15842     {
15843       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15844       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15845
15846       graphic_info[graphic].anim_mode = anim_mode;
15847     }
15848   }
15849
15850   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15851   {
15852     InitRND(tape.random_seed);
15853     for (i = 0; i < num_random_calls; i++)
15854       RND(1);
15855   }
15856
15857   if (game.num_random_calls != num_random_calls)
15858   {
15859     Error("number of random calls out of sync");
15860     Error("number of random calls should be %d", num_random_calls);
15861     Error("number of random calls is %d", game.num_random_calls);
15862
15863     Fail("this should not happen -- please debug");
15864   }
15865 }
15866
15867 void FreeEngineSnapshotSingle(void)
15868 {
15869   FreeSnapshotSingle();
15870
15871   setString(&snapshot_level_identifier, NULL);
15872   snapshot_level_nr = -1;
15873 }
15874
15875 void FreeEngineSnapshotList(void)
15876 {
15877   FreeSnapshotList();
15878 }
15879
15880 static ListNode *SaveEngineSnapshotBuffers(void)
15881 {
15882   ListNode *buffers = NULL;
15883
15884   // copy some special values to a structure better suited for the snapshot
15885
15886   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15887     SaveEngineSnapshotValues_RND();
15888   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15889     SaveEngineSnapshotValues_EM();
15890   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15891     SaveEngineSnapshotValues_SP(&buffers);
15892   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15893     SaveEngineSnapshotValues_MM(&buffers);
15894
15895   // save values stored in special snapshot structure
15896
15897   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15898     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15899   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15900     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15901   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15902     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15903   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15904     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15905
15906   // save further RND engine values
15907
15908   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15909   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15910   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15911
15912   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15913   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15914   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15915   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15916   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15917
15918   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15919   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15920   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15921
15922   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15923
15924   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15925   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15926
15927   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15928   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15929   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15930   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15931   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15932   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15934   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15936   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15945
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15948
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15952
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15955
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15962
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15965
15966 #if 0
15967   ListNode *node = engine_snapshot_list_rnd;
15968   int num_bytes = 0;
15969
15970   while (node != NULL)
15971   {
15972     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15973
15974     node = node->next;
15975   }
15976
15977   Debug("game:playing:SaveEngineSnapshotBuffers",
15978         "size of engine snapshot: %d bytes", num_bytes);
15979 #endif
15980
15981   return buffers;
15982 }
15983
15984 void SaveEngineSnapshotSingle(void)
15985 {
15986   ListNode *buffers = SaveEngineSnapshotBuffers();
15987
15988   // finally save all snapshot buffers to single snapshot
15989   SaveSnapshotSingle(buffers);
15990
15991   // save level identification information
15992   setString(&snapshot_level_identifier, leveldir_current->identifier);
15993   snapshot_level_nr = level_nr;
15994 }
15995
15996 boolean CheckSaveEngineSnapshotToList(void)
15997 {
15998   boolean save_snapshot =
15999     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16000      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16001       game.snapshot.changed_action) ||
16002      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16003       game.snapshot.collected_item));
16004
16005   game.snapshot.changed_action = FALSE;
16006   game.snapshot.collected_item = FALSE;
16007   game.snapshot.save_snapshot = save_snapshot;
16008
16009   return save_snapshot;
16010 }
16011
16012 void SaveEngineSnapshotToList(void)
16013 {
16014   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16015       tape.quick_resume)
16016     return;
16017
16018   ListNode *buffers = SaveEngineSnapshotBuffers();
16019
16020   // finally save all snapshot buffers to snapshot list
16021   SaveSnapshotToList(buffers);
16022 }
16023
16024 void SaveEngineSnapshotToListInitial(void)
16025 {
16026   FreeEngineSnapshotList();
16027
16028   SaveEngineSnapshotToList();
16029 }
16030
16031 static void LoadEngineSnapshotValues(void)
16032 {
16033   // restore special values from snapshot structure
16034
16035   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16036     LoadEngineSnapshotValues_RND();
16037   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16038     LoadEngineSnapshotValues_EM();
16039   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16040     LoadEngineSnapshotValues_SP();
16041   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16042     LoadEngineSnapshotValues_MM();
16043 }
16044
16045 void LoadEngineSnapshotSingle(void)
16046 {
16047   LoadSnapshotSingle();
16048
16049   LoadEngineSnapshotValues();
16050 }
16051
16052 static void LoadEngineSnapshot_Undo(int steps)
16053 {
16054   LoadSnapshotFromList_Older(steps);
16055
16056   LoadEngineSnapshotValues();
16057 }
16058
16059 static void LoadEngineSnapshot_Redo(int steps)
16060 {
16061   LoadSnapshotFromList_Newer(steps);
16062
16063   LoadEngineSnapshotValues();
16064 }
16065
16066 boolean CheckEngineSnapshotSingle(void)
16067 {
16068   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16069           snapshot_level_nr == level_nr);
16070 }
16071
16072 boolean CheckEngineSnapshotList(void)
16073 {
16074   return CheckSnapshotList();
16075 }
16076
16077
16078 // ---------- new game button stuff -------------------------------------------
16079
16080 static struct
16081 {
16082   int graphic;
16083   struct XY *pos;
16084   int gadget_id;
16085   boolean *setup_value;
16086   boolean allowed_on_tape;
16087   boolean is_touch_button;
16088   char *infotext;
16089 } gamebutton_info[NUM_GAME_BUTTONS] =
16090 {
16091   {
16092     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16093     GAME_CTRL_ID_STOP,                          NULL,
16094     TRUE, FALSE,                                "stop game"
16095   },
16096   {
16097     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16098     GAME_CTRL_ID_PAUSE,                         NULL,
16099     TRUE, FALSE,                                "pause game"
16100   },
16101   {
16102     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16103     GAME_CTRL_ID_PLAY,                          NULL,
16104     TRUE, FALSE,                                "play game"
16105   },
16106   {
16107     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16108     GAME_CTRL_ID_UNDO,                          NULL,
16109     TRUE, FALSE,                                "undo step"
16110   },
16111   {
16112     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16113     GAME_CTRL_ID_REDO,                          NULL,
16114     TRUE, FALSE,                                "redo step"
16115   },
16116   {
16117     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16118     GAME_CTRL_ID_SAVE,                          NULL,
16119     TRUE, FALSE,                                "save game"
16120   },
16121   {
16122     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16123     GAME_CTRL_ID_PAUSE2,                        NULL,
16124     TRUE, FALSE,                                "pause game"
16125   },
16126   {
16127     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16128     GAME_CTRL_ID_LOAD,                          NULL,
16129     TRUE, FALSE,                                "load game"
16130   },
16131   {
16132     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16133     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16134     FALSE, FALSE,                               "stop game"
16135   },
16136   {
16137     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16138     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16139     FALSE, FALSE,                               "pause game"
16140   },
16141   {
16142     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16143     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16144     FALSE, FALSE,                               "play game"
16145   },
16146   {
16147     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16148     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16149     FALSE, TRUE,                                "stop game"
16150   },
16151   {
16152     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16153     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16154     FALSE, TRUE,                                "pause game"
16155   },
16156   {
16157     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16158     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16159     TRUE, FALSE,                                "background music on/off"
16160   },
16161   {
16162     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16163     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16164     TRUE, FALSE,                                "sound loops on/off"
16165   },
16166   {
16167     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16168     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16169     TRUE, FALSE,                                "normal sounds on/off"
16170   },
16171   {
16172     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16173     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16174     FALSE, FALSE,                               "background music on/off"
16175   },
16176   {
16177     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16178     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16179     FALSE, FALSE,                               "sound loops on/off"
16180   },
16181   {
16182     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16183     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16184     FALSE, FALSE,                               "normal sounds on/off"
16185   }
16186 };
16187
16188 void CreateGameButtons(void)
16189 {
16190   int i;
16191
16192   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16193   {
16194     int graphic = gamebutton_info[i].graphic;
16195     struct GraphicInfo *gfx = &graphic_info[graphic];
16196     struct XY *pos = gamebutton_info[i].pos;
16197     struct GadgetInfo *gi;
16198     int button_type;
16199     boolean checked;
16200     unsigned int event_mask;
16201     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16202     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16203     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16204     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16205     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16206     int gd_x   = gfx->src_x;
16207     int gd_y   = gfx->src_y;
16208     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16209     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16210     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16211     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16212     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16213     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16214     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16215     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16216     int id = i;
16217
16218     // do not use touch buttons if overlay touch buttons are disabled
16219     if (is_touch_button && !setup.touch.overlay_buttons)
16220       continue;
16221
16222     if (gfx->bitmap == NULL)
16223     {
16224       game_gadget[id] = NULL;
16225
16226       continue;
16227     }
16228
16229     if (id == GAME_CTRL_ID_STOP ||
16230         id == GAME_CTRL_ID_PANEL_STOP ||
16231         id == GAME_CTRL_ID_TOUCH_STOP ||
16232         id == GAME_CTRL_ID_PLAY ||
16233         id == GAME_CTRL_ID_PANEL_PLAY ||
16234         id == GAME_CTRL_ID_SAVE ||
16235         id == GAME_CTRL_ID_LOAD)
16236     {
16237       button_type = GD_TYPE_NORMAL_BUTTON;
16238       checked = FALSE;
16239       event_mask = GD_EVENT_RELEASED;
16240     }
16241     else if (id == GAME_CTRL_ID_UNDO ||
16242              id == GAME_CTRL_ID_REDO)
16243     {
16244       button_type = GD_TYPE_NORMAL_BUTTON;
16245       checked = FALSE;
16246       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16247     }
16248     else
16249     {
16250       button_type = GD_TYPE_CHECK_BUTTON;
16251       checked = (gamebutton_info[i].setup_value != NULL ?
16252                  *gamebutton_info[i].setup_value : FALSE);
16253       event_mask = GD_EVENT_PRESSED;
16254     }
16255
16256     gi = CreateGadget(GDI_CUSTOM_ID, id,
16257                       GDI_IMAGE_ID, graphic,
16258                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16259                       GDI_X, base_x + x,
16260                       GDI_Y, base_y + y,
16261                       GDI_WIDTH, gfx->width,
16262                       GDI_HEIGHT, gfx->height,
16263                       GDI_TYPE, button_type,
16264                       GDI_STATE, GD_BUTTON_UNPRESSED,
16265                       GDI_CHECKED, checked,
16266                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16267                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16268                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16269                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16270                       GDI_DIRECT_DRAW, FALSE,
16271                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16272                       GDI_EVENT_MASK, event_mask,
16273                       GDI_CALLBACK_ACTION, HandleGameButtons,
16274                       GDI_END);
16275
16276     if (gi == NULL)
16277       Fail("cannot create gadget");
16278
16279     game_gadget[id] = gi;
16280   }
16281 }
16282
16283 void FreeGameButtons(void)
16284 {
16285   int i;
16286
16287   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16288     FreeGadget(game_gadget[i]);
16289 }
16290
16291 static void UnmapGameButtonsAtSamePosition(int id)
16292 {
16293   int i;
16294
16295   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16296     if (i != id &&
16297         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16298         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16299       UnmapGadget(game_gadget[i]);
16300 }
16301
16302 static void UnmapGameButtonsAtSamePosition_All(void)
16303 {
16304   if (setup.show_load_save_buttons)
16305   {
16306     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16307     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16308     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16309   }
16310   else if (setup.show_undo_redo_buttons)
16311   {
16312     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16313     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16314     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16315   }
16316   else
16317   {
16318     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16319     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16320     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16321
16322     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16323     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16324     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16325   }
16326 }
16327
16328 void MapLoadSaveButtons(void)
16329 {
16330   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16331   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16332
16333   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16334   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16335 }
16336
16337 void MapUndoRedoButtons(void)
16338 {
16339   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16340   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16341
16342   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16343   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16344 }
16345
16346 void ModifyPauseButtons(void)
16347 {
16348   static int ids[] =
16349   {
16350     GAME_CTRL_ID_PAUSE,
16351     GAME_CTRL_ID_PAUSE2,
16352     GAME_CTRL_ID_PANEL_PAUSE,
16353     GAME_CTRL_ID_TOUCH_PAUSE,
16354     -1
16355   };
16356   int i;
16357
16358   for (i = 0; ids[i] > -1; i++)
16359     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16360 }
16361
16362 static void MapGameButtonsExt(boolean on_tape)
16363 {
16364   int i;
16365
16366   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16367   {
16368     if ((i == GAME_CTRL_ID_UNDO ||
16369          i == GAME_CTRL_ID_REDO) &&
16370         game_status != GAME_MODE_PLAYING)
16371       continue;
16372
16373     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16374       MapGadget(game_gadget[i]);
16375   }
16376
16377   UnmapGameButtonsAtSamePosition_All();
16378
16379   RedrawGameButtons();
16380 }
16381
16382 static void UnmapGameButtonsExt(boolean on_tape)
16383 {
16384   int i;
16385
16386   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16387     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16388       UnmapGadget(game_gadget[i]);
16389 }
16390
16391 static void RedrawGameButtonsExt(boolean on_tape)
16392 {
16393   int i;
16394
16395   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16396     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16397       RedrawGadget(game_gadget[i]);
16398 }
16399
16400 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16401 {
16402   if (gi == NULL)
16403     return;
16404
16405   gi->checked = state;
16406 }
16407
16408 static void RedrawSoundButtonGadget(int id)
16409 {
16410   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16411              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16412              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16413              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16414              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16415              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16416              id);
16417
16418   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16419   RedrawGadget(game_gadget[id2]);
16420 }
16421
16422 void MapGameButtons(void)
16423 {
16424   MapGameButtonsExt(FALSE);
16425 }
16426
16427 void UnmapGameButtons(void)
16428 {
16429   UnmapGameButtonsExt(FALSE);
16430 }
16431
16432 void RedrawGameButtons(void)
16433 {
16434   RedrawGameButtonsExt(FALSE);
16435 }
16436
16437 void MapGameButtonsOnTape(void)
16438 {
16439   MapGameButtonsExt(TRUE);
16440 }
16441
16442 void UnmapGameButtonsOnTape(void)
16443 {
16444   UnmapGameButtonsExt(TRUE);
16445 }
16446
16447 void RedrawGameButtonsOnTape(void)
16448 {
16449   RedrawGameButtonsExt(TRUE);
16450 }
16451
16452 static void GameUndoRedoExt(void)
16453 {
16454   ClearPlayerAction();
16455
16456   tape.pausing = TRUE;
16457
16458   RedrawPlayfield();
16459   UpdateAndDisplayGameControlValues();
16460
16461   DrawCompleteVideoDisplay();
16462   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16463   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16464   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16465
16466   ModifyPauseButtons();
16467
16468   BackToFront();
16469 }
16470
16471 static void GameUndo(int steps)
16472 {
16473   if (!CheckEngineSnapshotList())
16474     return;
16475
16476   int tape_property_bits = tape.property_bits;
16477
16478   LoadEngineSnapshot_Undo(steps);
16479
16480   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16481
16482   GameUndoRedoExt();
16483 }
16484
16485 static void GameRedo(int steps)
16486 {
16487   if (!CheckEngineSnapshotList())
16488     return;
16489
16490   int tape_property_bits = tape.property_bits;
16491
16492   LoadEngineSnapshot_Redo(steps);
16493
16494   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16495
16496   GameUndoRedoExt();
16497 }
16498
16499 static void HandleGameButtonsExt(int id, int button)
16500 {
16501   static boolean game_undo_executed = FALSE;
16502   int steps = BUTTON_STEPSIZE(button);
16503   boolean handle_game_buttons =
16504     (game_status == GAME_MODE_PLAYING ||
16505      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16506
16507   if (!handle_game_buttons)
16508     return;
16509
16510   switch (id)
16511   {
16512     case GAME_CTRL_ID_STOP:
16513     case GAME_CTRL_ID_PANEL_STOP:
16514     case GAME_CTRL_ID_TOUCH_STOP:
16515       TapeStopGame();
16516
16517       break;
16518
16519     case GAME_CTRL_ID_PAUSE:
16520     case GAME_CTRL_ID_PAUSE2:
16521     case GAME_CTRL_ID_PANEL_PAUSE:
16522     case GAME_CTRL_ID_TOUCH_PAUSE:
16523       if (network.enabled && game_status == GAME_MODE_PLAYING)
16524       {
16525         if (tape.pausing)
16526           SendToServer_ContinuePlaying();
16527         else
16528           SendToServer_PausePlaying();
16529       }
16530       else
16531         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16532
16533       game_undo_executed = FALSE;
16534
16535       break;
16536
16537     case GAME_CTRL_ID_PLAY:
16538     case GAME_CTRL_ID_PANEL_PLAY:
16539       if (game_status == GAME_MODE_MAIN)
16540       {
16541         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16542       }
16543       else if (tape.pausing)
16544       {
16545         if (network.enabled)
16546           SendToServer_ContinuePlaying();
16547         else
16548           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16549       }
16550       break;
16551
16552     case GAME_CTRL_ID_UNDO:
16553       // Important: When using "save snapshot when collecting an item" mode,
16554       // load last (current) snapshot for first "undo" after pressing "pause"
16555       // (else the last-but-one snapshot would be loaded, because the snapshot
16556       // pointer already points to the last snapshot when pressing "pause",
16557       // which is fine for "every step/move" mode, but not for "every collect")
16558       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16559           !game_undo_executed)
16560         steps--;
16561
16562       game_undo_executed = TRUE;
16563
16564       GameUndo(steps);
16565       break;
16566
16567     case GAME_CTRL_ID_REDO:
16568       GameRedo(steps);
16569       break;
16570
16571     case GAME_CTRL_ID_SAVE:
16572       TapeQuickSave();
16573       break;
16574
16575     case GAME_CTRL_ID_LOAD:
16576       TapeQuickLoad();
16577       break;
16578
16579     case SOUND_CTRL_ID_MUSIC:
16580     case SOUND_CTRL_ID_PANEL_MUSIC:
16581       if (setup.sound_music)
16582       { 
16583         setup.sound_music = FALSE;
16584
16585         FadeMusic();
16586       }
16587       else if (audio.music_available)
16588       { 
16589         setup.sound = setup.sound_music = TRUE;
16590
16591         SetAudioMode(setup.sound);
16592
16593         if (game_status == GAME_MODE_PLAYING)
16594           PlayLevelMusic();
16595       }
16596
16597       RedrawSoundButtonGadget(id);
16598
16599       break;
16600
16601     case SOUND_CTRL_ID_LOOPS:
16602     case SOUND_CTRL_ID_PANEL_LOOPS:
16603       if (setup.sound_loops)
16604         setup.sound_loops = FALSE;
16605       else if (audio.loops_available)
16606       {
16607         setup.sound = setup.sound_loops = TRUE;
16608
16609         SetAudioMode(setup.sound);
16610       }
16611
16612       RedrawSoundButtonGadget(id);
16613
16614       break;
16615
16616     case SOUND_CTRL_ID_SIMPLE:
16617     case SOUND_CTRL_ID_PANEL_SIMPLE:
16618       if (setup.sound_simple)
16619         setup.sound_simple = FALSE;
16620       else if (audio.sound_available)
16621       {
16622         setup.sound = setup.sound_simple = TRUE;
16623
16624         SetAudioMode(setup.sound);
16625       }
16626
16627       RedrawSoundButtonGadget(id);
16628
16629       break;
16630
16631     default:
16632       break;
16633   }
16634 }
16635
16636 static void HandleGameButtons(struct GadgetInfo *gi)
16637 {
16638   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16639 }
16640
16641 void HandleSoundButtonKeys(Key key)
16642 {
16643   if (key == setup.shortcut.sound_simple)
16644     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16645   else if (key == setup.shortcut.sound_loops)
16646     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16647   else if (key == setup.shortcut.sound_music)
16648     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16649 }