fixed crash bug caused by accessing undefined sorting name
[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 TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 int NewHiScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3546   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3547   int initial_move_dir = MV_DOWN;
3548   int i, j, x, y;
3549
3550   // required here to update video display before fading (FIX THIS)
3551   DrawMaskedBorder(REDRAW_DOOR_2);
3552
3553   if (!game.restart_level)
3554     CloseDoor(DOOR_CLOSE_1);
3555
3556   SetGameStatus(GAME_MODE_PLAYING);
3557
3558   if (level_editor_test_game)
3559     FadeSkipNextFadeOut();
3560   else
3561     FadeSetEnterScreen();
3562
3563   if (CheckFadeAll())
3564     fade_mask = REDRAW_ALL;
3565
3566   FadeLevelSoundsAndMusic();
3567
3568   ExpireSoundLoops(TRUE);
3569
3570   FadeOut(fade_mask);
3571
3572   if (level_editor_test_game)
3573     FadeSkipNextFadeIn();
3574
3575   // needed if different viewport properties defined for playing
3576   ChangeViewportPropertiesIfNeeded();
3577
3578   ClearField();
3579
3580   DrawCompleteVideoDisplay();
3581
3582   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3583
3584   InitGameEngine();
3585   InitGameControlValues();
3586
3587   if (tape.recording)
3588   {
3589     // initialize tape actions from game when recording tape
3590     tape.use_key_actions   = game.use_key_actions;
3591     tape.use_mouse_actions = game.use_mouse_actions;
3592
3593     // initialize visible playfield size when recording tape (for team mode)
3594     tape.scr_fieldx = SCR_FIELDX;
3595     tape.scr_fieldy = SCR_FIELDY;
3596   }
3597
3598   // don't play tapes over network
3599   network_playing = (network.enabled && !tape.playing);
3600
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     player->index_nr = i;
3606     player->index_bit = (1 << i);
3607     player->element_nr = EL_PLAYER_1 + i;
3608
3609     player->present = FALSE;
3610     player->active = FALSE;
3611     player->mapped = FALSE;
3612
3613     player->killed = FALSE;
3614     player->reanimated = FALSE;
3615     player->buried = FALSE;
3616
3617     player->action = 0;
3618     player->effective_action = 0;
3619     player->programmed_action = 0;
3620     player->snap_action = 0;
3621
3622     player->mouse_action.lx = 0;
3623     player->mouse_action.ly = 0;
3624     player->mouse_action.button = 0;
3625     player->mouse_action.button_hint = 0;
3626
3627     player->effective_mouse_action.lx = 0;
3628     player->effective_mouse_action.ly = 0;
3629     player->effective_mouse_action.button = 0;
3630     player->effective_mouse_action.button_hint = 0;
3631
3632     for (j = 0; j < MAX_NUM_KEYS; j++)
3633       player->key[j] = FALSE;
3634
3635     player->num_white_keys = 0;
3636
3637     player->dynabomb_count = 0;
3638     player->dynabomb_size = 1;
3639     player->dynabombs_left = 0;
3640     player->dynabomb_xl = FALSE;
3641
3642     player->MovDir = initial_move_dir;
3643     player->MovPos = 0;
3644     player->GfxPos = 0;
3645     player->GfxDir = initial_move_dir;
3646     player->GfxAction = ACTION_DEFAULT;
3647     player->Frame = 0;
3648     player->StepFrame = 0;
3649
3650     player->initial_element = player->element_nr;
3651     player->artwork_element =
3652       (level.use_artwork_element[i] ? level.artwork_element[i] :
3653        player->element_nr);
3654     player->use_murphy = FALSE;
3655
3656     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3657     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3658
3659     player->gravity = level.initial_player_gravity[i];
3660
3661     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3662
3663     player->actual_frame_counter = 0;
3664
3665     player->step_counter = 0;
3666
3667     player->last_move_dir = initial_move_dir;
3668
3669     player->is_active = FALSE;
3670
3671     player->is_waiting = FALSE;
3672     player->is_moving = FALSE;
3673     player->is_auto_moving = FALSE;
3674     player->is_digging = FALSE;
3675     player->is_snapping = FALSE;
3676     player->is_collecting = FALSE;
3677     player->is_pushing = FALSE;
3678     player->is_switching = FALSE;
3679     player->is_dropping = FALSE;
3680     player->is_dropping_pressed = FALSE;
3681
3682     player->is_bored = FALSE;
3683     player->is_sleeping = FALSE;
3684
3685     player->was_waiting = TRUE;
3686     player->was_moving = FALSE;
3687     player->was_snapping = FALSE;
3688     player->was_dropping = FALSE;
3689
3690     player->force_dropping = FALSE;
3691
3692     player->frame_counter_bored = -1;
3693     player->frame_counter_sleeping = -1;
3694
3695     player->anim_delay_counter = 0;
3696     player->post_delay_counter = 0;
3697
3698     player->dir_waiting = initial_move_dir;
3699     player->action_waiting = ACTION_DEFAULT;
3700     player->last_action_waiting = ACTION_DEFAULT;
3701     player->special_action_bored = ACTION_DEFAULT;
3702     player->special_action_sleeping = ACTION_DEFAULT;
3703
3704     player->switch_x = -1;
3705     player->switch_y = -1;
3706
3707     player->drop_x = -1;
3708     player->drop_y = -1;
3709
3710     player->show_envelope = 0;
3711
3712     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3713
3714     player->push_delay       = -1;      // initialized when pushing starts
3715     player->push_delay_value = game.initial_push_delay_value;
3716
3717     player->drop_delay = 0;
3718     player->drop_pressed_delay = 0;
3719
3720     player->last_jx = -1;
3721     player->last_jy = -1;
3722     player->jx = -1;
3723     player->jy = -1;
3724
3725     player->shield_normal_time_left = 0;
3726     player->shield_deadly_time_left = 0;
3727
3728     player->last_removed_element = EL_UNDEFINED;
3729
3730     player->inventory_infinite_element = EL_UNDEFINED;
3731     player->inventory_size = 0;
3732
3733     if (level.use_initial_inventory[i])
3734     {
3735       for (j = 0; j < level.initial_inventory_size[i]; j++)
3736       {
3737         int element = level.initial_inventory_content[i][j];
3738         int collect_count = element_info[element].collect_count_initial;
3739         int k;
3740
3741         if (!IS_CUSTOM_ELEMENT(element))
3742           collect_count = 1;
3743
3744         if (collect_count == 0)
3745           player->inventory_infinite_element = element;
3746         else
3747           for (k = 0; k < collect_count; k++)
3748             if (player->inventory_size < MAX_INVENTORY_SIZE)
3749               player->inventory_element[player->inventory_size++] = element;
3750       }
3751     }
3752
3753     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3754     SnapField(player, 0, 0);
3755
3756     map_player_action[i] = i;
3757   }
3758
3759   network_player_action_received = FALSE;
3760
3761   // initial null action
3762   if (network_playing)
3763     SendToServer_MovePlayer(MV_NONE);
3764
3765   FrameCounter = 0;
3766   TimeFrames = 0;
3767   TimePlayed = 0;
3768   TimeLeft = level.time;
3769   TapeTime = 0;
3770
3771   ScreenMovDir = MV_NONE;
3772   ScreenMovPos = 0;
3773   ScreenGfxPos = 0;
3774
3775   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3776
3777   game.robot_wheel_x = -1;
3778   game.robot_wheel_y = -1;
3779
3780   game.exit_x = -1;
3781   game.exit_y = -1;
3782
3783   game.all_players_gone = FALSE;
3784
3785   game.LevelSolved = FALSE;
3786   game.GameOver = FALSE;
3787
3788   game.GamePlayed = !tape.playing;
3789
3790   game.LevelSolved_GameWon = FALSE;
3791   game.LevelSolved_GameEnd = FALSE;
3792   game.LevelSolved_SaveTape = FALSE;
3793   game.LevelSolved_SaveScore = FALSE;
3794
3795   game.LevelSolved_CountingTime = 0;
3796   game.LevelSolved_CountingScore = 0;
3797   game.LevelSolved_CountingHealth = 0;
3798
3799   game.panel.active = TRUE;
3800
3801   game.no_time_limit = (level.time == 0);
3802
3803   game.yamyam_content_nr = 0;
3804   game.robot_wheel_active = FALSE;
3805   game.magic_wall_active = FALSE;
3806   game.magic_wall_time_left = 0;
3807   game.light_time_left = 0;
3808   game.timegate_time_left = 0;
3809   game.switchgate_pos = 0;
3810   game.wind_direction = level.wind_direction_initial;
3811
3812   game.score = 0;
3813   game.score_final = 0;
3814
3815   game.health = MAX_HEALTH;
3816   game.health_final = MAX_HEALTH;
3817
3818   game.gems_still_needed = level.gems_needed;
3819   game.sokoban_fields_still_needed = 0;
3820   game.sokoban_objects_still_needed = 0;
3821   game.lights_still_needed = 0;
3822   game.players_still_needed = 0;
3823   game.friends_still_needed = 0;
3824
3825   game.lenses_time_left = 0;
3826   game.magnify_time_left = 0;
3827
3828   game.ball_active = level.ball_active_initial;
3829   game.ball_content_nr = 0;
3830
3831   game.explosions_delayed = TRUE;
3832
3833   game.envelope_active = FALSE;
3834
3835   for (i = 0; i < NUM_BELTS; i++)
3836   {
3837     game.belt_dir[i] = MV_NONE;
3838     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3839   }
3840
3841   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3842     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3843
3844 #if DEBUG_INIT_PLAYER
3845   DebugPrintPlayerStatus("Player status at level initialization");
3846 #endif
3847
3848   SCAN_PLAYFIELD(x, y)
3849   {
3850     Tile[x][y] = Last[x][y] = level.field[x][y];
3851     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3852     ChangeDelay[x][y] = 0;
3853     ChangePage[x][y] = -1;
3854     CustomValue[x][y] = 0;              // initialized in InitField()
3855     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3856     AmoebaNr[x][y] = 0;
3857     WasJustMoving[x][y] = 0;
3858     WasJustFalling[x][y] = 0;
3859     CheckCollision[x][y] = 0;
3860     CheckImpact[x][y] = 0;
3861     Stop[x][y] = FALSE;
3862     Pushed[x][y] = FALSE;
3863
3864     ChangeCount[x][y] = 0;
3865     ChangeEvent[x][y] = -1;
3866
3867     ExplodePhase[x][y] = 0;
3868     ExplodeDelay[x][y] = 0;
3869     ExplodeField[x][y] = EX_TYPE_NONE;
3870
3871     RunnerVisit[x][y] = 0;
3872     PlayerVisit[x][y] = 0;
3873
3874     GfxFrame[x][y] = 0;
3875     GfxRandom[x][y] = INIT_GFX_RANDOM();
3876     GfxElement[x][y] = EL_UNDEFINED;
3877     GfxAction[x][y] = ACTION_DEFAULT;
3878     GfxDir[x][y] = MV_NONE;
3879     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3880   }
3881
3882   SCAN_PLAYFIELD(x, y)
3883   {
3884     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3885       emulate_bd = FALSE;
3886     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3887       emulate_sb = FALSE;
3888     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3889       emulate_sp = FALSE;
3890
3891     InitField(x, y, TRUE);
3892
3893     ResetGfxAnimation(x, y);
3894   }
3895
3896   InitBeltMovement();
3897
3898   for (i = 0; i < MAX_PLAYERS; i++)
3899   {
3900     struct PlayerInfo *player = &stored_player[i];
3901
3902     // set number of special actions for bored and sleeping animation
3903     player->num_special_action_bored =
3904       get_num_special_action(player->artwork_element,
3905                              ACTION_BORING_1, ACTION_BORING_LAST);
3906     player->num_special_action_sleeping =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3909   }
3910
3911   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3912                     emulate_sb ? EMU_SOKOBAN :
3913                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3914
3915   // initialize type of slippery elements
3916   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3917   {
3918     if (!IS_CUSTOM_ELEMENT(i))
3919     {
3920       // default: elements slip down either to the left or right randomly
3921       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3922
3923       // SP style elements prefer to slip down on the left side
3924       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3925         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3926
3927       // BD style elements prefer to slip down on the left side
3928       if (game.emulation == EMU_BOULDERDASH)
3929         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3930     }
3931   }
3932
3933   // initialize explosion and ignition delay
3934   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3935   {
3936     if (!IS_CUSTOM_ELEMENT(i))
3937     {
3938       int num_phase = 8;
3939       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3940                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3941                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3942       int last_phase = (num_phase + 1) * delay;
3943       int half_phase = (num_phase / 2) * delay;
3944
3945       element_info[i].explosion_delay = last_phase - 1;
3946       element_info[i].ignition_delay = half_phase;
3947
3948       if (i == EL_BLACK_ORB)
3949         element_info[i].ignition_delay = 1;
3950     }
3951   }
3952
3953   // correct non-moving belts to start moving left
3954   for (i = 0; i < NUM_BELTS; i++)
3955     if (game.belt_dir[i] == MV_NONE)
3956       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3957
3958 #if USE_NEW_PLAYER_ASSIGNMENTS
3959   // use preferred player also in local single-player mode
3960   if (!network.enabled && !game.team_mode)
3961   {
3962     int new_index_nr = setup.network_player_nr;
3963
3964     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3965     {
3966       for (i = 0; i < MAX_PLAYERS; i++)
3967         stored_player[i].connected_locally = FALSE;
3968
3969       stored_player[new_index_nr].connected_locally = TRUE;
3970     }
3971   }
3972
3973   for (i = 0; i < MAX_PLAYERS; i++)
3974   {
3975     stored_player[i].connected = FALSE;
3976
3977     // in network game mode, the local player might not be the first player
3978     if (stored_player[i].connected_locally)
3979       local_player = &stored_player[i];
3980   }
3981
3982   if (!network.enabled)
3983     local_player->connected = TRUE;
3984
3985   if (tape.playing)
3986   {
3987     for (i = 0; i < MAX_PLAYERS; i++)
3988       stored_player[i].connected = tape.player_participates[i];
3989   }
3990   else if (network.enabled)
3991   {
3992     // add team mode players connected over the network (needed for correct
3993     // assignment of player figures from level to locally playing players)
3994
3995     for (i = 0; i < MAX_PLAYERS; i++)
3996       if (stored_player[i].connected_network)
3997         stored_player[i].connected = TRUE;
3998   }
3999   else if (game.team_mode)
4000   {
4001     // try to guess locally connected team mode players (needed for correct
4002     // assignment of player figures from level to locally playing players)
4003
4004     for (i = 0; i < MAX_PLAYERS; i++)
4005       if (setup.input[i].use_joystick ||
4006           setup.input[i].key.left != KSYM_UNDEFINED)
4007         stored_player[i].connected = TRUE;
4008   }
4009
4010 #if DEBUG_INIT_PLAYER
4011   DebugPrintPlayerStatus("Player status after level initialization");
4012 #endif
4013
4014 #if DEBUG_INIT_PLAYER
4015   Debug("game:init:player", "Reassigning players ...");
4016 #endif
4017
4018   // check if any connected player was not found in playfield
4019   for (i = 0; i < MAX_PLAYERS; i++)
4020   {
4021     struct PlayerInfo *player = &stored_player[i];
4022
4023     if (player->connected && !player->present)
4024     {
4025       struct PlayerInfo *field_player = NULL;
4026
4027 #if DEBUG_INIT_PLAYER
4028       Debug("game:init:player",
4029             "- looking for field player for player %d ...", i + 1);
4030 #endif
4031
4032       // assign first free player found that is present in the playfield
4033
4034       // first try: look for unmapped playfield player that is not connected
4035       for (j = 0; j < MAX_PLAYERS; j++)
4036         if (field_player == NULL &&
4037             stored_player[j].present &&
4038             !stored_player[j].mapped &&
4039             !stored_player[j].connected)
4040           field_player = &stored_player[j];
4041
4042       // second try: look for *any* unmapped playfield player
4043       for (j = 0; j < MAX_PLAYERS; j++)
4044         if (field_player == NULL &&
4045             stored_player[j].present &&
4046             !stored_player[j].mapped)
4047           field_player = &stored_player[j];
4048
4049       if (field_player != NULL)
4050       {
4051         int jx = field_player->jx, jy = field_player->jy;
4052
4053 #if DEBUG_INIT_PLAYER
4054         Debug("game:init:player", "- found player %d",
4055               field_player->index_nr + 1);
4056 #endif
4057
4058         player->present = FALSE;
4059         player->active = FALSE;
4060
4061         field_player->present = TRUE;
4062         field_player->active = TRUE;
4063
4064         /*
4065         player->initial_element = field_player->initial_element;
4066         player->artwork_element = field_player->artwork_element;
4067
4068         player->block_last_field       = field_player->block_last_field;
4069         player->block_delay_adjustment = field_player->block_delay_adjustment;
4070         */
4071
4072         StorePlayer[jx][jy] = field_player->element_nr;
4073
4074         field_player->jx = field_player->last_jx = jx;
4075         field_player->jy = field_player->last_jy = jy;
4076
4077         if (local_player == player)
4078           local_player = field_player;
4079
4080         map_player_action[field_player->index_nr] = i;
4081
4082         field_player->mapped = TRUE;
4083
4084 #if DEBUG_INIT_PLAYER
4085         Debug("game:init:player", "- map_player_action[%d] == %d",
4086               field_player->index_nr + 1, i + 1);
4087 #endif
4088       }
4089     }
4090
4091     if (player->connected && player->present)
4092       player->mapped = TRUE;
4093   }
4094
4095 #if DEBUG_INIT_PLAYER
4096   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4097 #endif
4098
4099 #else
4100
4101   // check if any connected player was not found in playfield
4102   for (i = 0; i < MAX_PLAYERS; i++)
4103   {
4104     struct PlayerInfo *player = &stored_player[i];
4105
4106     if (player->connected && !player->present)
4107     {
4108       for (j = 0; j < MAX_PLAYERS; j++)
4109       {
4110         struct PlayerInfo *field_player = &stored_player[j];
4111         int jx = field_player->jx, jy = field_player->jy;
4112
4113         // assign first free player found that is present in the playfield
4114         if (field_player->present && !field_player->connected)
4115         {
4116           player->present = TRUE;
4117           player->active = TRUE;
4118
4119           field_player->present = FALSE;
4120           field_player->active = FALSE;
4121
4122           player->initial_element = field_player->initial_element;
4123           player->artwork_element = field_player->artwork_element;
4124
4125           player->block_last_field       = field_player->block_last_field;
4126           player->block_delay_adjustment = field_player->block_delay_adjustment;
4127
4128           StorePlayer[jx][jy] = player->element_nr;
4129
4130           player->jx = player->last_jx = jx;
4131           player->jy = player->last_jy = jy;
4132
4133           break;
4134         }
4135       }
4136     }
4137   }
4138 #endif
4139
4140 #if 0
4141   Debug("game:init:player", "local_player->present == %d",
4142         local_player->present);
4143 #endif
4144
4145   // set focus to local player for network games, else to all players
4146   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4147   game.centered_player_nr_next = game.centered_player_nr;
4148   game.set_centered_player = FALSE;
4149   game.set_centered_player_wrap = FALSE;
4150
4151   if (network_playing && tape.recording)
4152   {
4153     // store client dependent player focus when recording network games
4154     tape.centered_player_nr_next = game.centered_player_nr_next;
4155     tape.set_centered_player = TRUE;
4156   }
4157
4158   if (tape.playing)
4159   {
4160     // when playing a tape, eliminate all players who do not participate
4161
4162 #if USE_NEW_PLAYER_ASSIGNMENTS
4163
4164     if (!game.team_mode)
4165     {
4166       for (i = 0; i < MAX_PLAYERS; i++)
4167       {
4168         if (stored_player[i].active &&
4169             !tape.player_participates[map_player_action[i]])
4170         {
4171           struct PlayerInfo *player = &stored_player[i];
4172           int jx = player->jx, jy = player->jy;
4173
4174 #if DEBUG_INIT_PLAYER
4175           Debug("game:init:player", "Removing player %d at (%d, %d)",
4176                 i + 1, jx, jy);
4177 #endif
4178
4179           player->active = FALSE;
4180           StorePlayer[jx][jy] = 0;
4181           Tile[jx][jy] = EL_EMPTY;
4182         }
4183       }
4184     }
4185
4186 #else
4187
4188     for (i = 0; i < MAX_PLAYERS; i++)
4189     {
4190       if (stored_player[i].active &&
4191           !tape.player_participates[i])
4192       {
4193         struct PlayerInfo *player = &stored_player[i];
4194         int jx = player->jx, jy = player->jy;
4195
4196         player->active = FALSE;
4197         StorePlayer[jx][jy] = 0;
4198         Tile[jx][jy] = EL_EMPTY;
4199       }
4200     }
4201 #endif
4202   }
4203   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4204   {
4205     // when in single player mode, eliminate all but the local player
4206
4207     for (i = 0; i < MAX_PLAYERS; i++)
4208     {
4209       struct PlayerInfo *player = &stored_player[i];
4210
4211       if (player->active && player != local_player)
4212       {
4213         int jx = player->jx, jy = player->jy;
4214
4215         player->active = FALSE;
4216         player->present = FALSE;
4217
4218         StorePlayer[jx][jy] = 0;
4219         Tile[jx][jy] = EL_EMPTY;
4220       }
4221     }
4222   }
4223
4224   for (i = 0; i < MAX_PLAYERS; i++)
4225     if (stored_player[i].active)
4226       game.players_still_needed++;
4227
4228   if (level.solved_by_one_player)
4229     game.players_still_needed = 1;
4230
4231   // when recording the game, store which players take part in the game
4232   if (tape.recording)
4233   {
4234 #if USE_NEW_PLAYER_ASSIGNMENTS
4235     for (i = 0; i < MAX_PLAYERS; i++)
4236       if (stored_player[i].connected)
4237         tape.player_participates[i] = TRUE;
4238 #else
4239     for (i = 0; i < MAX_PLAYERS; i++)
4240       if (stored_player[i].active)
4241         tape.player_participates[i] = TRUE;
4242 #endif
4243   }
4244
4245 #if DEBUG_INIT_PLAYER
4246   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4247 #endif
4248
4249   if (BorderElement == EL_EMPTY)
4250   {
4251     SBX_Left = 0;
4252     SBX_Right = lev_fieldx - SCR_FIELDX;
4253     SBY_Upper = 0;
4254     SBY_Lower = lev_fieldy - SCR_FIELDY;
4255   }
4256   else
4257   {
4258     SBX_Left = -1;
4259     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4260     SBY_Upper = -1;
4261     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4262   }
4263
4264   if (full_lev_fieldx <= SCR_FIELDX)
4265     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4266   if (full_lev_fieldy <= SCR_FIELDY)
4267     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4268
4269   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4270     SBX_Left--;
4271   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4272     SBY_Upper--;
4273
4274   // if local player not found, look for custom element that might create
4275   // the player (make some assumptions about the right custom element)
4276   if (!local_player->present)
4277   {
4278     int start_x = 0, start_y = 0;
4279     int found_rating = 0;
4280     int found_element = EL_UNDEFINED;
4281     int player_nr = local_player->index_nr;
4282
4283     SCAN_PLAYFIELD(x, y)
4284     {
4285       int element = Tile[x][y];
4286       int content;
4287       int xx, yy;
4288       boolean is_player;
4289
4290       if (level.use_start_element[player_nr] &&
4291           level.start_element[player_nr] == element &&
4292           found_rating < 4)
4293       {
4294         start_x = x;
4295         start_y = y;
4296
4297         found_rating = 4;
4298         found_element = element;
4299       }
4300
4301       if (!IS_CUSTOM_ELEMENT(element))
4302         continue;
4303
4304       if (CAN_CHANGE(element))
4305       {
4306         for (i = 0; i < element_info[element].num_change_pages; i++)
4307         {
4308           // check for player created from custom element as single target
4309           content = element_info[element].change_page[i].target_element;
4310           is_player = ELEM_IS_PLAYER(content);
4311
4312           if (is_player && (found_rating < 3 ||
4313                             (found_rating == 3 && element < found_element)))
4314           {
4315             start_x = x;
4316             start_y = y;
4317
4318             found_rating = 3;
4319             found_element = element;
4320           }
4321         }
4322       }
4323
4324       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4325       {
4326         // check for player created from custom element as explosion content
4327         content = element_info[element].content.e[xx][yy];
4328         is_player = ELEM_IS_PLAYER(content);
4329
4330         if (is_player && (found_rating < 2 ||
4331                           (found_rating == 2 && element < found_element)))
4332         {
4333           start_x = x + xx - 1;
4334           start_y = y + yy - 1;
4335
4336           found_rating = 2;
4337           found_element = element;
4338         }
4339
4340         if (!CAN_CHANGE(element))
4341           continue;
4342
4343         for (i = 0; i < element_info[element].num_change_pages; i++)
4344         {
4345           // check for player created from custom element as extended target
4346           content =
4347             element_info[element].change_page[i].target_content.e[xx][yy];
4348
4349           is_player = ELEM_IS_PLAYER(content);
4350
4351           if (is_player && (found_rating < 1 ||
4352                             (found_rating == 1 && element < found_element)))
4353           {
4354             start_x = x + xx - 1;
4355             start_y = y + yy - 1;
4356
4357             found_rating = 1;
4358             found_element = element;
4359           }
4360         }
4361       }
4362     }
4363
4364     scroll_x = SCROLL_POSITION_X(start_x);
4365     scroll_y = SCROLL_POSITION_Y(start_y);
4366   }
4367   else
4368   {
4369     scroll_x = SCROLL_POSITION_X(local_player->jx);
4370     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4371   }
4372
4373   // !!! FIX THIS (START) !!!
4374   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4375   {
4376     InitGameEngine_EM();
4377   }
4378   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4379   {
4380     InitGameEngine_SP();
4381   }
4382   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4383   {
4384     InitGameEngine_MM();
4385   }
4386   else
4387   {
4388     DrawLevel(REDRAW_FIELD);
4389     DrawAllPlayers();
4390
4391     // after drawing the level, correct some elements
4392     if (game.timegate_time_left == 0)
4393       CloseAllOpenTimegates();
4394   }
4395
4396   // blit playfield from scroll buffer to normal back buffer for fading in
4397   BlitScreenToBitmap(backbuffer);
4398   // !!! FIX THIS (END) !!!
4399
4400   DrawMaskedBorder(fade_mask);
4401
4402   FadeIn(fade_mask);
4403
4404 #if 1
4405   // full screen redraw is required at this point in the following cases:
4406   // - special editor door undrawn when game was started from level editor
4407   // - drawing area (playfield) was changed and has to be removed completely
4408   redraw_mask = REDRAW_ALL;
4409   BackToFront();
4410 #endif
4411
4412   if (!game.restart_level)
4413   {
4414     // copy default game door content to main double buffer
4415
4416     // !!! CHECK AGAIN !!!
4417     SetPanelBackground();
4418     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4419     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4420   }
4421
4422   SetPanelBackground();
4423   SetDrawBackgroundMask(REDRAW_DOOR_1);
4424
4425   UpdateAndDisplayGameControlValues();
4426
4427   if (!game.restart_level)
4428   {
4429     UnmapGameButtons();
4430     UnmapTapeButtons();
4431
4432     FreeGameButtons();
4433     CreateGameButtons();
4434
4435     MapGameButtons();
4436     MapTapeButtons();
4437
4438     // copy actual game door content to door double buffer for OpenDoor()
4439     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4440
4441     OpenDoor(DOOR_OPEN_ALL);
4442
4443     KeyboardAutoRepeatOffUnlessAutoplay();
4444
4445 #if DEBUG_INIT_PLAYER
4446     DebugPrintPlayerStatus("Player status (final)");
4447 #endif
4448   }
4449
4450   UnmapAllGadgets();
4451
4452   MapGameButtons();
4453   MapTapeButtons();
4454
4455   if (!game.restart_level && !tape.playing)
4456   {
4457     LevelStats_incPlayed(level_nr);
4458
4459     SaveLevelSetup_SeriesInfo();
4460   }
4461
4462   game.restart_level = FALSE;
4463   game.restart_game_message = NULL;
4464
4465   game.request_active = FALSE;
4466   game.request_active_or_moving = FALSE;
4467
4468   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4469     InitGameActions_MM();
4470
4471   SaveEngineSnapshotToListInitial();
4472
4473   if (!game.restart_level)
4474   {
4475     PlaySound(SND_GAME_STARTING);
4476
4477     if (setup.sound_music)
4478       PlayLevelMusic();
4479   }
4480 }
4481
4482 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4483                         int actual_player_x, int actual_player_y)
4484 {
4485   // this is used for non-R'n'D game engines to update certain engine values
4486
4487   // needed to determine if sounds are played within the visible screen area
4488   scroll_x = actual_scroll_x;
4489   scroll_y = actual_scroll_y;
4490
4491   // needed to get player position for "follow finger" playing input method
4492   local_player->jx = actual_player_x;
4493   local_player->jy = actual_player_y;
4494 }
4495
4496 void InitMovDir(int x, int y)
4497 {
4498   int i, element = Tile[x][y];
4499   static int xy[4][2] =
4500   {
4501     {  0, +1 },
4502     { +1,  0 },
4503     {  0, -1 },
4504     { -1,  0 }
4505   };
4506   static int direction[3][4] =
4507   {
4508     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4509     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4510     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4511   };
4512
4513   switch (element)
4514   {
4515     case EL_BUG_RIGHT:
4516     case EL_BUG_UP:
4517     case EL_BUG_LEFT:
4518     case EL_BUG_DOWN:
4519       Tile[x][y] = EL_BUG;
4520       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4521       break;
4522
4523     case EL_SPACESHIP_RIGHT:
4524     case EL_SPACESHIP_UP:
4525     case EL_SPACESHIP_LEFT:
4526     case EL_SPACESHIP_DOWN:
4527       Tile[x][y] = EL_SPACESHIP;
4528       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4529       break;
4530
4531     case EL_BD_BUTTERFLY_RIGHT:
4532     case EL_BD_BUTTERFLY_UP:
4533     case EL_BD_BUTTERFLY_LEFT:
4534     case EL_BD_BUTTERFLY_DOWN:
4535       Tile[x][y] = EL_BD_BUTTERFLY;
4536       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4537       break;
4538
4539     case EL_BD_FIREFLY_RIGHT:
4540     case EL_BD_FIREFLY_UP:
4541     case EL_BD_FIREFLY_LEFT:
4542     case EL_BD_FIREFLY_DOWN:
4543       Tile[x][y] = EL_BD_FIREFLY;
4544       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4545       break;
4546
4547     case EL_PACMAN_RIGHT:
4548     case EL_PACMAN_UP:
4549     case EL_PACMAN_LEFT:
4550     case EL_PACMAN_DOWN:
4551       Tile[x][y] = EL_PACMAN;
4552       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4553       break;
4554
4555     case EL_YAMYAM_LEFT:
4556     case EL_YAMYAM_RIGHT:
4557     case EL_YAMYAM_UP:
4558     case EL_YAMYAM_DOWN:
4559       Tile[x][y] = EL_YAMYAM;
4560       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4561       break;
4562
4563     case EL_SP_SNIKSNAK:
4564       MovDir[x][y] = MV_UP;
4565       break;
4566
4567     case EL_SP_ELECTRON:
4568       MovDir[x][y] = MV_LEFT;
4569       break;
4570
4571     case EL_MOLE_LEFT:
4572     case EL_MOLE_RIGHT:
4573     case EL_MOLE_UP:
4574     case EL_MOLE_DOWN:
4575       Tile[x][y] = EL_MOLE;
4576       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4577       break;
4578
4579     case EL_SPRING_LEFT:
4580     case EL_SPRING_RIGHT:
4581       Tile[x][y] = EL_SPRING;
4582       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4583       break;
4584
4585     default:
4586       if (IS_CUSTOM_ELEMENT(element))
4587       {
4588         struct ElementInfo *ei = &element_info[element];
4589         int move_direction_initial = ei->move_direction_initial;
4590         int move_pattern = ei->move_pattern;
4591
4592         if (move_direction_initial == MV_START_PREVIOUS)
4593         {
4594           if (MovDir[x][y] != MV_NONE)
4595             return;
4596
4597           move_direction_initial = MV_START_AUTOMATIC;
4598         }
4599
4600         if (move_direction_initial == MV_START_RANDOM)
4601           MovDir[x][y] = 1 << RND(4);
4602         else if (move_direction_initial & MV_ANY_DIRECTION)
4603           MovDir[x][y] = move_direction_initial;
4604         else if (move_pattern == MV_ALL_DIRECTIONS ||
4605                  move_pattern == MV_TURNING_LEFT ||
4606                  move_pattern == MV_TURNING_RIGHT ||
4607                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4608                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4609                  move_pattern == MV_TURNING_RANDOM)
4610           MovDir[x][y] = 1 << RND(4);
4611         else if (move_pattern == MV_HORIZONTAL)
4612           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4613         else if (move_pattern == MV_VERTICAL)
4614           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4615         else if (move_pattern & MV_ANY_DIRECTION)
4616           MovDir[x][y] = element_info[element].move_pattern;
4617         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4618                  move_pattern == MV_ALONG_RIGHT_SIDE)
4619         {
4620           // use random direction as default start direction
4621           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4622             MovDir[x][y] = 1 << RND(4);
4623
4624           for (i = 0; i < NUM_DIRECTIONS; i++)
4625           {
4626             int x1 = x + xy[i][0];
4627             int y1 = y + xy[i][1];
4628
4629             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4630             {
4631               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4632                 MovDir[x][y] = direction[0][i];
4633               else
4634                 MovDir[x][y] = direction[1][i];
4635
4636               break;
4637             }
4638           }
4639         }                
4640       }
4641       else
4642       {
4643         MovDir[x][y] = 1 << RND(4);
4644
4645         if (element != EL_BUG &&
4646             element != EL_SPACESHIP &&
4647             element != EL_BD_BUTTERFLY &&
4648             element != EL_BD_FIREFLY)
4649           break;
4650
4651         for (i = 0; i < NUM_DIRECTIONS; i++)
4652         {
4653           int x1 = x + xy[i][0];
4654           int y1 = y + xy[i][1];
4655
4656           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4657           {
4658             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4659             {
4660               MovDir[x][y] = direction[0][i];
4661               break;
4662             }
4663             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4664                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4665             {
4666               MovDir[x][y] = direction[1][i];
4667               break;
4668             }
4669           }
4670         }
4671       }
4672       break;
4673   }
4674
4675   GfxDir[x][y] = MovDir[x][y];
4676 }
4677
4678 void InitAmoebaNr(int x, int y)
4679 {
4680   int i;
4681   int group_nr = AmoebaNeighbourNr(x, y);
4682
4683   if (group_nr == 0)
4684   {
4685     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4686     {
4687       if (AmoebaCnt[i] == 0)
4688       {
4689         group_nr = i;
4690         break;
4691       }
4692     }
4693   }
4694
4695   AmoebaNr[x][y] = group_nr;
4696   AmoebaCnt[group_nr]++;
4697   AmoebaCnt2[group_nr]++;
4698 }
4699
4700 static void LevelSolved(void)
4701 {
4702   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4703       game.players_still_needed > 0)
4704     return;
4705
4706   game.LevelSolved = TRUE;
4707   game.GameOver = TRUE;
4708
4709   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4710                       game_em.lev->score :
4711                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4712                       game_mm.score :
4713                       game.score);
4714   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4715                        MM_HEALTH(game_mm.laser_overload_value) :
4716                        game.health);
4717
4718   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4719   game.LevelSolved_CountingScore = game.score_final;
4720   game.LevelSolved_CountingHealth = game.health_final;
4721 }
4722
4723 void GameWon(void)
4724 {
4725   static int time_count_steps;
4726   static int time, time_final;
4727   static float score, score_final; // needed for time score < 10 for 10 seconds
4728   static int health, health_final;
4729   static int game_over_delay_1 = 0;
4730   static int game_over_delay_2 = 0;
4731   static int game_over_delay_3 = 0;
4732   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4733   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4734
4735   if (!game.LevelSolved_GameWon)
4736   {
4737     int i;
4738
4739     // do not start end game actions before the player stops moving (to exit)
4740     if (local_player->active && local_player->MovPos)
4741       return;
4742
4743     game.LevelSolved_GameWon = TRUE;
4744     game.LevelSolved_SaveTape = tape.recording;
4745     game.LevelSolved_SaveScore = !tape.playing;
4746
4747     if (!tape.playing)
4748     {
4749       LevelStats_incSolved(level_nr);
4750
4751       SaveLevelSetup_SeriesInfo();
4752     }
4753
4754     if (tape.auto_play)         // tape might already be stopped here
4755       tape.auto_play_level_solved = TRUE;
4756
4757     TapeStop();
4758
4759     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4760     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4761     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4762
4763     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4764     score = score_final = game.score_final;
4765     health = health_final = game.health_final;
4766
4767     if (time_score > 0)
4768     {
4769       int time_frames = 0;
4770
4771       if (TimeLeft > 0)
4772       {
4773         time_final = 0;
4774         time_frames = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4775       }
4776       else if (game.no_time_limit && TimePlayed < 999)
4777       {
4778         time_final = 999;
4779         time_frames = (999 - TimePlayed) * FRAMES_PER_SECOND - TimeFrames;
4780       }
4781
4782       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4783
4784       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4785
4786       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4787       {
4788         health_final = 0;
4789         score_final += health * time_score;
4790       }
4791
4792       game.score_final = score_final;
4793       game.health_final = health_final;
4794     }
4795
4796     if (level_editor_test_game || !setup.count_score_after_game)
4797     {
4798       time = time_final;
4799       score = score_final;
4800
4801       game.LevelSolved_CountingTime = time;
4802       game.LevelSolved_CountingScore = score;
4803
4804       game_panel_controls[GAME_PANEL_TIME].value = time;
4805       game_panel_controls[GAME_PANEL_SCORE].value = score;
4806
4807       DisplayGameControlValues();
4808     }
4809
4810     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4811     {
4812       // check if last player has left the level
4813       if (game.exit_x >= 0 &&
4814           game.exit_y >= 0)
4815       {
4816         int x = game.exit_x;
4817         int y = game.exit_y;
4818         int element = Tile[x][y];
4819
4820         // close exit door after last player
4821         if ((game.all_players_gone &&
4822              (element == EL_EXIT_OPEN ||
4823               element == EL_SP_EXIT_OPEN ||
4824               element == EL_STEEL_EXIT_OPEN)) ||
4825             element == EL_EM_EXIT_OPEN ||
4826             element == EL_EM_STEEL_EXIT_OPEN)
4827         {
4828
4829           Tile[x][y] =
4830             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4831              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4832              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4833              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4834              EL_EM_STEEL_EXIT_CLOSING);
4835
4836           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4837         }
4838
4839         // player disappears
4840         DrawLevelField(x, y);
4841       }
4842
4843       for (i = 0; i < MAX_PLAYERS; i++)
4844       {
4845         struct PlayerInfo *player = &stored_player[i];
4846
4847         if (player->present)
4848         {
4849           RemovePlayer(player);
4850
4851           // player disappears
4852           DrawLevelField(player->jx, player->jy);
4853         }
4854       }
4855     }
4856
4857     PlaySound(SND_GAME_WINNING);
4858   }
4859
4860   if (setup.count_score_after_game)
4861   {
4862     if (time != time_final)
4863     {
4864       if (game_over_delay_1 > 0)
4865       {
4866         game_over_delay_1--;
4867
4868         return;
4869       }
4870
4871       int time_to_go = ABS(time_final - time);
4872       int time_count_dir = (time < time_final ? +1 : -1);
4873
4874       if (time_to_go < time_count_steps)
4875         time_count_steps = 1;
4876
4877       time  += time_count_steps * time_count_dir;
4878       score += time_count_steps * time_score;
4879
4880       // set final score to correct rounding differences after counting score
4881       if (time == time_final)
4882         score = score_final;
4883
4884       game.LevelSolved_CountingTime = time;
4885       game.LevelSolved_CountingScore = score;
4886
4887       game_panel_controls[GAME_PANEL_TIME].value = time;
4888       game_panel_controls[GAME_PANEL_SCORE].value = score;
4889
4890       DisplayGameControlValues();
4891
4892       if (time == time_final)
4893         StopSound(SND_GAME_LEVELTIME_BONUS);
4894       else if (setup.sound_loops)
4895         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4896       else
4897         PlaySound(SND_GAME_LEVELTIME_BONUS);
4898
4899       return;
4900     }
4901
4902     if (health != health_final)
4903     {
4904       if (game_over_delay_2 > 0)
4905       {
4906         game_over_delay_2--;
4907
4908         return;
4909       }
4910
4911       int health_count_dir = (health < health_final ? +1 : -1);
4912
4913       health += health_count_dir;
4914       score  += time_score;
4915
4916       game.LevelSolved_CountingHealth = health;
4917       game.LevelSolved_CountingScore = score;
4918
4919       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4920       game_panel_controls[GAME_PANEL_SCORE].value = score;
4921
4922       DisplayGameControlValues();
4923
4924       if (health == health_final)
4925         StopSound(SND_GAME_LEVELTIME_BONUS);
4926       else if (setup.sound_loops)
4927         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4928       else
4929         PlaySound(SND_GAME_LEVELTIME_BONUS);
4930
4931       return;
4932     }
4933   }
4934
4935   game.panel.active = FALSE;
4936
4937   if (game_over_delay_3 > 0)
4938   {
4939     game_over_delay_3--;
4940
4941     return;
4942   }
4943
4944   GameEnd();
4945 }
4946
4947 void GameEnd(void)
4948 {
4949   // used instead of "level_nr" (needed for network games)
4950   int last_level_nr = levelset.level_nr;
4951   int hi_pos;
4952
4953   game.LevelSolved_GameEnd = TRUE;
4954
4955   if (game.LevelSolved_SaveTape)
4956   {
4957     // make sure that request dialog to save tape does not open door again
4958     if (!global.use_envelope_request)
4959       CloseDoor(DOOR_CLOSE_1);
4960
4961     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4962   }
4963
4964   // if no tape is to be saved, close both doors simultaneously
4965   CloseDoor(DOOR_CLOSE_ALL);
4966
4967   if (level_editor_test_game)
4968   {
4969     SetGameStatus(GAME_MODE_MAIN);
4970
4971     DrawMainMenu();
4972
4973     return;
4974   }
4975
4976   if (!game.LevelSolved_SaveScore)
4977   {
4978     SetGameStatus(GAME_MODE_MAIN);
4979
4980     DrawMainMenu();
4981
4982     return;
4983   }
4984
4985   if (level_nr == leveldir_current->handicap_level)
4986   {
4987     leveldir_current->handicap_level++;
4988
4989     SaveLevelSetup_SeriesInfo();
4990   }
4991
4992   if (setup.increment_levels &&
4993       level_nr < leveldir_current->last_level &&
4994       !network_playing)
4995   {
4996     level_nr++;         // advance to next level
4997     TapeErase();        // start with empty tape
4998
4999     if (setup.auto_play_next_level)
5000     {
5001       LoadLevel(level_nr);
5002
5003       SaveLevelSetup_SeriesInfo();
5004     }
5005   }
5006
5007   hi_pos = NewHiScore(last_level_nr);
5008
5009   if (hi_pos >= 0 && setup.show_scores_after_game)
5010   {
5011     SetGameStatus(GAME_MODE_SCORES);
5012
5013     DrawHallOfFame(last_level_nr, hi_pos);
5014   }
5015   else if (setup.auto_play_next_level && setup.increment_levels &&
5016            last_level_nr < leveldir_current->last_level &&
5017            !network_playing)
5018   {
5019     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5020   }
5021   else
5022   {
5023     SetGameStatus(GAME_MODE_MAIN);
5024
5025     DrawMainMenu();
5026   }
5027 }
5028
5029 int NewHiScore(int level_nr)
5030 {
5031   int k, l;
5032   int position = -1;
5033   boolean one_score_entry_per_name = !program.many_scores_per_name;
5034
5035   LoadScore(level_nr);
5036
5037   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5038       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5039     return -1;
5040
5041   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5042   {
5043     if (game.score_final > highscore[k].Score)
5044     {
5045       // player has made it to the hall of fame
5046
5047       if (k < MAX_SCORE_ENTRIES - 1)
5048       {
5049         int m = MAX_SCORE_ENTRIES - 1;
5050
5051         if (one_score_entry_per_name)
5052         {
5053           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5054             if (strEqual(setup.player_name, highscore[l].Name))
5055               m = l;
5056
5057           if (m == k)   // player's new highscore overwrites his old one
5058             goto put_into_list;
5059         }
5060
5061         for (l = m; l > k; l--)
5062         {
5063           strcpy(highscore[l].Name, highscore[l - 1].Name);
5064           highscore[l].Score = highscore[l - 1].Score;
5065         }
5066       }
5067
5068       put_into_list:
5069
5070       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5071       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5072       highscore[k].Score = game.score_final;
5073       position = k;
5074
5075       break;
5076     }
5077     else if (one_score_entry_per_name &&
5078              !strncmp(setup.player_name, highscore[k].Name,
5079                       MAX_PLAYER_NAME_LEN))
5080       break;    // player already there with a higher score
5081   }
5082
5083   if (position >= 0) 
5084     SaveScore(level_nr);
5085
5086   return position;
5087 }
5088
5089 static int getElementMoveStepsizeExt(int x, int y, int direction)
5090 {
5091   int element = Tile[x][y];
5092   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5093   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5094   int horiz_move = (dx != 0);
5095   int sign = (horiz_move ? dx : dy);
5096   int step = sign * element_info[element].move_stepsize;
5097
5098   // special values for move stepsize for spring and things on conveyor belt
5099   if (horiz_move)
5100   {
5101     if (CAN_FALL(element) &&
5102         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5103       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5104     else if (element == EL_SPRING)
5105       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5106   }
5107
5108   return step;
5109 }
5110
5111 static int getElementMoveStepsize(int x, int y)
5112 {
5113   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5114 }
5115
5116 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5117 {
5118   if (player->GfxAction != action || player->GfxDir != dir)
5119   {
5120     player->GfxAction = action;
5121     player->GfxDir = dir;
5122     player->Frame = 0;
5123     player->StepFrame = 0;
5124   }
5125 }
5126
5127 static void ResetGfxFrame(int x, int y)
5128 {
5129   // profiling showed that "autotest" spends 10~20% of its time in this function
5130   if (DrawingDeactivatedField())
5131     return;
5132
5133   int element = Tile[x][y];
5134   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5135
5136   if (graphic_info[graphic].anim_global_sync)
5137     GfxFrame[x][y] = FrameCounter;
5138   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5139     GfxFrame[x][y] = CustomValue[x][y];
5140   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5141     GfxFrame[x][y] = element_info[element].collect_score;
5142   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5143     GfxFrame[x][y] = ChangeDelay[x][y];
5144 }
5145
5146 static void ResetGfxAnimation(int x, int y)
5147 {
5148   GfxAction[x][y] = ACTION_DEFAULT;
5149   GfxDir[x][y] = MovDir[x][y];
5150   GfxFrame[x][y] = 0;
5151
5152   ResetGfxFrame(x, y);
5153 }
5154
5155 static void ResetRandomAnimationValue(int x, int y)
5156 {
5157   GfxRandom[x][y] = INIT_GFX_RANDOM();
5158 }
5159
5160 static void InitMovingField(int x, int y, int direction)
5161 {
5162   int element = Tile[x][y];
5163   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5164   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5165   int newx = x + dx;
5166   int newy = y + dy;
5167   boolean is_moving_before, is_moving_after;
5168
5169   // check if element was/is moving or being moved before/after mode change
5170   is_moving_before = (WasJustMoving[x][y] != 0);
5171   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5172
5173   // reset animation only for moving elements which change direction of moving
5174   // or which just started or stopped moving
5175   // (else CEs with property "can move" / "not moving" are reset each frame)
5176   if (is_moving_before != is_moving_after ||
5177       direction != MovDir[x][y])
5178     ResetGfxAnimation(x, y);
5179
5180   MovDir[x][y] = direction;
5181   GfxDir[x][y] = direction;
5182
5183   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5184                      direction == MV_DOWN && CAN_FALL(element) ?
5185                      ACTION_FALLING : ACTION_MOVING);
5186
5187   // this is needed for CEs with property "can move" / "not moving"
5188
5189   if (is_moving_after)
5190   {
5191     if (Tile[newx][newy] == EL_EMPTY)
5192       Tile[newx][newy] = EL_BLOCKED;
5193
5194     MovDir[newx][newy] = MovDir[x][y];
5195
5196     CustomValue[newx][newy] = CustomValue[x][y];
5197
5198     GfxFrame[newx][newy] = GfxFrame[x][y];
5199     GfxRandom[newx][newy] = GfxRandom[x][y];
5200     GfxAction[newx][newy] = GfxAction[x][y];
5201     GfxDir[newx][newy] = GfxDir[x][y];
5202   }
5203 }
5204
5205 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5206 {
5207   int direction = MovDir[x][y];
5208   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5209   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5210
5211   *goes_to_x = newx;
5212   *goes_to_y = newy;
5213 }
5214
5215 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5216 {
5217   int oldx = x, oldy = y;
5218   int direction = MovDir[x][y];
5219
5220   if (direction == MV_LEFT)
5221     oldx++;
5222   else if (direction == MV_RIGHT)
5223     oldx--;
5224   else if (direction == MV_UP)
5225     oldy++;
5226   else if (direction == MV_DOWN)
5227     oldy--;
5228
5229   *comes_from_x = oldx;
5230   *comes_from_y = oldy;
5231 }
5232
5233 static int MovingOrBlocked2Element(int x, int y)
5234 {
5235   int element = Tile[x][y];
5236
5237   if (element == EL_BLOCKED)
5238   {
5239     int oldx, oldy;
5240
5241     Blocked2Moving(x, y, &oldx, &oldy);
5242     return Tile[oldx][oldy];
5243   }
5244   else
5245     return element;
5246 }
5247
5248 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5249 {
5250   // like MovingOrBlocked2Element(), but if element is moving
5251   // and (x,y) is the field the moving element is just leaving,
5252   // return EL_BLOCKED instead of the element value
5253   int element = Tile[x][y];
5254
5255   if (IS_MOVING(x, y))
5256   {
5257     if (element == EL_BLOCKED)
5258     {
5259       int oldx, oldy;
5260
5261       Blocked2Moving(x, y, &oldx, &oldy);
5262       return Tile[oldx][oldy];
5263     }
5264     else
5265       return EL_BLOCKED;
5266   }
5267   else
5268     return element;
5269 }
5270
5271 static void RemoveField(int x, int y)
5272 {
5273   Tile[x][y] = EL_EMPTY;
5274
5275   MovPos[x][y] = 0;
5276   MovDir[x][y] = 0;
5277   MovDelay[x][y] = 0;
5278
5279   CustomValue[x][y] = 0;
5280
5281   AmoebaNr[x][y] = 0;
5282   ChangeDelay[x][y] = 0;
5283   ChangePage[x][y] = -1;
5284   Pushed[x][y] = FALSE;
5285
5286   GfxElement[x][y] = EL_UNDEFINED;
5287   GfxAction[x][y] = ACTION_DEFAULT;
5288   GfxDir[x][y] = MV_NONE;
5289 }
5290
5291 static void RemoveMovingField(int x, int y)
5292 {
5293   int oldx = x, oldy = y, newx = x, newy = y;
5294   int element = Tile[x][y];
5295   int next_element = EL_UNDEFINED;
5296
5297   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5298     return;
5299
5300   if (IS_MOVING(x, y))
5301   {
5302     Moving2Blocked(x, y, &newx, &newy);
5303
5304     if (Tile[newx][newy] != EL_BLOCKED)
5305     {
5306       // element is moving, but target field is not free (blocked), but
5307       // already occupied by something different (example: acid pool);
5308       // in this case, only remove the moving field, but not the target
5309
5310       RemoveField(oldx, oldy);
5311
5312       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5313
5314       TEST_DrawLevelField(oldx, oldy);
5315
5316       return;
5317     }
5318   }
5319   else if (element == EL_BLOCKED)
5320   {
5321     Blocked2Moving(x, y, &oldx, &oldy);
5322     if (!IS_MOVING(oldx, oldy))
5323       return;
5324   }
5325
5326   if (element == EL_BLOCKED &&
5327       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5328        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5329        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5330        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5331        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5332        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5333     next_element = get_next_element(Tile[oldx][oldy]);
5334
5335   RemoveField(oldx, oldy);
5336   RemoveField(newx, newy);
5337
5338   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5339
5340   if (next_element != EL_UNDEFINED)
5341     Tile[oldx][oldy] = next_element;
5342
5343   TEST_DrawLevelField(oldx, oldy);
5344   TEST_DrawLevelField(newx, newy);
5345 }
5346
5347 void DrawDynamite(int x, int y)
5348 {
5349   int sx = SCREENX(x), sy = SCREENY(y);
5350   int graphic = el2img(Tile[x][y]);
5351   int frame;
5352
5353   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5354     return;
5355
5356   if (IS_WALKABLE_INSIDE(Back[x][y]))
5357     return;
5358
5359   if (Back[x][y])
5360     DrawLevelElement(x, y, Back[x][y]);
5361   else if (Store[x][y])
5362     DrawLevelElement(x, y, Store[x][y]);
5363   else if (game.use_masked_elements)
5364     DrawLevelElement(x, y, EL_EMPTY);
5365
5366   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5367
5368   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5369     DrawGraphicThruMask(sx, sy, graphic, frame);
5370   else
5371     DrawGraphic(sx, sy, graphic, frame);
5372 }
5373
5374 static void CheckDynamite(int x, int y)
5375 {
5376   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5377   {
5378     MovDelay[x][y]--;
5379
5380     if (MovDelay[x][y] != 0)
5381     {
5382       DrawDynamite(x, y);
5383       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5384
5385       return;
5386     }
5387   }
5388
5389   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5390
5391   Bang(x, y);
5392 }
5393
5394 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5395 {
5396   boolean num_checked_players = 0;
5397   int i;
5398
5399   for (i = 0; i < MAX_PLAYERS; i++)
5400   {
5401     if (stored_player[i].active)
5402     {
5403       int sx = stored_player[i].jx;
5404       int sy = stored_player[i].jy;
5405
5406       if (num_checked_players == 0)
5407       {
5408         *sx1 = *sx2 = sx;
5409         *sy1 = *sy2 = sy;
5410       }
5411       else
5412       {
5413         *sx1 = MIN(*sx1, sx);
5414         *sy1 = MIN(*sy1, sy);
5415         *sx2 = MAX(*sx2, sx);
5416         *sy2 = MAX(*sy2, sy);
5417       }
5418
5419       num_checked_players++;
5420     }
5421   }
5422 }
5423
5424 static boolean checkIfAllPlayersFitToScreen_RND(void)
5425 {
5426   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5427
5428   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5429
5430   return (sx2 - sx1 < SCR_FIELDX &&
5431           sy2 - sy1 < SCR_FIELDY);
5432 }
5433
5434 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5435 {
5436   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5437
5438   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5439
5440   *sx = (sx1 + sx2) / 2;
5441   *sy = (sy1 + sy2) / 2;
5442 }
5443
5444 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5445                                boolean center_screen, boolean quick_relocation)
5446 {
5447   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5448   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5449   boolean no_delay = (tape.warp_forward);
5450   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5451   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5452   int new_scroll_x, new_scroll_y;
5453
5454   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5455   {
5456     // case 1: quick relocation inside visible screen (without scrolling)
5457
5458     RedrawPlayfield();
5459
5460     return;
5461   }
5462
5463   if (!level.shifted_relocation || center_screen)
5464   {
5465     // relocation _with_ centering of screen
5466
5467     new_scroll_x = SCROLL_POSITION_X(x);
5468     new_scroll_y = SCROLL_POSITION_Y(y);
5469   }
5470   else
5471   {
5472     // relocation _without_ centering of screen
5473
5474     int center_scroll_x = SCROLL_POSITION_X(old_x);
5475     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5476     int offset_x = x + (scroll_x - center_scroll_x);
5477     int offset_y = y + (scroll_y - center_scroll_y);
5478
5479     // for new screen position, apply previous offset to center position
5480     new_scroll_x = SCROLL_POSITION_X(offset_x);
5481     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5482   }
5483
5484   if (quick_relocation)
5485   {
5486     // case 2: quick relocation (redraw without visible scrolling)
5487
5488     scroll_x = new_scroll_x;
5489     scroll_y = new_scroll_y;
5490
5491     RedrawPlayfield();
5492
5493     return;
5494   }
5495
5496   // case 3: visible relocation (with scrolling to new position)
5497
5498   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5499
5500   SetVideoFrameDelay(wait_delay_value);
5501
5502   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5503   {
5504     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5505     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5506
5507     if (dx == 0 && dy == 0)             // no scrolling needed at all
5508       break;
5509
5510     scroll_x -= dx;
5511     scroll_y -= dy;
5512
5513     // set values for horizontal/vertical screen scrolling (half tile size)
5514     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5515     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5516     int pos_x = dx * TILEX / 2;
5517     int pos_y = dy * TILEY / 2;
5518     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5519     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5520
5521     ScrollLevel(dx, dy);
5522     DrawAllPlayers();
5523
5524     // scroll in two steps of half tile size to make things smoother
5525     BlitScreenToBitmapExt_RND(window, fx, fy);
5526
5527     // scroll second step to align at full tile size
5528     BlitScreenToBitmap(window);
5529   }
5530
5531   DrawAllPlayers();
5532   BackToFront();
5533
5534   SetVideoFrameDelay(frame_delay_value_old);
5535 }
5536
5537 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5538 {
5539   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5540   int player_nr = GET_PLAYER_NR(el_player);
5541   struct PlayerInfo *player = &stored_player[player_nr];
5542   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5543   boolean no_delay = (tape.warp_forward);
5544   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5545   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5546   int old_jx = player->jx;
5547   int old_jy = player->jy;
5548   int old_element = Tile[old_jx][old_jy];
5549   int element = Tile[jx][jy];
5550   boolean player_relocated = (old_jx != jx || old_jy != jy);
5551
5552   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5553   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5554   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5555   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5556   int leave_side_horiz = move_dir_horiz;
5557   int leave_side_vert  = move_dir_vert;
5558   int enter_side = enter_side_horiz | enter_side_vert;
5559   int leave_side = leave_side_horiz | leave_side_vert;
5560
5561   if (player->buried)           // do not reanimate dead player
5562     return;
5563
5564   if (!player_relocated)        // no need to relocate the player
5565     return;
5566
5567   if (IS_PLAYER(jx, jy))        // player already placed at new position
5568   {
5569     RemoveField(jx, jy);        // temporarily remove newly placed player
5570     DrawLevelField(jx, jy);
5571   }
5572
5573   if (player->present)
5574   {
5575     while (player->MovPos)
5576     {
5577       ScrollPlayer(player, SCROLL_GO_ON);
5578       ScrollScreen(NULL, SCROLL_GO_ON);
5579
5580       AdvanceFrameAndPlayerCounters(player->index_nr);
5581
5582       DrawPlayer(player);
5583
5584       BackToFront_WithFrameDelay(wait_delay_value);
5585     }
5586
5587     DrawPlayer(player);         // needed here only to cleanup last field
5588     DrawLevelField(player->jx, player->jy);     // remove player graphic
5589
5590     player->is_moving = FALSE;
5591   }
5592
5593   if (IS_CUSTOM_ELEMENT(old_element))
5594     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5595                                CE_LEFT_BY_PLAYER,
5596                                player->index_bit, leave_side);
5597
5598   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5599                                       CE_PLAYER_LEAVES_X,
5600                                       player->index_bit, leave_side);
5601
5602   Tile[jx][jy] = el_player;
5603   InitPlayerField(jx, jy, el_player, TRUE);
5604
5605   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5606      possible that the relocation target field did not contain a player element,
5607      but a walkable element, to which the new player was relocated -- in this
5608      case, restore that (already initialized!) element on the player field */
5609   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5610   {
5611     Tile[jx][jy] = element;     // restore previously existing element
5612   }
5613
5614   // only visually relocate centered player
5615   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5616                      FALSE, level.instant_relocation);
5617
5618   TestIfPlayerTouchesBadThing(jx, jy);
5619   TestIfPlayerTouchesCustomElement(jx, jy);
5620
5621   if (IS_CUSTOM_ELEMENT(element))
5622     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5623                                player->index_bit, enter_side);
5624
5625   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5626                                       player->index_bit, enter_side);
5627
5628   if (player->is_switching)
5629   {
5630     /* ensure that relocation while still switching an element does not cause
5631        a new element to be treated as also switched directly after relocation
5632        (this is important for teleporter switches that teleport the player to
5633        a place where another teleporter switch is in the same direction, which
5634        would then incorrectly be treated as immediately switched before the
5635        direction key that caused the switch was released) */
5636
5637     player->switch_x += jx - old_jx;
5638     player->switch_y += jy - old_jy;
5639   }
5640 }
5641
5642 static void Explode(int ex, int ey, int phase, int mode)
5643 {
5644   int x, y;
5645   int last_phase;
5646   int border_element;
5647
5648   // !!! eliminate this variable !!!
5649   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5650
5651   if (game.explosions_delayed)
5652   {
5653     ExplodeField[ex][ey] = mode;
5654     return;
5655   }
5656
5657   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5658   {
5659     int center_element = Tile[ex][ey];
5660     int artwork_element, explosion_element;     // set these values later
5661
5662     // remove things displayed in background while burning dynamite
5663     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5664       Back[ex][ey] = 0;
5665
5666     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5667     {
5668       // put moving element to center field (and let it explode there)
5669       center_element = MovingOrBlocked2Element(ex, ey);
5670       RemoveMovingField(ex, ey);
5671       Tile[ex][ey] = center_element;
5672     }
5673
5674     // now "center_element" is finally determined -- set related values now
5675     artwork_element = center_element;           // for custom player artwork
5676     explosion_element = center_element;         // for custom player artwork
5677
5678     if (IS_PLAYER(ex, ey))
5679     {
5680       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5681
5682       artwork_element = stored_player[player_nr].artwork_element;
5683
5684       if (level.use_explosion_element[player_nr])
5685       {
5686         explosion_element = level.explosion_element[player_nr];
5687         artwork_element = explosion_element;
5688       }
5689     }
5690
5691     if (mode == EX_TYPE_NORMAL ||
5692         mode == EX_TYPE_CENTER ||
5693         mode == EX_TYPE_CROSS)
5694       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5695
5696     last_phase = element_info[explosion_element].explosion_delay + 1;
5697
5698     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5699     {
5700       int xx = x - ex + 1;
5701       int yy = y - ey + 1;
5702       int element;
5703
5704       if (!IN_LEV_FIELD(x, y) ||
5705           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5706           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5707         continue;
5708
5709       element = Tile[x][y];
5710
5711       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5712       {
5713         element = MovingOrBlocked2Element(x, y);
5714
5715         if (!IS_EXPLOSION_PROOF(element))
5716           RemoveMovingField(x, y);
5717       }
5718
5719       // indestructible elements can only explode in center (but not flames)
5720       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5721                                            mode == EX_TYPE_BORDER)) ||
5722           element == EL_FLAMES)
5723         continue;
5724
5725       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5726          behaviour, for example when touching a yamyam that explodes to rocks
5727          with active deadly shield, a rock is created under the player !!! */
5728       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5729 #if 0
5730       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5731           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5732            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5733 #else
5734       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5735 #endif
5736       {
5737         if (IS_ACTIVE_BOMB(element))
5738         {
5739           // re-activate things under the bomb like gate or penguin
5740           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5741           Back[x][y] = 0;
5742         }
5743
5744         continue;
5745       }
5746
5747       // save walkable background elements while explosion on same tile
5748       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5749           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5750         Back[x][y] = element;
5751
5752       // ignite explodable elements reached by other explosion
5753       if (element == EL_EXPLOSION)
5754         element = Store2[x][y];
5755
5756       if (AmoebaNr[x][y] &&
5757           (element == EL_AMOEBA_FULL ||
5758            element == EL_BD_AMOEBA ||
5759            element == EL_AMOEBA_GROWING))
5760       {
5761         AmoebaCnt[AmoebaNr[x][y]]--;
5762         AmoebaCnt2[AmoebaNr[x][y]]--;
5763       }
5764
5765       RemoveField(x, y);
5766
5767       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5768       {
5769         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5770
5771         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5772
5773         if (PLAYERINFO(ex, ey)->use_murphy)
5774           Store[x][y] = EL_EMPTY;
5775       }
5776
5777       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5778       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5779       else if (ELEM_IS_PLAYER(center_element))
5780         Store[x][y] = EL_EMPTY;
5781       else if (center_element == EL_YAMYAM)
5782         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5783       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5784         Store[x][y] = element_info[center_element].content.e[xx][yy];
5785 #if 1
5786       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5787       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5788       // otherwise) -- FIX THIS !!!
5789       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5790         Store[x][y] = element_info[element].content.e[1][1];
5791 #else
5792       else if (!CAN_EXPLODE(element))
5793         Store[x][y] = element_info[element].content.e[1][1];
5794 #endif
5795       else
5796         Store[x][y] = EL_EMPTY;
5797
5798       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5799           center_element == EL_AMOEBA_TO_DIAMOND)
5800         Store2[x][y] = element;
5801
5802       Tile[x][y] = EL_EXPLOSION;
5803       GfxElement[x][y] = artwork_element;
5804
5805       ExplodePhase[x][y] = 1;
5806       ExplodeDelay[x][y] = last_phase;
5807
5808       Stop[x][y] = TRUE;
5809     }
5810
5811     if (center_element == EL_YAMYAM)
5812       game.yamyam_content_nr =
5813         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5814
5815     return;
5816   }
5817
5818   if (Stop[ex][ey])
5819     return;
5820
5821   x = ex;
5822   y = ey;
5823
5824   if (phase == 1)
5825     GfxFrame[x][y] = 0;         // restart explosion animation
5826
5827   last_phase = ExplodeDelay[x][y];
5828
5829   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5830
5831   // this can happen if the player leaves an explosion just in time
5832   if (GfxElement[x][y] == EL_UNDEFINED)
5833     GfxElement[x][y] = EL_EMPTY;
5834
5835   border_element = Store2[x][y];
5836   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5837     border_element = StorePlayer[x][y];
5838
5839   if (phase == element_info[border_element].ignition_delay ||
5840       phase == last_phase)
5841   {
5842     boolean border_explosion = FALSE;
5843
5844     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5845         !PLAYER_EXPLOSION_PROTECTED(x, y))
5846     {
5847       KillPlayerUnlessExplosionProtected(x, y);
5848       border_explosion = TRUE;
5849     }
5850     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5851     {
5852       Tile[x][y] = Store2[x][y];
5853       Store2[x][y] = 0;
5854       Bang(x, y);
5855       border_explosion = TRUE;
5856     }
5857     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5858     {
5859       AmoebaToDiamond(x, y);
5860       Store2[x][y] = 0;
5861       border_explosion = TRUE;
5862     }
5863
5864     // if an element just explodes due to another explosion (chain-reaction),
5865     // do not immediately end the new explosion when it was the last frame of
5866     // the explosion (as it would be done in the following "if"-statement!)
5867     if (border_explosion && phase == last_phase)
5868       return;
5869   }
5870
5871   if (phase == last_phase)
5872   {
5873     int element;
5874
5875     element = Tile[x][y] = Store[x][y];
5876     Store[x][y] = Store2[x][y] = 0;
5877     GfxElement[x][y] = EL_UNDEFINED;
5878
5879     // player can escape from explosions and might therefore be still alive
5880     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5881         element <= EL_PLAYER_IS_EXPLODING_4)
5882     {
5883       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5884       int explosion_element = EL_PLAYER_1 + player_nr;
5885       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5886       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5887
5888       if (level.use_explosion_element[player_nr])
5889         explosion_element = level.explosion_element[player_nr];
5890
5891       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5892                     element_info[explosion_element].content.e[xx][yy]);
5893     }
5894
5895     // restore probably existing indestructible background element
5896     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5897       element = Tile[x][y] = Back[x][y];
5898     Back[x][y] = 0;
5899
5900     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5901     GfxDir[x][y] = MV_NONE;
5902     ChangeDelay[x][y] = 0;
5903     ChangePage[x][y] = -1;
5904
5905     CustomValue[x][y] = 0;
5906
5907     InitField_WithBug2(x, y, FALSE);
5908
5909     TEST_DrawLevelField(x, y);
5910
5911     TestIfElementTouchesCustomElement(x, y);
5912
5913     if (GFX_CRUMBLED(element))
5914       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5915
5916     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5917       StorePlayer[x][y] = 0;
5918
5919     if (ELEM_IS_PLAYER(element))
5920       RelocatePlayer(x, y, element);
5921   }
5922   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5923   {
5924     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5925     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5926
5927     if (phase == delay)
5928       TEST_DrawLevelFieldCrumbled(x, y);
5929
5930     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5931     {
5932       DrawLevelElement(x, y, Back[x][y]);
5933       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5934     }
5935     else if (IS_WALKABLE_UNDER(Back[x][y]))
5936     {
5937       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5938       DrawLevelElementThruMask(x, y, Back[x][y]);
5939     }
5940     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5941       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5942   }
5943 }
5944
5945 static void DynaExplode(int ex, int ey)
5946 {
5947   int i, j;
5948   int dynabomb_element = Tile[ex][ey];
5949   int dynabomb_size = 1;
5950   boolean dynabomb_xl = FALSE;
5951   struct PlayerInfo *player;
5952   static int xy[4][2] =
5953   {
5954     { 0, -1 },
5955     { -1, 0 },
5956     { +1, 0 },
5957     { 0, +1 }
5958   };
5959
5960   if (IS_ACTIVE_BOMB(dynabomb_element))
5961   {
5962     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5963     dynabomb_size = player->dynabomb_size;
5964     dynabomb_xl = player->dynabomb_xl;
5965     player->dynabombs_left++;
5966   }
5967
5968   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5969
5970   for (i = 0; i < NUM_DIRECTIONS; i++)
5971   {
5972     for (j = 1; j <= dynabomb_size; j++)
5973     {
5974       int x = ex + j * xy[i][0];
5975       int y = ey + j * xy[i][1];
5976       int element;
5977
5978       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5979         break;
5980
5981       element = Tile[x][y];
5982
5983       // do not restart explosions of fields with active bombs
5984       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5985         continue;
5986
5987       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5988
5989       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5990           !IS_DIGGABLE(element) && !dynabomb_xl)
5991         break;
5992     }
5993   }
5994 }
5995
5996 void Bang(int x, int y)
5997 {
5998   int element = MovingOrBlocked2Element(x, y);
5999   int explosion_type = EX_TYPE_NORMAL;
6000
6001   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6002   {
6003     struct PlayerInfo *player = PLAYERINFO(x, y);
6004
6005     element = Tile[x][y] = player->initial_element;
6006
6007     if (level.use_explosion_element[player->index_nr])
6008     {
6009       int explosion_element = level.explosion_element[player->index_nr];
6010
6011       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6012         explosion_type = EX_TYPE_CROSS;
6013       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6014         explosion_type = EX_TYPE_CENTER;
6015     }
6016   }
6017
6018   switch (element)
6019   {
6020     case EL_BUG:
6021     case EL_SPACESHIP:
6022     case EL_BD_BUTTERFLY:
6023     case EL_BD_FIREFLY:
6024     case EL_YAMYAM:
6025     case EL_DARK_YAMYAM:
6026     case EL_ROBOT:
6027     case EL_PACMAN:
6028     case EL_MOLE:
6029       RaiseScoreElement(element);
6030       break;
6031
6032     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6033     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6034     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6035     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6036     case EL_DYNABOMB_INCREASE_NUMBER:
6037     case EL_DYNABOMB_INCREASE_SIZE:
6038     case EL_DYNABOMB_INCREASE_POWER:
6039       explosion_type = EX_TYPE_DYNA;
6040       break;
6041
6042     case EL_DC_LANDMINE:
6043       explosion_type = EX_TYPE_CENTER;
6044       break;
6045
6046     case EL_PENGUIN:
6047     case EL_LAMP:
6048     case EL_LAMP_ACTIVE:
6049     case EL_AMOEBA_TO_DIAMOND:
6050       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6051         explosion_type = EX_TYPE_CENTER;
6052       break;
6053
6054     default:
6055       if (element_info[element].explosion_type == EXPLODES_CROSS)
6056         explosion_type = EX_TYPE_CROSS;
6057       else if (element_info[element].explosion_type == EXPLODES_1X1)
6058         explosion_type = EX_TYPE_CENTER;
6059       break;
6060   }
6061
6062   if (explosion_type == EX_TYPE_DYNA)
6063     DynaExplode(x, y);
6064   else
6065     Explode(x, y, EX_PHASE_START, explosion_type);
6066
6067   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6068 }
6069
6070 static void SplashAcid(int x, int y)
6071 {
6072   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6073       (!IN_LEV_FIELD(x - 1, y - 2) ||
6074        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6075     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6076
6077   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6078       (!IN_LEV_FIELD(x + 1, y - 2) ||
6079        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6080     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6081
6082   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6083 }
6084
6085 static void InitBeltMovement(void)
6086 {
6087   static int belt_base_element[4] =
6088   {
6089     EL_CONVEYOR_BELT_1_LEFT,
6090     EL_CONVEYOR_BELT_2_LEFT,
6091     EL_CONVEYOR_BELT_3_LEFT,
6092     EL_CONVEYOR_BELT_4_LEFT
6093   };
6094   static int belt_base_active_element[4] =
6095   {
6096     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6097     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6098     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6099     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6100   };
6101
6102   int x, y, i, j;
6103
6104   // set frame order for belt animation graphic according to belt direction
6105   for (i = 0; i < NUM_BELTS; i++)
6106   {
6107     int belt_nr = i;
6108
6109     for (j = 0; j < NUM_BELT_PARTS; j++)
6110     {
6111       int element = belt_base_active_element[belt_nr] + j;
6112       int graphic_1 = el2img(element);
6113       int graphic_2 = el2panelimg(element);
6114
6115       if (game.belt_dir[i] == MV_LEFT)
6116       {
6117         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6118         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6119       }
6120       else
6121       {
6122         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6123         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6124       }
6125     }
6126   }
6127
6128   SCAN_PLAYFIELD(x, y)
6129   {
6130     int element = Tile[x][y];
6131
6132     for (i = 0; i < NUM_BELTS; i++)
6133     {
6134       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6135       {
6136         int e_belt_nr = getBeltNrFromBeltElement(element);
6137         int belt_nr = i;
6138
6139         if (e_belt_nr == belt_nr)
6140         {
6141           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6142
6143           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6144         }
6145       }
6146     }
6147   }
6148 }
6149
6150 static void ToggleBeltSwitch(int x, int y)
6151 {
6152   static int belt_base_element[4] =
6153   {
6154     EL_CONVEYOR_BELT_1_LEFT,
6155     EL_CONVEYOR_BELT_2_LEFT,
6156     EL_CONVEYOR_BELT_3_LEFT,
6157     EL_CONVEYOR_BELT_4_LEFT
6158   };
6159   static int belt_base_active_element[4] =
6160   {
6161     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6162     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6163     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6164     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6165   };
6166   static int belt_base_switch_element[4] =
6167   {
6168     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6169     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6170     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6171     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6172   };
6173   static int belt_move_dir[4] =
6174   {
6175     MV_LEFT,
6176     MV_NONE,
6177     MV_RIGHT,
6178     MV_NONE,
6179   };
6180
6181   int element = Tile[x][y];
6182   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6183   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6184   int belt_dir = belt_move_dir[belt_dir_nr];
6185   int xx, yy, i;
6186
6187   if (!IS_BELT_SWITCH(element))
6188     return;
6189
6190   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6191   game.belt_dir[belt_nr] = belt_dir;
6192
6193   if (belt_dir_nr == 3)
6194     belt_dir_nr = 1;
6195
6196   // set frame order for belt animation graphic according to belt direction
6197   for (i = 0; i < NUM_BELT_PARTS; i++)
6198   {
6199     int element = belt_base_active_element[belt_nr] + i;
6200     int graphic_1 = el2img(element);
6201     int graphic_2 = el2panelimg(element);
6202
6203     if (belt_dir == MV_LEFT)
6204     {
6205       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6206       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6207     }
6208     else
6209     {
6210       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6211       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6212     }
6213   }
6214
6215   SCAN_PLAYFIELD(xx, yy)
6216   {
6217     int element = Tile[xx][yy];
6218
6219     if (IS_BELT_SWITCH(element))
6220     {
6221       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6222
6223       if (e_belt_nr == belt_nr)
6224       {
6225         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6226         TEST_DrawLevelField(xx, yy);
6227       }
6228     }
6229     else if (IS_BELT(element) && belt_dir != MV_NONE)
6230     {
6231       int e_belt_nr = getBeltNrFromBeltElement(element);
6232
6233       if (e_belt_nr == belt_nr)
6234       {
6235         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6236
6237         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6238         TEST_DrawLevelField(xx, yy);
6239       }
6240     }
6241     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6242     {
6243       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6244
6245       if (e_belt_nr == belt_nr)
6246       {
6247         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6248
6249         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6250         TEST_DrawLevelField(xx, yy);
6251       }
6252     }
6253   }
6254 }
6255
6256 static void ToggleSwitchgateSwitch(int x, int y)
6257 {
6258   int xx, yy;
6259
6260   game.switchgate_pos = !game.switchgate_pos;
6261
6262   SCAN_PLAYFIELD(xx, yy)
6263   {
6264     int element = Tile[xx][yy];
6265
6266     if (element == EL_SWITCHGATE_SWITCH_UP)
6267     {
6268       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6269       TEST_DrawLevelField(xx, yy);
6270     }
6271     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6272     {
6273       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6274       TEST_DrawLevelField(xx, yy);
6275     }
6276     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6277     {
6278       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6279       TEST_DrawLevelField(xx, yy);
6280     }
6281     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6282     {
6283       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6284       TEST_DrawLevelField(xx, yy);
6285     }
6286     else if (element == EL_SWITCHGATE_OPEN ||
6287              element == EL_SWITCHGATE_OPENING)
6288     {
6289       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6290
6291       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6292     }
6293     else if (element == EL_SWITCHGATE_CLOSED ||
6294              element == EL_SWITCHGATE_CLOSING)
6295     {
6296       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6297
6298       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6299     }
6300   }
6301 }
6302
6303 static int getInvisibleActiveFromInvisibleElement(int element)
6304 {
6305   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6306           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6307           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6308           element);
6309 }
6310
6311 static int getInvisibleFromInvisibleActiveElement(int element)
6312 {
6313   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6314           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6315           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6316           element);
6317 }
6318
6319 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6320 {
6321   int x, y;
6322
6323   SCAN_PLAYFIELD(x, y)
6324   {
6325     int element = Tile[x][y];
6326
6327     if (element == EL_LIGHT_SWITCH &&
6328         game.light_time_left > 0)
6329     {
6330       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6331       TEST_DrawLevelField(x, y);
6332     }
6333     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6334              game.light_time_left == 0)
6335     {
6336       Tile[x][y] = EL_LIGHT_SWITCH;
6337       TEST_DrawLevelField(x, y);
6338     }
6339     else if (element == EL_EMC_DRIPPER &&
6340              game.light_time_left > 0)
6341     {
6342       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6343       TEST_DrawLevelField(x, y);
6344     }
6345     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6346              game.light_time_left == 0)
6347     {
6348       Tile[x][y] = EL_EMC_DRIPPER;
6349       TEST_DrawLevelField(x, y);
6350     }
6351     else if (element == EL_INVISIBLE_STEELWALL ||
6352              element == EL_INVISIBLE_WALL ||
6353              element == EL_INVISIBLE_SAND)
6354     {
6355       if (game.light_time_left > 0)
6356         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6357
6358       TEST_DrawLevelField(x, y);
6359
6360       // uncrumble neighbour fields, if needed
6361       if (element == EL_INVISIBLE_SAND)
6362         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6363     }
6364     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6365              element == EL_INVISIBLE_WALL_ACTIVE ||
6366              element == EL_INVISIBLE_SAND_ACTIVE)
6367     {
6368       if (game.light_time_left == 0)
6369         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6370
6371       TEST_DrawLevelField(x, y);
6372
6373       // re-crumble neighbour fields, if needed
6374       if (element == EL_INVISIBLE_SAND)
6375         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6376     }
6377   }
6378 }
6379
6380 static void RedrawAllInvisibleElementsForLenses(void)
6381 {
6382   int x, y;
6383
6384   SCAN_PLAYFIELD(x, y)
6385   {
6386     int element = Tile[x][y];
6387
6388     if (element == EL_EMC_DRIPPER &&
6389         game.lenses_time_left > 0)
6390     {
6391       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6392       TEST_DrawLevelField(x, y);
6393     }
6394     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6395              game.lenses_time_left == 0)
6396     {
6397       Tile[x][y] = EL_EMC_DRIPPER;
6398       TEST_DrawLevelField(x, y);
6399     }
6400     else if (element == EL_INVISIBLE_STEELWALL ||
6401              element == EL_INVISIBLE_WALL ||
6402              element == EL_INVISIBLE_SAND)
6403     {
6404       if (game.lenses_time_left > 0)
6405         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6406
6407       TEST_DrawLevelField(x, y);
6408
6409       // uncrumble neighbour fields, if needed
6410       if (element == EL_INVISIBLE_SAND)
6411         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6412     }
6413     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6414              element == EL_INVISIBLE_WALL_ACTIVE ||
6415              element == EL_INVISIBLE_SAND_ACTIVE)
6416     {
6417       if (game.lenses_time_left == 0)
6418         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6419
6420       TEST_DrawLevelField(x, y);
6421
6422       // re-crumble neighbour fields, if needed
6423       if (element == EL_INVISIBLE_SAND)
6424         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6425     }
6426   }
6427 }
6428
6429 static void RedrawAllInvisibleElementsForMagnifier(void)
6430 {
6431   int x, y;
6432
6433   SCAN_PLAYFIELD(x, y)
6434   {
6435     int element = Tile[x][y];
6436
6437     if (element == EL_EMC_FAKE_GRASS &&
6438         game.magnify_time_left > 0)
6439     {
6440       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6441       TEST_DrawLevelField(x, y);
6442     }
6443     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6444              game.magnify_time_left == 0)
6445     {
6446       Tile[x][y] = EL_EMC_FAKE_GRASS;
6447       TEST_DrawLevelField(x, y);
6448     }
6449     else if (IS_GATE_GRAY(element) &&
6450              game.magnify_time_left > 0)
6451     {
6452       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6453                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6454                     IS_EM_GATE_GRAY(element) ?
6455                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6456                     IS_EMC_GATE_GRAY(element) ?
6457                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6458                     IS_DC_GATE_GRAY(element) ?
6459                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6460                     element);
6461       TEST_DrawLevelField(x, y);
6462     }
6463     else if (IS_GATE_GRAY_ACTIVE(element) &&
6464              game.magnify_time_left == 0)
6465     {
6466       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6467                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6468                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6469                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6470                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6471                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6472                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6473                     EL_DC_GATE_WHITE_GRAY :
6474                     element);
6475       TEST_DrawLevelField(x, y);
6476     }
6477   }
6478 }
6479
6480 static void ToggleLightSwitch(int x, int y)
6481 {
6482   int element = Tile[x][y];
6483
6484   game.light_time_left =
6485     (element == EL_LIGHT_SWITCH ?
6486      level.time_light * FRAMES_PER_SECOND : 0);
6487
6488   RedrawAllLightSwitchesAndInvisibleElements();
6489 }
6490
6491 static void ActivateTimegateSwitch(int x, int y)
6492 {
6493   int xx, yy;
6494
6495   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6496
6497   SCAN_PLAYFIELD(xx, yy)
6498   {
6499     int element = Tile[xx][yy];
6500
6501     if (element == EL_TIMEGATE_CLOSED ||
6502         element == EL_TIMEGATE_CLOSING)
6503     {
6504       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6505       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6506     }
6507
6508     /*
6509     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6510     {
6511       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6512       TEST_DrawLevelField(xx, yy);
6513     }
6514     */
6515
6516   }
6517
6518   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6519                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6520 }
6521
6522 static void Impact(int x, int y)
6523 {
6524   boolean last_line = (y == lev_fieldy - 1);
6525   boolean object_hit = FALSE;
6526   boolean impact = (last_line || object_hit);
6527   int element = Tile[x][y];
6528   int smashed = EL_STEELWALL;
6529
6530   if (!last_line)       // check if element below was hit
6531   {
6532     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6533       return;
6534
6535     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6536                                          MovDir[x][y + 1] != MV_DOWN ||
6537                                          MovPos[x][y + 1] <= TILEY / 2));
6538
6539     // do not smash moving elements that left the smashed field in time
6540     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6541         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6542       object_hit = FALSE;
6543
6544 #if USE_QUICKSAND_IMPACT_BUGFIX
6545     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6546     {
6547       RemoveMovingField(x, y + 1);
6548       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6549       Tile[x][y + 2] = EL_ROCK;
6550       TEST_DrawLevelField(x, y + 2);
6551
6552       object_hit = TRUE;
6553     }
6554
6555     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6556     {
6557       RemoveMovingField(x, y + 1);
6558       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6559       Tile[x][y + 2] = EL_ROCK;
6560       TEST_DrawLevelField(x, y + 2);
6561
6562       object_hit = TRUE;
6563     }
6564 #endif
6565
6566     if (object_hit)
6567       smashed = MovingOrBlocked2Element(x, y + 1);
6568
6569     impact = (last_line || object_hit);
6570   }
6571
6572   if (!last_line && smashed == EL_ACID) // element falls into acid
6573   {
6574     SplashAcid(x, y + 1);
6575     return;
6576   }
6577
6578   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6579   // only reset graphic animation if graphic really changes after impact
6580   if (impact &&
6581       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6582   {
6583     ResetGfxAnimation(x, y);
6584     TEST_DrawLevelField(x, y);
6585   }
6586
6587   if (impact && CAN_EXPLODE_IMPACT(element))
6588   {
6589     Bang(x, y);
6590     return;
6591   }
6592   else if (impact && element == EL_PEARL &&
6593            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6594   {
6595     ResetGfxAnimation(x, y);
6596
6597     Tile[x][y] = EL_PEARL_BREAKING;
6598     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6599     return;
6600   }
6601   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6602   {
6603     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6604
6605     return;
6606   }
6607
6608   if (impact && element == EL_AMOEBA_DROP)
6609   {
6610     if (object_hit && IS_PLAYER(x, y + 1))
6611       KillPlayerUnlessEnemyProtected(x, y + 1);
6612     else if (object_hit && smashed == EL_PENGUIN)
6613       Bang(x, y + 1);
6614     else
6615     {
6616       Tile[x][y] = EL_AMOEBA_GROWING;
6617       Store[x][y] = EL_AMOEBA_WET;
6618
6619       ResetRandomAnimationValue(x, y);
6620     }
6621     return;
6622   }
6623
6624   if (object_hit)               // check which object was hit
6625   {
6626     if ((CAN_PASS_MAGIC_WALL(element) && 
6627          (smashed == EL_MAGIC_WALL ||
6628           smashed == EL_BD_MAGIC_WALL)) ||
6629         (CAN_PASS_DC_MAGIC_WALL(element) &&
6630          smashed == EL_DC_MAGIC_WALL))
6631     {
6632       int xx, yy;
6633       int activated_magic_wall =
6634         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6635          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6636          EL_DC_MAGIC_WALL_ACTIVE);
6637
6638       // activate magic wall / mill
6639       SCAN_PLAYFIELD(xx, yy)
6640       {
6641         if (Tile[xx][yy] == smashed)
6642           Tile[xx][yy] = activated_magic_wall;
6643       }
6644
6645       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6646       game.magic_wall_active = TRUE;
6647
6648       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6649                             SND_MAGIC_WALL_ACTIVATING :
6650                             smashed == EL_BD_MAGIC_WALL ?
6651                             SND_BD_MAGIC_WALL_ACTIVATING :
6652                             SND_DC_MAGIC_WALL_ACTIVATING));
6653     }
6654
6655     if (IS_PLAYER(x, y + 1))
6656     {
6657       if (CAN_SMASH_PLAYER(element))
6658       {
6659         KillPlayerUnlessEnemyProtected(x, y + 1);
6660         return;
6661       }
6662     }
6663     else if (smashed == EL_PENGUIN)
6664     {
6665       if (CAN_SMASH_PLAYER(element))
6666       {
6667         Bang(x, y + 1);
6668         return;
6669       }
6670     }
6671     else if (element == EL_BD_DIAMOND)
6672     {
6673       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6674       {
6675         Bang(x, y + 1);
6676         return;
6677       }
6678     }
6679     else if (((element == EL_SP_INFOTRON ||
6680                element == EL_SP_ZONK) &&
6681               (smashed == EL_SP_SNIKSNAK ||
6682                smashed == EL_SP_ELECTRON ||
6683                smashed == EL_SP_DISK_ORANGE)) ||
6684              (element == EL_SP_INFOTRON &&
6685               smashed == EL_SP_DISK_YELLOW))
6686     {
6687       Bang(x, y + 1);
6688       return;
6689     }
6690     else if (CAN_SMASH_EVERYTHING(element))
6691     {
6692       if (IS_CLASSIC_ENEMY(smashed) ||
6693           CAN_EXPLODE_SMASHED(smashed))
6694       {
6695         Bang(x, y + 1);
6696         return;
6697       }
6698       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6699       {
6700         if (smashed == EL_LAMP ||
6701             smashed == EL_LAMP_ACTIVE)
6702         {
6703           Bang(x, y + 1);
6704           return;
6705         }
6706         else if (smashed == EL_NUT)
6707         {
6708           Tile[x][y + 1] = EL_NUT_BREAKING;
6709           PlayLevelSound(x, y, SND_NUT_BREAKING);
6710           RaiseScoreElement(EL_NUT);
6711           return;
6712         }
6713         else if (smashed == EL_PEARL)
6714         {
6715           ResetGfxAnimation(x, y);
6716
6717           Tile[x][y + 1] = EL_PEARL_BREAKING;
6718           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6719           return;
6720         }
6721         else if (smashed == EL_DIAMOND)
6722         {
6723           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6724           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6725           return;
6726         }
6727         else if (IS_BELT_SWITCH(smashed))
6728         {
6729           ToggleBeltSwitch(x, y + 1);
6730         }
6731         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6732                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6733                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6734                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6735         {
6736           ToggleSwitchgateSwitch(x, y + 1);
6737         }
6738         else if (smashed == EL_LIGHT_SWITCH ||
6739                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6740         {
6741           ToggleLightSwitch(x, y + 1);
6742         }
6743         else
6744         {
6745           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6746
6747           CheckElementChangeBySide(x, y + 1, smashed, element,
6748                                    CE_SWITCHED, CH_SIDE_TOP);
6749           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6750                                             CH_SIDE_TOP);
6751         }
6752       }
6753       else
6754       {
6755         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6756       }
6757     }
6758   }
6759
6760   // play sound of magic wall / mill
6761   if (!last_line &&
6762       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6763        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6764        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6765   {
6766     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6767       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6768     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6769       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6770     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6771       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6772
6773     return;
6774   }
6775
6776   // play sound of object that hits the ground
6777   if (last_line || object_hit)
6778     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6779 }
6780
6781 static void TurnRoundExt(int x, int y)
6782 {
6783   static struct
6784   {
6785     int dx, dy;
6786   } move_xy[] =
6787   {
6788     {  0,  0 },
6789     { -1,  0 },
6790     { +1,  0 },
6791     {  0,  0 },
6792     {  0, -1 },
6793     {  0,  0 }, { 0, 0 }, { 0, 0 },
6794     {  0, +1 }
6795   };
6796   static struct
6797   {
6798     int left, right, back;
6799   } turn[] =
6800   {
6801     { 0,        0,              0        },
6802     { MV_DOWN,  MV_UP,          MV_RIGHT },
6803     { MV_UP,    MV_DOWN,        MV_LEFT  },
6804     { 0,        0,              0        },
6805     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6806     { 0,        0,              0        },
6807     { 0,        0,              0        },
6808     { 0,        0,              0        },
6809     { MV_RIGHT, MV_LEFT,        MV_UP    }
6810   };
6811
6812   int element = Tile[x][y];
6813   int move_pattern = element_info[element].move_pattern;
6814
6815   int old_move_dir = MovDir[x][y];
6816   int left_dir  = turn[old_move_dir].left;
6817   int right_dir = turn[old_move_dir].right;
6818   int back_dir  = turn[old_move_dir].back;
6819
6820   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6821   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6822   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6823   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6824
6825   int left_x  = x + left_dx,  left_y  = y + left_dy;
6826   int right_x = x + right_dx, right_y = y + right_dy;
6827   int move_x  = x + move_dx,  move_y  = y + move_dy;
6828
6829   int xx, yy;
6830
6831   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6832   {
6833     TestIfBadThingTouchesOtherBadThing(x, y);
6834
6835     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6836       MovDir[x][y] = right_dir;
6837     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6838       MovDir[x][y] = left_dir;
6839
6840     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6841       MovDelay[x][y] = 9;
6842     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6843       MovDelay[x][y] = 1;
6844   }
6845   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6846   {
6847     TestIfBadThingTouchesOtherBadThing(x, y);
6848
6849     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6850       MovDir[x][y] = left_dir;
6851     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6852       MovDir[x][y] = right_dir;
6853
6854     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6855       MovDelay[x][y] = 9;
6856     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6857       MovDelay[x][y] = 1;
6858   }
6859   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6860   {
6861     TestIfBadThingTouchesOtherBadThing(x, y);
6862
6863     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6864       MovDir[x][y] = left_dir;
6865     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6866       MovDir[x][y] = right_dir;
6867
6868     if (MovDir[x][y] != old_move_dir)
6869       MovDelay[x][y] = 9;
6870   }
6871   else if (element == EL_YAMYAM)
6872   {
6873     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6874     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6875
6876     if (can_turn_left && can_turn_right)
6877       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6878     else if (can_turn_left)
6879       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6880     else if (can_turn_right)
6881       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6882     else
6883       MovDir[x][y] = back_dir;
6884
6885     MovDelay[x][y] = 16 + 16 * RND(3);
6886   }
6887   else if (element == EL_DARK_YAMYAM)
6888   {
6889     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6890                                                          left_x, left_y);
6891     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6892                                                          right_x, right_y);
6893
6894     if (can_turn_left && can_turn_right)
6895       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6896     else if (can_turn_left)
6897       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6898     else if (can_turn_right)
6899       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6900     else
6901       MovDir[x][y] = back_dir;
6902
6903     MovDelay[x][y] = 16 + 16 * RND(3);
6904   }
6905   else if (element == EL_PACMAN)
6906   {
6907     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6908     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6909
6910     if (can_turn_left && can_turn_right)
6911       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6912     else if (can_turn_left)
6913       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6914     else if (can_turn_right)
6915       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6916     else
6917       MovDir[x][y] = back_dir;
6918
6919     MovDelay[x][y] = 6 + RND(40);
6920   }
6921   else if (element == EL_PIG)
6922   {
6923     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6924     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6925     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6926     boolean should_turn_left, should_turn_right, should_move_on;
6927     int rnd_value = 24;
6928     int rnd = RND(rnd_value);
6929
6930     should_turn_left = (can_turn_left &&
6931                         (!can_move_on ||
6932                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6933                                                    y + back_dy + left_dy)));
6934     should_turn_right = (can_turn_right &&
6935                          (!can_move_on ||
6936                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6937                                                     y + back_dy + right_dy)));
6938     should_move_on = (can_move_on &&
6939                       (!can_turn_left ||
6940                        !can_turn_right ||
6941                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6942                                                  y + move_dy + left_dy) ||
6943                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6944                                                  y + move_dy + right_dy)));
6945
6946     if (should_turn_left || should_turn_right || should_move_on)
6947     {
6948       if (should_turn_left && should_turn_right && should_move_on)
6949         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6950                         rnd < 2 * rnd_value / 3 ? right_dir :
6951                         old_move_dir);
6952       else if (should_turn_left && should_turn_right)
6953         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6954       else if (should_turn_left && should_move_on)
6955         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6956       else if (should_turn_right && should_move_on)
6957         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6958       else if (should_turn_left)
6959         MovDir[x][y] = left_dir;
6960       else if (should_turn_right)
6961         MovDir[x][y] = right_dir;
6962       else if (should_move_on)
6963         MovDir[x][y] = old_move_dir;
6964     }
6965     else if (can_move_on && rnd > rnd_value / 8)
6966       MovDir[x][y] = old_move_dir;
6967     else if (can_turn_left && can_turn_right)
6968       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6969     else if (can_turn_left && rnd > rnd_value / 8)
6970       MovDir[x][y] = left_dir;
6971     else if (can_turn_right && rnd > rnd_value/8)
6972       MovDir[x][y] = right_dir;
6973     else
6974       MovDir[x][y] = back_dir;
6975
6976     xx = x + move_xy[MovDir[x][y]].dx;
6977     yy = y + move_xy[MovDir[x][y]].dy;
6978
6979     if (!IN_LEV_FIELD(xx, yy) ||
6980         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6981       MovDir[x][y] = old_move_dir;
6982
6983     MovDelay[x][y] = 0;
6984   }
6985   else if (element == EL_DRAGON)
6986   {
6987     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6988     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6989     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6990     int rnd_value = 24;
6991     int rnd = RND(rnd_value);
6992
6993     if (can_move_on && rnd > rnd_value / 8)
6994       MovDir[x][y] = old_move_dir;
6995     else if (can_turn_left && can_turn_right)
6996       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6997     else if (can_turn_left && rnd > rnd_value / 8)
6998       MovDir[x][y] = left_dir;
6999     else if (can_turn_right && rnd > rnd_value / 8)
7000       MovDir[x][y] = right_dir;
7001     else
7002       MovDir[x][y] = back_dir;
7003
7004     xx = x + move_xy[MovDir[x][y]].dx;
7005     yy = y + move_xy[MovDir[x][y]].dy;
7006
7007     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7008       MovDir[x][y] = old_move_dir;
7009
7010     MovDelay[x][y] = 0;
7011   }
7012   else if (element == EL_MOLE)
7013   {
7014     boolean can_move_on =
7015       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7016                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7017                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7018     if (!can_move_on)
7019     {
7020       boolean can_turn_left =
7021         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7022                               IS_AMOEBOID(Tile[left_x][left_y])));
7023
7024       boolean can_turn_right =
7025         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7026                               IS_AMOEBOID(Tile[right_x][right_y])));
7027
7028       if (can_turn_left && can_turn_right)
7029         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7030       else if (can_turn_left)
7031         MovDir[x][y] = left_dir;
7032       else
7033         MovDir[x][y] = right_dir;
7034     }
7035
7036     if (MovDir[x][y] != old_move_dir)
7037       MovDelay[x][y] = 9;
7038   }
7039   else if (element == EL_BALLOON)
7040   {
7041     MovDir[x][y] = game.wind_direction;
7042     MovDelay[x][y] = 0;
7043   }
7044   else if (element == EL_SPRING)
7045   {
7046     if (MovDir[x][y] & MV_HORIZONTAL)
7047     {
7048       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7049           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7050       {
7051         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7052         ResetGfxAnimation(move_x, move_y);
7053         TEST_DrawLevelField(move_x, move_y);
7054
7055         MovDir[x][y] = back_dir;
7056       }
7057       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7058                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7059         MovDir[x][y] = MV_NONE;
7060     }
7061
7062     MovDelay[x][y] = 0;
7063   }
7064   else if (element == EL_ROBOT ||
7065            element == EL_SATELLITE ||
7066            element == EL_PENGUIN ||
7067            element == EL_EMC_ANDROID)
7068   {
7069     int attr_x = -1, attr_y = -1;
7070
7071     if (game.all_players_gone)
7072     {
7073       attr_x = game.exit_x;
7074       attr_y = game.exit_y;
7075     }
7076     else
7077     {
7078       int i;
7079
7080       for (i = 0; i < MAX_PLAYERS; i++)
7081       {
7082         struct PlayerInfo *player = &stored_player[i];
7083         int jx = player->jx, jy = player->jy;
7084
7085         if (!player->active)
7086           continue;
7087
7088         if (attr_x == -1 ||
7089             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7090         {
7091           attr_x = jx;
7092           attr_y = jy;
7093         }
7094       }
7095     }
7096
7097     if (element == EL_ROBOT &&
7098         game.robot_wheel_x >= 0 &&
7099         game.robot_wheel_y >= 0 &&
7100         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7101          game.engine_version < VERSION_IDENT(3,1,0,0)))
7102     {
7103       attr_x = game.robot_wheel_x;
7104       attr_y = game.robot_wheel_y;
7105     }
7106
7107     if (element == EL_PENGUIN)
7108     {
7109       int i;
7110       static int xy[4][2] =
7111       {
7112         { 0, -1 },
7113         { -1, 0 },
7114         { +1, 0 },
7115         { 0, +1 }
7116       };
7117
7118       for (i = 0; i < NUM_DIRECTIONS; i++)
7119       {
7120         int ex = x + xy[i][0];
7121         int ey = y + xy[i][1];
7122
7123         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7124                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7125                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7126                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7127         {
7128           attr_x = ex;
7129           attr_y = ey;
7130           break;
7131         }
7132       }
7133     }
7134
7135     MovDir[x][y] = MV_NONE;
7136     if (attr_x < x)
7137       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7138     else if (attr_x > x)
7139       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7140     if (attr_y < y)
7141       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7142     else if (attr_y > y)
7143       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7144
7145     if (element == EL_ROBOT)
7146     {
7147       int newx, newy;
7148
7149       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7150         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7151       Moving2Blocked(x, y, &newx, &newy);
7152
7153       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7154         MovDelay[x][y] = 8 + 8 * !RND(3);
7155       else
7156         MovDelay[x][y] = 16;
7157     }
7158     else if (element == EL_PENGUIN)
7159     {
7160       int newx, newy;
7161
7162       MovDelay[x][y] = 1;
7163
7164       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7165       {
7166         boolean first_horiz = RND(2);
7167         int new_move_dir = MovDir[x][y];
7168
7169         MovDir[x][y] =
7170           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7171         Moving2Blocked(x, y, &newx, &newy);
7172
7173         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7174           return;
7175
7176         MovDir[x][y] =
7177           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7178         Moving2Blocked(x, y, &newx, &newy);
7179
7180         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7181           return;
7182
7183         MovDir[x][y] = old_move_dir;
7184         return;
7185       }
7186     }
7187     else if (element == EL_SATELLITE)
7188     {
7189       int newx, newy;
7190
7191       MovDelay[x][y] = 1;
7192
7193       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7194       {
7195         boolean first_horiz = RND(2);
7196         int new_move_dir = MovDir[x][y];
7197
7198         MovDir[x][y] =
7199           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7200         Moving2Blocked(x, y, &newx, &newy);
7201
7202         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7203           return;
7204
7205         MovDir[x][y] =
7206           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7207         Moving2Blocked(x, y, &newx, &newy);
7208
7209         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7210           return;
7211
7212         MovDir[x][y] = old_move_dir;
7213         return;
7214       }
7215     }
7216     else if (element == EL_EMC_ANDROID)
7217     {
7218       static int check_pos[16] =
7219       {
7220         -1,             //  0 => (invalid)
7221         7,              //  1 => MV_LEFT
7222         3,              //  2 => MV_RIGHT
7223         -1,             //  3 => (invalid)
7224         1,              //  4 =>            MV_UP
7225         0,              //  5 => MV_LEFT  | MV_UP
7226         2,              //  6 => MV_RIGHT | MV_UP
7227         -1,             //  7 => (invalid)
7228         5,              //  8 =>            MV_DOWN
7229         6,              //  9 => MV_LEFT  | MV_DOWN
7230         4,              // 10 => MV_RIGHT | MV_DOWN
7231         -1,             // 11 => (invalid)
7232         -1,             // 12 => (invalid)
7233         -1,             // 13 => (invalid)
7234         -1,             // 14 => (invalid)
7235         -1,             // 15 => (invalid)
7236       };
7237       static struct
7238       {
7239         int dx, dy;
7240         int dir;
7241       } check_xy[8] =
7242       {
7243         { -1, -1,       MV_LEFT  | MV_UP   },
7244         {  0, -1,                  MV_UP   },
7245         { +1, -1,       MV_RIGHT | MV_UP   },
7246         { +1,  0,       MV_RIGHT           },
7247         { +1, +1,       MV_RIGHT | MV_DOWN },
7248         {  0, +1,                  MV_DOWN },
7249         { -1, +1,       MV_LEFT  | MV_DOWN },
7250         { -1,  0,       MV_LEFT            },
7251       };
7252       int start_pos, check_order;
7253       boolean can_clone = FALSE;
7254       int i;
7255
7256       // check if there is any free field around current position
7257       for (i = 0; i < 8; i++)
7258       {
7259         int newx = x + check_xy[i].dx;
7260         int newy = y + check_xy[i].dy;
7261
7262         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7263         {
7264           can_clone = TRUE;
7265
7266           break;
7267         }
7268       }
7269
7270       if (can_clone)            // randomly find an element to clone
7271       {
7272         can_clone = FALSE;
7273
7274         start_pos = check_pos[RND(8)];
7275         check_order = (RND(2) ? -1 : +1);
7276
7277         for (i = 0; i < 8; i++)
7278         {
7279           int pos_raw = start_pos + i * check_order;
7280           int pos = (pos_raw + 8) % 8;
7281           int newx = x + check_xy[pos].dx;
7282           int newy = y + check_xy[pos].dy;
7283
7284           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7285           {
7286             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7287             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7288
7289             Store[x][y] = Tile[newx][newy];
7290
7291             can_clone = TRUE;
7292
7293             break;
7294           }
7295         }
7296       }
7297
7298       if (can_clone)            // randomly find a direction to move
7299       {
7300         can_clone = FALSE;
7301
7302         start_pos = check_pos[RND(8)];
7303         check_order = (RND(2) ? -1 : +1);
7304
7305         for (i = 0; i < 8; i++)
7306         {
7307           int pos_raw = start_pos + i * check_order;
7308           int pos = (pos_raw + 8) % 8;
7309           int newx = x + check_xy[pos].dx;
7310           int newy = y + check_xy[pos].dy;
7311           int new_move_dir = check_xy[pos].dir;
7312
7313           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7314           {
7315             MovDir[x][y] = new_move_dir;
7316             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7317
7318             can_clone = TRUE;
7319
7320             break;
7321           }
7322         }
7323       }
7324
7325       if (can_clone)            // cloning and moving successful
7326         return;
7327
7328       // cannot clone -- try to move towards player
7329
7330       start_pos = check_pos[MovDir[x][y] & 0x0f];
7331       check_order = (RND(2) ? -1 : +1);
7332
7333       for (i = 0; i < 3; i++)
7334       {
7335         // first check start_pos, then previous/next or (next/previous) pos
7336         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7337         int pos = (pos_raw + 8) % 8;
7338         int newx = x + check_xy[pos].dx;
7339         int newy = y + check_xy[pos].dy;
7340         int new_move_dir = check_xy[pos].dir;
7341
7342         if (IS_PLAYER(newx, newy))
7343           break;
7344
7345         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7346         {
7347           MovDir[x][y] = new_move_dir;
7348           MovDelay[x][y] = level.android_move_time * 8 + 1;
7349
7350           break;
7351         }
7352       }
7353     }
7354   }
7355   else if (move_pattern == MV_TURNING_LEFT ||
7356            move_pattern == MV_TURNING_RIGHT ||
7357            move_pattern == MV_TURNING_LEFT_RIGHT ||
7358            move_pattern == MV_TURNING_RIGHT_LEFT ||
7359            move_pattern == MV_TURNING_RANDOM ||
7360            move_pattern == MV_ALL_DIRECTIONS)
7361   {
7362     boolean can_turn_left =
7363       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7364     boolean can_turn_right =
7365       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7366
7367     if (element_info[element].move_stepsize == 0)       // "not moving"
7368       return;
7369
7370     if (move_pattern == MV_TURNING_LEFT)
7371       MovDir[x][y] = left_dir;
7372     else if (move_pattern == MV_TURNING_RIGHT)
7373       MovDir[x][y] = right_dir;
7374     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7375       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7376     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7377       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7378     else if (move_pattern == MV_TURNING_RANDOM)
7379       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7380                       can_turn_right && !can_turn_left ? right_dir :
7381                       RND(2) ? left_dir : right_dir);
7382     else if (can_turn_left && can_turn_right)
7383       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7384     else if (can_turn_left)
7385       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7386     else if (can_turn_right)
7387       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7388     else
7389       MovDir[x][y] = back_dir;
7390
7391     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7392   }
7393   else if (move_pattern == MV_HORIZONTAL ||
7394            move_pattern == MV_VERTICAL)
7395   {
7396     if (move_pattern & old_move_dir)
7397       MovDir[x][y] = back_dir;
7398     else if (move_pattern == MV_HORIZONTAL)
7399       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7400     else if (move_pattern == MV_VERTICAL)
7401       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7402
7403     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7404   }
7405   else if (move_pattern & MV_ANY_DIRECTION)
7406   {
7407     MovDir[x][y] = move_pattern;
7408     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7409   }
7410   else if (move_pattern & MV_WIND_DIRECTION)
7411   {
7412     MovDir[x][y] = game.wind_direction;
7413     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7414   }
7415   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7416   {
7417     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7418       MovDir[x][y] = left_dir;
7419     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7420       MovDir[x][y] = right_dir;
7421
7422     if (MovDir[x][y] != old_move_dir)
7423       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7424   }
7425   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7426   {
7427     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7428       MovDir[x][y] = right_dir;
7429     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7430       MovDir[x][y] = left_dir;
7431
7432     if (MovDir[x][y] != old_move_dir)
7433       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7434   }
7435   else if (move_pattern == MV_TOWARDS_PLAYER ||
7436            move_pattern == MV_AWAY_FROM_PLAYER)
7437   {
7438     int attr_x = -1, attr_y = -1;
7439     int newx, newy;
7440     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7441
7442     if (game.all_players_gone)
7443     {
7444       attr_x = game.exit_x;
7445       attr_y = game.exit_y;
7446     }
7447     else
7448     {
7449       int i;
7450
7451       for (i = 0; i < MAX_PLAYERS; i++)
7452       {
7453         struct PlayerInfo *player = &stored_player[i];
7454         int jx = player->jx, jy = player->jy;
7455
7456         if (!player->active)
7457           continue;
7458
7459         if (attr_x == -1 ||
7460             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7461         {
7462           attr_x = jx;
7463           attr_y = jy;
7464         }
7465       }
7466     }
7467
7468     MovDir[x][y] = MV_NONE;
7469     if (attr_x < x)
7470       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7471     else if (attr_x > x)
7472       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7473     if (attr_y < y)
7474       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7475     else if (attr_y > y)
7476       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7477
7478     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7479
7480     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7481     {
7482       boolean first_horiz = RND(2);
7483       int new_move_dir = MovDir[x][y];
7484
7485       if (element_info[element].move_stepsize == 0)     // "not moving"
7486       {
7487         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7488         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7489
7490         return;
7491       }
7492
7493       MovDir[x][y] =
7494         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7495       Moving2Blocked(x, y, &newx, &newy);
7496
7497       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7498         return;
7499
7500       MovDir[x][y] =
7501         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7502       Moving2Blocked(x, y, &newx, &newy);
7503
7504       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7505         return;
7506
7507       MovDir[x][y] = old_move_dir;
7508     }
7509   }
7510   else if (move_pattern == MV_WHEN_PUSHED ||
7511            move_pattern == MV_WHEN_DROPPED)
7512   {
7513     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7514       MovDir[x][y] = MV_NONE;
7515
7516     MovDelay[x][y] = 0;
7517   }
7518   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7519   {
7520     static int test_xy[7][2] =
7521     {
7522       { 0, -1 },
7523       { -1, 0 },
7524       { +1, 0 },
7525       { 0, +1 },
7526       { 0, -1 },
7527       { -1, 0 },
7528       { +1, 0 },
7529     };
7530     static int test_dir[7] =
7531     {
7532       MV_UP,
7533       MV_LEFT,
7534       MV_RIGHT,
7535       MV_DOWN,
7536       MV_UP,
7537       MV_LEFT,
7538       MV_RIGHT,
7539     };
7540     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7541     int move_preference = -1000000;     // start with very low preference
7542     int new_move_dir = MV_NONE;
7543     int start_test = RND(4);
7544     int i;
7545
7546     for (i = 0; i < NUM_DIRECTIONS; i++)
7547     {
7548       int move_dir = test_dir[start_test + i];
7549       int move_dir_preference;
7550
7551       xx = x + test_xy[start_test + i][0];
7552       yy = y + test_xy[start_test + i][1];
7553
7554       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7555           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7556       {
7557         new_move_dir = move_dir;
7558
7559         break;
7560       }
7561
7562       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7563         continue;
7564
7565       move_dir_preference = -1 * RunnerVisit[xx][yy];
7566       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7567         move_dir_preference = PlayerVisit[xx][yy];
7568
7569       if (move_dir_preference > move_preference)
7570       {
7571         // prefer field that has not been visited for the longest time
7572         move_preference = move_dir_preference;
7573         new_move_dir = move_dir;
7574       }
7575       else if (move_dir_preference == move_preference &&
7576                move_dir == old_move_dir)
7577       {
7578         // prefer last direction when all directions are preferred equally
7579         move_preference = move_dir_preference;
7580         new_move_dir = move_dir;
7581       }
7582     }
7583
7584     MovDir[x][y] = new_move_dir;
7585     if (old_move_dir != new_move_dir)
7586       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7587   }
7588 }
7589
7590 static void TurnRound(int x, int y)
7591 {
7592   int direction = MovDir[x][y];
7593
7594   TurnRoundExt(x, y);
7595
7596   GfxDir[x][y] = MovDir[x][y];
7597
7598   if (direction != MovDir[x][y])
7599     GfxFrame[x][y] = 0;
7600
7601   if (MovDelay[x][y])
7602     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7603
7604   ResetGfxFrame(x, y);
7605 }
7606
7607 static boolean JustBeingPushed(int x, int y)
7608 {
7609   int i;
7610
7611   for (i = 0; i < MAX_PLAYERS; i++)
7612   {
7613     struct PlayerInfo *player = &stored_player[i];
7614
7615     if (player->active && player->is_pushing && player->MovPos)
7616     {
7617       int next_jx = player->jx + (player->jx - player->last_jx);
7618       int next_jy = player->jy + (player->jy - player->last_jy);
7619
7620       if (x == next_jx && y == next_jy)
7621         return TRUE;
7622     }
7623   }
7624
7625   return FALSE;
7626 }
7627
7628 static void StartMoving(int x, int y)
7629 {
7630   boolean started_moving = FALSE;       // some elements can fall _and_ move
7631   int element = Tile[x][y];
7632
7633   if (Stop[x][y])
7634     return;
7635
7636   if (MovDelay[x][y] == 0)
7637     GfxAction[x][y] = ACTION_DEFAULT;
7638
7639   if (CAN_FALL(element) && y < lev_fieldy - 1)
7640   {
7641     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7642         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7643       if (JustBeingPushed(x, y))
7644         return;
7645
7646     if (element == EL_QUICKSAND_FULL)
7647     {
7648       if (IS_FREE(x, y + 1))
7649       {
7650         InitMovingField(x, y, MV_DOWN);
7651         started_moving = TRUE;
7652
7653         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7654 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7655         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7656           Store[x][y] = EL_ROCK;
7657 #else
7658         Store[x][y] = EL_ROCK;
7659 #endif
7660
7661         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7662       }
7663       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7664       {
7665         if (!MovDelay[x][y])
7666         {
7667           MovDelay[x][y] = TILEY + 1;
7668
7669           ResetGfxAnimation(x, y);
7670           ResetGfxAnimation(x, y + 1);
7671         }
7672
7673         if (MovDelay[x][y])
7674         {
7675           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7676           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7677
7678           MovDelay[x][y]--;
7679           if (MovDelay[x][y])
7680             return;
7681         }
7682
7683         Tile[x][y] = EL_QUICKSAND_EMPTY;
7684         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7685         Store[x][y + 1] = Store[x][y];
7686         Store[x][y] = 0;
7687
7688         PlayLevelSoundAction(x, y, ACTION_FILLING);
7689       }
7690       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7691       {
7692         if (!MovDelay[x][y])
7693         {
7694           MovDelay[x][y] = TILEY + 1;
7695
7696           ResetGfxAnimation(x, y);
7697           ResetGfxAnimation(x, y + 1);
7698         }
7699
7700         if (MovDelay[x][y])
7701         {
7702           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7703           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7704
7705           MovDelay[x][y]--;
7706           if (MovDelay[x][y])
7707             return;
7708         }
7709
7710         Tile[x][y] = EL_QUICKSAND_EMPTY;
7711         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7712         Store[x][y + 1] = Store[x][y];
7713         Store[x][y] = 0;
7714
7715         PlayLevelSoundAction(x, y, ACTION_FILLING);
7716       }
7717     }
7718     else if (element == EL_QUICKSAND_FAST_FULL)
7719     {
7720       if (IS_FREE(x, y + 1))
7721       {
7722         InitMovingField(x, y, MV_DOWN);
7723         started_moving = TRUE;
7724
7725         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7726 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7727         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7728           Store[x][y] = EL_ROCK;
7729 #else
7730         Store[x][y] = EL_ROCK;
7731 #endif
7732
7733         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7734       }
7735       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7736       {
7737         if (!MovDelay[x][y])
7738         {
7739           MovDelay[x][y] = TILEY + 1;
7740
7741           ResetGfxAnimation(x, y);
7742           ResetGfxAnimation(x, y + 1);
7743         }
7744
7745         if (MovDelay[x][y])
7746         {
7747           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7748           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7749
7750           MovDelay[x][y]--;
7751           if (MovDelay[x][y])
7752             return;
7753         }
7754
7755         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7756         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7757         Store[x][y + 1] = Store[x][y];
7758         Store[x][y] = 0;
7759
7760         PlayLevelSoundAction(x, y, ACTION_FILLING);
7761       }
7762       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7763       {
7764         if (!MovDelay[x][y])
7765         {
7766           MovDelay[x][y] = TILEY + 1;
7767
7768           ResetGfxAnimation(x, y);
7769           ResetGfxAnimation(x, y + 1);
7770         }
7771
7772         if (MovDelay[x][y])
7773         {
7774           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7775           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7776
7777           MovDelay[x][y]--;
7778           if (MovDelay[x][y])
7779             return;
7780         }
7781
7782         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7783         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7784         Store[x][y + 1] = Store[x][y];
7785         Store[x][y] = 0;
7786
7787         PlayLevelSoundAction(x, y, ACTION_FILLING);
7788       }
7789     }
7790     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7791              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7792     {
7793       InitMovingField(x, y, MV_DOWN);
7794       started_moving = TRUE;
7795
7796       Tile[x][y] = EL_QUICKSAND_FILLING;
7797       Store[x][y] = element;
7798
7799       PlayLevelSoundAction(x, y, ACTION_FILLING);
7800     }
7801     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7802              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7803     {
7804       InitMovingField(x, y, MV_DOWN);
7805       started_moving = TRUE;
7806
7807       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7808       Store[x][y] = element;
7809
7810       PlayLevelSoundAction(x, y, ACTION_FILLING);
7811     }
7812     else if (element == EL_MAGIC_WALL_FULL)
7813     {
7814       if (IS_FREE(x, y + 1))
7815       {
7816         InitMovingField(x, y, MV_DOWN);
7817         started_moving = TRUE;
7818
7819         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7820         Store[x][y] = EL_CHANGED(Store[x][y]);
7821       }
7822       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7823       {
7824         if (!MovDelay[x][y])
7825           MovDelay[x][y] = TILEY / 4 + 1;
7826
7827         if (MovDelay[x][y])
7828         {
7829           MovDelay[x][y]--;
7830           if (MovDelay[x][y])
7831             return;
7832         }
7833
7834         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7835         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7836         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7837         Store[x][y] = 0;
7838       }
7839     }
7840     else if (element == EL_BD_MAGIC_WALL_FULL)
7841     {
7842       if (IS_FREE(x, y + 1))
7843       {
7844         InitMovingField(x, y, MV_DOWN);
7845         started_moving = TRUE;
7846
7847         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7848         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7849       }
7850       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7851       {
7852         if (!MovDelay[x][y])
7853           MovDelay[x][y] = TILEY / 4 + 1;
7854
7855         if (MovDelay[x][y])
7856         {
7857           MovDelay[x][y]--;
7858           if (MovDelay[x][y])
7859             return;
7860         }
7861
7862         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7863         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7864         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7865         Store[x][y] = 0;
7866       }
7867     }
7868     else if (element == EL_DC_MAGIC_WALL_FULL)
7869     {
7870       if (IS_FREE(x, y + 1))
7871       {
7872         InitMovingField(x, y, MV_DOWN);
7873         started_moving = TRUE;
7874
7875         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7876         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7877       }
7878       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7879       {
7880         if (!MovDelay[x][y])
7881           MovDelay[x][y] = TILEY / 4 + 1;
7882
7883         if (MovDelay[x][y])
7884         {
7885           MovDelay[x][y]--;
7886           if (MovDelay[x][y])
7887             return;
7888         }
7889
7890         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7891         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7892         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7893         Store[x][y] = 0;
7894       }
7895     }
7896     else if ((CAN_PASS_MAGIC_WALL(element) &&
7897               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7898                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7899              (CAN_PASS_DC_MAGIC_WALL(element) &&
7900               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7901
7902     {
7903       InitMovingField(x, y, MV_DOWN);
7904       started_moving = TRUE;
7905
7906       Tile[x][y] =
7907         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7908          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7909          EL_DC_MAGIC_WALL_FILLING);
7910       Store[x][y] = element;
7911     }
7912     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7913     {
7914       SplashAcid(x, y + 1);
7915
7916       InitMovingField(x, y, MV_DOWN);
7917       started_moving = TRUE;
7918
7919       Store[x][y] = EL_ACID;
7920     }
7921     else if (
7922              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7923               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7924              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7925               CAN_FALL(element) && WasJustFalling[x][y] &&
7926               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7927
7928              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7929               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7930               (Tile[x][y + 1] == EL_BLOCKED)))
7931     {
7932       /* this is needed for a special case not covered by calling "Impact()"
7933          from "ContinueMoving()": if an element moves to a tile directly below
7934          another element which was just falling on that tile (which was empty
7935          in the previous frame), the falling element above would just stop
7936          instead of smashing the element below (in previous version, the above
7937          element was just checked for "moving" instead of "falling", resulting
7938          in incorrect smashes caused by horizontal movement of the above
7939          element; also, the case of the player being the element to smash was
7940          simply not covered here... :-/ ) */
7941
7942       CheckCollision[x][y] = 0;
7943       CheckImpact[x][y] = 0;
7944
7945       Impact(x, y);
7946     }
7947     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7948     {
7949       if (MovDir[x][y] == MV_NONE)
7950       {
7951         InitMovingField(x, y, MV_DOWN);
7952         started_moving = TRUE;
7953       }
7954     }
7955     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7956     {
7957       if (WasJustFalling[x][y]) // prevent animation from being restarted
7958         MovDir[x][y] = MV_DOWN;
7959
7960       InitMovingField(x, y, MV_DOWN);
7961       started_moving = TRUE;
7962     }
7963     else if (element == EL_AMOEBA_DROP)
7964     {
7965       Tile[x][y] = EL_AMOEBA_GROWING;
7966       Store[x][y] = EL_AMOEBA_WET;
7967     }
7968     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7969               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7970              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7971              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7972     {
7973       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7974                                 (IS_FREE(x - 1, y + 1) ||
7975                                  Tile[x - 1][y + 1] == EL_ACID));
7976       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7977                                 (IS_FREE(x + 1, y + 1) ||
7978                                  Tile[x + 1][y + 1] == EL_ACID));
7979       boolean can_fall_any  = (can_fall_left || can_fall_right);
7980       boolean can_fall_both = (can_fall_left && can_fall_right);
7981       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7982
7983       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7984       {
7985         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7986           can_fall_right = FALSE;
7987         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7988           can_fall_left = FALSE;
7989         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7990           can_fall_right = FALSE;
7991         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7992           can_fall_left = FALSE;
7993
7994         can_fall_any  = (can_fall_left || can_fall_right);
7995         can_fall_both = FALSE;
7996       }
7997
7998       if (can_fall_both)
7999       {
8000         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8001           can_fall_right = FALSE;       // slip down on left side
8002         else
8003           can_fall_left = !(can_fall_right = RND(2));
8004
8005         can_fall_both = FALSE;
8006       }
8007
8008       if (can_fall_any)
8009       {
8010         // if not determined otherwise, prefer left side for slipping down
8011         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8012         started_moving = TRUE;
8013       }
8014     }
8015     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8016     {
8017       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8018       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8019       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8020       int belt_dir = game.belt_dir[belt_nr];
8021
8022       if ((belt_dir == MV_LEFT  && left_is_free) ||
8023           (belt_dir == MV_RIGHT && right_is_free))
8024       {
8025         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8026
8027         InitMovingField(x, y, belt_dir);
8028         started_moving = TRUE;
8029
8030         Pushed[x][y] = TRUE;
8031         Pushed[nextx][y] = TRUE;
8032
8033         GfxAction[x][y] = ACTION_DEFAULT;
8034       }
8035       else
8036       {
8037         MovDir[x][y] = 0;       // if element was moving, stop it
8038       }
8039     }
8040   }
8041
8042   // not "else if" because of elements that can fall and move (EL_SPRING)
8043   if (CAN_MOVE(element) && !started_moving)
8044   {
8045     int move_pattern = element_info[element].move_pattern;
8046     int newx, newy;
8047
8048     Moving2Blocked(x, y, &newx, &newy);
8049
8050     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8051       return;
8052
8053     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8054         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8055     {
8056       WasJustMoving[x][y] = 0;
8057       CheckCollision[x][y] = 0;
8058
8059       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8060
8061       if (Tile[x][y] != element)        // element has changed
8062         return;
8063     }
8064
8065     if (!MovDelay[x][y])        // start new movement phase
8066     {
8067       // all objects that can change their move direction after each step
8068       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8069
8070       if (element != EL_YAMYAM &&
8071           element != EL_DARK_YAMYAM &&
8072           element != EL_PACMAN &&
8073           !(move_pattern & MV_ANY_DIRECTION) &&
8074           move_pattern != MV_TURNING_LEFT &&
8075           move_pattern != MV_TURNING_RIGHT &&
8076           move_pattern != MV_TURNING_LEFT_RIGHT &&
8077           move_pattern != MV_TURNING_RIGHT_LEFT &&
8078           move_pattern != MV_TURNING_RANDOM)
8079       {
8080         TurnRound(x, y);
8081
8082         if (MovDelay[x][y] && (element == EL_BUG ||
8083                                element == EL_SPACESHIP ||
8084                                element == EL_SP_SNIKSNAK ||
8085                                element == EL_SP_ELECTRON ||
8086                                element == EL_MOLE))
8087           TEST_DrawLevelField(x, y);
8088       }
8089     }
8090
8091     if (MovDelay[x][y])         // wait some time before next movement
8092     {
8093       MovDelay[x][y]--;
8094
8095       if (element == EL_ROBOT ||
8096           element == EL_YAMYAM ||
8097           element == EL_DARK_YAMYAM)
8098       {
8099         DrawLevelElementAnimationIfNeeded(x, y, element);
8100         PlayLevelSoundAction(x, y, ACTION_WAITING);
8101       }
8102       else if (element == EL_SP_ELECTRON)
8103         DrawLevelElementAnimationIfNeeded(x, y, element);
8104       else if (element == EL_DRAGON)
8105       {
8106         int i;
8107         int dir = MovDir[x][y];
8108         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8109         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8110         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8111                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8112                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8113                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8114         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8115
8116         GfxAction[x][y] = ACTION_ATTACKING;
8117
8118         if (IS_PLAYER(x, y))
8119           DrawPlayerField(x, y);
8120         else
8121           TEST_DrawLevelField(x, y);
8122
8123         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8124
8125         for (i = 1; i <= 3; i++)
8126         {
8127           int xx = x + i * dx;
8128           int yy = y + i * dy;
8129           int sx = SCREENX(xx);
8130           int sy = SCREENY(yy);
8131           int flame_graphic = graphic + (i - 1);
8132
8133           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8134             break;
8135
8136           if (MovDelay[x][y])
8137           {
8138             int flamed = MovingOrBlocked2Element(xx, yy);
8139
8140             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8141               Bang(xx, yy);
8142             else
8143               RemoveMovingField(xx, yy);
8144
8145             ChangeDelay[xx][yy] = 0;
8146
8147             Tile[xx][yy] = EL_FLAMES;
8148
8149             if (IN_SCR_FIELD(sx, sy))
8150             {
8151               TEST_DrawLevelFieldCrumbled(xx, yy);
8152               DrawGraphic(sx, sy, flame_graphic, frame);
8153             }
8154           }
8155           else
8156           {
8157             if (Tile[xx][yy] == EL_FLAMES)
8158               Tile[xx][yy] = EL_EMPTY;
8159             TEST_DrawLevelField(xx, yy);
8160           }
8161         }
8162       }
8163
8164       if (MovDelay[x][y])       // element still has to wait some time
8165       {
8166         PlayLevelSoundAction(x, y, ACTION_WAITING);
8167
8168         return;
8169       }
8170     }
8171
8172     // now make next step
8173
8174     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8175
8176     if (DONT_COLLIDE_WITH(element) &&
8177         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8178         !PLAYER_ENEMY_PROTECTED(newx, newy))
8179     {
8180       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8181
8182       return;
8183     }
8184
8185     else if (CAN_MOVE_INTO_ACID(element) &&
8186              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8187              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8188              (MovDir[x][y] == MV_DOWN ||
8189               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8190     {
8191       SplashAcid(newx, newy);
8192       Store[x][y] = EL_ACID;
8193     }
8194     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8195     {
8196       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8197           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8198           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8199           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8200       {
8201         RemoveField(x, y);
8202         TEST_DrawLevelField(x, y);
8203
8204         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8205         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8206           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8207
8208         game.friends_still_needed--;
8209         if (!game.friends_still_needed &&
8210             !game.GameOver &&
8211             game.all_players_gone)
8212           LevelSolved();
8213
8214         return;
8215       }
8216       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8217       {
8218         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8219           TEST_DrawLevelField(newx, newy);
8220         else
8221           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8222       }
8223       else if (!IS_FREE(newx, newy))
8224       {
8225         GfxAction[x][y] = ACTION_WAITING;
8226
8227         if (IS_PLAYER(x, y))
8228           DrawPlayerField(x, y);
8229         else
8230           TEST_DrawLevelField(x, y);
8231
8232         return;
8233       }
8234     }
8235     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8236     {
8237       if (IS_FOOD_PIG(Tile[newx][newy]))
8238       {
8239         if (IS_MOVING(newx, newy))
8240           RemoveMovingField(newx, newy);
8241         else
8242         {
8243           Tile[newx][newy] = EL_EMPTY;
8244           TEST_DrawLevelField(newx, newy);
8245         }
8246
8247         PlayLevelSound(x, y, SND_PIG_DIGGING);
8248       }
8249       else if (!IS_FREE(newx, newy))
8250       {
8251         if (IS_PLAYER(x, y))
8252           DrawPlayerField(x, y);
8253         else
8254           TEST_DrawLevelField(x, y);
8255
8256         return;
8257       }
8258     }
8259     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8260     {
8261       if (Store[x][y] != EL_EMPTY)
8262       {
8263         boolean can_clone = FALSE;
8264         int xx, yy;
8265
8266         // check if element to clone is still there
8267         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8268         {
8269           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8270           {
8271             can_clone = TRUE;
8272
8273             break;
8274           }
8275         }
8276
8277         // cannot clone or target field not free anymore -- do not clone
8278         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8279           Store[x][y] = EL_EMPTY;
8280       }
8281
8282       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8283       {
8284         if (IS_MV_DIAGONAL(MovDir[x][y]))
8285         {
8286           int diagonal_move_dir = MovDir[x][y];
8287           int stored = Store[x][y];
8288           int change_delay = 8;
8289           int graphic;
8290
8291           // android is moving diagonally
8292
8293           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8294
8295           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8296           GfxElement[x][y] = EL_EMC_ANDROID;
8297           GfxAction[x][y] = ACTION_SHRINKING;
8298           GfxDir[x][y] = diagonal_move_dir;
8299           ChangeDelay[x][y] = change_delay;
8300
8301           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8302                                    GfxDir[x][y]);
8303
8304           DrawLevelGraphicAnimation(x, y, graphic);
8305           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8306
8307           if (Tile[newx][newy] == EL_ACID)
8308           {
8309             SplashAcid(newx, newy);
8310
8311             return;
8312           }
8313
8314           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8315
8316           Store[newx][newy] = EL_EMC_ANDROID;
8317           GfxElement[newx][newy] = EL_EMC_ANDROID;
8318           GfxAction[newx][newy] = ACTION_GROWING;
8319           GfxDir[newx][newy] = diagonal_move_dir;
8320           ChangeDelay[newx][newy] = change_delay;
8321
8322           graphic = el_act_dir2img(GfxElement[newx][newy],
8323                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8324
8325           DrawLevelGraphicAnimation(newx, newy, graphic);
8326           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8327
8328           return;
8329         }
8330         else
8331         {
8332           Tile[newx][newy] = EL_EMPTY;
8333           TEST_DrawLevelField(newx, newy);
8334
8335           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8336         }
8337       }
8338       else if (!IS_FREE(newx, newy))
8339       {
8340         return;
8341       }
8342     }
8343     else if (IS_CUSTOM_ELEMENT(element) &&
8344              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8345     {
8346       if (!DigFieldByCE(newx, newy, element))
8347         return;
8348
8349       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8350       {
8351         RunnerVisit[x][y] = FrameCounter;
8352         PlayerVisit[x][y] /= 8;         // expire player visit path
8353       }
8354     }
8355     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8356     {
8357       if (!IS_FREE(newx, newy))
8358       {
8359         if (IS_PLAYER(x, y))
8360           DrawPlayerField(x, y);
8361         else
8362           TEST_DrawLevelField(x, y);
8363
8364         return;
8365       }
8366       else
8367       {
8368         boolean wanna_flame = !RND(10);
8369         int dx = newx - x, dy = newy - y;
8370         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8371         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8372         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8373                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8374         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8375                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8376
8377         if ((wanna_flame ||
8378              IS_CLASSIC_ENEMY(element1) ||
8379              IS_CLASSIC_ENEMY(element2)) &&
8380             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8381             element1 != EL_FLAMES && element2 != EL_FLAMES)
8382         {
8383           ResetGfxAnimation(x, y);
8384           GfxAction[x][y] = ACTION_ATTACKING;
8385
8386           if (IS_PLAYER(x, y))
8387             DrawPlayerField(x, y);
8388           else
8389             TEST_DrawLevelField(x, y);
8390
8391           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8392
8393           MovDelay[x][y] = 50;
8394
8395           Tile[newx][newy] = EL_FLAMES;
8396           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8397             Tile[newx1][newy1] = EL_FLAMES;
8398           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8399             Tile[newx2][newy2] = EL_FLAMES;
8400
8401           return;
8402         }
8403       }
8404     }
8405     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8406              Tile[newx][newy] == EL_DIAMOND)
8407     {
8408       if (IS_MOVING(newx, newy))
8409         RemoveMovingField(newx, newy);
8410       else
8411       {
8412         Tile[newx][newy] = EL_EMPTY;
8413         TEST_DrawLevelField(newx, newy);
8414       }
8415
8416       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8417     }
8418     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8419              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8420     {
8421       if (AmoebaNr[newx][newy])
8422       {
8423         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8424         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8425             Tile[newx][newy] == EL_BD_AMOEBA)
8426           AmoebaCnt[AmoebaNr[newx][newy]]--;
8427       }
8428
8429       if (IS_MOVING(newx, newy))
8430       {
8431         RemoveMovingField(newx, newy);
8432       }
8433       else
8434       {
8435         Tile[newx][newy] = EL_EMPTY;
8436         TEST_DrawLevelField(newx, newy);
8437       }
8438
8439       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8440     }
8441     else if ((element == EL_PACMAN || element == EL_MOLE)
8442              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8443     {
8444       if (AmoebaNr[newx][newy])
8445       {
8446         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8447         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8448             Tile[newx][newy] == EL_BD_AMOEBA)
8449           AmoebaCnt[AmoebaNr[newx][newy]]--;
8450       }
8451
8452       if (element == EL_MOLE)
8453       {
8454         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8455         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8456
8457         ResetGfxAnimation(x, y);
8458         GfxAction[x][y] = ACTION_DIGGING;
8459         TEST_DrawLevelField(x, y);
8460
8461         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8462
8463         return;                         // wait for shrinking amoeba
8464       }
8465       else      // element == EL_PACMAN
8466       {
8467         Tile[newx][newy] = EL_EMPTY;
8468         TEST_DrawLevelField(newx, newy);
8469         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8470       }
8471     }
8472     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8473              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8474               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8475     {
8476       // wait for shrinking amoeba to completely disappear
8477       return;
8478     }
8479     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8480     {
8481       // object was running against a wall
8482
8483       TurnRound(x, y);
8484
8485       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8486         DrawLevelElementAnimation(x, y, element);
8487
8488       if (DONT_TOUCH(element))
8489         TestIfBadThingTouchesPlayer(x, y);
8490
8491       return;
8492     }
8493
8494     InitMovingField(x, y, MovDir[x][y]);
8495
8496     PlayLevelSoundAction(x, y, ACTION_MOVING);
8497   }
8498
8499   if (MovDir[x][y])
8500     ContinueMoving(x, y);
8501 }
8502
8503 void ContinueMoving(int x, int y)
8504 {
8505   int element = Tile[x][y];
8506   struct ElementInfo *ei = &element_info[element];
8507   int direction = MovDir[x][y];
8508   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8509   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8510   int newx = x + dx, newy = y + dy;
8511   int stored = Store[x][y];
8512   int stored_new = Store[newx][newy];
8513   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8514   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8515   boolean last_line = (newy == lev_fieldy - 1);
8516   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8517
8518   if (pushed_by_player)         // special case: moving object pushed by player
8519   {
8520     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8521   }
8522   else if (use_step_delay)      // special case: moving object has step delay
8523   {
8524     if (!MovDelay[x][y])
8525       MovPos[x][y] += getElementMoveStepsize(x, y);
8526
8527     if (MovDelay[x][y])
8528       MovDelay[x][y]--;
8529     else
8530       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8531
8532     if (MovDelay[x][y])
8533     {
8534       TEST_DrawLevelField(x, y);
8535
8536       return;   // element is still waiting
8537     }
8538   }
8539   else                          // normal case: generically moving object
8540   {
8541     MovPos[x][y] += getElementMoveStepsize(x, y);
8542   }
8543
8544   if (ABS(MovPos[x][y]) < TILEX)
8545   {
8546     TEST_DrawLevelField(x, y);
8547
8548     return;     // element is still moving
8549   }
8550
8551   // element reached destination field
8552
8553   Tile[x][y] = EL_EMPTY;
8554   Tile[newx][newy] = element;
8555   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8556
8557   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8558   {
8559     element = Tile[newx][newy] = EL_ACID;
8560   }
8561   else if (element == EL_MOLE)
8562   {
8563     Tile[x][y] = EL_SAND;
8564
8565     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8566   }
8567   else if (element == EL_QUICKSAND_FILLING)
8568   {
8569     element = Tile[newx][newy] = get_next_element(element);
8570     Store[newx][newy] = Store[x][y];
8571   }
8572   else if (element == EL_QUICKSAND_EMPTYING)
8573   {
8574     Tile[x][y] = get_next_element(element);
8575     element = Tile[newx][newy] = Store[x][y];
8576   }
8577   else if (element == EL_QUICKSAND_FAST_FILLING)
8578   {
8579     element = Tile[newx][newy] = get_next_element(element);
8580     Store[newx][newy] = Store[x][y];
8581   }
8582   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8583   {
8584     Tile[x][y] = get_next_element(element);
8585     element = Tile[newx][newy] = Store[x][y];
8586   }
8587   else if (element == EL_MAGIC_WALL_FILLING)
8588   {
8589     element = Tile[newx][newy] = get_next_element(element);
8590     if (!game.magic_wall_active)
8591       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8592     Store[newx][newy] = Store[x][y];
8593   }
8594   else if (element == EL_MAGIC_WALL_EMPTYING)
8595   {
8596     Tile[x][y] = get_next_element(element);
8597     if (!game.magic_wall_active)
8598       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8599     element = Tile[newx][newy] = Store[x][y];
8600
8601     InitField(newx, newy, FALSE);
8602   }
8603   else if (element == EL_BD_MAGIC_WALL_FILLING)
8604   {
8605     element = Tile[newx][newy] = get_next_element(element);
8606     if (!game.magic_wall_active)
8607       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8608     Store[newx][newy] = Store[x][y];
8609   }
8610   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8611   {
8612     Tile[x][y] = get_next_element(element);
8613     if (!game.magic_wall_active)
8614       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8615     element = Tile[newx][newy] = Store[x][y];
8616
8617     InitField(newx, newy, FALSE);
8618   }
8619   else if (element == EL_DC_MAGIC_WALL_FILLING)
8620   {
8621     element = Tile[newx][newy] = get_next_element(element);
8622     if (!game.magic_wall_active)
8623       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8624     Store[newx][newy] = Store[x][y];
8625   }
8626   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8627   {
8628     Tile[x][y] = get_next_element(element);
8629     if (!game.magic_wall_active)
8630       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8631     element = Tile[newx][newy] = Store[x][y];
8632
8633     InitField(newx, newy, FALSE);
8634   }
8635   else if (element == EL_AMOEBA_DROPPING)
8636   {
8637     Tile[x][y] = get_next_element(element);
8638     element = Tile[newx][newy] = Store[x][y];
8639   }
8640   else if (element == EL_SOKOBAN_OBJECT)
8641   {
8642     if (Back[x][y])
8643       Tile[x][y] = Back[x][y];
8644
8645     if (Back[newx][newy])
8646       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8647
8648     Back[x][y] = Back[newx][newy] = 0;
8649   }
8650
8651   Store[x][y] = EL_EMPTY;
8652   MovPos[x][y] = 0;
8653   MovDir[x][y] = 0;
8654   MovDelay[x][y] = 0;
8655
8656   MovDelay[newx][newy] = 0;
8657
8658   if (CAN_CHANGE_OR_HAS_ACTION(element))
8659   {
8660     // copy element change control values to new field
8661     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8662     ChangePage[newx][newy]  = ChangePage[x][y];
8663     ChangeCount[newx][newy] = ChangeCount[x][y];
8664     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8665   }
8666
8667   CustomValue[newx][newy] = CustomValue[x][y];
8668
8669   ChangeDelay[x][y] = 0;
8670   ChangePage[x][y] = -1;
8671   ChangeCount[x][y] = 0;
8672   ChangeEvent[x][y] = -1;
8673
8674   CustomValue[x][y] = 0;
8675
8676   // copy animation control values to new field
8677   GfxFrame[newx][newy]  = GfxFrame[x][y];
8678   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8679   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8680   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8681
8682   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8683
8684   // some elements can leave other elements behind after moving
8685   if (ei->move_leave_element != EL_EMPTY &&
8686       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8687       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8688   {
8689     int move_leave_element = ei->move_leave_element;
8690
8691     // this makes it possible to leave the removed element again
8692     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8693       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8694
8695     Tile[x][y] = move_leave_element;
8696
8697     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8698       MovDir[x][y] = direction;
8699
8700     InitField(x, y, FALSE);
8701
8702     if (GFX_CRUMBLED(Tile[x][y]))
8703       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8704
8705     if (ELEM_IS_PLAYER(move_leave_element))
8706       RelocatePlayer(x, y, move_leave_element);
8707   }
8708
8709   // do this after checking for left-behind element
8710   ResetGfxAnimation(x, y);      // reset animation values for old field
8711
8712   if (!CAN_MOVE(element) ||
8713       (CAN_FALL(element) && direction == MV_DOWN &&
8714        (element == EL_SPRING ||
8715         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8716         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8717     GfxDir[x][y] = MovDir[newx][newy] = 0;
8718
8719   TEST_DrawLevelField(x, y);
8720   TEST_DrawLevelField(newx, newy);
8721
8722   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8723
8724   // prevent pushed element from moving on in pushed direction
8725   if (pushed_by_player && CAN_MOVE(element) &&
8726       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8727       !(element_info[element].move_pattern & direction))
8728     TurnRound(newx, newy);
8729
8730   // prevent elements on conveyor belt from moving on in last direction
8731   if (pushed_by_conveyor && CAN_FALL(element) &&
8732       direction & MV_HORIZONTAL)
8733     MovDir[newx][newy] = 0;
8734
8735   if (!pushed_by_player)
8736   {
8737     int nextx = newx + dx, nexty = newy + dy;
8738     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8739
8740     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8741
8742     if (CAN_FALL(element) && direction == MV_DOWN)
8743       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8744
8745     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8746       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8747
8748     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8749       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8750   }
8751
8752   if (DONT_TOUCH(element))      // object may be nasty to player or others
8753   {
8754     TestIfBadThingTouchesPlayer(newx, newy);
8755     TestIfBadThingTouchesFriend(newx, newy);
8756
8757     if (!IS_CUSTOM_ELEMENT(element))
8758       TestIfBadThingTouchesOtherBadThing(newx, newy);
8759   }
8760   else if (element == EL_PENGUIN)
8761     TestIfFriendTouchesBadThing(newx, newy);
8762
8763   if (DONT_GET_HIT_BY(element))
8764   {
8765     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8766   }
8767
8768   // give the player one last chance (one more frame) to move away
8769   if (CAN_FALL(element) && direction == MV_DOWN &&
8770       (last_line || (!IS_FREE(x, newy + 1) &&
8771                      (!IS_PLAYER(x, newy + 1) ||
8772                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8773     Impact(x, newy);
8774
8775   if (pushed_by_player && !game.use_change_when_pushing_bug)
8776   {
8777     int push_side = MV_DIR_OPPOSITE(direction);
8778     struct PlayerInfo *player = PLAYERINFO(x, y);
8779
8780     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8781                                player->index_bit, push_side);
8782     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8783                                         player->index_bit, push_side);
8784   }
8785
8786   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8787     MovDelay[newx][newy] = 1;
8788
8789   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8790
8791   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8792   TestIfElementHitsCustomElement(newx, newy, direction);
8793   TestIfPlayerTouchesCustomElement(newx, newy);
8794   TestIfElementTouchesCustomElement(newx, newy);
8795
8796   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8797       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8798     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8799                              MV_DIR_OPPOSITE(direction));
8800 }
8801
8802 int AmoebaNeighbourNr(int ax, int ay)
8803 {
8804   int i;
8805   int element = Tile[ax][ay];
8806   int group_nr = 0;
8807   static int xy[4][2] =
8808   {
8809     { 0, -1 },
8810     { -1, 0 },
8811     { +1, 0 },
8812     { 0, +1 }
8813   };
8814
8815   for (i = 0; i < NUM_DIRECTIONS; i++)
8816   {
8817     int x = ax + xy[i][0];
8818     int y = ay + xy[i][1];
8819
8820     if (!IN_LEV_FIELD(x, y))
8821       continue;
8822
8823     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8824       group_nr = AmoebaNr[x][y];
8825   }
8826
8827   return group_nr;
8828 }
8829
8830 static void AmoebaMerge(int ax, int ay)
8831 {
8832   int i, x, y, xx, yy;
8833   int new_group_nr = AmoebaNr[ax][ay];
8834   static int xy[4][2] =
8835   {
8836     { 0, -1 },
8837     { -1, 0 },
8838     { +1, 0 },
8839     { 0, +1 }
8840   };
8841
8842   if (new_group_nr == 0)
8843     return;
8844
8845   for (i = 0; i < NUM_DIRECTIONS; i++)
8846   {
8847     x = ax + xy[i][0];
8848     y = ay + xy[i][1];
8849
8850     if (!IN_LEV_FIELD(x, y))
8851       continue;
8852
8853     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8854          Tile[x][y] == EL_BD_AMOEBA ||
8855          Tile[x][y] == EL_AMOEBA_DEAD) &&
8856         AmoebaNr[x][y] != new_group_nr)
8857     {
8858       int old_group_nr = AmoebaNr[x][y];
8859
8860       if (old_group_nr == 0)
8861         return;
8862
8863       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8864       AmoebaCnt[old_group_nr] = 0;
8865       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8866       AmoebaCnt2[old_group_nr] = 0;
8867
8868       SCAN_PLAYFIELD(xx, yy)
8869       {
8870         if (AmoebaNr[xx][yy] == old_group_nr)
8871           AmoebaNr[xx][yy] = new_group_nr;
8872       }
8873     }
8874   }
8875 }
8876
8877 void AmoebaToDiamond(int ax, int ay)
8878 {
8879   int i, x, y;
8880
8881   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8882   {
8883     int group_nr = AmoebaNr[ax][ay];
8884
8885 #ifdef DEBUG
8886     if (group_nr == 0)
8887     {
8888       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8889       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8890
8891       return;
8892     }
8893 #endif
8894
8895     SCAN_PLAYFIELD(x, y)
8896     {
8897       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8898       {
8899         AmoebaNr[x][y] = 0;
8900         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8901       }
8902     }
8903
8904     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8905                             SND_AMOEBA_TURNING_TO_GEM :
8906                             SND_AMOEBA_TURNING_TO_ROCK));
8907     Bang(ax, ay);
8908   }
8909   else
8910   {
8911     static int xy[4][2] =
8912     {
8913       { 0, -1 },
8914       { -1, 0 },
8915       { +1, 0 },
8916       { 0, +1 }
8917     };
8918
8919     for (i = 0; i < NUM_DIRECTIONS; i++)
8920     {
8921       x = ax + xy[i][0];
8922       y = ay + xy[i][1];
8923
8924       if (!IN_LEV_FIELD(x, y))
8925         continue;
8926
8927       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8928       {
8929         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8930                               SND_AMOEBA_TURNING_TO_GEM :
8931                               SND_AMOEBA_TURNING_TO_ROCK));
8932         Bang(x, y);
8933       }
8934     }
8935   }
8936 }
8937
8938 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8939 {
8940   int x, y;
8941   int group_nr = AmoebaNr[ax][ay];
8942   boolean done = FALSE;
8943
8944 #ifdef DEBUG
8945   if (group_nr == 0)
8946   {
8947     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8948     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8949
8950     return;
8951   }
8952 #endif
8953
8954   SCAN_PLAYFIELD(x, y)
8955   {
8956     if (AmoebaNr[x][y] == group_nr &&
8957         (Tile[x][y] == EL_AMOEBA_DEAD ||
8958          Tile[x][y] == EL_BD_AMOEBA ||
8959          Tile[x][y] == EL_AMOEBA_GROWING))
8960     {
8961       AmoebaNr[x][y] = 0;
8962       Tile[x][y] = new_element;
8963       InitField(x, y, FALSE);
8964       TEST_DrawLevelField(x, y);
8965       done = TRUE;
8966     }
8967   }
8968
8969   if (done)
8970     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8971                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8972                             SND_BD_AMOEBA_TURNING_TO_GEM));
8973 }
8974
8975 static void AmoebaGrowing(int x, int y)
8976 {
8977   static unsigned int sound_delay = 0;
8978   static unsigned int sound_delay_value = 0;
8979
8980   if (!MovDelay[x][y])          // start new growing cycle
8981   {
8982     MovDelay[x][y] = 7;
8983
8984     if (DelayReached(&sound_delay, sound_delay_value))
8985     {
8986       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8987       sound_delay_value = 30;
8988     }
8989   }
8990
8991   if (MovDelay[x][y])           // wait some time before growing bigger
8992   {
8993     MovDelay[x][y]--;
8994     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8995     {
8996       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8997                                            6 - MovDelay[x][y]);
8998
8999       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9000     }
9001
9002     if (!MovDelay[x][y])
9003     {
9004       Tile[x][y] = Store[x][y];
9005       Store[x][y] = 0;
9006       TEST_DrawLevelField(x, y);
9007     }
9008   }
9009 }
9010
9011 static void AmoebaShrinking(int x, int y)
9012 {
9013   static unsigned int sound_delay = 0;
9014   static unsigned int sound_delay_value = 0;
9015
9016   if (!MovDelay[x][y])          // start new shrinking cycle
9017   {
9018     MovDelay[x][y] = 7;
9019
9020     if (DelayReached(&sound_delay, sound_delay_value))
9021       sound_delay_value = 30;
9022   }
9023
9024   if (MovDelay[x][y])           // wait some time before shrinking
9025   {
9026     MovDelay[x][y]--;
9027     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9028     {
9029       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9030                                            6 - MovDelay[x][y]);
9031
9032       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9033     }
9034
9035     if (!MovDelay[x][y])
9036     {
9037       Tile[x][y] = EL_EMPTY;
9038       TEST_DrawLevelField(x, y);
9039
9040       // don't let mole enter this field in this cycle;
9041       // (give priority to objects falling to this field from above)
9042       Stop[x][y] = TRUE;
9043     }
9044   }
9045 }
9046
9047 static void AmoebaReproduce(int ax, int ay)
9048 {
9049   int i;
9050   int element = Tile[ax][ay];
9051   int graphic = el2img(element);
9052   int newax = ax, neway = ay;
9053   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9054   static int xy[4][2] =
9055   {
9056     { 0, -1 },
9057     { -1, 0 },
9058     { +1, 0 },
9059     { 0, +1 }
9060   };
9061
9062   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9063   {
9064     Tile[ax][ay] = EL_AMOEBA_DEAD;
9065     TEST_DrawLevelField(ax, ay);
9066     return;
9067   }
9068
9069   if (IS_ANIMATED(graphic))
9070     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9071
9072   if (!MovDelay[ax][ay])        // start making new amoeba field
9073     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9074
9075   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9076   {
9077     MovDelay[ax][ay]--;
9078     if (MovDelay[ax][ay])
9079       return;
9080   }
9081
9082   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9083   {
9084     int start = RND(4);
9085     int x = ax + xy[start][0];
9086     int y = ay + xy[start][1];
9087
9088     if (!IN_LEV_FIELD(x, y))
9089       return;
9090
9091     if (IS_FREE(x, y) ||
9092         CAN_GROW_INTO(Tile[x][y]) ||
9093         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9094         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9095     {
9096       newax = x;
9097       neway = y;
9098     }
9099
9100     if (newax == ax && neway == ay)
9101       return;
9102   }
9103   else                          // normal or "filled" (BD style) amoeba
9104   {
9105     int start = RND(4);
9106     boolean waiting_for_player = FALSE;
9107
9108     for (i = 0; i < NUM_DIRECTIONS; i++)
9109     {
9110       int j = (start + i) % 4;
9111       int x = ax + xy[j][0];
9112       int y = ay + xy[j][1];
9113
9114       if (!IN_LEV_FIELD(x, y))
9115         continue;
9116
9117       if (IS_FREE(x, y) ||
9118           CAN_GROW_INTO(Tile[x][y]) ||
9119           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9120           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9121       {
9122         newax = x;
9123         neway = y;
9124         break;
9125       }
9126       else if (IS_PLAYER(x, y))
9127         waiting_for_player = TRUE;
9128     }
9129
9130     if (newax == ax && neway == ay)             // amoeba cannot grow
9131     {
9132       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9133       {
9134         Tile[ax][ay] = EL_AMOEBA_DEAD;
9135         TEST_DrawLevelField(ax, ay);
9136         AmoebaCnt[AmoebaNr[ax][ay]]--;
9137
9138         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9139         {
9140           if (element == EL_AMOEBA_FULL)
9141             AmoebaToDiamond(ax, ay);
9142           else if (element == EL_BD_AMOEBA)
9143             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9144         }
9145       }
9146       return;
9147     }
9148     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9149     {
9150       // amoeba gets larger by growing in some direction
9151
9152       int new_group_nr = AmoebaNr[ax][ay];
9153
9154 #ifdef DEBUG
9155   if (new_group_nr == 0)
9156   {
9157     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9158           newax, neway);
9159     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9160
9161     return;
9162   }
9163 #endif
9164
9165       AmoebaNr[newax][neway] = new_group_nr;
9166       AmoebaCnt[new_group_nr]++;
9167       AmoebaCnt2[new_group_nr]++;
9168
9169       // if amoeba touches other amoeba(s) after growing, unify them
9170       AmoebaMerge(newax, neway);
9171
9172       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9173       {
9174         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9175         return;
9176       }
9177     }
9178   }
9179
9180   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9181       (neway == lev_fieldy - 1 && newax != ax))
9182   {
9183     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9184     Store[newax][neway] = element;
9185   }
9186   else if (neway == ay || element == EL_EMC_DRIPPER)
9187   {
9188     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9189
9190     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9191   }
9192   else
9193   {
9194     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9195     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9196     Store[ax][ay] = EL_AMOEBA_DROP;
9197     ContinueMoving(ax, ay);
9198     return;
9199   }
9200
9201   TEST_DrawLevelField(newax, neway);
9202 }
9203
9204 static void Life(int ax, int ay)
9205 {
9206   int x1, y1, x2, y2;
9207   int life_time = 40;
9208   int element = Tile[ax][ay];
9209   int graphic = el2img(element);
9210   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9211                          level.biomaze);
9212   boolean changed = FALSE;
9213
9214   if (IS_ANIMATED(graphic))
9215     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9216
9217   if (Stop[ax][ay])
9218     return;
9219
9220   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9221     MovDelay[ax][ay] = life_time;
9222
9223   if (MovDelay[ax][ay])         // wait some time before next cycle
9224   {
9225     MovDelay[ax][ay]--;
9226     if (MovDelay[ax][ay])
9227       return;
9228   }
9229
9230   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9231   {
9232     int xx = ax+x1, yy = ay+y1;
9233     int old_element = Tile[xx][yy];
9234     int num_neighbours = 0;
9235
9236     if (!IN_LEV_FIELD(xx, yy))
9237       continue;
9238
9239     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9240     {
9241       int x = xx+x2, y = yy+y2;
9242
9243       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9244         continue;
9245
9246       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9247       boolean is_neighbour = FALSE;
9248
9249       if (level.use_life_bugs)
9250         is_neighbour =
9251           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9252            (IS_FREE(x, y)                             &&  Stop[x][y]));
9253       else
9254         is_neighbour =
9255           (Last[x][y] == element || is_player_cell);
9256
9257       if (is_neighbour)
9258         num_neighbours++;
9259     }
9260
9261     boolean is_free = FALSE;
9262
9263     if (level.use_life_bugs)
9264       is_free = (IS_FREE(xx, yy));
9265     else
9266       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9267
9268     if (xx == ax && yy == ay)           // field in the middle
9269     {
9270       if (num_neighbours < life_parameter[0] ||
9271           num_neighbours > life_parameter[1])
9272       {
9273         Tile[xx][yy] = EL_EMPTY;
9274         if (Tile[xx][yy] != old_element)
9275           TEST_DrawLevelField(xx, yy);
9276         Stop[xx][yy] = TRUE;
9277         changed = TRUE;
9278       }
9279     }
9280     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9281     {                                   // free border field
9282       if (num_neighbours >= life_parameter[2] &&
9283           num_neighbours <= life_parameter[3])
9284       {
9285         Tile[xx][yy] = element;
9286         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9287         if (Tile[xx][yy] != old_element)
9288           TEST_DrawLevelField(xx, yy);
9289         Stop[xx][yy] = TRUE;
9290         changed = TRUE;
9291       }
9292     }
9293   }
9294
9295   if (changed)
9296     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9297                    SND_GAME_OF_LIFE_GROWING);
9298 }
9299
9300 static void InitRobotWheel(int x, int y)
9301 {
9302   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9303 }
9304
9305 static void RunRobotWheel(int x, int y)
9306 {
9307   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9308 }
9309
9310 static void StopRobotWheel(int x, int y)
9311 {
9312   if (game.robot_wheel_x == x &&
9313       game.robot_wheel_y == y)
9314   {
9315     game.robot_wheel_x = -1;
9316     game.robot_wheel_y = -1;
9317     game.robot_wheel_active = FALSE;
9318   }
9319 }
9320
9321 static void InitTimegateWheel(int x, int y)
9322 {
9323   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9324 }
9325
9326 static void RunTimegateWheel(int x, int y)
9327 {
9328   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9329 }
9330
9331 static void InitMagicBallDelay(int x, int y)
9332 {
9333   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9334 }
9335
9336 static void ActivateMagicBall(int bx, int by)
9337 {
9338   int x, y;
9339
9340   if (level.ball_random)
9341   {
9342     int pos_border = RND(8);    // select one of the eight border elements
9343     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9344     int xx = pos_content % 3;
9345     int yy = pos_content / 3;
9346
9347     x = bx - 1 + xx;
9348     y = by - 1 + yy;
9349
9350     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9351       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9352   }
9353   else
9354   {
9355     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9356     {
9357       int xx = x - bx + 1;
9358       int yy = y - by + 1;
9359
9360       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9361         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9362     }
9363   }
9364
9365   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9366 }
9367
9368 static void CheckExit(int x, int y)
9369 {
9370   if (game.gems_still_needed > 0 ||
9371       game.sokoban_fields_still_needed > 0 ||
9372       game.sokoban_objects_still_needed > 0 ||
9373       game.lights_still_needed > 0)
9374   {
9375     int element = Tile[x][y];
9376     int graphic = el2img(element);
9377
9378     if (IS_ANIMATED(graphic))
9379       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9380
9381     return;
9382   }
9383
9384   // do not re-open exit door closed after last player
9385   if (game.all_players_gone)
9386     return;
9387
9388   Tile[x][y] = EL_EXIT_OPENING;
9389
9390   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9391 }
9392
9393 static void CheckExitEM(int x, int y)
9394 {
9395   if (game.gems_still_needed > 0 ||
9396       game.sokoban_fields_still_needed > 0 ||
9397       game.sokoban_objects_still_needed > 0 ||
9398       game.lights_still_needed > 0)
9399   {
9400     int element = Tile[x][y];
9401     int graphic = el2img(element);
9402
9403     if (IS_ANIMATED(graphic))
9404       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9405
9406     return;
9407   }
9408
9409   // do not re-open exit door closed after last player
9410   if (game.all_players_gone)
9411     return;
9412
9413   Tile[x][y] = EL_EM_EXIT_OPENING;
9414
9415   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9416 }
9417
9418 static void CheckExitSteel(int x, int y)
9419 {
9420   if (game.gems_still_needed > 0 ||
9421       game.sokoban_fields_still_needed > 0 ||
9422       game.sokoban_objects_still_needed > 0 ||
9423       game.lights_still_needed > 0)
9424   {
9425     int element = Tile[x][y];
9426     int graphic = el2img(element);
9427
9428     if (IS_ANIMATED(graphic))
9429       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9430
9431     return;
9432   }
9433
9434   // do not re-open exit door closed after last player
9435   if (game.all_players_gone)
9436     return;
9437
9438   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9439
9440   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9441 }
9442
9443 static void CheckExitSteelEM(int x, int y)
9444 {
9445   if (game.gems_still_needed > 0 ||
9446       game.sokoban_fields_still_needed > 0 ||
9447       game.sokoban_objects_still_needed > 0 ||
9448       game.lights_still_needed > 0)
9449   {
9450     int element = Tile[x][y];
9451     int graphic = el2img(element);
9452
9453     if (IS_ANIMATED(graphic))
9454       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9455
9456     return;
9457   }
9458
9459   // do not re-open exit door closed after last player
9460   if (game.all_players_gone)
9461     return;
9462
9463   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9464
9465   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9466 }
9467
9468 static void CheckExitSP(int x, int y)
9469 {
9470   if (game.gems_still_needed > 0)
9471   {
9472     int element = Tile[x][y];
9473     int graphic = el2img(element);
9474
9475     if (IS_ANIMATED(graphic))
9476       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9477
9478     return;
9479   }
9480
9481   // do not re-open exit door closed after last player
9482   if (game.all_players_gone)
9483     return;
9484
9485   Tile[x][y] = EL_SP_EXIT_OPENING;
9486
9487   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9488 }
9489
9490 static void CloseAllOpenTimegates(void)
9491 {
9492   int x, y;
9493
9494   SCAN_PLAYFIELD(x, y)
9495   {
9496     int element = Tile[x][y];
9497
9498     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9499     {
9500       Tile[x][y] = EL_TIMEGATE_CLOSING;
9501
9502       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9503     }
9504   }
9505 }
9506
9507 static void DrawTwinkleOnField(int x, int y)
9508 {
9509   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9510     return;
9511
9512   if (Tile[x][y] == EL_BD_DIAMOND)
9513     return;
9514
9515   if (MovDelay[x][y] == 0)      // next animation frame
9516     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9517
9518   if (MovDelay[x][y] != 0)      // wait some time before next frame
9519   {
9520     MovDelay[x][y]--;
9521
9522     DrawLevelElementAnimation(x, y, Tile[x][y]);
9523
9524     if (MovDelay[x][y] != 0)
9525     {
9526       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9527                                            10 - MovDelay[x][y]);
9528
9529       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9530     }
9531   }
9532 }
9533
9534 static void MauerWaechst(int x, int y)
9535 {
9536   int delay = 6;
9537
9538   if (!MovDelay[x][y])          // next animation frame
9539     MovDelay[x][y] = 3 * delay;
9540
9541   if (MovDelay[x][y])           // wait some time before next frame
9542   {
9543     MovDelay[x][y]--;
9544
9545     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9546     {
9547       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9548       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9549
9550       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9551     }
9552
9553     if (!MovDelay[x][y])
9554     {
9555       if (MovDir[x][y] == MV_LEFT)
9556       {
9557         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9558           TEST_DrawLevelField(x - 1, y);
9559       }
9560       else if (MovDir[x][y] == MV_RIGHT)
9561       {
9562         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9563           TEST_DrawLevelField(x + 1, y);
9564       }
9565       else if (MovDir[x][y] == MV_UP)
9566       {
9567         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9568           TEST_DrawLevelField(x, y - 1);
9569       }
9570       else
9571       {
9572         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9573           TEST_DrawLevelField(x, y + 1);
9574       }
9575
9576       Tile[x][y] = Store[x][y];
9577       Store[x][y] = 0;
9578       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9579       TEST_DrawLevelField(x, y);
9580     }
9581   }
9582 }
9583
9584 static void MauerAbleger(int ax, int ay)
9585 {
9586   int element = Tile[ax][ay];
9587   int graphic = el2img(element);
9588   boolean oben_frei = FALSE, unten_frei = FALSE;
9589   boolean links_frei = FALSE, rechts_frei = FALSE;
9590   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9591   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9592   boolean new_wall = FALSE;
9593
9594   if (IS_ANIMATED(graphic))
9595     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9596
9597   if (!MovDelay[ax][ay])        // start building new wall
9598     MovDelay[ax][ay] = 6;
9599
9600   if (MovDelay[ax][ay])         // wait some time before building new wall
9601   {
9602     MovDelay[ax][ay]--;
9603     if (MovDelay[ax][ay])
9604       return;
9605   }
9606
9607   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9608     oben_frei = TRUE;
9609   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9610     unten_frei = TRUE;
9611   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9612     links_frei = TRUE;
9613   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9614     rechts_frei = TRUE;
9615
9616   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9617       element == EL_EXPANDABLE_WALL_ANY)
9618   {
9619     if (oben_frei)
9620     {
9621       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9622       Store[ax][ay-1] = element;
9623       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9624       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9625         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9626                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9627       new_wall = TRUE;
9628     }
9629     if (unten_frei)
9630     {
9631       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9632       Store[ax][ay+1] = element;
9633       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9634       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9635         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9636                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9637       new_wall = TRUE;
9638     }
9639   }
9640
9641   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9642       element == EL_EXPANDABLE_WALL_ANY ||
9643       element == EL_EXPANDABLE_WALL ||
9644       element == EL_BD_EXPANDABLE_WALL)
9645   {
9646     if (links_frei)
9647     {
9648       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9649       Store[ax-1][ay] = element;
9650       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9651       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9652         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9653                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9654       new_wall = TRUE;
9655     }
9656
9657     if (rechts_frei)
9658     {
9659       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9660       Store[ax+1][ay] = element;
9661       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9662       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9663         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9664                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9665       new_wall = TRUE;
9666     }
9667   }
9668
9669   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9670     TEST_DrawLevelField(ax, ay);
9671
9672   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9673     oben_massiv = TRUE;
9674   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9675     unten_massiv = TRUE;
9676   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9677     links_massiv = TRUE;
9678   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9679     rechts_massiv = TRUE;
9680
9681   if (((oben_massiv && unten_massiv) ||
9682        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9683        element == EL_EXPANDABLE_WALL) &&
9684       ((links_massiv && rechts_massiv) ||
9685        element == EL_EXPANDABLE_WALL_VERTICAL))
9686     Tile[ax][ay] = EL_WALL;
9687
9688   if (new_wall)
9689     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9690 }
9691
9692 static void MauerAblegerStahl(int ax, int ay)
9693 {
9694   int element = Tile[ax][ay];
9695   int graphic = el2img(element);
9696   boolean oben_frei = FALSE, unten_frei = FALSE;
9697   boolean links_frei = FALSE, rechts_frei = FALSE;
9698   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9699   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9700   boolean new_wall = FALSE;
9701
9702   if (IS_ANIMATED(graphic))
9703     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9704
9705   if (!MovDelay[ax][ay])        // start building new wall
9706     MovDelay[ax][ay] = 6;
9707
9708   if (MovDelay[ax][ay])         // wait some time before building new wall
9709   {
9710     MovDelay[ax][ay]--;
9711     if (MovDelay[ax][ay])
9712       return;
9713   }
9714
9715   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9716     oben_frei = TRUE;
9717   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9718     unten_frei = TRUE;
9719   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9720     links_frei = TRUE;
9721   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9722     rechts_frei = TRUE;
9723
9724   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9725       element == EL_EXPANDABLE_STEELWALL_ANY)
9726   {
9727     if (oben_frei)
9728     {
9729       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9730       Store[ax][ay-1] = element;
9731       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9732       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9733         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9734                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9735       new_wall = TRUE;
9736     }
9737     if (unten_frei)
9738     {
9739       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9740       Store[ax][ay+1] = element;
9741       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9742       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9743         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9744                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9745       new_wall = TRUE;
9746     }
9747   }
9748
9749   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9750       element == EL_EXPANDABLE_STEELWALL_ANY)
9751   {
9752     if (links_frei)
9753     {
9754       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9755       Store[ax-1][ay] = element;
9756       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9757       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9758         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9759                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9760       new_wall = TRUE;
9761     }
9762
9763     if (rechts_frei)
9764     {
9765       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9766       Store[ax+1][ay] = element;
9767       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9768       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9769         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9770                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9771       new_wall = TRUE;
9772     }
9773   }
9774
9775   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9776     oben_massiv = TRUE;
9777   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9778     unten_massiv = TRUE;
9779   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9780     links_massiv = TRUE;
9781   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9782     rechts_massiv = TRUE;
9783
9784   if (((oben_massiv && unten_massiv) ||
9785        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9786       ((links_massiv && rechts_massiv) ||
9787        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9788     Tile[ax][ay] = EL_STEELWALL;
9789
9790   if (new_wall)
9791     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9792 }
9793
9794 static void CheckForDragon(int x, int y)
9795 {
9796   int i, j;
9797   boolean dragon_found = FALSE;
9798   static int xy[4][2] =
9799   {
9800     { 0, -1 },
9801     { -1, 0 },
9802     { +1, 0 },
9803     { 0, +1 }
9804   };
9805
9806   for (i = 0; i < NUM_DIRECTIONS; i++)
9807   {
9808     for (j = 0; j < 4; j++)
9809     {
9810       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9811
9812       if (IN_LEV_FIELD(xx, yy) &&
9813           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9814       {
9815         if (Tile[xx][yy] == EL_DRAGON)
9816           dragon_found = TRUE;
9817       }
9818       else
9819         break;
9820     }
9821   }
9822
9823   if (!dragon_found)
9824   {
9825     for (i = 0; i < NUM_DIRECTIONS; i++)
9826     {
9827       for (j = 0; j < 3; j++)
9828       {
9829         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9830   
9831         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9832         {
9833           Tile[xx][yy] = EL_EMPTY;
9834           TEST_DrawLevelField(xx, yy);
9835         }
9836         else
9837           break;
9838       }
9839     }
9840   }
9841 }
9842
9843 static void InitBuggyBase(int x, int y)
9844 {
9845   int element = Tile[x][y];
9846   int activating_delay = FRAMES_PER_SECOND / 4;
9847
9848   ChangeDelay[x][y] =
9849     (element == EL_SP_BUGGY_BASE ?
9850      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9851      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9852      activating_delay :
9853      element == EL_SP_BUGGY_BASE_ACTIVE ?
9854      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9855 }
9856
9857 static void WarnBuggyBase(int x, int y)
9858 {
9859   int i;
9860   static int xy[4][2] =
9861   {
9862     { 0, -1 },
9863     { -1, 0 },
9864     { +1, 0 },
9865     { 0, +1 }
9866   };
9867
9868   for (i = 0; i < NUM_DIRECTIONS; i++)
9869   {
9870     int xx = x + xy[i][0];
9871     int yy = y + xy[i][1];
9872
9873     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9874     {
9875       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9876
9877       break;
9878     }
9879   }
9880 }
9881
9882 static void InitTrap(int x, int y)
9883 {
9884   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9885 }
9886
9887 static void ActivateTrap(int x, int y)
9888 {
9889   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9890 }
9891
9892 static void ChangeActiveTrap(int x, int y)
9893 {
9894   int graphic = IMG_TRAP_ACTIVE;
9895
9896   // if new animation frame was drawn, correct crumbled sand border
9897   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9898     TEST_DrawLevelFieldCrumbled(x, y);
9899 }
9900
9901 static int getSpecialActionElement(int element, int number, int base_element)
9902 {
9903   return (element != EL_EMPTY ? element :
9904           number != -1 ? base_element + number - 1 :
9905           EL_EMPTY);
9906 }
9907
9908 static int getModifiedActionNumber(int value_old, int operator, int operand,
9909                                    int value_min, int value_max)
9910 {
9911   int value_new = (operator == CA_MODE_SET      ? operand :
9912                    operator == CA_MODE_ADD      ? value_old + operand :
9913                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9914                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9915                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9916                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9917                    value_old);
9918
9919   return (value_new < value_min ? value_min :
9920           value_new > value_max ? value_max :
9921           value_new);
9922 }
9923
9924 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9925 {
9926   struct ElementInfo *ei = &element_info[element];
9927   struct ElementChangeInfo *change = &ei->change_page[page];
9928   int target_element = change->target_element;
9929   int action_type = change->action_type;
9930   int action_mode = change->action_mode;
9931   int action_arg = change->action_arg;
9932   int action_element = change->action_element;
9933   int i;
9934
9935   if (!change->has_action)
9936     return;
9937
9938   // ---------- determine action paramater values -----------------------------
9939
9940   int level_time_value =
9941     (level.time > 0 ? TimeLeft :
9942      TimePlayed);
9943
9944   int action_arg_element_raw =
9945     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9946      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9947      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9948      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9949      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9950      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9951      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9952      EL_EMPTY);
9953   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9954
9955   int action_arg_direction =
9956     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9957      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9958      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9959      change->actual_trigger_side :
9960      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9961      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9962      MV_NONE);
9963
9964   int action_arg_number_min =
9965     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9966      CA_ARG_MIN);
9967
9968   int action_arg_number_max =
9969     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9970      action_type == CA_SET_LEVEL_GEMS ? 999 :
9971      action_type == CA_SET_LEVEL_TIME ? 9999 :
9972      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9973      action_type == CA_SET_CE_VALUE ? 9999 :
9974      action_type == CA_SET_CE_SCORE ? 9999 :
9975      CA_ARG_MAX);
9976
9977   int action_arg_number_reset =
9978     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9979      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9980      action_type == CA_SET_LEVEL_TIME ? level.time :
9981      action_type == CA_SET_LEVEL_SCORE ? 0 :
9982      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9983      action_type == CA_SET_CE_SCORE ? 0 :
9984      0);
9985
9986   int action_arg_number =
9987     (action_arg <= CA_ARG_MAX ? action_arg :
9988      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9989      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9990      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9991      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9992      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9993      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9994      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9995      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9996      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9997      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9998      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9999      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10000      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10001      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10002      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10003      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10004      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10005      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10006      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10007      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10008      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10009      -1);
10010
10011   int action_arg_number_old =
10012     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10013      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10014      action_type == CA_SET_LEVEL_SCORE ? game.score :
10015      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10016      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10017      0);
10018
10019   int action_arg_number_new =
10020     getModifiedActionNumber(action_arg_number_old,
10021                             action_mode, action_arg_number,
10022                             action_arg_number_min, action_arg_number_max);
10023
10024   int trigger_player_bits =
10025     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10026      change->actual_trigger_player_bits : change->trigger_player);
10027
10028   int action_arg_player_bits =
10029     (action_arg >= CA_ARG_PLAYER_1 &&
10030      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10031      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10032      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10033      PLAYER_BITS_ANY);
10034
10035   // ---------- execute action  -----------------------------------------------
10036
10037   switch (action_type)
10038   {
10039     case CA_NO_ACTION:
10040     {
10041       return;
10042     }
10043
10044     // ---------- level actions  ----------------------------------------------
10045
10046     case CA_RESTART_LEVEL:
10047     {
10048       game.restart_level = TRUE;
10049
10050       break;
10051     }
10052
10053     case CA_SHOW_ENVELOPE:
10054     {
10055       int element = getSpecialActionElement(action_arg_element,
10056                                             action_arg_number, EL_ENVELOPE_1);
10057
10058       if (IS_ENVELOPE(element))
10059         local_player->show_envelope = element;
10060
10061       break;
10062     }
10063
10064     case CA_SET_LEVEL_TIME:
10065     {
10066       if (level.time > 0)       // only modify limited time value
10067       {
10068         TimeLeft = action_arg_number_new;
10069
10070         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10071
10072         DisplayGameControlValues();
10073
10074         if (!TimeLeft && setup.time_limit)
10075           for (i = 0; i < MAX_PLAYERS; i++)
10076             KillPlayer(&stored_player[i]);
10077       }
10078
10079       break;
10080     }
10081
10082     case CA_SET_LEVEL_SCORE:
10083     {
10084       game.score = action_arg_number_new;
10085
10086       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10087
10088       DisplayGameControlValues();
10089
10090       break;
10091     }
10092
10093     case CA_SET_LEVEL_GEMS:
10094     {
10095       game.gems_still_needed = action_arg_number_new;
10096
10097       game.snapshot.collected_item = TRUE;
10098
10099       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10100
10101       DisplayGameControlValues();
10102
10103       break;
10104     }
10105
10106     case CA_SET_LEVEL_WIND:
10107     {
10108       game.wind_direction = action_arg_direction;
10109
10110       break;
10111     }
10112
10113     case CA_SET_LEVEL_RANDOM_SEED:
10114     {
10115       // ensure that setting a new random seed while playing is predictable
10116       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10117
10118       break;
10119     }
10120
10121     // ---------- player actions  ---------------------------------------------
10122
10123     case CA_MOVE_PLAYER:
10124     case CA_MOVE_PLAYER_NEW:
10125     {
10126       // automatically move to the next field in specified direction
10127       for (i = 0; i < MAX_PLAYERS; i++)
10128         if (trigger_player_bits & (1 << i))
10129           if (action_type == CA_MOVE_PLAYER ||
10130               stored_player[i].MovPos == 0)
10131             stored_player[i].programmed_action = action_arg_direction;
10132
10133       break;
10134     }
10135
10136     case CA_EXIT_PLAYER:
10137     {
10138       for (i = 0; i < MAX_PLAYERS; i++)
10139         if (action_arg_player_bits & (1 << i))
10140           ExitPlayer(&stored_player[i]);
10141
10142       if (game.players_still_needed == 0)
10143         LevelSolved();
10144
10145       break;
10146     }
10147
10148     case CA_KILL_PLAYER:
10149     {
10150       for (i = 0; i < MAX_PLAYERS; i++)
10151         if (action_arg_player_bits & (1 << i))
10152           KillPlayer(&stored_player[i]);
10153
10154       break;
10155     }
10156
10157     case CA_SET_PLAYER_KEYS:
10158     {
10159       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10160       int element = getSpecialActionElement(action_arg_element,
10161                                             action_arg_number, EL_KEY_1);
10162
10163       if (IS_KEY(element))
10164       {
10165         for (i = 0; i < MAX_PLAYERS; i++)
10166         {
10167           if (trigger_player_bits & (1 << i))
10168           {
10169             stored_player[i].key[KEY_NR(element)] = key_state;
10170
10171             DrawGameDoorValues();
10172           }
10173         }
10174       }
10175
10176       break;
10177     }
10178
10179     case CA_SET_PLAYER_SPEED:
10180     {
10181       for (i = 0; i < MAX_PLAYERS; i++)
10182       {
10183         if (trigger_player_bits & (1 << i))
10184         {
10185           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10186
10187           if (action_arg == CA_ARG_SPEED_FASTER &&
10188               stored_player[i].cannot_move)
10189           {
10190             action_arg_number = STEPSIZE_VERY_SLOW;
10191           }
10192           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10193                    action_arg == CA_ARG_SPEED_FASTER)
10194           {
10195             action_arg_number = 2;
10196             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10197                            CA_MODE_MULTIPLY);
10198           }
10199           else if (action_arg == CA_ARG_NUMBER_RESET)
10200           {
10201             action_arg_number = level.initial_player_stepsize[i];
10202           }
10203
10204           move_stepsize =
10205             getModifiedActionNumber(move_stepsize,
10206                                     action_mode,
10207                                     action_arg_number,
10208                                     action_arg_number_min,
10209                                     action_arg_number_max);
10210
10211           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10212         }
10213       }
10214
10215       break;
10216     }
10217
10218     case CA_SET_PLAYER_SHIELD:
10219     {
10220       for (i = 0; i < MAX_PLAYERS; i++)
10221       {
10222         if (trigger_player_bits & (1 << i))
10223         {
10224           if (action_arg == CA_ARG_SHIELD_OFF)
10225           {
10226             stored_player[i].shield_normal_time_left = 0;
10227             stored_player[i].shield_deadly_time_left = 0;
10228           }
10229           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10230           {
10231             stored_player[i].shield_normal_time_left = 999999;
10232           }
10233           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10234           {
10235             stored_player[i].shield_normal_time_left = 999999;
10236             stored_player[i].shield_deadly_time_left = 999999;
10237           }
10238         }
10239       }
10240
10241       break;
10242     }
10243
10244     case CA_SET_PLAYER_GRAVITY:
10245     {
10246       for (i = 0; i < MAX_PLAYERS; i++)
10247       {
10248         if (trigger_player_bits & (1 << i))
10249         {
10250           stored_player[i].gravity =
10251             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10252              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10253              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10254              stored_player[i].gravity);
10255         }
10256       }
10257
10258       break;
10259     }
10260
10261     case CA_SET_PLAYER_ARTWORK:
10262     {
10263       for (i = 0; i < MAX_PLAYERS; i++)
10264       {
10265         if (trigger_player_bits & (1 << i))
10266         {
10267           int artwork_element = action_arg_element;
10268
10269           if (action_arg == CA_ARG_ELEMENT_RESET)
10270             artwork_element =
10271               (level.use_artwork_element[i] ? level.artwork_element[i] :
10272                stored_player[i].element_nr);
10273
10274           if (stored_player[i].artwork_element != artwork_element)
10275             stored_player[i].Frame = 0;
10276
10277           stored_player[i].artwork_element = artwork_element;
10278
10279           SetPlayerWaiting(&stored_player[i], FALSE);
10280
10281           // set number of special actions for bored and sleeping animation
10282           stored_player[i].num_special_action_bored =
10283             get_num_special_action(artwork_element,
10284                                    ACTION_BORING_1, ACTION_BORING_LAST);
10285           stored_player[i].num_special_action_sleeping =
10286             get_num_special_action(artwork_element,
10287                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10288         }
10289       }
10290
10291       break;
10292     }
10293
10294     case CA_SET_PLAYER_INVENTORY:
10295     {
10296       for (i = 0; i < MAX_PLAYERS; i++)
10297       {
10298         struct PlayerInfo *player = &stored_player[i];
10299         int j, k;
10300
10301         if (trigger_player_bits & (1 << i))
10302         {
10303           int inventory_element = action_arg_element;
10304
10305           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10306               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10307               action_arg == CA_ARG_ELEMENT_ACTION)
10308           {
10309             int element = inventory_element;
10310             int collect_count = element_info[element].collect_count_initial;
10311
10312             if (!IS_CUSTOM_ELEMENT(element))
10313               collect_count = 1;
10314
10315             if (collect_count == 0)
10316               player->inventory_infinite_element = element;
10317             else
10318               for (k = 0; k < collect_count; k++)
10319                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10320                   player->inventory_element[player->inventory_size++] =
10321                     element;
10322           }
10323           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10324                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10325                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10326           {
10327             if (player->inventory_infinite_element != EL_UNDEFINED &&
10328                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10329                                      action_arg_element_raw))
10330               player->inventory_infinite_element = EL_UNDEFINED;
10331
10332             for (k = 0, j = 0; j < player->inventory_size; j++)
10333             {
10334               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10335                                         action_arg_element_raw))
10336                 player->inventory_element[k++] = player->inventory_element[j];
10337             }
10338
10339             player->inventory_size = k;
10340           }
10341           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10342           {
10343             if (player->inventory_size > 0)
10344             {
10345               for (j = 0; j < player->inventory_size - 1; j++)
10346                 player->inventory_element[j] = player->inventory_element[j + 1];
10347
10348               player->inventory_size--;
10349             }
10350           }
10351           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10352           {
10353             if (player->inventory_size > 0)
10354               player->inventory_size--;
10355           }
10356           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10357           {
10358             player->inventory_infinite_element = EL_UNDEFINED;
10359             player->inventory_size = 0;
10360           }
10361           else if (action_arg == CA_ARG_INVENTORY_RESET)
10362           {
10363             player->inventory_infinite_element = EL_UNDEFINED;
10364             player->inventory_size = 0;
10365
10366             if (level.use_initial_inventory[i])
10367             {
10368               for (j = 0; j < level.initial_inventory_size[i]; j++)
10369               {
10370                 int element = level.initial_inventory_content[i][j];
10371                 int collect_count = element_info[element].collect_count_initial;
10372
10373                 if (!IS_CUSTOM_ELEMENT(element))
10374                   collect_count = 1;
10375
10376                 if (collect_count == 0)
10377                   player->inventory_infinite_element = element;
10378                 else
10379                   for (k = 0; k < collect_count; k++)
10380                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10381                       player->inventory_element[player->inventory_size++] =
10382                         element;
10383               }
10384             }
10385           }
10386         }
10387       }
10388
10389       break;
10390     }
10391
10392     // ---------- CE actions  -------------------------------------------------
10393
10394     case CA_SET_CE_VALUE:
10395     {
10396       int last_ce_value = CustomValue[x][y];
10397
10398       CustomValue[x][y] = action_arg_number_new;
10399
10400       if (CustomValue[x][y] != last_ce_value)
10401       {
10402         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10403         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10404
10405         if (CustomValue[x][y] == 0)
10406         {
10407           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10408           ChangeCount[x][y] = 0;        // allow at least one more change
10409
10410           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10411           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10412         }
10413       }
10414
10415       break;
10416     }
10417
10418     case CA_SET_CE_SCORE:
10419     {
10420       int last_ce_score = ei->collect_score;
10421
10422       ei->collect_score = action_arg_number_new;
10423
10424       if (ei->collect_score != last_ce_score)
10425       {
10426         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10427         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10428
10429         if (ei->collect_score == 0)
10430         {
10431           int xx, yy;
10432
10433           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10434           ChangeCount[x][y] = 0;        // allow at least one more change
10435
10436           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10437           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10438
10439           /*
10440             This is a very special case that seems to be a mixture between
10441             CheckElementChange() and CheckTriggeredElementChange(): while
10442             the first one only affects single elements that are triggered
10443             directly, the second one affects multiple elements in the playfield
10444             that are triggered indirectly by another element. This is a third
10445             case: Changing the CE score always affects multiple identical CEs,
10446             so every affected CE must be checked, not only the single CE for
10447             which the CE score was changed in the first place (as every instance
10448             of that CE shares the same CE score, and therefore also can change)!
10449           */
10450           SCAN_PLAYFIELD(xx, yy)
10451           {
10452             if (Tile[xx][yy] == element)
10453               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10454                                  CE_SCORE_GETS_ZERO);
10455           }
10456         }
10457       }
10458
10459       break;
10460     }
10461
10462     case CA_SET_CE_ARTWORK:
10463     {
10464       int artwork_element = action_arg_element;
10465       boolean reset_frame = FALSE;
10466       int xx, yy;
10467
10468       if (action_arg == CA_ARG_ELEMENT_RESET)
10469         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10470                            element);
10471
10472       if (ei->gfx_element != artwork_element)
10473         reset_frame = TRUE;
10474
10475       ei->gfx_element = artwork_element;
10476
10477       SCAN_PLAYFIELD(xx, yy)
10478       {
10479         if (Tile[xx][yy] == element)
10480         {
10481           if (reset_frame)
10482           {
10483             ResetGfxAnimation(xx, yy);
10484             ResetRandomAnimationValue(xx, yy);
10485           }
10486
10487           TEST_DrawLevelField(xx, yy);
10488         }
10489       }
10490
10491       break;
10492     }
10493
10494     // ---------- engine actions  ---------------------------------------------
10495
10496     case CA_SET_ENGINE_SCAN_MODE:
10497     {
10498       InitPlayfieldScanMode(action_arg);
10499
10500       break;
10501     }
10502
10503     default:
10504       break;
10505   }
10506 }
10507
10508 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10509 {
10510   int old_element = Tile[x][y];
10511   int new_element = GetElementFromGroupElement(element);
10512   int previous_move_direction = MovDir[x][y];
10513   int last_ce_value = CustomValue[x][y];
10514   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10515   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10516   boolean add_player_onto_element = (new_element_is_player &&
10517                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10518                                      IS_WALKABLE(old_element));
10519
10520   if (!add_player_onto_element)
10521   {
10522     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10523       RemoveMovingField(x, y);
10524     else
10525       RemoveField(x, y);
10526
10527     Tile[x][y] = new_element;
10528
10529     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10530       MovDir[x][y] = previous_move_direction;
10531
10532     if (element_info[new_element].use_last_ce_value)
10533       CustomValue[x][y] = last_ce_value;
10534
10535     InitField_WithBug1(x, y, FALSE);
10536
10537     new_element = Tile[x][y];   // element may have changed
10538
10539     ResetGfxAnimation(x, y);
10540     ResetRandomAnimationValue(x, y);
10541
10542     TEST_DrawLevelField(x, y);
10543
10544     if (GFX_CRUMBLED(new_element))
10545       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10546   }
10547
10548   // check if element under the player changes from accessible to unaccessible
10549   // (needed for special case of dropping element which then changes)
10550   // (must be checked after creating new element for walkable group elements)
10551   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10552       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10553   {
10554     Bang(x, y);
10555
10556     return;
10557   }
10558
10559   // "ChangeCount" not set yet to allow "entered by player" change one time
10560   if (new_element_is_player)
10561     RelocatePlayer(x, y, new_element);
10562
10563   if (is_change)
10564     ChangeCount[x][y]++;        // count number of changes in the same frame
10565
10566   TestIfBadThingTouchesPlayer(x, y);
10567   TestIfPlayerTouchesCustomElement(x, y);
10568   TestIfElementTouchesCustomElement(x, y);
10569 }
10570
10571 static void CreateField(int x, int y, int element)
10572 {
10573   CreateFieldExt(x, y, element, FALSE);
10574 }
10575
10576 static void CreateElementFromChange(int x, int y, int element)
10577 {
10578   element = GET_VALID_RUNTIME_ELEMENT(element);
10579
10580   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10581   {
10582     int old_element = Tile[x][y];
10583
10584     // prevent changed element from moving in same engine frame
10585     // unless both old and new element can either fall or move
10586     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10587         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10588       Stop[x][y] = TRUE;
10589   }
10590
10591   CreateFieldExt(x, y, element, TRUE);
10592 }
10593
10594 static boolean ChangeElement(int x, int y, int element, int page)
10595 {
10596   struct ElementInfo *ei = &element_info[element];
10597   struct ElementChangeInfo *change = &ei->change_page[page];
10598   int ce_value = CustomValue[x][y];
10599   int ce_score = ei->collect_score;
10600   int target_element;
10601   int old_element = Tile[x][y];
10602
10603   // always use default change event to prevent running into a loop
10604   if (ChangeEvent[x][y] == -1)
10605     ChangeEvent[x][y] = CE_DELAY;
10606
10607   if (ChangeEvent[x][y] == CE_DELAY)
10608   {
10609     // reset actual trigger element, trigger player and action element
10610     change->actual_trigger_element = EL_EMPTY;
10611     change->actual_trigger_player = EL_EMPTY;
10612     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10613     change->actual_trigger_side = CH_SIDE_NONE;
10614     change->actual_trigger_ce_value = 0;
10615     change->actual_trigger_ce_score = 0;
10616   }
10617
10618   // do not change elements more than a specified maximum number of changes
10619   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10620     return FALSE;
10621
10622   ChangeCount[x][y]++;          // count number of changes in the same frame
10623
10624   if (change->explode)
10625   {
10626     Bang(x, y);
10627
10628     return TRUE;
10629   }
10630
10631   if (change->use_target_content)
10632   {
10633     boolean complete_replace = TRUE;
10634     boolean can_replace[3][3];
10635     int xx, yy;
10636
10637     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10638     {
10639       boolean is_empty;
10640       boolean is_walkable;
10641       boolean is_diggable;
10642       boolean is_collectible;
10643       boolean is_removable;
10644       boolean is_destructible;
10645       int ex = x + xx - 1;
10646       int ey = y + yy - 1;
10647       int content_element = change->target_content.e[xx][yy];
10648       int e;
10649
10650       can_replace[xx][yy] = TRUE;
10651
10652       if (ex == x && ey == y)   // do not check changing element itself
10653         continue;
10654
10655       if (content_element == EL_EMPTY_SPACE)
10656       {
10657         can_replace[xx][yy] = FALSE;    // do not replace border with space
10658
10659         continue;
10660       }
10661
10662       if (!IN_LEV_FIELD(ex, ey))
10663       {
10664         can_replace[xx][yy] = FALSE;
10665         complete_replace = FALSE;
10666
10667         continue;
10668       }
10669
10670       e = Tile[ex][ey];
10671
10672       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10673         e = MovingOrBlocked2Element(ex, ey);
10674
10675       is_empty = (IS_FREE(ex, ey) ||
10676                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10677
10678       is_walkable     = (is_empty || IS_WALKABLE(e));
10679       is_diggable     = (is_empty || IS_DIGGABLE(e));
10680       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10681       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10682       is_removable    = (is_diggable || is_collectible);
10683
10684       can_replace[xx][yy] =
10685         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10686           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10687           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10688           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10689           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10690           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10691          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10692
10693       if (!can_replace[xx][yy])
10694         complete_replace = FALSE;
10695     }
10696
10697     if (!change->only_if_complete || complete_replace)
10698     {
10699       boolean something_has_changed = FALSE;
10700
10701       if (change->only_if_complete && change->use_random_replace &&
10702           RND(100) < change->random_percentage)
10703         return FALSE;
10704
10705       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10706       {
10707         int ex = x + xx - 1;
10708         int ey = y + yy - 1;
10709         int content_element;
10710
10711         if (can_replace[xx][yy] && (!change->use_random_replace ||
10712                                     RND(100) < change->random_percentage))
10713         {
10714           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10715             RemoveMovingField(ex, ey);
10716
10717           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10718
10719           content_element = change->target_content.e[xx][yy];
10720           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10721                                               ce_value, ce_score);
10722
10723           CreateElementFromChange(ex, ey, target_element);
10724
10725           something_has_changed = TRUE;
10726
10727           // for symmetry reasons, freeze newly created border elements
10728           if (ex != x || ey != y)
10729             Stop[ex][ey] = TRUE;        // no more moving in this frame
10730         }
10731       }
10732
10733       if (something_has_changed)
10734       {
10735         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10736         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10737       }
10738     }
10739   }
10740   else
10741   {
10742     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10743                                         ce_value, ce_score);
10744
10745     if (element == EL_DIAGONAL_GROWING ||
10746         element == EL_DIAGONAL_SHRINKING)
10747     {
10748       target_element = Store[x][y];
10749
10750       Store[x][y] = EL_EMPTY;
10751     }
10752
10753     CreateElementFromChange(x, y, target_element);
10754
10755     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10756     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10757   }
10758
10759   // this uses direct change before indirect change
10760   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10761
10762   return TRUE;
10763 }
10764
10765 static void HandleElementChange(int x, int y, int page)
10766 {
10767   int element = MovingOrBlocked2Element(x, y);
10768   struct ElementInfo *ei = &element_info[element];
10769   struct ElementChangeInfo *change = &ei->change_page[page];
10770   boolean handle_action_before_change = FALSE;
10771
10772 #ifdef DEBUG
10773   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10774       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10775   {
10776     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10777           x, y, element, element_info[element].token_name);
10778     Debug("game:playing:HandleElementChange", "This should never happen!");
10779   }
10780 #endif
10781
10782   // this can happen with classic bombs on walkable, changing elements
10783   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10784   {
10785     return;
10786   }
10787
10788   if (ChangeDelay[x][y] == 0)           // initialize element change
10789   {
10790     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10791
10792     if (change->can_change)
10793     {
10794       // !!! not clear why graphic animation should be reset at all here !!!
10795       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10796       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10797
10798       /*
10799         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10800
10801         When using an animation frame delay of 1 (this only happens with
10802         "sp_zonk.moving.left/right" in the classic graphics), the default
10803         (non-moving) animation shows wrong animation frames (while the
10804         moving animation, like "sp_zonk.moving.left/right", is correct,
10805         so this graphical bug never shows up with the classic graphics).
10806         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10807         be drawn instead of the correct frames 0,1,2,3. This is caused by
10808         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10809         an element change: First when the change delay ("ChangeDelay[][]")
10810         counter has reached zero after decrementing, then a second time in
10811         the next frame (after "GfxFrame[][]" was already incremented) when
10812         "ChangeDelay[][]" is reset to the initial delay value again.
10813
10814         This causes frame 0 to be drawn twice, while the last frame won't
10815         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10816
10817         As some animations may already be cleverly designed around this bug
10818         (at least the "Snake Bite" snake tail animation does this), it cannot
10819         simply be fixed here without breaking such existing animations.
10820         Unfortunately, it cannot easily be detected if a graphics set was
10821         designed "before" or "after" the bug was fixed. As a workaround,
10822         a new graphics set option "game.graphics_engine_version" was added
10823         to be able to specify the game's major release version for which the
10824         graphics set was designed, which can then be used to decide if the
10825         bugfix should be used (version 4 and above) or not (version 3 or
10826         below, or if no version was specified at all, as with old sets).
10827
10828         (The wrong/fixed animation frames can be tested with the test level set
10829         "test_gfxframe" and level "000", which contains a specially prepared
10830         custom element at level position (x/y) == (11/9) which uses the zonk
10831         animation mentioned above. Using "game.graphics_engine_version: 4"
10832         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10833         This can also be seen from the debug output for this test element.)
10834       */
10835
10836       // when a custom element is about to change (for example by change delay),
10837       // do not reset graphic animation when the custom element is moving
10838       if (game.graphics_engine_version < 4 &&
10839           !IS_MOVING(x, y))
10840       {
10841         ResetGfxAnimation(x, y);
10842         ResetRandomAnimationValue(x, y);
10843       }
10844
10845       if (change->pre_change_function)
10846         change->pre_change_function(x, y);
10847     }
10848   }
10849
10850   ChangeDelay[x][y]--;
10851
10852   if (ChangeDelay[x][y] != 0)           // continue element change
10853   {
10854     if (change->can_change)
10855     {
10856       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10857
10858       if (IS_ANIMATED(graphic))
10859         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10860
10861       if (change->change_function)
10862         change->change_function(x, y);
10863     }
10864   }
10865   else                                  // finish element change
10866   {
10867     if (ChangePage[x][y] != -1)         // remember page from delayed change
10868     {
10869       page = ChangePage[x][y];
10870       ChangePage[x][y] = -1;
10871
10872       change = &ei->change_page[page];
10873     }
10874
10875     if (IS_MOVING(x, y))                // never change a running system ;-)
10876     {
10877       ChangeDelay[x][y] = 1;            // try change after next move step
10878       ChangePage[x][y] = page;          // remember page to use for change
10879
10880       return;
10881     }
10882
10883     // special case: set new level random seed before changing element
10884     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10885       handle_action_before_change = TRUE;
10886
10887     if (change->has_action && handle_action_before_change)
10888       ExecuteCustomElementAction(x, y, element, page);
10889
10890     if (change->can_change)
10891     {
10892       if (ChangeElement(x, y, element, page))
10893       {
10894         if (change->post_change_function)
10895           change->post_change_function(x, y);
10896       }
10897     }
10898
10899     if (change->has_action && !handle_action_before_change)
10900       ExecuteCustomElementAction(x, y, element, page);
10901   }
10902 }
10903
10904 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10905                                               int trigger_element,
10906                                               int trigger_event,
10907                                               int trigger_player,
10908                                               int trigger_side,
10909                                               int trigger_page)
10910 {
10911   boolean change_done_any = FALSE;
10912   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10913   int i;
10914
10915   if (!(trigger_events[trigger_element][trigger_event]))
10916     return FALSE;
10917
10918   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10919
10920   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10921   {
10922     int element = EL_CUSTOM_START + i;
10923     boolean change_done = FALSE;
10924     int p;
10925
10926     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10927         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10928       continue;
10929
10930     for (p = 0; p < element_info[element].num_change_pages; p++)
10931     {
10932       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10933
10934       if (change->can_change_or_has_action &&
10935           change->has_event[trigger_event] &&
10936           change->trigger_side & trigger_side &&
10937           change->trigger_player & trigger_player &&
10938           change->trigger_page & trigger_page_bits &&
10939           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10940       {
10941         change->actual_trigger_element = trigger_element;
10942         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10943         change->actual_trigger_player_bits = trigger_player;
10944         change->actual_trigger_side = trigger_side;
10945         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10946         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10947
10948         if ((change->can_change && !change_done) || change->has_action)
10949         {
10950           int x, y;
10951
10952           SCAN_PLAYFIELD(x, y)
10953           {
10954             if (Tile[x][y] == element)
10955             {
10956               if (change->can_change && !change_done)
10957               {
10958                 // if element already changed in this frame, not only prevent
10959                 // another element change (checked in ChangeElement()), but
10960                 // also prevent additional element actions for this element
10961
10962                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10963                     !level.use_action_after_change_bug)
10964                   continue;
10965
10966                 ChangeDelay[x][y] = 1;
10967                 ChangeEvent[x][y] = trigger_event;
10968
10969                 HandleElementChange(x, y, p);
10970               }
10971               else if (change->has_action)
10972               {
10973                 // if element already changed in this frame, not only prevent
10974                 // another element change (checked in ChangeElement()), but
10975                 // also prevent additional element actions for this element
10976
10977                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10978                     !level.use_action_after_change_bug)
10979                   continue;
10980
10981                 ExecuteCustomElementAction(x, y, element, p);
10982                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10983               }
10984             }
10985           }
10986
10987           if (change->can_change)
10988           {
10989             change_done = TRUE;
10990             change_done_any = TRUE;
10991           }
10992         }
10993       }
10994     }
10995   }
10996
10997   RECURSION_LOOP_DETECTION_END();
10998
10999   return change_done_any;
11000 }
11001
11002 static boolean CheckElementChangeExt(int x, int y,
11003                                      int element,
11004                                      int trigger_element,
11005                                      int trigger_event,
11006                                      int trigger_player,
11007                                      int trigger_side)
11008 {
11009   boolean change_done = FALSE;
11010   int p;
11011
11012   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11013       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11014     return FALSE;
11015
11016   if (Tile[x][y] == EL_BLOCKED)
11017   {
11018     Blocked2Moving(x, y, &x, &y);
11019     element = Tile[x][y];
11020   }
11021
11022   // check if element has already changed or is about to change after moving
11023   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11024        Tile[x][y] != element) ||
11025
11026       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11027        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11028         ChangePage[x][y] != -1)))
11029     return FALSE;
11030
11031   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11032
11033   for (p = 0; p < element_info[element].num_change_pages; p++)
11034   {
11035     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11036
11037     /* check trigger element for all events where the element that is checked
11038        for changing interacts with a directly adjacent element -- this is
11039        different to element changes that affect other elements to change on the
11040        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11041     boolean check_trigger_element =
11042       (trigger_event == CE_TOUCHING_X ||
11043        trigger_event == CE_HITTING_X ||
11044        trigger_event == CE_HIT_BY_X ||
11045        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11046
11047     if (change->can_change_or_has_action &&
11048         change->has_event[trigger_event] &&
11049         change->trigger_side & trigger_side &&
11050         change->trigger_player & trigger_player &&
11051         (!check_trigger_element ||
11052          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11053     {
11054       change->actual_trigger_element = trigger_element;
11055       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11056       change->actual_trigger_player_bits = trigger_player;
11057       change->actual_trigger_side = trigger_side;
11058       change->actual_trigger_ce_value = CustomValue[x][y];
11059       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11060
11061       // special case: trigger element not at (x,y) position for some events
11062       if (check_trigger_element)
11063       {
11064         static struct
11065         {
11066           int dx, dy;
11067         } move_xy[] =
11068           {
11069             {  0,  0 },
11070             { -1,  0 },
11071             { +1,  0 },
11072             {  0,  0 },
11073             {  0, -1 },
11074             {  0,  0 }, { 0, 0 }, { 0, 0 },
11075             {  0, +1 }
11076           };
11077
11078         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11079         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11080
11081         change->actual_trigger_ce_value = CustomValue[xx][yy];
11082         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11083       }
11084
11085       if (change->can_change && !change_done)
11086       {
11087         ChangeDelay[x][y] = 1;
11088         ChangeEvent[x][y] = trigger_event;
11089
11090         HandleElementChange(x, y, p);
11091
11092         change_done = TRUE;
11093       }
11094       else if (change->has_action)
11095       {
11096         ExecuteCustomElementAction(x, y, element, p);
11097         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11098       }
11099     }
11100   }
11101
11102   RECURSION_LOOP_DETECTION_END();
11103
11104   return change_done;
11105 }
11106
11107 static void PlayPlayerSound(struct PlayerInfo *player)
11108 {
11109   int jx = player->jx, jy = player->jy;
11110   int sound_element = player->artwork_element;
11111   int last_action = player->last_action_waiting;
11112   int action = player->action_waiting;
11113
11114   if (player->is_waiting)
11115   {
11116     if (action != last_action)
11117       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11118     else
11119       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11120   }
11121   else
11122   {
11123     if (action != last_action)
11124       StopSound(element_info[sound_element].sound[last_action]);
11125
11126     if (last_action == ACTION_SLEEPING)
11127       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11128   }
11129 }
11130
11131 static void PlayAllPlayersSound(void)
11132 {
11133   int i;
11134
11135   for (i = 0; i < MAX_PLAYERS; i++)
11136     if (stored_player[i].active)
11137       PlayPlayerSound(&stored_player[i]);
11138 }
11139
11140 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11141 {
11142   boolean last_waiting = player->is_waiting;
11143   int move_dir = player->MovDir;
11144
11145   player->dir_waiting = move_dir;
11146   player->last_action_waiting = player->action_waiting;
11147
11148   if (is_waiting)
11149   {
11150     if (!last_waiting)          // not waiting -> waiting
11151     {
11152       player->is_waiting = TRUE;
11153
11154       player->frame_counter_bored =
11155         FrameCounter +
11156         game.player_boring_delay_fixed +
11157         GetSimpleRandom(game.player_boring_delay_random);
11158       player->frame_counter_sleeping =
11159         FrameCounter +
11160         game.player_sleeping_delay_fixed +
11161         GetSimpleRandom(game.player_sleeping_delay_random);
11162
11163       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11164     }
11165
11166     if (game.player_sleeping_delay_fixed +
11167         game.player_sleeping_delay_random > 0 &&
11168         player->anim_delay_counter == 0 &&
11169         player->post_delay_counter == 0 &&
11170         FrameCounter >= player->frame_counter_sleeping)
11171       player->is_sleeping = TRUE;
11172     else if (game.player_boring_delay_fixed +
11173              game.player_boring_delay_random > 0 &&
11174              FrameCounter >= player->frame_counter_bored)
11175       player->is_bored = TRUE;
11176
11177     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11178                               player->is_bored ? ACTION_BORING :
11179                               ACTION_WAITING);
11180
11181     if (player->is_sleeping && player->use_murphy)
11182     {
11183       // special case for sleeping Murphy when leaning against non-free tile
11184
11185       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11186           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11187            !IS_MOVING(player->jx - 1, player->jy)))
11188         move_dir = MV_LEFT;
11189       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11190                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11191                 !IS_MOVING(player->jx + 1, player->jy)))
11192         move_dir = MV_RIGHT;
11193       else
11194         player->is_sleeping = FALSE;
11195
11196       player->dir_waiting = move_dir;
11197     }
11198
11199     if (player->is_sleeping)
11200     {
11201       if (player->num_special_action_sleeping > 0)
11202       {
11203         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11204         {
11205           int last_special_action = player->special_action_sleeping;
11206           int num_special_action = player->num_special_action_sleeping;
11207           int special_action =
11208             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11209              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11210              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11211              last_special_action + 1 : ACTION_SLEEPING);
11212           int special_graphic =
11213             el_act_dir2img(player->artwork_element, special_action, move_dir);
11214
11215           player->anim_delay_counter =
11216             graphic_info[special_graphic].anim_delay_fixed +
11217             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11218           player->post_delay_counter =
11219             graphic_info[special_graphic].post_delay_fixed +
11220             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11221
11222           player->special_action_sleeping = special_action;
11223         }
11224
11225         if (player->anim_delay_counter > 0)
11226         {
11227           player->action_waiting = player->special_action_sleeping;
11228           player->anim_delay_counter--;
11229         }
11230         else if (player->post_delay_counter > 0)
11231         {
11232           player->post_delay_counter--;
11233         }
11234       }
11235     }
11236     else if (player->is_bored)
11237     {
11238       if (player->num_special_action_bored > 0)
11239       {
11240         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11241         {
11242           int special_action =
11243             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11244           int special_graphic =
11245             el_act_dir2img(player->artwork_element, special_action, move_dir);
11246
11247           player->anim_delay_counter =
11248             graphic_info[special_graphic].anim_delay_fixed +
11249             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11250           player->post_delay_counter =
11251             graphic_info[special_graphic].post_delay_fixed +
11252             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11253
11254           player->special_action_bored = special_action;
11255         }
11256
11257         if (player->anim_delay_counter > 0)
11258         {
11259           player->action_waiting = player->special_action_bored;
11260           player->anim_delay_counter--;
11261         }
11262         else if (player->post_delay_counter > 0)
11263         {
11264           player->post_delay_counter--;
11265         }
11266       }
11267     }
11268   }
11269   else if (last_waiting)        // waiting -> not waiting
11270   {
11271     player->is_waiting = FALSE;
11272     player->is_bored = FALSE;
11273     player->is_sleeping = FALSE;
11274
11275     player->frame_counter_bored = -1;
11276     player->frame_counter_sleeping = -1;
11277
11278     player->anim_delay_counter = 0;
11279     player->post_delay_counter = 0;
11280
11281     player->dir_waiting = player->MovDir;
11282     player->action_waiting = ACTION_DEFAULT;
11283
11284     player->special_action_bored = ACTION_DEFAULT;
11285     player->special_action_sleeping = ACTION_DEFAULT;
11286   }
11287 }
11288
11289 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11290 {
11291   if ((!player->is_moving  && player->was_moving) ||
11292       (player->MovPos == 0 && player->was_moving) ||
11293       (player->is_snapping && !player->was_snapping) ||
11294       (player->is_dropping && !player->was_dropping))
11295   {
11296     if (!CheckSaveEngineSnapshotToList())
11297       return;
11298
11299     player->was_moving = FALSE;
11300     player->was_snapping = TRUE;
11301     player->was_dropping = TRUE;
11302   }
11303   else
11304   {
11305     if (player->is_moving)
11306       player->was_moving = TRUE;
11307
11308     if (!player->is_snapping)
11309       player->was_snapping = FALSE;
11310
11311     if (!player->is_dropping)
11312       player->was_dropping = FALSE;
11313   }
11314
11315   static struct MouseActionInfo mouse_action_last = { 0 };
11316   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11317   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11318
11319   if (new_released)
11320     CheckSaveEngineSnapshotToList();
11321
11322   mouse_action_last = mouse_action;
11323 }
11324
11325 static void CheckSingleStepMode(struct PlayerInfo *player)
11326 {
11327   if (tape.single_step && tape.recording && !tape.pausing)
11328   {
11329     // as it is called "single step mode", just return to pause mode when the
11330     // player stopped moving after one tile (or never starts moving at all)
11331     // (reverse logic needed here in case single step mode used in team mode)
11332     if (player->is_moving ||
11333         player->is_pushing ||
11334         player->is_dropping_pressed ||
11335         player->effective_mouse_action.button)
11336       game.enter_single_step_mode = FALSE;
11337   }
11338
11339   CheckSaveEngineSnapshot(player);
11340 }
11341
11342 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11343 {
11344   int left      = player_action & JOY_LEFT;
11345   int right     = player_action & JOY_RIGHT;
11346   int up        = player_action & JOY_UP;
11347   int down      = player_action & JOY_DOWN;
11348   int button1   = player_action & JOY_BUTTON_1;
11349   int button2   = player_action & JOY_BUTTON_2;
11350   int dx        = (left ? -1 : right ? 1 : 0);
11351   int dy        = (up   ? -1 : down  ? 1 : 0);
11352
11353   if (!player->active || tape.pausing)
11354     return 0;
11355
11356   if (player_action)
11357   {
11358     if (button1)
11359       SnapField(player, dx, dy);
11360     else
11361     {
11362       if (button2)
11363         DropElement(player);
11364
11365       MovePlayer(player, dx, dy);
11366     }
11367
11368     CheckSingleStepMode(player);
11369
11370     SetPlayerWaiting(player, FALSE);
11371
11372     return player_action;
11373   }
11374   else
11375   {
11376     // no actions for this player (no input at player's configured device)
11377
11378     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11379     SnapField(player, 0, 0);
11380     CheckGravityMovementWhenNotMoving(player);
11381
11382     if (player->MovPos == 0)
11383       SetPlayerWaiting(player, TRUE);
11384
11385     if (player->MovPos == 0)    // needed for tape.playing
11386       player->is_moving = FALSE;
11387
11388     player->is_dropping = FALSE;
11389     player->is_dropping_pressed = FALSE;
11390     player->drop_pressed_delay = 0;
11391
11392     CheckSingleStepMode(player);
11393
11394     return 0;
11395   }
11396 }
11397
11398 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11399                                          byte *tape_action)
11400 {
11401   if (!tape.use_mouse_actions)
11402     return;
11403
11404   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11405   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11406   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11407 }
11408
11409 static void SetTapeActionFromMouseAction(byte *tape_action,
11410                                          struct MouseActionInfo *mouse_action)
11411 {
11412   if (!tape.use_mouse_actions)
11413     return;
11414
11415   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11416   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11417   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11418 }
11419
11420 static void CheckLevelSolved(void)
11421 {
11422   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11423   {
11424     if (game_em.level_solved &&
11425         !game_em.game_over)                             // game won
11426     {
11427       LevelSolved();
11428
11429       game_em.game_over = TRUE;
11430
11431       game.all_players_gone = TRUE;
11432     }
11433
11434     if (game_em.game_over)                              // game lost
11435       game.all_players_gone = TRUE;
11436   }
11437   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11438   {
11439     if (game_sp.level_solved &&
11440         !game_sp.game_over)                             // game won
11441     {
11442       LevelSolved();
11443
11444       game_sp.game_over = TRUE;
11445
11446       game.all_players_gone = TRUE;
11447     }
11448
11449     if (game_sp.game_over)                              // game lost
11450       game.all_players_gone = TRUE;
11451   }
11452   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11453   {
11454     if (game_mm.level_solved &&
11455         !game_mm.game_over)                             // game won
11456     {
11457       LevelSolved();
11458
11459       game_mm.game_over = TRUE;
11460
11461       game.all_players_gone = TRUE;
11462     }
11463
11464     if (game_mm.game_over)                              // game lost
11465       game.all_players_gone = TRUE;
11466   }
11467 }
11468
11469 static void CheckLevelTime(void)
11470 {
11471   int i;
11472
11473   if (TimeFrames >= FRAMES_PER_SECOND)
11474   {
11475     TimeFrames = 0;
11476     TapeTime++;
11477
11478     for (i = 0; i < MAX_PLAYERS; i++)
11479     {
11480       struct PlayerInfo *player = &stored_player[i];
11481
11482       if (SHIELD_ON(player))
11483       {
11484         player->shield_normal_time_left--;
11485
11486         if (player->shield_deadly_time_left > 0)
11487           player->shield_deadly_time_left--;
11488       }
11489     }
11490
11491     if (!game.LevelSolved && !level.use_step_counter)
11492     {
11493       TimePlayed++;
11494
11495       if (TimeLeft > 0)
11496       {
11497         TimeLeft--;
11498
11499         if (TimeLeft <= 10 && setup.time_limit)
11500           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11501
11502         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11503            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11504
11505         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11506
11507         if (!TimeLeft && setup.time_limit)
11508         {
11509           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11510             game_em.lev->killed_out_of_time = TRUE;
11511           else
11512             for (i = 0; i < MAX_PLAYERS; i++)
11513               KillPlayer(&stored_player[i]);
11514         }
11515       }
11516       else if (game.no_time_limit && !game.all_players_gone)
11517       {
11518         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11519       }
11520
11521       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11522     }
11523
11524     if (tape.recording || tape.playing)
11525       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11526   }
11527
11528   if (tape.recording || tape.playing)
11529     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11530
11531   UpdateAndDisplayGameControlValues();
11532 }
11533
11534 void AdvanceFrameAndPlayerCounters(int player_nr)
11535 {
11536   int i;
11537
11538   // advance frame counters (global frame counter and time frame counter)
11539   FrameCounter++;
11540   TimeFrames++;
11541
11542   // advance player counters (counters for move delay, move animation etc.)
11543   for (i = 0; i < MAX_PLAYERS; i++)
11544   {
11545     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11546     int move_delay_value = stored_player[i].move_delay_value;
11547     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11548
11549     if (!advance_player_counters)       // not all players may be affected
11550       continue;
11551
11552     if (move_frames == 0)       // less than one move per game frame
11553     {
11554       int stepsize = TILEX / move_delay_value;
11555       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11556       int count = (stored_player[i].is_moving ?
11557                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11558
11559       if (count % delay == 0)
11560         move_frames = 1;
11561     }
11562
11563     stored_player[i].Frame += move_frames;
11564
11565     if (stored_player[i].MovPos != 0)
11566       stored_player[i].StepFrame += move_frames;
11567
11568     if (stored_player[i].move_delay > 0)
11569       stored_player[i].move_delay--;
11570
11571     // due to bugs in previous versions, counter must count up, not down
11572     if (stored_player[i].push_delay != -1)
11573       stored_player[i].push_delay++;
11574
11575     if (stored_player[i].drop_delay > 0)
11576       stored_player[i].drop_delay--;
11577
11578     if (stored_player[i].is_dropping_pressed)
11579       stored_player[i].drop_pressed_delay++;
11580   }
11581 }
11582
11583 void StartGameActions(boolean init_network_game, boolean record_tape,
11584                       int random_seed)
11585 {
11586   unsigned int new_random_seed = InitRND(random_seed);
11587
11588   if (record_tape)
11589     TapeStartRecording(new_random_seed);
11590
11591   if (init_network_game)
11592   {
11593     SendToServer_LevelFile();
11594     SendToServer_StartPlaying();
11595
11596     return;
11597   }
11598
11599   InitGame();
11600 }
11601
11602 static void GameActionsExt(void)
11603 {
11604 #if 0
11605   static unsigned int game_frame_delay = 0;
11606 #endif
11607   unsigned int game_frame_delay_value;
11608   byte *recorded_player_action;
11609   byte summarized_player_action = 0;
11610   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11611   int i;
11612
11613   // detect endless loops, caused by custom element programming
11614   if (recursion_loop_detected && recursion_loop_depth == 0)
11615   {
11616     char *message = getStringCat3("Internal Error! Element ",
11617                                   EL_NAME(recursion_loop_element),
11618                                   " caused endless loop! Quit the game?");
11619
11620     Warn("element '%s' caused endless loop in game engine",
11621          EL_NAME(recursion_loop_element));
11622
11623     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11624
11625     recursion_loop_detected = FALSE;    // if game should be continued
11626
11627     free(message);
11628
11629     return;
11630   }
11631
11632   if (game.restart_level)
11633     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11634
11635   CheckLevelSolved();
11636
11637   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11638     GameWon();
11639
11640   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11641     TapeStop();
11642
11643   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11644     return;
11645
11646   game_frame_delay_value =
11647     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11648
11649   if (tape.playing && tape.warp_forward && !tape.pausing)
11650     game_frame_delay_value = 0;
11651
11652   SetVideoFrameDelay(game_frame_delay_value);
11653
11654   // (de)activate virtual buttons depending on current game status
11655   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11656   {
11657     if (game.all_players_gone)  // if no players there to be controlled anymore
11658       SetOverlayActive(FALSE);
11659     else if (!tape.playing)     // if game continues after tape stopped playing
11660       SetOverlayActive(TRUE);
11661   }
11662
11663 #if 0
11664 #if 0
11665   // ---------- main game synchronization point ----------
11666
11667   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11668
11669   Debug("game:playing:skip", "skip == %d", skip);
11670
11671 #else
11672   // ---------- main game synchronization point ----------
11673
11674   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11675 #endif
11676 #endif
11677
11678   if (network_playing && !network_player_action_received)
11679   {
11680     // try to get network player actions in time
11681
11682     // last chance to get network player actions without main loop delay
11683     HandleNetworking();
11684
11685     // game was quit by network peer
11686     if (game_status != GAME_MODE_PLAYING)
11687       return;
11688
11689     // check if network player actions still missing and game still running
11690     if (!network_player_action_received && !checkGameEnded())
11691       return;           // failed to get network player actions in time
11692
11693     // do not yet reset "network_player_action_received" (for tape.pausing)
11694   }
11695
11696   if (tape.pausing)
11697     return;
11698
11699   // at this point we know that we really continue executing the game
11700
11701   network_player_action_received = FALSE;
11702
11703   // when playing tape, read previously recorded player input from tape data
11704   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11705
11706   local_player->effective_mouse_action = local_player->mouse_action;
11707
11708   if (recorded_player_action != NULL)
11709     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11710                                  recorded_player_action);
11711
11712   // TapePlayAction() may return NULL when toggling to "pause before death"
11713   if (tape.pausing)
11714     return;
11715
11716   if (tape.set_centered_player)
11717   {
11718     game.centered_player_nr_next = tape.centered_player_nr_next;
11719     game.set_centered_player = TRUE;
11720   }
11721
11722   for (i = 0; i < MAX_PLAYERS; i++)
11723   {
11724     summarized_player_action |= stored_player[i].action;
11725
11726     if (!network_playing && (game.team_mode || tape.playing))
11727       stored_player[i].effective_action = stored_player[i].action;
11728   }
11729
11730   if (network_playing && !checkGameEnded())
11731     SendToServer_MovePlayer(summarized_player_action);
11732
11733   // summarize all actions at local players mapped input device position
11734   // (this allows using different input devices in single player mode)
11735   if (!network.enabled && !game.team_mode)
11736     stored_player[map_player_action[local_player->index_nr]].effective_action =
11737       summarized_player_action;
11738
11739   // summarize all actions at centered player in local team mode
11740   if (tape.recording &&
11741       setup.team_mode && !network.enabled &&
11742       setup.input_on_focus &&
11743       game.centered_player_nr != -1)
11744   {
11745     for (i = 0; i < MAX_PLAYERS; i++)
11746       stored_player[map_player_action[i]].effective_action =
11747         (i == game.centered_player_nr ? summarized_player_action : 0);
11748   }
11749
11750   if (recorded_player_action != NULL)
11751     for (i = 0; i < MAX_PLAYERS; i++)
11752       stored_player[i].effective_action = recorded_player_action[i];
11753
11754   for (i = 0; i < MAX_PLAYERS; i++)
11755   {
11756     tape_action[i] = stored_player[i].effective_action;
11757
11758     /* (this may happen in the RND game engine if a player was not present on
11759        the playfield on level start, but appeared later from a custom element */
11760     if (setup.team_mode &&
11761         tape.recording &&
11762         tape_action[i] &&
11763         !tape.player_participates[i])
11764       tape.player_participates[i] = TRUE;
11765   }
11766
11767   SetTapeActionFromMouseAction(tape_action,
11768                                &local_player->effective_mouse_action);
11769
11770   // only record actions from input devices, but not programmed actions
11771   if (tape.recording)
11772     TapeRecordAction(tape_action);
11773
11774   // remember if game was played (especially after tape stopped playing)
11775   if (!tape.playing && summarized_player_action)
11776     game.GamePlayed = TRUE;
11777
11778 #if USE_NEW_PLAYER_ASSIGNMENTS
11779   // !!! also map player actions in single player mode !!!
11780   // if (game.team_mode)
11781   if (1)
11782   {
11783     byte mapped_action[MAX_PLAYERS];
11784
11785 #if DEBUG_PLAYER_ACTIONS
11786     for (i = 0; i < MAX_PLAYERS; i++)
11787       DebugContinued("", "%d, ", stored_player[i].effective_action);
11788 #endif
11789
11790     for (i = 0; i < MAX_PLAYERS; i++)
11791       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11792
11793     for (i = 0; i < MAX_PLAYERS; i++)
11794       stored_player[i].effective_action = mapped_action[i];
11795
11796 #if DEBUG_PLAYER_ACTIONS
11797     DebugContinued("", "=> ");
11798     for (i = 0; i < MAX_PLAYERS; i++)
11799       DebugContinued("", "%d, ", stored_player[i].effective_action);
11800     DebugContinued("game:playing:player", "\n");
11801 #endif
11802   }
11803 #if DEBUG_PLAYER_ACTIONS
11804   else
11805   {
11806     for (i = 0; i < MAX_PLAYERS; i++)
11807       DebugContinued("", "%d, ", stored_player[i].effective_action);
11808     DebugContinued("game:playing:player", "\n");
11809   }
11810 #endif
11811 #endif
11812
11813   for (i = 0; i < MAX_PLAYERS; i++)
11814   {
11815     // allow engine snapshot in case of changed movement attempt
11816     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11817         (stored_player[i].effective_action & KEY_MOTION))
11818       game.snapshot.changed_action = TRUE;
11819
11820     // allow engine snapshot in case of snapping/dropping attempt
11821     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11822         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11823       game.snapshot.changed_action = TRUE;
11824
11825     game.snapshot.last_action[i] = stored_player[i].effective_action;
11826   }
11827
11828   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11829   {
11830     GameActions_EM_Main();
11831   }
11832   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11833   {
11834     GameActions_SP_Main();
11835   }
11836   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11837   {
11838     GameActions_MM_Main();
11839   }
11840   else
11841   {
11842     GameActions_RND_Main();
11843   }
11844
11845   BlitScreenToBitmap(backbuffer);
11846
11847   CheckLevelSolved();
11848   CheckLevelTime();
11849
11850   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11851
11852   if (global.show_frames_per_second)
11853   {
11854     static unsigned int fps_counter = 0;
11855     static int fps_frames = 0;
11856     unsigned int fps_delay_ms = Counter() - fps_counter;
11857
11858     fps_frames++;
11859
11860     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11861     {
11862       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11863
11864       fps_frames = 0;
11865       fps_counter = Counter();
11866
11867       // always draw FPS to screen after FPS value was updated
11868       redraw_mask |= REDRAW_FPS;
11869     }
11870
11871     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11872     if (GetDrawDeactivationMask() == REDRAW_NONE)
11873       redraw_mask |= REDRAW_FPS;
11874   }
11875 }
11876
11877 static void GameActions_CheckSaveEngineSnapshot(void)
11878 {
11879   if (!game.snapshot.save_snapshot)
11880     return;
11881
11882   // clear flag for saving snapshot _before_ saving snapshot
11883   game.snapshot.save_snapshot = FALSE;
11884
11885   SaveEngineSnapshotToList();
11886 }
11887
11888 void GameActions(void)
11889 {
11890   GameActionsExt();
11891
11892   GameActions_CheckSaveEngineSnapshot();
11893 }
11894
11895 void GameActions_EM_Main(void)
11896 {
11897   byte effective_action[MAX_PLAYERS];
11898   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11899   int i;
11900
11901   for (i = 0; i < MAX_PLAYERS; i++)
11902     effective_action[i] = stored_player[i].effective_action;
11903
11904   GameActions_EM(effective_action, warp_mode);
11905 }
11906
11907 void GameActions_SP_Main(void)
11908 {
11909   byte effective_action[MAX_PLAYERS];
11910   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11911   int i;
11912
11913   for (i = 0; i < MAX_PLAYERS; i++)
11914     effective_action[i] = stored_player[i].effective_action;
11915
11916   GameActions_SP(effective_action, warp_mode);
11917
11918   for (i = 0; i < MAX_PLAYERS; i++)
11919   {
11920     if (stored_player[i].force_dropping)
11921       stored_player[i].action |= KEY_BUTTON_DROP;
11922
11923     stored_player[i].force_dropping = FALSE;
11924   }
11925 }
11926
11927 void GameActions_MM_Main(void)
11928 {
11929   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11930
11931   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11932 }
11933
11934 void GameActions_RND_Main(void)
11935 {
11936   GameActions_RND();
11937 }
11938
11939 void GameActions_RND(void)
11940 {
11941   static struct MouseActionInfo mouse_action_last = { 0 };
11942   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11943   int magic_wall_x = 0, magic_wall_y = 0;
11944   int i, x, y, element, graphic, last_gfx_frame;
11945
11946   InitPlayfieldScanModeVars();
11947
11948   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11949   {
11950     SCAN_PLAYFIELD(x, y)
11951     {
11952       ChangeCount[x][y] = 0;
11953       ChangeEvent[x][y] = -1;
11954     }
11955   }
11956
11957   if (game.set_centered_player)
11958   {
11959     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11960
11961     // switching to "all players" only possible if all players fit to screen
11962     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11963     {
11964       game.centered_player_nr_next = game.centered_player_nr;
11965       game.set_centered_player = FALSE;
11966     }
11967
11968     // do not switch focus to non-existing (or non-active) player
11969     if (game.centered_player_nr_next >= 0 &&
11970         !stored_player[game.centered_player_nr_next].active)
11971     {
11972       game.centered_player_nr_next = game.centered_player_nr;
11973       game.set_centered_player = FALSE;
11974     }
11975   }
11976
11977   if (game.set_centered_player &&
11978       ScreenMovPos == 0)        // screen currently aligned at tile position
11979   {
11980     int sx, sy;
11981
11982     if (game.centered_player_nr_next == -1)
11983     {
11984       setScreenCenteredToAllPlayers(&sx, &sy);
11985     }
11986     else
11987     {
11988       sx = stored_player[game.centered_player_nr_next].jx;
11989       sy = stored_player[game.centered_player_nr_next].jy;
11990     }
11991
11992     game.centered_player_nr = game.centered_player_nr_next;
11993     game.set_centered_player = FALSE;
11994
11995     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11996     DrawGameDoorValues();
11997   }
11998
11999   // check single step mode (set flag and clear again if any player is active)
12000   game.enter_single_step_mode =
12001     (tape.single_step && tape.recording && !tape.pausing);
12002
12003   for (i = 0; i < MAX_PLAYERS; i++)
12004   {
12005     int actual_player_action = stored_player[i].effective_action;
12006
12007 #if 1
12008     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12009        - rnd_equinox_tetrachloride 048
12010        - rnd_equinox_tetrachloride_ii 096
12011        - rnd_emanuel_schmieg 002
12012        - doctor_sloan_ww 001, 020
12013     */
12014     if (stored_player[i].MovPos == 0)
12015       CheckGravityMovement(&stored_player[i]);
12016 #endif
12017
12018     // overwrite programmed action with tape action
12019     if (stored_player[i].programmed_action)
12020       actual_player_action = stored_player[i].programmed_action;
12021
12022     PlayerActions(&stored_player[i], actual_player_action);
12023
12024     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12025   }
12026
12027   // single step pause mode may already have been toggled by "ScrollPlayer()"
12028   if (game.enter_single_step_mode && !tape.pausing)
12029     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12030
12031   ScrollScreen(NULL, SCROLL_GO_ON);
12032
12033   /* for backwards compatibility, the following code emulates a fixed bug that
12034      occured when pushing elements (causing elements that just made their last
12035      pushing step to already (if possible) make their first falling step in the
12036      same game frame, which is bad); this code is also needed to use the famous
12037      "spring push bug" which is used in older levels and might be wanted to be
12038      used also in newer levels, but in this case the buggy pushing code is only
12039      affecting the "spring" element and no other elements */
12040
12041   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12042   {
12043     for (i = 0; i < MAX_PLAYERS; i++)
12044     {
12045       struct PlayerInfo *player = &stored_player[i];
12046       int x = player->jx;
12047       int y = player->jy;
12048
12049       if (player->active && player->is_pushing && player->is_moving &&
12050           IS_MOVING(x, y) &&
12051           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12052            Tile[x][y] == EL_SPRING))
12053       {
12054         ContinueMoving(x, y);
12055
12056         // continue moving after pushing (this is actually a bug)
12057         if (!IS_MOVING(x, y))
12058           Stop[x][y] = FALSE;
12059       }
12060     }
12061   }
12062
12063   SCAN_PLAYFIELD(x, y)
12064   {
12065     Last[x][y] = Tile[x][y];
12066
12067     ChangeCount[x][y] = 0;
12068     ChangeEvent[x][y] = -1;
12069
12070     // this must be handled before main playfield loop
12071     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12072     {
12073       MovDelay[x][y]--;
12074       if (MovDelay[x][y] <= 0)
12075         RemoveField(x, y);
12076     }
12077
12078     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12079     {
12080       MovDelay[x][y]--;
12081       if (MovDelay[x][y] <= 0)
12082       {
12083         int element = Store[x][y];
12084         int move_direction = MovDir[x][y];
12085         int player_index_bit = Store2[x][y];
12086
12087         Store[x][y] = 0;
12088         Store2[x][y] = 0;
12089
12090         RemoveField(x, y);
12091         TEST_DrawLevelField(x, y);
12092
12093         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12094       }
12095     }
12096
12097 #if DEBUG
12098     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12099     {
12100       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12101             x, y);
12102       Debug("game:playing:GameActions_RND", "This should never happen!");
12103
12104       ChangePage[x][y] = -1;
12105     }
12106 #endif
12107
12108     Stop[x][y] = FALSE;
12109     if (WasJustMoving[x][y] > 0)
12110       WasJustMoving[x][y]--;
12111     if (WasJustFalling[x][y] > 0)
12112       WasJustFalling[x][y]--;
12113     if (CheckCollision[x][y] > 0)
12114       CheckCollision[x][y]--;
12115     if (CheckImpact[x][y] > 0)
12116       CheckImpact[x][y]--;
12117
12118     GfxFrame[x][y]++;
12119
12120     /* reset finished pushing action (not done in ContinueMoving() to allow
12121        continuous pushing animation for elements with zero push delay) */
12122     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12123     {
12124       ResetGfxAnimation(x, y);
12125       TEST_DrawLevelField(x, y);
12126     }
12127
12128 #if DEBUG
12129     if (IS_BLOCKED(x, y))
12130     {
12131       int oldx, oldy;
12132
12133       Blocked2Moving(x, y, &oldx, &oldy);
12134       if (!IS_MOVING(oldx, oldy))
12135       {
12136         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12137         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12138         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12139         Debug("game:playing:GameActions_RND", "This should never happen!");
12140       }
12141     }
12142 #endif
12143   }
12144
12145   if (mouse_action.button)
12146   {
12147     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12148     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12149
12150     x = mouse_action.lx;
12151     y = mouse_action.ly;
12152     element = Tile[x][y];
12153
12154     if (new_button)
12155     {
12156       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12157       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12158                                          ch_button);
12159     }
12160
12161     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12162     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12163                                        ch_button);
12164   }
12165
12166   SCAN_PLAYFIELD(x, y)
12167   {
12168     element = Tile[x][y];
12169     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12170     last_gfx_frame = GfxFrame[x][y];
12171
12172     ResetGfxFrame(x, y);
12173
12174     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12175       DrawLevelGraphicAnimation(x, y, graphic);
12176
12177     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12178         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12179       ResetRandomAnimationValue(x, y);
12180
12181     SetRandomAnimationValue(x, y);
12182
12183     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12184
12185     if (IS_INACTIVE(element))
12186     {
12187       if (IS_ANIMATED(graphic))
12188         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12189
12190       continue;
12191     }
12192
12193     // this may take place after moving, so 'element' may have changed
12194     if (IS_CHANGING(x, y) &&
12195         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12196     {
12197       int page = element_info[element].event_page_nr[CE_DELAY];
12198
12199       HandleElementChange(x, y, page);
12200
12201       element = Tile[x][y];
12202       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12203     }
12204
12205     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12206     {
12207       StartMoving(x, y);
12208
12209       element = Tile[x][y];
12210       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12211
12212       if (IS_ANIMATED(graphic) &&
12213           !IS_MOVING(x, y) &&
12214           !Stop[x][y])
12215         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12216
12217       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12218         TEST_DrawTwinkleOnField(x, y);
12219     }
12220     else if (element == EL_ACID)
12221     {
12222       if (!Stop[x][y])
12223         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12224     }
12225     else if ((element == EL_EXIT_OPEN ||
12226               element == EL_EM_EXIT_OPEN ||
12227               element == EL_SP_EXIT_OPEN ||
12228               element == EL_STEEL_EXIT_OPEN ||
12229               element == EL_EM_STEEL_EXIT_OPEN ||
12230               element == EL_SP_TERMINAL ||
12231               element == EL_SP_TERMINAL_ACTIVE ||
12232               element == EL_EXTRA_TIME ||
12233               element == EL_SHIELD_NORMAL ||
12234               element == EL_SHIELD_DEADLY) &&
12235              IS_ANIMATED(graphic))
12236       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12237     else if (IS_MOVING(x, y))
12238       ContinueMoving(x, y);
12239     else if (IS_ACTIVE_BOMB(element))
12240       CheckDynamite(x, y);
12241     else if (element == EL_AMOEBA_GROWING)
12242       AmoebaGrowing(x, y);
12243     else if (element == EL_AMOEBA_SHRINKING)
12244       AmoebaShrinking(x, y);
12245
12246 #if !USE_NEW_AMOEBA_CODE
12247     else if (IS_AMOEBALIVE(element))
12248       AmoebaReproduce(x, y);
12249 #endif
12250
12251     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12252       Life(x, y);
12253     else if (element == EL_EXIT_CLOSED)
12254       CheckExit(x, y);
12255     else if (element == EL_EM_EXIT_CLOSED)
12256       CheckExitEM(x, y);
12257     else if (element == EL_STEEL_EXIT_CLOSED)
12258       CheckExitSteel(x, y);
12259     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12260       CheckExitSteelEM(x, y);
12261     else if (element == EL_SP_EXIT_CLOSED)
12262       CheckExitSP(x, y);
12263     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12264              element == EL_EXPANDABLE_STEELWALL_GROWING)
12265       MauerWaechst(x, y);
12266     else if (element == EL_EXPANDABLE_WALL ||
12267              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12268              element == EL_EXPANDABLE_WALL_VERTICAL ||
12269              element == EL_EXPANDABLE_WALL_ANY ||
12270              element == EL_BD_EXPANDABLE_WALL)
12271       MauerAbleger(x, y);
12272     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12273              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12274              element == EL_EXPANDABLE_STEELWALL_ANY)
12275       MauerAblegerStahl(x, y);
12276     else if (element == EL_FLAMES)
12277       CheckForDragon(x, y);
12278     else if (element == EL_EXPLOSION)
12279       ; // drawing of correct explosion animation is handled separately
12280     else if (element == EL_ELEMENT_SNAPPING ||
12281              element == EL_DIAGONAL_SHRINKING ||
12282              element == EL_DIAGONAL_GROWING)
12283     {
12284       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12285
12286       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12287     }
12288     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12289       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12290
12291     if (IS_BELT_ACTIVE(element))
12292       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12293
12294     if (game.magic_wall_active)
12295     {
12296       int jx = local_player->jx, jy = local_player->jy;
12297
12298       // play the element sound at the position nearest to the player
12299       if ((element == EL_MAGIC_WALL_FULL ||
12300            element == EL_MAGIC_WALL_ACTIVE ||
12301            element == EL_MAGIC_WALL_EMPTYING ||
12302            element == EL_BD_MAGIC_WALL_FULL ||
12303            element == EL_BD_MAGIC_WALL_ACTIVE ||
12304            element == EL_BD_MAGIC_WALL_EMPTYING ||
12305            element == EL_DC_MAGIC_WALL_FULL ||
12306            element == EL_DC_MAGIC_WALL_ACTIVE ||
12307            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12308           ABS(x - jx) + ABS(y - jy) <
12309           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12310       {
12311         magic_wall_x = x;
12312         magic_wall_y = y;
12313       }
12314     }
12315   }
12316
12317 #if USE_NEW_AMOEBA_CODE
12318   // new experimental amoeba growth stuff
12319   if (!(FrameCounter % 8))
12320   {
12321     static unsigned int random = 1684108901;
12322
12323     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12324     {
12325       x = RND(lev_fieldx);
12326       y = RND(lev_fieldy);
12327       element = Tile[x][y];
12328
12329       if (!IS_PLAYER(x,y) &&
12330           (element == EL_EMPTY ||
12331            CAN_GROW_INTO(element) ||
12332            element == EL_QUICKSAND_EMPTY ||
12333            element == EL_QUICKSAND_FAST_EMPTY ||
12334            element == EL_ACID_SPLASH_LEFT ||
12335            element == EL_ACID_SPLASH_RIGHT))
12336       {
12337         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12338             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12339             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12340             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12341           Tile[x][y] = EL_AMOEBA_DROP;
12342       }
12343
12344       random = random * 129 + 1;
12345     }
12346   }
12347 #endif
12348
12349   game.explosions_delayed = FALSE;
12350
12351   SCAN_PLAYFIELD(x, y)
12352   {
12353     element = Tile[x][y];
12354
12355     if (ExplodeField[x][y])
12356       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12357     else if (element == EL_EXPLOSION)
12358       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12359
12360     ExplodeField[x][y] = EX_TYPE_NONE;
12361   }
12362
12363   game.explosions_delayed = TRUE;
12364
12365   if (game.magic_wall_active)
12366   {
12367     if (!(game.magic_wall_time_left % 4))
12368     {
12369       int element = Tile[magic_wall_x][magic_wall_y];
12370
12371       if (element == EL_BD_MAGIC_WALL_FULL ||
12372           element == EL_BD_MAGIC_WALL_ACTIVE ||
12373           element == EL_BD_MAGIC_WALL_EMPTYING)
12374         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12375       else if (element == EL_DC_MAGIC_WALL_FULL ||
12376                element == EL_DC_MAGIC_WALL_ACTIVE ||
12377                element == EL_DC_MAGIC_WALL_EMPTYING)
12378         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12379       else
12380         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12381     }
12382
12383     if (game.magic_wall_time_left > 0)
12384     {
12385       game.magic_wall_time_left--;
12386
12387       if (!game.magic_wall_time_left)
12388       {
12389         SCAN_PLAYFIELD(x, y)
12390         {
12391           element = Tile[x][y];
12392
12393           if (element == EL_MAGIC_WALL_ACTIVE ||
12394               element == EL_MAGIC_WALL_FULL)
12395           {
12396             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12397             TEST_DrawLevelField(x, y);
12398           }
12399           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12400                    element == EL_BD_MAGIC_WALL_FULL)
12401           {
12402             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12403             TEST_DrawLevelField(x, y);
12404           }
12405           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12406                    element == EL_DC_MAGIC_WALL_FULL)
12407           {
12408             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12409             TEST_DrawLevelField(x, y);
12410           }
12411         }
12412
12413         game.magic_wall_active = FALSE;
12414       }
12415     }
12416   }
12417
12418   if (game.light_time_left > 0)
12419   {
12420     game.light_time_left--;
12421
12422     if (game.light_time_left == 0)
12423       RedrawAllLightSwitchesAndInvisibleElements();
12424   }
12425
12426   if (game.timegate_time_left > 0)
12427   {
12428     game.timegate_time_left--;
12429
12430     if (game.timegate_time_left == 0)
12431       CloseAllOpenTimegates();
12432   }
12433
12434   if (game.lenses_time_left > 0)
12435   {
12436     game.lenses_time_left--;
12437
12438     if (game.lenses_time_left == 0)
12439       RedrawAllInvisibleElementsForLenses();
12440   }
12441
12442   if (game.magnify_time_left > 0)
12443   {
12444     game.magnify_time_left--;
12445
12446     if (game.magnify_time_left == 0)
12447       RedrawAllInvisibleElementsForMagnifier();
12448   }
12449
12450   for (i = 0; i < MAX_PLAYERS; i++)
12451   {
12452     struct PlayerInfo *player = &stored_player[i];
12453
12454     if (SHIELD_ON(player))
12455     {
12456       if (player->shield_deadly_time_left)
12457         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12458       else if (player->shield_normal_time_left)
12459         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12460     }
12461   }
12462
12463 #if USE_DELAYED_GFX_REDRAW
12464   SCAN_PLAYFIELD(x, y)
12465   {
12466     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12467     {
12468       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12469          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12470
12471       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12472         DrawLevelField(x, y);
12473
12474       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12475         DrawLevelFieldCrumbled(x, y);
12476
12477       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12478         DrawLevelFieldCrumbledNeighbours(x, y);
12479
12480       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12481         DrawTwinkleOnField(x, y);
12482     }
12483
12484     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12485   }
12486 #endif
12487
12488   DrawAllPlayers();
12489   PlayAllPlayersSound();
12490
12491   for (i = 0; i < MAX_PLAYERS; i++)
12492   {
12493     struct PlayerInfo *player = &stored_player[i];
12494
12495     if (player->show_envelope != 0 && (!player->active ||
12496                                        player->MovPos == 0))
12497     {
12498       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12499
12500       player->show_envelope = 0;
12501     }
12502   }
12503
12504   // use random number generator in every frame to make it less predictable
12505   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12506     RND(1);
12507
12508   mouse_action_last = mouse_action;
12509 }
12510
12511 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12512 {
12513   int min_x = x, min_y = y, max_x = x, max_y = y;
12514   int scr_fieldx = getScreenFieldSizeX();
12515   int scr_fieldy = getScreenFieldSizeY();
12516   int i;
12517
12518   for (i = 0; i < MAX_PLAYERS; i++)
12519   {
12520     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12521
12522     if (!stored_player[i].active || &stored_player[i] == player)
12523       continue;
12524
12525     min_x = MIN(min_x, jx);
12526     min_y = MIN(min_y, jy);
12527     max_x = MAX(max_x, jx);
12528     max_y = MAX(max_y, jy);
12529   }
12530
12531   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12532 }
12533
12534 static boolean AllPlayersInVisibleScreen(void)
12535 {
12536   int i;
12537
12538   for (i = 0; i < MAX_PLAYERS; i++)
12539   {
12540     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12541
12542     if (!stored_player[i].active)
12543       continue;
12544
12545     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12546       return FALSE;
12547   }
12548
12549   return TRUE;
12550 }
12551
12552 void ScrollLevel(int dx, int dy)
12553 {
12554   int scroll_offset = 2 * TILEX_VAR;
12555   int x, y;
12556
12557   BlitBitmap(drawto_field, drawto_field,
12558              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12559              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12560              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12561              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12562              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12563              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12564
12565   if (dx != 0)
12566   {
12567     x = (dx == 1 ? BX1 : BX2);
12568     for (y = BY1; y <= BY2; y++)
12569       DrawScreenField(x, y);
12570   }
12571
12572   if (dy != 0)
12573   {
12574     y = (dy == 1 ? BY1 : BY2);
12575     for (x = BX1; x <= BX2; x++)
12576       DrawScreenField(x, y);
12577   }
12578
12579   redraw_mask |= REDRAW_FIELD;
12580 }
12581
12582 static boolean canFallDown(struct PlayerInfo *player)
12583 {
12584   int jx = player->jx, jy = player->jy;
12585
12586   return (IN_LEV_FIELD(jx, jy + 1) &&
12587           (IS_FREE(jx, jy + 1) ||
12588            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12589           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12590           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12591 }
12592
12593 static boolean canPassField(int x, int y, int move_dir)
12594 {
12595   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12596   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12597   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12598   int nextx = x + dx;
12599   int nexty = y + dy;
12600   int element = Tile[x][y];
12601
12602   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12603           !CAN_MOVE(element) &&
12604           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12605           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12606           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12607 }
12608
12609 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12610 {
12611   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12612   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12613   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12614   int newx = x + dx;
12615   int newy = y + dy;
12616
12617   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12618           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12619           (IS_DIGGABLE(Tile[newx][newy]) ||
12620            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12621            canPassField(newx, newy, move_dir)));
12622 }
12623
12624 static void CheckGravityMovement(struct PlayerInfo *player)
12625 {
12626   if (player->gravity && !player->programmed_action)
12627   {
12628     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12629     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12630     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12631     int jx = player->jx, jy = player->jy;
12632     boolean player_is_moving_to_valid_field =
12633       (!player_is_snapping &&
12634        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12635         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12636     boolean player_can_fall_down = canFallDown(player);
12637
12638     if (player_can_fall_down &&
12639         !player_is_moving_to_valid_field)
12640       player->programmed_action = MV_DOWN;
12641   }
12642 }
12643
12644 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12645 {
12646   return CheckGravityMovement(player);
12647
12648   if (player->gravity && !player->programmed_action)
12649   {
12650     int jx = player->jx, jy = player->jy;
12651     boolean field_under_player_is_free =
12652       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12653     boolean player_is_standing_on_valid_field =
12654       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12655        (IS_WALKABLE(Tile[jx][jy]) &&
12656         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12657
12658     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12659       player->programmed_action = MV_DOWN;
12660   }
12661 }
12662
12663 /*
12664   MovePlayerOneStep()
12665   -----------------------------------------------------------------------------
12666   dx, dy:               direction (non-diagonal) to try to move the player to
12667   real_dx, real_dy:     direction as read from input device (can be diagonal)
12668 */
12669
12670 boolean MovePlayerOneStep(struct PlayerInfo *player,
12671                           int dx, int dy, int real_dx, int real_dy)
12672 {
12673   int jx = player->jx, jy = player->jy;
12674   int new_jx = jx + dx, new_jy = jy + dy;
12675   int can_move;
12676   boolean player_can_move = !player->cannot_move;
12677
12678   if (!player->active || (!dx && !dy))
12679     return MP_NO_ACTION;
12680
12681   player->MovDir = (dx < 0 ? MV_LEFT :
12682                     dx > 0 ? MV_RIGHT :
12683                     dy < 0 ? MV_UP :
12684                     dy > 0 ? MV_DOWN :  MV_NONE);
12685
12686   if (!IN_LEV_FIELD(new_jx, new_jy))
12687     return MP_NO_ACTION;
12688
12689   if (!player_can_move)
12690   {
12691     if (player->MovPos == 0)
12692     {
12693       player->is_moving = FALSE;
12694       player->is_digging = FALSE;
12695       player->is_collecting = FALSE;
12696       player->is_snapping = FALSE;
12697       player->is_pushing = FALSE;
12698     }
12699   }
12700
12701   if (!network.enabled && game.centered_player_nr == -1 &&
12702       !AllPlayersInSight(player, new_jx, new_jy))
12703     return MP_NO_ACTION;
12704
12705   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12706   if (can_move != MP_MOVING)
12707     return can_move;
12708
12709   // check if DigField() has caused relocation of the player
12710   if (player->jx != jx || player->jy != jy)
12711     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12712
12713   StorePlayer[jx][jy] = 0;
12714   player->last_jx = jx;
12715   player->last_jy = jy;
12716   player->jx = new_jx;
12717   player->jy = new_jy;
12718   StorePlayer[new_jx][new_jy] = player->element_nr;
12719
12720   if (player->move_delay_value_next != -1)
12721   {
12722     player->move_delay_value = player->move_delay_value_next;
12723     player->move_delay_value_next = -1;
12724   }
12725
12726   player->MovPos =
12727     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12728
12729   player->step_counter++;
12730
12731   PlayerVisit[jx][jy] = FrameCounter;
12732
12733   player->is_moving = TRUE;
12734
12735 #if 1
12736   // should better be called in MovePlayer(), but this breaks some tapes
12737   ScrollPlayer(player, SCROLL_INIT);
12738 #endif
12739
12740   return MP_MOVING;
12741 }
12742
12743 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12744 {
12745   int jx = player->jx, jy = player->jy;
12746   int old_jx = jx, old_jy = jy;
12747   int moved = MP_NO_ACTION;
12748
12749   if (!player->active)
12750     return FALSE;
12751
12752   if (!dx && !dy)
12753   {
12754     if (player->MovPos == 0)
12755     {
12756       player->is_moving = FALSE;
12757       player->is_digging = FALSE;
12758       player->is_collecting = FALSE;
12759       player->is_snapping = FALSE;
12760       player->is_pushing = FALSE;
12761     }
12762
12763     return FALSE;
12764   }
12765
12766   if (player->move_delay > 0)
12767     return FALSE;
12768
12769   player->move_delay = -1;              // set to "uninitialized" value
12770
12771   // store if player is automatically moved to next field
12772   player->is_auto_moving = (player->programmed_action != MV_NONE);
12773
12774   // remove the last programmed player action
12775   player->programmed_action = 0;
12776
12777   if (player->MovPos)
12778   {
12779     // should only happen if pre-1.2 tape recordings are played
12780     // this is only for backward compatibility
12781
12782     int original_move_delay_value = player->move_delay_value;
12783
12784 #if DEBUG
12785     Debug("game:playing:MovePlayer",
12786           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12787           tape.counter);
12788 #endif
12789
12790     // scroll remaining steps with finest movement resolution
12791     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12792
12793     while (player->MovPos)
12794     {
12795       ScrollPlayer(player, SCROLL_GO_ON);
12796       ScrollScreen(NULL, SCROLL_GO_ON);
12797
12798       AdvanceFrameAndPlayerCounters(player->index_nr);
12799
12800       DrawAllPlayers();
12801       BackToFront_WithFrameDelay(0);
12802     }
12803
12804     player->move_delay_value = original_move_delay_value;
12805   }
12806
12807   player->is_active = FALSE;
12808
12809   if (player->last_move_dir & MV_HORIZONTAL)
12810   {
12811     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12812       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12813   }
12814   else
12815   {
12816     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12817       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12818   }
12819
12820   if (!moved && !player->is_active)
12821   {
12822     player->is_moving = FALSE;
12823     player->is_digging = FALSE;
12824     player->is_collecting = FALSE;
12825     player->is_snapping = FALSE;
12826     player->is_pushing = FALSE;
12827   }
12828
12829   jx = player->jx;
12830   jy = player->jy;
12831
12832   if (moved & MP_MOVING && !ScreenMovPos &&
12833       (player->index_nr == game.centered_player_nr ||
12834        game.centered_player_nr == -1))
12835   {
12836     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12837
12838     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12839     {
12840       // actual player has left the screen -- scroll in that direction
12841       if (jx != old_jx)         // player has moved horizontally
12842         scroll_x += (jx - old_jx);
12843       else                      // player has moved vertically
12844         scroll_y += (jy - old_jy);
12845     }
12846     else
12847     {
12848       int offset_raw = game.scroll_delay_value;
12849
12850       if (jx != old_jx)         // player has moved horizontally
12851       {
12852         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12853         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12854         int new_scroll_x = jx - MIDPOSX + offset_x;
12855
12856         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12857             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12858           scroll_x = new_scroll_x;
12859
12860         // don't scroll over playfield boundaries
12861         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12862
12863         // don't scroll more than one field at a time
12864         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12865
12866         // don't scroll against the player's moving direction
12867         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12868             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12869           scroll_x = old_scroll_x;
12870       }
12871       else                      // player has moved vertically
12872       {
12873         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12874         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12875         int new_scroll_y = jy - MIDPOSY + offset_y;
12876
12877         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12878             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12879           scroll_y = new_scroll_y;
12880
12881         // don't scroll over playfield boundaries
12882         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12883
12884         // don't scroll more than one field at a time
12885         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12886
12887         // don't scroll against the player's moving direction
12888         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12889             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12890           scroll_y = old_scroll_y;
12891       }
12892     }
12893
12894     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12895     {
12896       if (!network.enabled && game.centered_player_nr == -1 &&
12897           !AllPlayersInVisibleScreen())
12898       {
12899         scroll_x = old_scroll_x;
12900         scroll_y = old_scroll_y;
12901       }
12902       else
12903       {
12904         ScrollScreen(player, SCROLL_INIT);
12905         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12906       }
12907     }
12908   }
12909
12910   player->StepFrame = 0;
12911
12912   if (moved & MP_MOVING)
12913   {
12914     if (old_jx != jx && old_jy == jy)
12915       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12916     else if (old_jx == jx && old_jy != jy)
12917       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12918
12919     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12920
12921     player->last_move_dir = player->MovDir;
12922     player->is_moving = TRUE;
12923     player->is_snapping = FALSE;
12924     player->is_switching = FALSE;
12925     player->is_dropping = FALSE;
12926     player->is_dropping_pressed = FALSE;
12927     player->drop_pressed_delay = 0;
12928
12929 #if 0
12930     // should better be called here than above, but this breaks some tapes
12931     ScrollPlayer(player, SCROLL_INIT);
12932 #endif
12933   }
12934   else
12935   {
12936     CheckGravityMovementWhenNotMoving(player);
12937
12938     player->is_moving = FALSE;
12939
12940     /* at this point, the player is allowed to move, but cannot move right now
12941        (e.g. because of something blocking the way) -- ensure that the player
12942        is also allowed to move in the next frame (in old versions before 3.1.1,
12943        the player was forced to wait again for eight frames before next try) */
12944
12945     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12946       player->move_delay = 0;   // allow direct movement in the next frame
12947   }
12948
12949   if (player->move_delay == -1)         // not yet initialized by DigField()
12950     player->move_delay = player->move_delay_value;
12951
12952   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12953   {
12954     TestIfPlayerTouchesBadThing(jx, jy);
12955     TestIfPlayerTouchesCustomElement(jx, jy);
12956   }
12957
12958   if (!player->active)
12959     RemovePlayer(player);
12960
12961   return moved;
12962 }
12963
12964 void ScrollPlayer(struct PlayerInfo *player, int mode)
12965 {
12966   int jx = player->jx, jy = player->jy;
12967   int last_jx = player->last_jx, last_jy = player->last_jy;
12968   int move_stepsize = TILEX / player->move_delay_value;
12969
12970   if (!player->active)
12971     return;
12972
12973   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12974     return;
12975
12976   if (mode == SCROLL_INIT)
12977   {
12978     player->actual_frame_counter = FrameCounter;
12979     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12980
12981     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12982         Tile[last_jx][last_jy] == EL_EMPTY)
12983     {
12984       int last_field_block_delay = 0;   // start with no blocking at all
12985       int block_delay_adjustment = player->block_delay_adjustment;
12986
12987       // if player blocks last field, add delay for exactly one move
12988       if (player->block_last_field)
12989       {
12990         last_field_block_delay += player->move_delay_value;
12991
12992         // when blocking enabled, prevent moving up despite gravity
12993         if (player->gravity && player->MovDir == MV_UP)
12994           block_delay_adjustment = -1;
12995       }
12996
12997       // add block delay adjustment (also possible when not blocking)
12998       last_field_block_delay += block_delay_adjustment;
12999
13000       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13001       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13002     }
13003
13004     if (player->MovPos != 0)    // player has not yet reached destination
13005       return;
13006   }
13007   else if (!FrameReached(&player->actual_frame_counter, 1))
13008     return;
13009
13010   if (player->MovPos != 0)
13011   {
13012     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13013     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13014
13015     // before DrawPlayer() to draw correct player graphic for this case
13016     if (player->MovPos == 0)
13017       CheckGravityMovement(player);
13018   }
13019
13020   if (player->MovPos == 0)      // player reached destination field
13021   {
13022     if (player->move_delay_reset_counter > 0)
13023     {
13024       player->move_delay_reset_counter--;
13025
13026       if (player->move_delay_reset_counter == 0)
13027       {
13028         // continue with normal speed after quickly moving through gate
13029         HALVE_PLAYER_SPEED(player);
13030
13031         // be able to make the next move without delay
13032         player->move_delay = 0;
13033       }
13034     }
13035
13036     player->last_jx = jx;
13037     player->last_jy = jy;
13038
13039     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13040         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13041         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13042         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13043         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13044         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13045         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13046         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13047     {
13048       ExitPlayer(player);
13049
13050       if (game.players_still_needed == 0 &&
13051           (game.friends_still_needed == 0 ||
13052            IS_SP_ELEMENT(Tile[jx][jy])))
13053         LevelSolved();
13054     }
13055
13056     // this breaks one level: "machine", level 000
13057     {
13058       int move_direction = player->MovDir;
13059       int enter_side = MV_DIR_OPPOSITE(move_direction);
13060       int leave_side = move_direction;
13061       int old_jx = last_jx;
13062       int old_jy = last_jy;
13063       int old_element = Tile[old_jx][old_jy];
13064       int new_element = Tile[jx][jy];
13065
13066       if (IS_CUSTOM_ELEMENT(old_element))
13067         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13068                                    CE_LEFT_BY_PLAYER,
13069                                    player->index_bit, leave_side);
13070
13071       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13072                                           CE_PLAYER_LEAVES_X,
13073                                           player->index_bit, leave_side);
13074
13075       if (IS_CUSTOM_ELEMENT(new_element))
13076         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13077                                    player->index_bit, enter_side);
13078
13079       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13080                                           CE_PLAYER_ENTERS_X,
13081                                           player->index_bit, enter_side);
13082
13083       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13084                                         CE_MOVE_OF_X, move_direction);
13085     }
13086
13087     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13088     {
13089       TestIfPlayerTouchesBadThing(jx, jy);
13090       TestIfPlayerTouchesCustomElement(jx, jy);
13091
13092       /* needed because pushed element has not yet reached its destination,
13093          so it would trigger a change event at its previous field location */
13094       if (!player->is_pushing)
13095         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13096
13097       if (level.finish_dig_collect &&
13098           (player->is_digging || player->is_collecting))
13099       {
13100         int last_element = player->last_removed_element;
13101         int move_direction = player->MovDir;
13102         int enter_side = MV_DIR_OPPOSITE(move_direction);
13103         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13104                             CE_PLAYER_COLLECTS_X);
13105
13106         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13107                                             player->index_bit, enter_side);
13108
13109         player->last_removed_element = EL_UNDEFINED;
13110       }
13111
13112       if (!player->active)
13113         RemovePlayer(player);
13114     }
13115
13116     if (!game.LevelSolved && level.use_step_counter)
13117     {
13118       int i;
13119
13120       TimePlayed++;
13121
13122       if (TimeLeft > 0)
13123       {
13124         TimeLeft--;
13125
13126         if (TimeLeft <= 10 && setup.time_limit)
13127           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13128
13129         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13130
13131         DisplayGameControlValues();
13132
13133         if (!TimeLeft && setup.time_limit)
13134           for (i = 0; i < MAX_PLAYERS; i++)
13135             KillPlayer(&stored_player[i]);
13136       }
13137       else if (game.no_time_limit && !game.all_players_gone)
13138       {
13139         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13140
13141         DisplayGameControlValues();
13142       }
13143     }
13144
13145     if (tape.single_step && tape.recording && !tape.pausing &&
13146         !player->programmed_action)
13147       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13148
13149     if (!player->programmed_action)
13150       CheckSaveEngineSnapshot(player);
13151   }
13152 }
13153
13154 void ScrollScreen(struct PlayerInfo *player, int mode)
13155 {
13156   static unsigned int screen_frame_counter = 0;
13157
13158   if (mode == SCROLL_INIT)
13159   {
13160     // set scrolling step size according to actual player's moving speed
13161     ScrollStepSize = TILEX / player->move_delay_value;
13162
13163     screen_frame_counter = FrameCounter;
13164     ScreenMovDir = player->MovDir;
13165     ScreenMovPos = player->MovPos;
13166     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13167     return;
13168   }
13169   else if (!FrameReached(&screen_frame_counter, 1))
13170     return;
13171
13172   if (ScreenMovPos)
13173   {
13174     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13175     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13176     redraw_mask |= REDRAW_FIELD;
13177   }
13178   else
13179     ScreenMovDir = MV_NONE;
13180 }
13181
13182 void TestIfPlayerTouchesCustomElement(int x, int y)
13183 {
13184   static int xy[4][2] =
13185   {
13186     { 0, -1 },
13187     { -1, 0 },
13188     { +1, 0 },
13189     { 0, +1 }
13190   };
13191   static int trigger_sides[4][2] =
13192   {
13193     // center side       border side
13194     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13195     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13196     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13197     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13198   };
13199   static int touch_dir[4] =
13200   {
13201     MV_LEFT | MV_RIGHT,
13202     MV_UP   | MV_DOWN,
13203     MV_UP   | MV_DOWN,
13204     MV_LEFT | MV_RIGHT
13205   };
13206   int center_element = Tile[x][y];      // should always be non-moving!
13207   int i;
13208
13209   for (i = 0; i < NUM_DIRECTIONS; i++)
13210   {
13211     int xx = x + xy[i][0];
13212     int yy = y + xy[i][1];
13213     int center_side = trigger_sides[i][0];
13214     int border_side = trigger_sides[i][1];
13215     int border_element;
13216
13217     if (!IN_LEV_FIELD(xx, yy))
13218       continue;
13219
13220     if (IS_PLAYER(x, y))                // player found at center element
13221     {
13222       struct PlayerInfo *player = PLAYERINFO(x, y);
13223
13224       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13225         border_element = Tile[xx][yy];          // may be moving!
13226       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13227         border_element = Tile[xx][yy];
13228       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13229         border_element = MovingOrBlocked2Element(xx, yy);
13230       else
13231         continue;               // center and border element do not touch
13232
13233       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13234                                  player->index_bit, border_side);
13235       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13236                                           CE_PLAYER_TOUCHES_X,
13237                                           player->index_bit, border_side);
13238
13239       {
13240         /* use player element that is initially defined in the level playfield,
13241            not the player element that corresponds to the runtime player number
13242            (example: a level that contains EL_PLAYER_3 as the only player would
13243            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13244         int player_element = PLAYERINFO(x, y)->initial_element;
13245
13246         CheckElementChangeBySide(xx, yy, border_element, player_element,
13247                                  CE_TOUCHING_X, border_side);
13248       }
13249     }
13250     else if (IS_PLAYER(xx, yy))         // player found at border element
13251     {
13252       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13253
13254       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13255       {
13256         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13257           continue;             // center and border element do not touch
13258       }
13259
13260       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13261                                  player->index_bit, center_side);
13262       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13263                                           CE_PLAYER_TOUCHES_X,
13264                                           player->index_bit, center_side);
13265
13266       {
13267         /* use player element that is initially defined in the level playfield,
13268            not the player element that corresponds to the runtime player number
13269            (example: a level that contains EL_PLAYER_3 as the only player would
13270            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13271         int player_element = PLAYERINFO(xx, yy)->initial_element;
13272
13273         CheckElementChangeBySide(x, y, center_element, player_element,
13274                                  CE_TOUCHING_X, center_side);
13275       }
13276
13277       break;
13278     }
13279   }
13280 }
13281
13282 void TestIfElementTouchesCustomElement(int x, int y)
13283 {
13284   static int xy[4][2] =
13285   {
13286     { 0, -1 },
13287     { -1, 0 },
13288     { +1, 0 },
13289     { 0, +1 }
13290   };
13291   static int trigger_sides[4][2] =
13292   {
13293     // center side      border side
13294     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13295     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13296     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13297     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13298   };
13299   static int touch_dir[4] =
13300   {
13301     MV_LEFT | MV_RIGHT,
13302     MV_UP   | MV_DOWN,
13303     MV_UP   | MV_DOWN,
13304     MV_LEFT | MV_RIGHT
13305   };
13306   boolean change_center_element = FALSE;
13307   int center_element = Tile[x][y];      // should always be non-moving!
13308   int border_element_old[NUM_DIRECTIONS];
13309   int i;
13310
13311   for (i = 0; i < NUM_DIRECTIONS; i++)
13312   {
13313     int xx = x + xy[i][0];
13314     int yy = y + xy[i][1];
13315     int border_element;
13316
13317     border_element_old[i] = -1;
13318
13319     if (!IN_LEV_FIELD(xx, yy))
13320       continue;
13321
13322     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13323       border_element = Tile[xx][yy];    // may be moving!
13324     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13325       border_element = Tile[xx][yy];
13326     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13327       border_element = MovingOrBlocked2Element(xx, yy);
13328     else
13329       continue;                 // center and border element do not touch
13330
13331     border_element_old[i] = border_element;
13332   }
13333
13334   for (i = 0; i < NUM_DIRECTIONS; i++)
13335   {
13336     int xx = x + xy[i][0];
13337     int yy = y + xy[i][1];
13338     int center_side = trigger_sides[i][0];
13339     int border_element = border_element_old[i];
13340
13341     if (border_element == -1)
13342       continue;
13343
13344     // check for change of border element
13345     CheckElementChangeBySide(xx, yy, border_element, center_element,
13346                              CE_TOUCHING_X, center_side);
13347
13348     // (center element cannot be player, so we dont have to check this here)
13349   }
13350
13351   for (i = 0; i < NUM_DIRECTIONS; i++)
13352   {
13353     int xx = x + xy[i][0];
13354     int yy = y + xy[i][1];
13355     int border_side = trigger_sides[i][1];
13356     int border_element = border_element_old[i];
13357
13358     if (border_element == -1)
13359       continue;
13360
13361     // check for change of center element (but change it only once)
13362     if (!change_center_element)
13363       change_center_element =
13364         CheckElementChangeBySide(x, y, center_element, border_element,
13365                                  CE_TOUCHING_X, border_side);
13366
13367     if (IS_PLAYER(xx, yy))
13368     {
13369       /* use player element that is initially defined in the level playfield,
13370          not the player element that corresponds to the runtime player number
13371          (example: a level that contains EL_PLAYER_3 as the only player would
13372          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13373       int player_element = PLAYERINFO(xx, yy)->initial_element;
13374
13375       CheckElementChangeBySide(x, y, center_element, player_element,
13376                                CE_TOUCHING_X, border_side);
13377     }
13378   }
13379 }
13380
13381 void TestIfElementHitsCustomElement(int x, int y, int direction)
13382 {
13383   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13384   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13385   int hitx = x + dx, hity = y + dy;
13386   int hitting_element = Tile[x][y];
13387   int touched_element;
13388
13389   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13390     return;
13391
13392   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13393                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13394
13395   if (IN_LEV_FIELD(hitx, hity))
13396   {
13397     int opposite_direction = MV_DIR_OPPOSITE(direction);
13398     int hitting_side = direction;
13399     int touched_side = opposite_direction;
13400     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13401                           MovDir[hitx][hity] != direction ||
13402                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13403
13404     object_hit = TRUE;
13405
13406     if (object_hit)
13407     {
13408       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13409                                CE_HITTING_X, touched_side);
13410
13411       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13412                                CE_HIT_BY_X, hitting_side);
13413
13414       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13415                                CE_HIT_BY_SOMETHING, opposite_direction);
13416
13417       if (IS_PLAYER(hitx, hity))
13418       {
13419         /* use player element that is initially defined in the level playfield,
13420            not the player element that corresponds to the runtime player number
13421            (example: a level that contains EL_PLAYER_3 as the only player would
13422            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13423         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13424
13425         CheckElementChangeBySide(x, y, hitting_element, player_element,
13426                                  CE_HITTING_X, touched_side);
13427       }
13428     }
13429   }
13430
13431   // "hitting something" is also true when hitting the playfield border
13432   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13433                            CE_HITTING_SOMETHING, direction);
13434 }
13435
13436 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13437 {
13438   int i, kill_x = -1, kill_y = -1;
13439
13440   int bad_element = -1;
13441   static int test_xy[4][2] =
13442   {
13443     { 0, -1 },
13444     { -1, 0 },
13445     { +1, 0 },
13446     { 0, +1 }
13447   };
13448   static int test_dir[4] =
13449   {
13450     MV_UP,
13451     MV_LEFT,
13452     MV_RIGHT,
13453     MV_DOWN
13454   };
13455
13456   for (i = 0; i < NUM_DIRECTIONS; i++)
13457   {
13458     int test_x, test_y, test_move_dir, test_element;
13459
13460     test_x = good_x + test_xy[i][0];
13461     test_y = good_y + test_xy[i][1];
13462
13463     if (!IN_LEV_FIELD(test_x, test_y))
13464       continue;
13465
13466     test_move_dir =
13467       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13468
13469     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13470
13471     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13472        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13473     */
13474     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13475         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13476     {
13477       kill_x = test_x;
13478       kill_y = test_y;
13479       bad_element = test_element;
13480
13481       break;
13482     }
13483   }
13484
13485   if (kill_x != -1 || kill_y != -1)
13486   {
13487     if (IS_PLAYER(good_x, good_y))
13488     {
13489       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13490
13491       if (player->shield_deadly_time_left > 0 &&
13492           !IS_INDESTRUCTIBLE(bad_element))
13493         Bang(kill_x, kill_y);
13494       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13495         KillPlayer(player);
13496     }
13497     else
13498       Bang(good_x, good_y);
13499   }
13500 }
13501
13502 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13503 {
13504   int i, kill_x = -1, kill_y = -1;
13505   int bad_element = Tile[bad_x][bad_y];
13506   static int test_xy[4][2] =
13507   {
13508     { 0, -1 },
13509     { -1, 0 },
13510     { +1, 0 },
13511     { 0, +1 }
13512   };
13513   static int touch_dir[4] =
13514   {
13515     MV_LEFT | MV_RIGHT,
13516     MV_UP   | MV_DOWN,
13517     MV_UP   | MV_DOWN,
13518     MV_LEFT | MV_RIGHT
13519   };
13520   static int test_dir[4] =
13521   {
13522     MV_UP,
13523     MV_LEFT,
13524     MV_RIGHT,
13525     MV_DOWN
13526   };
13527
13528   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13529     return;
13530
13531   for (i = 0; i < NUM_DIRECTIONS; i++)
13532   {
13533     int test_x, test_y, test_move_dir, test_element;
13534
13535     test_x = bad_x + test_xy[i][0];
13536     test_y = bad_y + test_xy[i][1];
13537
13538     if (!IN_LEV_FIELD(test_x, test_y))
13539       continue;
13540
13541     test_move_dir =
13542       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13543
13544     test_element = Tile[test_x][test_y];
13545
13546     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13547        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13548     */
13549     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13550         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13551     {
13552       // good thing is player or penguin that does not move away
13553       if (IS_PLAYER(test_x, test_y))
13554       {
13555         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13556
13557         if (bad_element == EL_ROBOT && player->is_moving)
13558           continue;     // robot does not kill player if he is moving
13559
13560         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13561         {
13562           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13563             continue;           // center and border element do not touch
13564         }
13565
13566         kill_x = test_x;
13567         kill_y = test_y;
13568
13569         break;
13570       }
13571       else if (test_element == EL_PENGUIN)
13572       {
13573         kill_x = test_x;
13574         kill_y = test_y;
13575
13576         break;
13577       }
13578     }
13579   }
13580
13581   if (kill_x != -1 || kill_y != -1)
13582   {
13583     if (IS_PLAYER(kill_x, kill_y))
13584     {
13585       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13586
13587       if (player->shield_deadly_time_left > 0 &&
13588           !IS_INDESTRUCTIBLE(bad_element))
13589         Bang(bad_x, bad_y);
13590       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13591         KillPlayer(player);
13592     }
13593     else
13594       Bang(kill_x, kill_y);
13595   }
13596 }
13597
13598 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13599 {
13600   int bad_element = Tile[bad_x][bad_y];
13601   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13602   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13603   int test_x = bad_x + dx, test_y = bad_y + dy;
13604   int test_move_dir, test_element;
13605   int kill_x = -1, kill_y = -1;
13606
13607   if (!IN_LEV_FIELD(test_x, test_y))
13608     return;
13609
13610   test_move_dir =
13611     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13612
13613   test_element = Tile[test_x][test_y];
13614
13615   if (test_move_dir != bad_move_dir)
13616   {
13617     // good thing can be player or penguin that does not move away
13618     if (IS_PLAYER(test_x, test_y))
13619     {
13620       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13621
13622       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13623          player as being hit when he is moving towards the bad thing, because
13624          the "get hit by" condition would be lost after the player stops) */
13625       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13626         return;         // player moves away from bad thing
13627
13628       kill_x = test_x;
13629       kill_y = test_y;
13630     }
13631     else if (test_element == EL_PENGUIN)
13632     {
13633       kill_x = test_x;
13634       kill_y = test_y;
13635     }
13636   }
13637
13638   if (kill_x != -1 || kill_y != -1)
13639   {
13640     if (IS_PLAYER(kill_x, kill_y))
13641     {
13642       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13643
13644       if (player->shield_deadly_time_left > 0 &&
13645           !IS_INDESTRUCTIBLE(bad_element))
13646         Bang(bad_x, bad_y);
13647       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13648         KillPlayer(player);
13649     }
13650     else
13651       Bang(kill_x, kill_y);
13652   }
13653 }
13654
13655 void TestIfPlayerTouchesBadThing(int x, int y)
13656 {
13657   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13658 }
13659
13660 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13661 {
13662   TestIfGoodThingHitsBadThing(x, y, move_dir);
13663 }
13664
13665 void TestIfBadThingTouchesPlayer(int x, int y)
13666 {
13667   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13668 }
13669
13670 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13671 {
13672   TestIfBadThingHitsGoodThing(x, y, move_dir);
13673 }
13674
13675 void TestIfFriendTouchesBadThing(int x, int y)
13676 {
13677   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13678 }
13679
13680 void TestIfBadThingTouchesFriend(int x, int y)
13681 {
13682   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13683 }
13684
13685 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13686 {
13687   int i, kill_x = bad_x, kill_y = bad_y;
13688   static int xy[4][2] =
13689   {
13690     { 0, -1 },
13691     { -1, 0 },
13692     { +1, 0 },
13693     { 0, +1 }
13694   };
13695
13696   for (i = 0; i < NUM_DIRECTIONS; i++)
13697   {
13698     int x, y, element;
13699
13700     x = bad_x + xy[i][0];
13701     y = bad_y + xy[i][1];
13702     if (!IN_LEV_FIELD(x, y))
13703       continue;
13704
13705     element = Tile[x][y];
13706     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13707         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13708     {
13709       kill_x = x;
13710       kill_y = y;
13711       break;
13712     }
13713   }
13714
13715   if (kill_x != bad_x || kill_y != bad_y)
13716     Bang(bad_x, bad_y);
13717 }
13718
13719 void KillPlayer(struct PlayerInfo *player)
13720 {
13721   int jx = player->jx, jy = player->jy;
13722
13723   if (!player->active)
13724     return;
13725
13726 #if 0
13727   Debug("game:playing:KillPlayer",
13728         "0: killed == %d, active == %d, reanimated == %d",
13729         player->killed, player->active, player->reanimated);
13730 #endif
13731
13732   /* the following code was introduced to prevent an infinite loop when calling
13733      -> Bang()
13734      -> CheckTriggeredElementChangeExt()
13735      -> ExecuteCustomElementAction()
13736      -> KillPlayer()
13737      -> (infinitely repeating the above sequence of function calls)
13738      which occurs when killing the player while having a CE with the setting
13739      "kill player X when explosion of <player X>"; the solution using a new
13740      field "player->killed" was chosen for backwards compatibility, although
13741      clever use of the fields "player->active" etc. would probably also work */
13742 #if 1
13743   if (player->killed)
13744     return;
13745 #endif
13746
13747   player->killed = TRUE;
13748
13749   // remove accessible field at the player's position
13750   Tile[jx][jy] = EL_EMPTY;
13751
13752   // deactivate shield (else Bang()/Explode() would not work right)
13753   player->shield_normal_time_left = 0;
13754   player->shield_deadly_time_left = 0;
13755
13756 #if 0
13757   Debug("game:playing:KillPlayer",
13758         "1: killed == %d, active == %d, reanimated == %d",
13759         player->killed, player->active, player->reanimated);
13760 #endif
13761
13762   Bang(jx, jy);
13763
13764 #if 0
13765   Debug("game:playing:KillPlayer",
13766         "2: killed == %d, active == %d, reanimated == %d",
13767         player->killed, player->active, player->reanimated);
13768 #endif
13769
13770   if (player->reanimated)       // killed player may have been reanimated
13771     player->killed = player->reanimated = FALSE;
13772   else
13773     BuryPlayer(player);
13774 }
13775
13776 static void KillPlayerUnlessEnemyProtected(int x, int y)
13777 {
13778   if (!PLAYER_ENEMY_PROTECTED(x, y))
13779     KillPlayer(PLAYERINFO(x, y));
13780 }
13781
13782 static void KillPlayerUnlessExplosionProtected(int x, int y)
13783 {
13784   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13785     KillPlayer(PLAYERINFO(x, y));
13786 }
13787
13788 void BuryPlayer(struct PlayerInfo *player)
13789 {
13790   int jx = player->jx, jy = player->jy;
13791
13792   if (!player->active)
13793     return;
13794
13795   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13796   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13797
13798   RemovePlayer(player);
13799
13800   player->buried = TRUE;
13801
13802   if (game.all_players_gone)
13803     game.GameOver = TRUE;
13804 }
13805
13806 void RemovePlayer(struct PlayerInfo *player)
13807 {
13808   int jx = player->jx, jy = player->jy;
13809   int i, found = FALSE;
13810
13811   player->present = FALSE;
13812   player->active = FALSE;
13813
13814   // required for some CE actions (even if the player is not active anymore)
13815   player->MovPos = 0;
13816
13817   if (!ExplodeField[jx][jy])
13818     StorePlayer[jx][jy] = 0;
13819
13820   if (player->is_moving)
13821     TEST_DrawLevelField(player->last_jx, player->last_jy);
13822
13823   for (i = 0; i < MAX_PLAYERS; i++)
13824     if (stored_player[i].active)
13825       found = TRUE;
13826
13827   if (!found)
13828   {
13829     game.all_players_gone = TRUE;
13830     game.GameOver = TRUE;
13831   }
13832
13833   game.exit_x = game.robot_wheel_x = jx;
13834   game.exit_y = game.robot_wheel_y = jy;
13835 }
13836
13837 void ExitPlayer(struct PlayerInfo *player)
13838 {
13839   DrawPlayer(player);   // needed here only to cleanup last field
13840   RemovePlayer(player);
13841
13842   if (game.players_still_needed > 0)
13843     game.players_still_needed--;
13844 }
13845
13846 static void SetFieldForSnapping(int x, int y, int element, int direction,
13847                                 int player_index_bit)
13848 {
13849   struct ElementInfo *ei = &element_info[element];
13850   int direction_bit = MV_DIR_TO_BIT(direction);
13851   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13852   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13853                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13854
13855   Tile[x][y] = EL_ELEMENT_SNAPPING;
13856   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13857   MovDir[x][y] = direction;
13858   Store[x][y] = element;
13859   Store2[x][y] = player_index_bit;
13860
13861   ResetGfxAnimation(x, y);
13862
13863   GfxElement[x][y] = element;
13864   GfxAction[x][y] = action;
13865   GfxDir[x][y] = direction;
13866   GfxFrame[x][y] = -1;
13867 }
13868
13869 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13870                                    int player_index_bit)
13871 {
13872   TestIfElementTouchesCustomElement(x, y);      // for empty space
13873
13874   if (level.finish_dig_collect)
13875   {
13876     int dig_side = MV_DIR_OPPOSITE(direction);
13877
13878     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13879                                         player_index_bit, dig_side);
13880   }
13881 }
13882
13883 /*
13884   =============================================================================
13885   checkDiagonalPushing()
13886   -----------------------------------------------------------------------------
13887   check if diagonal input device direction results in pushing of object
13888   (by checking if the alternative direction is walkable, diggable, ...)
13889   =============================================================================
13890 */
13891
13892 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13893                                     int x, int y, int real_dx, int real_dy)
13894 {
13895   int jx, jy, dx, dy, xx, yy;
13896
13897   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13898     return TRUE;
13899
13900   // diagonal direction: check alternative direction
13901   jx = player->jx;
13902   jy = player->jy;
13903   dx = x - jx;
13904   dy = y - jy;
13905   xx = jx + (dx == 0 ? real_dx : 0);
13906   yy = jy + (dy == 0 ? real_dy : 0);
13907
13908   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13909 }
13910
13911 /*
13912   =============================================================================
13913   DigField()
13914   -----------------------------------------------------------------------------
13915   x, y:                 field next to player (non-diagonal) to try to dig to
13916   real_dx, real_dy:     direction as read from input device (can be diagonal)
13917   =============================================================================
13918 */
13919
13920 static int DigField(struct PlayerInfo *player,
13921                     int oldx, int oldy, int x, int y,
13922                     int real_dx, int real_dy, int mode)
13923 {
13924   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13925   boolean player_was_pushing = player->is_pushing;
13926   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13927   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13928   int jx = oldx, jy = oldy;
13929   int dx = x - jx, dy = y - jy;
13930   int nextx = x + dx, nexty = y + dy;
13931   int move_direction = (dx == -1 ? MV_LEFT  :
13932                         dx == +1 ? MV_RIGHT :
13933                         dy == -1 ? MV_UP    :
13934                         dy == +1 ? MV_DOWN  : MV_NONE);
13935   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13936   int dig_side = MV_DIR_OPPOSITE(move_direction);
13937   int old_element = Tile[jx][jy];
13938   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13939   int collect_count;
13940
13941   if (is_player)                // function can also be called by EL_PENGUIN
13942   {
13943     if (player->MovPos == 0)
13944     {
13945       player->is_digging = FALSE;
13946       player->is_collecting = FALSE;
13947     }
13948
13949     if (player->MovPos == 0)    // last pushing move finished
13950       player->is_pushing = FALSE;
13951
13952     if (mode == DF_NO_PUSH)     // player just stopped pushing
13953     {
13954       player->is_switching = FALSE;
13955       player->push_delay = -1;
13956
13957       return MP_NO_ACTION;
13958     }
13959   }
13960
13961   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13962     old_element = Back[jx][jy];
13963
13964   // in case of element dropped at player position, check background
13965   else if (Back[jx][jy] != EL_EMPTY &&
13966            game.engine_version >= VERSION_IDENT(2,2,0,0))
13967     old_element = Back[jx][jy];
13968
13969   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13970     return MP_NO_ACTION;        // field has no opening in this direction
13971
13972   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13973     return MP_NO_ACTION;        // field has no opening in this direction
13974
13975   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13976   {
13977     SplashAcid(x, y);
13978
13979     Tile[jx][jy] = player->artwork_element;
13980     InitMovingField(jx, jy, MV_DOWN);
13981     Store[jx][jy] = EL_ACID;
13982     ContinueMoving(jx, jy);
13983     BuryPlayer(player);
13984
13985     return MP_DONT_RUN_INTO;
13986   }
13987
13988   if (player_can_move && DONT_RUN_INTO(element))
13989   {
13990     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13991
13992     return MP_DONT_RUN_INTO;
13993   }
13994
13995   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13996     return MP_NO_ACTION;
13997
13998   collect_count = element_info[element].collect_count_initial;
13999
14000   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14001     return MP_NO_ACTION;
14002
14003   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14004     player_can_move = player_can_move_or_snap;
14005
14006   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14007       game.engine_version >= VERSION_IDENT(2,2,0,0))
14008   {
14009     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14010                                player->index_bit, dig_side);
14011     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14012                                         player->index_bit, dig_side);
14013
14014     if (element == EL_DC_LANDMINE)
14015       Bang(x, y);
14016
14017     if (Tile[x][y] != element)          // field changed by snapping
14018       return MP_ACTION;
14019
14020     return MP_NO_ACTION;
14021   }
14022
14023   if (player->gravity && is_player && !player->is_auto_moving &&
14024       canFallDown(player) && move_direction != MV_DOWN &&
14025       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14026     return MP_NO_ACTION;        // player cannot walk here due to gravity
14027
14028   if (player_can_move &&
14029       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14030   {
14031     int sound_element = SND_ELEMENT(element);
14032     int sound_action = ACTION_WALKING;
14033
14034     if (IS_RND_GATE(element))
14035     {
14036       if (!player->key[RND_GATE_NR(element)])
14037         return MP_NO_ACTION;
14038     }
14039     else if (IS_RND_GATE_GRAY(element))
14040     {
14041       if (!player->key[RND_GATE_GRAY_NR(element)])
14042         return MP_NO_ACTION;
14043     }
14044     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14045     {
14046       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14047         return MP_NO_ACTION;
14048     }
14049     else if (element == EL_EXIT_OPEN ||
14050              element == EL_EM_EXIT_OPEN ||
14051              element == EL_EM_EXIT_OPENING ||
14052              element == EL_STEEL_EXIT_OPEN ||
14053              element == EL_EM_STEEL_EXIT_OPEN ||
14054              element == EL_EM_STEEL_EXIT_OPENING ||
14055              element == EL_SP_EXIT_OPEN ||
14056              element == EL_SP_EXIT_OPENING)
14057     {
14058       sound_action = ACTION_PASSING;    // player is passing exit
14059     }
14060     else if (element == EL_EMPTY)
14061     {
14062       sound_action = ACTION_MOVING;             // nothing to walk on
14063     }
14064
14065     // play sound from background or player, whatever is available
14066     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14067       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14068     else
14069       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14070   }
14071   else if (player_can_move &&
14072            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14073   {
14074     if (!ACCESS_FROM(element, opposite_direction))
14075       return MP_NO_ACTION;      // field not accessible from this direction
14076
14077     if (CAN_MOVE(element))      // only fixed elements can be passed!
14078       return MP_NO_ACTION;
14079
14080     if (IS_EM_GATE(element))
14081     {
14082       if (!player->key[EM_GATE_NR(element)])
14083         return MP_NO_ACTION;
14084     }
14085     else if (IS_EM_GATE_GRAY(element))
14086     {
14087       if (!player->key[EM_GATE_GRAY_NR(element)])
14088         return MP_NO_ACTION;
14089     }
14090     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14091     {
14092       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14093         return MP_NO_ACTION;
14094     }
14095     else if (IS_EMC_GATE(element))
14096     {
14097       if (!player->key[EMC_GATE_NR(element)])
14098         return MP_NO_ACTION;
14099     }
14100     else if (IS_EMC_GATE_GRAY(element))
14101     {
14102       if (!player->key[EMC_GATE_GRAY_NR(element)])
14103         return MP_NO_ACTION;
14104     }
14105     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14106     {
14107       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14108         return MP_NO_ACTION;
14109     }
14110     else if (element == EL_DC_GATE_WHITE ||
14111              element == EL_DC_GATE_WHITE_GRAY ||
14112              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14113     {
14114       if (player->num_white_keys == 0)
14115         return MP_NO_ACTION;
14116
14117       player->num_white_keys--;
14118     }
14119     else if (IS_SP_PORT(element))
14120     {
14121       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14122           element == EL_SP_GRAVITY_PORT_RIGHT ||
14123           element == EL_SP_GRAVITY_PORT_UP ||
14124           element == EL_SP_GRAVITY_PORT_DOWN)
14125         player->gravity = !player->gravity;
14126       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14127                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14128                element == EL_SP_GRAVITY_ON_PORT_UP ||
14129                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14130         player->gravity = TRUE;
14131       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14132                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14133                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14134                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14135         player->gravity = FALSE;
14136     }
14137
14138     // automatically move to the next field with double speed
14139     player->programmed_action = move_direction;
14140
14141     if (player->move_delay_reset_counter == 0)
14142     {
14143       player->move_delay_reset_counter = 2;     // two double speed steps
14144
14145       DOUBLE_PLAYER_SPEED(player);
14146     }
14147
14148     PlayLevelSoundAction(x, y, ACTION_PASSING);
14149   }
14150   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14151   {
14152     RemoveField(x, y);
14153
14154     if (mode != DF_SNAP)
14155     {
14156       GfxElement[x][y] = GFX_ELEMENT(element);
14157       player->is_digging = TRUE;
14158     }
14159
14160     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14161
14162     // use old behaviour for old levels (digging)
14163     if (!level.finish_dig_collect)
14164     {
14165       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14166                                           player->index_bit, dig_side);
14167
14168       // if digging triggered player relocation, finish digging tile
14169       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14170         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14171     }
14172
14173     if (mode == DF_SNAP)
14174     {
14175       if (level.block_snap_field)
14176         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14177       else
14178         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14179
14180       // use old behaviour for old levels (snapping)
14181       if (!level.finish_dig_collect)
14182         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14183                                             player->index_bit, dig_side);
14184     }
14185   }
14186   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14187   {
14188     RemoveField(x, y);
14189
14190     if (is_player && mode != DF_SNAP)
14191     {
14192       GfxElement[x][y] = element;
14193       player->is_collecting = TRUE;
14194     }
14195
14196     if (element == EL_SPEED_PILL)
14197     {
14198       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14199     }
14200     else if (element == EL_EXTRA_TIME && level.time > 0)
14201     {
14202       TimeLeft += level.extra_time;
14203
14204       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14205
14206       DisplayGameControlValues();
14207     }
14208     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14209     {
14210       player->shield_normal_time_left += level.shield_normal_time;
14211       if (element == EL_SHIELD_DEADLY)
14212         player->shield_deadly_time_left += level.shield_deadly_time;
14213     }
14214     else if (element == EL_DYNAMITE ||
14215              element == EL_EM_DYNAMITE ||
14216              element == EL_SP_DISK_RED)
14217     {
14218       if (player->inventory_size < MAX_INVENTORY_SIZE)
14219         player->inventory_element[player->inventory_size++] = element;
14220
14221       DrawGameDoorValues();
14222     }
14223     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14224     {
14225       player->dynabomb_count++;
14226       player->dynabombs_left++;
14227     }
14228     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14229     {
14230       player->dynabomb_size++;
14231     }
14232     else if (element == EL_DYNABOMB_INCREASE_POWER)
14233     {
14234       player->dynabomb_xl = TRUE;
14235     }
14236     else if (IS_KEY(element))
14237     {
14238       player->key[KEY_NR(element)] = TRUE;
14239
14240       DrawGameDoorValues();
14241     }
14242     else if (element == EL_DC_KEY_WHITE)
14243     {
14244       player->num_white_keys++;
14245
14246       // display white keys?
14247       // DrawGameDoorValues();
14248     }
14249     else if (IS_ENVELOPE(element))
14250     {
14251       player->show_envelope = element;
14252     }
14253     else if (element == EL_EMC_LENSES)
14254     {
14255       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14256
14257       RedrawAllInvisibleElementsForLenses();
14258     }
14259     else if (element == EL_EMC_MAGNIFIER)
14260     {
14261       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14262
14263       RedrawAllInvisibleElementsForMagnifier();
14264     }
14265     else if (IS_DROPPABLE(element) ||
14266              IS_THROWABLE(element))     // can be collected and dropped
14267     {
14268       int i;
14269
14270       if (collect_count == 0)
14271         player->inventory_infinite_element = element;
14272       else
14273         for (i = 0; i < collect_count; i++)
14274           if (player->inventory_size < MAX_INVENTORY_SIZE)
14275             player->inventory_element[player->inventory_size++] = element;
14276
14277       DrawGameDoorValues();
14278     }
14279     else if (collect_count > 0)
14280     {
14281       game.gems_still_needed -= collect_count;
14282       if (game.gems_still_needed < 0)
14283         game.gems_still_needed = 0;
14284
14285       game.snapshot.collected_item = TRUE;
14286
14287       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14288
14289       DisplayGameControlValues();
14290     }
14291
14292     RaiseScoreElement(element);
14293     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14294
14295     // use old behaviour for old levels (collecting)
14296     if (!level.finish_dig_collect && is_player)
14297     {
14298       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14299                                           player->index_bit, dig_side);
14300
14301       // if collecting triggered player relocation, finish collecting tile
14302       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14303         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14304     }
14305
14306     if (mode == DF_SNAP)
14307     {
14308       if (level.block_snap_field)
14309         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14310       else
14311         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14312
14313       // use old behaviour for old levels (snapping)
14314       if (!level.finish_dig_collect)
14315         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14316                                             player->index_bit, dig_side);
14317     }
14318   }
14319   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14320   {
14321     if (mode == DF_SNAP && element != EL_BD_ROCK)
14322       return MP_NO_ACTION;
14323
14324     if (CAN_FALL(element) && dy)
14325       return MP_NO_ACTION;
14326
14327     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14328         !(element == EL_SPRING && level.use_spring_bug))
14329       return MP_NO_ACTION;
14330
14331     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14332         ((move_direction & MV_VERTICAL &&
14333           ((element_info[element].move_pattern & MV_LEFT &&
14334             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14335            (element_info[element].move_pattern & MV_RIGHT &&
14336             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14337          (move_direction & MV_HORIZONTAL &&
14338           ((element_info[element].move_pattern & MV_UP &&
14339             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14340            (element_info[element].move_pattern & MV_DOWN &&
14341             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14342       return MP_NO_ACTION;
14343
14344     // do not push elements already moving away faster than player
14345     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14346         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14347       return MP_NO_ACTION;
14348
14349     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14350     {
14351       if (player->push_delay_value == -1 || !player_was_pushing)
14352         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14353     }
14354     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14355     {
14356       if (player->push_delay_value == -1)
14357         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14358     }
14359     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14360     {
14361       if (!player->is_pushing)
14362         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14363     }
14364
14365     player->is_pushing = TRUE;
14366     player->is_active = TRUE;
14367
14368     if (!(IN_LEV_FIELD(nextx, nexty) &&
14369           (IS_FREE(nextx, nexty) ||
14370            (IS_SB_ELEMENT(element) &&
14371             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14372            (IS_CUSTOM_ELEMENT(element) &&
14373             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14374       return MP_NO_ACTION;
14375
14376     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14377       return MP_NO_ACTION;
14378
14379     if (player->push_delay == -1)       // new pushing; restart delay
14380       player->push_delay = 0;
14381
14382     if (player->push_delay < player->push_delay_value &&
14383         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14384         element != EL_SPRING && element != EL_BALLOON)
14385     {
14386       // make sure that there is no move delay before next try to push
14387       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14388         player->move_delay = 0;
14389
14390       return MP_NO_ACTION;
14391     }
14392
14393     if (IS_CUSTOM_ELEMENT(element) &&
14394         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14395     {
14396       if (!DigFieldByCE(nextx, nexty, element))
14397         return MP_NO_ACTION;
14398     }
14399
14400     if (IS_SB_ELEMENT(element))
14401     {
14402       boolean sokoban_task_solved = FALSE;
14403
14404       if (element == EL_SOKOBAN_FIELD_FULL)
14405       {
14406         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14407
14408         IncrementSokobanFieldsNeeded();
14409         IncrementSokobanObjectsNeeded();
14410       }
14411
14412       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14413       {
14414         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14415
14416         DecrementSokobanFieldsNeeded();
14417         DecrementSokobanObjectsNeeded();
14418
14419         // sokoban object was pushed from empty field to sokoban field
14420         if (Back[x][y] == EL_EMPTY)
14421           sokoban_task_solved = TRUE;
14422       }
14423
14424       Tile[x][y] = EL_SOKOBAN_OBJECT;
14425
14426       if (Back[x][y] == Back[nextx][nexty])
14427         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14428       else if (Back[x][y] != 0)
14429         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14430                                     ACTION_EMPTYING);
14431       else
14432         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14433                                     ACTION_FILLING);
14434
14435       if (sokoban_task_solved &&
14436           game.sokoban_fields_still_needed == 0 &&
14437           game.sokoban_objects_still_needed == 0 &&
14438           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14439       {
14440         game.players_still_needed = 0;
14441
14442         LevelSolved();
14443
14444         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14445       }
14446     }
14447     else
14448       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14449
14450     InitMovingField(x, y, move_direction);
14451     GfxAction[x][y] = ACTION_PUSHING;
14452
14453     if (mode == DF_SNAP)
14454       ContinueMoving(x, y);
14455     else
14456       MovPos[x][y] = (dx != 0 ? dx : dy);
14457
14458     Pushed[x][y] = TRUE;
14459     Pushed[nextx][nexty] = TRUE;
14460
14461     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14462       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14463     else
14464       player->push_delay_value = -1;    // get new value later
14465
14466     // check for element change _after_ element has been pushed
14467     if (game.use_change_when_pushing_bug)
14468     {
14469       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14470                                  player->index_bit, dig_side);
14471       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14472                                           player->index_bit, dig_side);
14473     }
14474   }
14475   else if (IS_SWITCHABLE(element))
14476   {
14477     if (PLAYER_SWITCHING(player, x, y))
14478     {
14479       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14480                                           player->index_bit, dig_side);
14481
14482       return MP_ACTION;
14483     }
14484
14485     player->is_switching = TRUE;
14486     player->switch_x = x;
14487     player->switch_y = y;
14488
14489     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14490
14491     if (element == EL_ROBOT_WHEEL)
14492     {
14493       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14494
14495       game.robot_wheel_x = x;
14496       game.robot_wheel_y = y;
14497       game.robot_wheel_active = TRUE;
14498
14499       TEST_DrawLevelField(x, y);
14500     }
14501     else if (element == EL_SP_TERMINAL)
14502     {
14503       int xx, yy;
14504
14505       SCAN_PLAYFIELD(xx, yy)
14506       {
14507         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14508         {
14509           Bang(xx, yy);
14510         }
14511         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14512         {
14513           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14514
14515           ResetGfxAnimation(xx, yy);
14516           TEST_DrawLevelField(xx, yy);
14517         }
14518       }
14519     }
14520     else if (IS_BELT_SWITCH(element))
14521     {
14522       ToggleBeltSwitch(x, y);
14523     }
14524     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14525              element == EL_SWITCHGATE_SWITCH_DOWN ||
14526              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14527              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14528     {
14529       ToggleSwitchgateSwitch(x, y);
14530     }
14531     else if (element == EL_LIGHT_SWITCH ||
14532              element == EL_LIGHT_SWITCH_ACTIVE)
14533     {
14534       ToggleLightSwitch(x, y);
14535     }
14536     else if (element == EL_TIMEGATE_SWITCH ||
14537              element == EL_DC_TIMEGATE_SWITCH)
14538     {
14539       ActivateTimegateSwitch(x, y);
14540     }
14541     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14542              element == EL_BALLOON_SWITCH_RIGHT ||
14543              element == EL_BALLOON_SWITCH_UP    ||
14544              element == EL_BALLOON_SWITCH_DOWN  ||
14545              element == EL_BALLOON_SWITCH_NONE  ||
14546              element == EL_BALLOON_SWITCH_ANY)
14547     {
14548       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14549                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14550                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14551                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14552                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14553                              move_direction);
14554     }
14555     else if (element == EL_LAMP)
14556     {
14557       Tile[x][y] = EL_LAMP_ACTIVE;
14558       game.lights_still_needed--;
14559
14560       ResetGfxAnimation(x, y);
14561       TEST_DrawLevelField(x, y);
14562     }
14563     else if (element == EL_TIME_ORB_FULL)
14564     {
14565       Tile[x][y] = EL_TIME_ORB_EMPTY;
14566
14567       if (level.time > 0 || level.use_time_orb_bug)
14568       {
14569         TimeLeft += level.time_orb_time;
14570         game.no_time_limit = FALSE;
14571
14572         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14573
14574         DisplayGameControlValues();
14575       }
14576
14577       ResetGfxAnimation(x, y);
14578       TEST_DrawLevelField(x, y);
14579     }
14580     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14581              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14582     {
14583       int xx, yy;
14584
14585       game.ball_active = !game.ball_active;
14586
14587       SCAN_PLAYFIELD(xx, yy)
14588       {
14589         int e = Tile[xx][yy];
14590
14591         if (game.ball_active)
14592         {
14593           if (e == EL_EMC_MAGIC_BALL)
14594             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14595           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14596             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14597         }
14598         else
14599         {
14600           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14601             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14602           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14603             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14604         }
14605       }
14606     }
14607
14608     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14609                                         player->index_bit, dig_side);
14610
14611     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14612                                         player->index_bit, dig_side);
14613
14614     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14615                                         player->index_bit, dig_side);
14616
14617     return MP_ACTION;
14618   }
14619   else
14620   {
14621     if (!PLAYER_SWITCHING(player, x, y))
14622     {
14623       player->is_switching = TRUE;
14624       player->switch_x = x;
14625       player->switch_y = y;
14626
14627       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14628                                  player->index_bit, dig_side);
14629       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14630                                           player->index_bit, dig_side);
14631
14632       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14633                                  player->index_bit, dig_side);
14634       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14635                                           player->index_bit, dig_side);
14636     }
14637
14638     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14639                                player->index_bit, dig_side);
14640     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14641                                         player->index_bit, dig_side);
14642
14643     return MP_NO_ACTION;
14644   }
14645
14646   player->push_delay = -1;
14647
14648   if (is_player)                // function can also be called by EL_PENGUIN
14649   {
14650     if (Tile[x][y] != element)          // really digged/collected something
14651     {
14652       player->is_collecting = !player->is_digging;
14653       player->is_active = TRUE;
14654
14655       player->last_removed_element = element;
14656     }
14657   }
14658
14659   return MP_MOVING;
14660 }
14661
14662 static boolean DigFieldByCE(int x, int y, int digging_element)
14663 {
14664   int element = Tile[x][y];
14665
14666   if (!IS_FREE(x, y))
14667   {
14668     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14669                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14670                   ACTION_BREAKING);
14671
14672     // no element can dig solid indestructible elements
14673     if (IS_INDESTRUCTIBLE(element) &&
14674         !IS_DIGGABLE(element) &&
14675         !IS_COLLECTIBLE(element))
14676       return FALSE;
14677
14678     if (AmoebaNr[x][y] &&
14679         (element == EL_AMOEBA_FULL ||
14680          element == EL_BD_AMOEBA ||
14681          element == EL_AMOEBA_GROWING))
14682     {
14683       AmoebaCnt[AmoebaNr[x][y]]--;
14684       AmoebaCnt2[AmoebaNr[x][y]]--;
14685     }
14686
14687     if (IS_MOVING(x, y))
14688       RemoveMovingField(x, y);
14689     else
14690     {
14691       RemoveField(x, y);
14692       TEST_DrawLevelField(x, y);
14693     }
14694
14695     // if digged element was about to explode, prevent the explosion
14696     ExplodeField[x][y] = EX_TYPE_NONE;
14697
14698     PlayLevelSoundAction(x, y, action);
14699   }
14700
14701   Store[x][y] = EL_EMPTY;
14702
14703   // this makes it possible to leave the removed element again
14704   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14705     Store[x][y] = element;
14706
14707   return TRUE;
14708 }
14709
14710 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14711 {
14712   int jx = player->jx, jy = player->jy;
14713   int x = jx + dx, y = jy + dy;
14714   int snap_direction = (dx == -1 ? MV_LEFT  :
14715                         dx == +1 ? MV_RIGHT :
14716                         dy == -1 ? MV_UP    :
14717                         dy == +1 ? MV_DOWN  : MV_NONE);
14718   boolean can_continue_snapping = (level.continuous_snapping &&
14719                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14720
14721   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14722     return FALSE;
14723
14724   if (!player->active || !IN_LEV_FIELD(x, y))
14725     return FALSE;
14726
14727   if (dx && dy)
14728     return FALSE;
14729
14730   if (!dx && !dy)
14731   {
14732     if (player->MovPos == 0)
14733       player->is_pushing = FALSE;
14734
14735     player->is_snapping = FALSE;
14736
14737     if (player->MovPos == 0)
14738     {
14739       player->is_moving = FALSE;
14740       player->is_digging = FALSE;
14741       player->is_collecting = FALSE;
14742     }
14743
14744     return FALSE;
14745   }
14746
14747   // prevent snapping with already pressed snap key when not allowed
14748   if (player->is_snapping && !can_continue_snapping)
14749     return FALSE;
14750
14751   player->MovDir = snap_direction;
14752
14753   if (player->MovPos == 0)
14754   {
14755     player->is_moving = FALSE;
14756     player->is_digging = FALSE;
14757     player->is_collecting = FALSE;
14758   }
14759
14760   player->is_dropping = FALSE;
14761   player->is_dropping_pressed = FALSE;
14762   player->drop_pressed_delay = 0;
14763
14764   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14765     return FALSE;
14766
14767   player->is_snapping = TRUE;
14768   player->is_active = TRUE;
14769
14770   if (player->MovPos == 0)
14771   {
14772     player->is_moving = FALSE;
14773     player->is_digging = FALSE;
14774     player->is_collecting = FALSE;
14775   }
14776
14777   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14778     TEST_DrawLevelField(player->last_jx, player->last_jy);
14779
14780   TEST_DrawLevelField(x, y);
14781
14782   return TRUE;
14783 }
14784
14785 static boolean DropElement(struct PlayerInfo *player)
14786 {
14787   int old_element, new_element;
14788   int dropx = player->jx, dropy = player->jy;
14789   int drop_direction = player->MovDir;
14790   int drop_side = drop_direction;
14791   int drop_element = get_next_dropped_element(player);
14792
14793   /* do not drop an element on top of another element; when holding drop key
14794      pressed without moving, dropped element must move away before the next
14795      element can be dropped (this is especially important if the next element
14796      is dynamite, which can be placed on background for historical reasons) */
14797   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14798     return MP_ACTION;
14799
14800   if (IS_THROWABLE(drop_element))
14801   {
14802     dropx += GET_DX_FROM_DIR(drop_direction);
14803     dropy += GET_DY_FROM_DIR(drop_direction);
14804
14805     if (!IN_LEV_FIELD(dropx, dropy))
14806       return FALSE;
14807   }
14808
14809   old_element = Tile[dropx][dropy];     // old element at dropping position
14810   new_element = drop_element;           // default: no change when dropping
14811
14812   // check if player is active, not moving and ready to drop
14813   if (!player->active || player->MovPos || player->drop_delay > 0)
14814     return FALSE;
14815
14816   // check if player has anything that can be dropped
14817   if (new_element == EL_UNDEFINED)
14818     return FALSE;
14819
14820   // only set if player has anything that can be dropped
14821   player->is_dropping_pressed = TRUE;
14822
14823   // check if drop key was pressed long enough for EM style dynamite
14824   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14825     return FALSE;
14826
14827   // check if anything can be dropped at the current position
14828   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14829     return FALSE;
14830
14831   // collected custom elements can only be dropped on empty fields
14832   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14833     return FALSE;
14834
14835   if (old_element != EL_EMPTY)
14836     Back[dropx][dropy] = old_element;   // store old element on this field
14837
14838   ResetGfxAnimation(dropx, dropy);
14839   ResetRandomAnimationValue(dropx, dropy);
14840
14841   if (player->inventory_size > 0 ||
14842       player->inventory_infinite_element != EL_UNDEFINED)
14843   {
14844     if (player->inventory_size > 0)
14845     {
14846       player->inventory_size--;
14847
14848       DrawGameDoorValues();
14849
14850       if (new_element == EL_DYNAMITE)
14851         new_element = EL_DYNAMITE_ACTIVE;
14852       else if (new_element == EL_EM_DYNAMITE)
14853         new_element = EL_EM_DYNAMITE_ACTIVE;
14854       else if (new_element == EL_SP_DISK_RED)
14855         new_element = EL_SP_DISK_RED_ACTIVE;
14856     }
14857
14858     Tile[dropx][dropy] = new_element;
14859
14860     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14861       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14862                           el2img(Tile[dropx][dropy]), 0);
14863
14864     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14865
14866     // needed if previous element just changed to "empty" in the last frame
14867     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14868
14869     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14870                                player->index_bit, drop_side);
14871     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14872                                         CE_PLAYER_DROPS_X,
14873                                         player->index_bit, drop_side);
14874
14875     TestIfElementTouchesCustomElement(dropx, dropy);
14876   }
14877   else          // player is dropping a dyna bomb
14878   {
14879     player->dynabombs_left--;
14880
14881     Tile[dropx][dropy] = new_element;
14882
14883     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14884       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14885                           el2img(Tile[dropx][dropy]), 0);
14886
14887     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14888   }
14889
14890   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14891     InitField_WithBug1(dropx, dropy, FALSE);
14892
14893   new_element = Tile[dropx][dropy];     // element might have changed
14894
14895   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14896       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14897   {
14898     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14899       MovDir[dropx][dropy] = drop_direction;
14900
14901     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14902
14903     // do not cause impact style collision by dropping elements that can fall
14904     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14905   }
14906
14907   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14908   player->is_dropping = TRUE;
14909
14910   player->drop_pressed_delay = 0;
14911   player->is_dropping_pressed = FALSE;
14912
14913   player->drop_x = dropx;
14914   player->drop_y = dropy;
14915
14916   return TRUE;
14917 }
14918
14919 // ----------------------------------------------------------------------------
14920 // game sound playing functions
14921 // ----------------------------------------------------------------------------
14922
14923 static int *loop_sound_frame = NULL;
14924 static int *loop_sound_volume = NULL;
14925
14926 void InitPlayLevelSound(void)
14927 {
14928   int num_sounds = getSoundListSize();
14929
14930   checked_free(loop_sound_frame);
14931   checked_free(loop_sound_volume);
14932
14933   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14934   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14935 }
14936
14937 static void PlayLevelSound(int x, int y, int nr)
14938 {
14939   int sx = SCREENX(x), sy = SCREENY(y);
14940   int volume, stereo_position;
14941   int max_distance = 8;
14942   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14943
14944   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14945       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14946     return;
14947
14948   if (!IN_LEV_FIELD(x, y) ||
14949       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14950       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14951     return;
14952
14953   volume = SOUND_MAX_VOLUME;
14954
14955   if (!IN_SCR_FIELD(sx, sy))
14956   {
14957     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14958     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14959
14960     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14961   }
14962
14963   stereo_position = (SOUND_MAX_LEFT +
14964                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14965                      (SCR_FIELDX + 2 * max_distance));
14966
14967   if (IS_LOOP_SOUND(nr))
14968   {
14969     /* This assures that quieter loop sounds do not overwrite louder ones,
14970        while restarting sound volume comparison with each new game frame. */
14971
14972     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14973       return;
14974
14975     loop_sound_volume[nr] = volume;
14976     loop_sound_frame[nr] = FrameCounter;
14977   }
14978
14979   PlaySoundExt(nr, volume, stereo_position, type);
14980 }
14981
14982 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14983 {
14984   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14985                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14986                  y < LEVELY(BY1) ? LEVELY(BY1) :
14987                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14988                  sound_action);
14989 }
14990
14991 static void PlayLevelSoundAction(int x, int y, int action)
14992 {
14993   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14994 }
14995
14996 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14997 {
14998   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14999
15000   if (sound_effect != SND_UNDEFINED)
15001     PlayLevelSound(x, y, sound_effect);
15002 }
15003
15004 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15005                                               int action)
15006 {
15007   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15008
15009   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15010     PlayLevelSound(x, y, sound_effect);
15011 }
15012
15013 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15014 {
15015   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15016
15017   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15018     PlayLevelSound(x, y, sound_effect);
15019 }
15020
15021 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15022 {
15023   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15024
15025   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15026     StopSound(sound_effect);
15027 }
15028
15029 static int getLevelMusicNr(void)
15030 {
15031   if (levelset.music[level_nr] != MUS_UNDEFINED)
15032     return levelset.music[level_nr];            // from config file
15033   else
15034     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15035 }
15036
15037 static void FadeLevelSounds(void)
15038 {
15039   FadeSounds();
15040 }
15041
15042 static void FadeLevelMusic(void)
15043 {
15044   int music_nr = getLevelMusicNr();
15045   char *curr_music = getCurrentlyPlayingMusicFilename();
15046   char *next_music = getMusicInfoEntryFilename(music_nr);
15047
15048   if (!strEqual(curr_music, next_music))
15049     FadeMusic();
15050 }
15051
15052 void FadeLevelSoundsAndMusic(void)
15053 {
15054   FadeLevelSounds();
15055   FadeLevelMusic();
15056 }
15057
15058 static void PlayLevelMusic(void)
15059 {
15060   int music_nr = getLevelMusicNr();
15061   char *curr_music = getCurrentlyPlayingMusicFilename();
15062   char *next_music = getMusicInfoEntryFilename(music_nr);
15063
15064   if (!strEqual(curr_music, next_music))
15065     PlayMusicLoop(music_nr);
15066 }
15067
15068 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15069 {
15070   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15071   int offset = 0;
15072   int x = xx - offset;
15073   int y = yy - offset;
15074
15075   switch (sample)
15076   {
15077     case SOUND_blank:
15078       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15079       break;
15080
15081     case SOUND_roll:
15082       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15083       break;
15084
15085     case SOUND_stone:
15086       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15087       break;
15088
15089     case SOUND_nut:
15090       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15091       break;
15092
15093     case SOUND_crack:
15094       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15095       break;
15096
15097     case SOUND_bug:
15098       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15099       break;
15100
15101     case SOUND_tank:
15102       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15103       break;
15104
15105     case SOUND_android_clone:
15106       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15107       break;
15108
15109     case SOUND_android_move:
15110       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15111       break;
15112
15113     case SOUND_spring:
15114       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15115       break;
15116
15117     case SOUND_slurp:
15118       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15119       break;
15120
15121     case SOUND_eater:
15122       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15123       break;
15124
15125     case SOUND_eater_eat:
15126       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15127       break;
15128
15129     case SOUND_alien:
15130       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15131       break;
15132
15133     case SOUND_collect:
15134       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15135       break;
15136
15137     case SOUND_diamond:
15138       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15139       break;
15140
15141     case SOUND_squash:
15142       // !!! CHECK THIS !!!
15143 #if 1
15144       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15145 #else
15146       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15147 #endif
15148       break;
15149
15150     case SOUND_wonderfall:
15151       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15152       break;
15153
15154     case SOUND_drip:
15155       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15156       break;
15157
15158     case SOUND_push:
15159       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15160       break;
15161
15162     case SOUND_dirt:
15163       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15164       break;
15165
15166     case SOUND_acid:
15167       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15168       break;
15169
15170     case SOUND_ball:
15171       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15172       break;
15173
15174     case SOUND_slide:
15175       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15176       break;
15177
15178     case SOUND_wonder:
15179       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15180       break;
15181
15182     case SOUND_door:
15183       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15184       break;
15185
15186     case SOUND_exit_open:
15187       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15188       break;
15189
15190     case SOUND_exit_leave:
15191       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15192       break;
15193
15194     case SOUND_dynamite:
15195       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15196       break;
15197
15198     case SOUND_tick:
15199       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15200       break;
15201
15202     case SOUND_press:
15203       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15204       break;
15205
15206     case SOUND_wheel:
15207       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15208       break;
15209
15210     case SOUND_boom:
15211       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15212       break;
15213
15214     case SOUND_die:
15215       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15216       break;
15217
15218     case SOUND_time:
15219       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15220       break;
15221
15222     default:
15223       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15224       break;
15225   }
15226 }
15227
15228 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15229 {
15230   int element = map_element_SP_to_RND(element_sp);
15231   int action = map_action_SP_to_RND(action_sp);
15232   int offset = (setup.sp_show_border_elements ? 0 : 1);
15233   int x = xx - offset;
15234   int y = yy - offset;
15235
15236   PlayLevelSoundElementAction(x, y, element, action);
15237 }
15238
15239 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15240 {
15241   int element = map_element_MM_to_RND(element_mm);
15242   int action = map_action_MM_to_RND(action_mm);
15243   int offset = 0;
15244   int x = xx - offset;
15245   int y = yy - offset;
15246
15247   if (!IS_MM_ELEMENT(element))
15248     element = EL_MM_DEFAULT;
15249
15250   PlayLevelSoundElementAction(x, y, element, action);
15251 }
15252
15253 void PlaySound_MM(int sound_mm)
15254 {
15255   int sound = map_sound_MM_to_RND(sound_mm);
15256
15257   if (sound == SND_UNDEFINED)
15258     return;
15259
15260   PlaySound(sound);
15261 }
15262
15263 void PlaySoundLoop_MM(int sound_mm)
15264 {
15265   int sound = map_sound_MM_to_RND(sound_mm);
15266
15267   if (sound == SND_UNDEFINED)
15268     return;
15269
15270   PlaySoundLoop(sound);
15271 }
15272
15273 void StopSound_MM(int sound_mm)
15274 {
15275   int sound = map_sound_MM_to_RND(sound_mm);
15276
15277   if (sound == SND_UNDEFINED)
15278     return;
15279
15280   StopSound(sound);
15281 }
15282
15283 void RaiseScore(int value)
15284 {
15285   game.score += value;
15286
15287   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15288
15289   DisplayGameControlValues();
15290 }
15291
15292 void RaiseScoreElement(int element)
15293 {
15294   switch (element)
15295   {
15296     case EL_EMERALD:
15297     case EL_BD_DIAMOND:
15298     case EL_EMERALD_YELLOW:
15299     case EL_EMERALD_RED:
15300     case EL_EMERALD_PURPLE:
15301     case EL_SP_INFOTRON:
15302       RaiseScore(level.score[SC_EMERALD]);
15303       break;
15304     case EL_DIAMOND:
15305       RaiseScore(level.score[SC_DIAMOND]);
15306       break;
15307     case EL_CRYSTAL:
15308       RaiseScore(level.score[SC_CRYSTAL]);
15309       break;
15310     case EL_PEARL:
15311       RaiseScore(level.score[SC_PEARL]);
15312       break;
15313     case EL_BUG:
15314     case EL_BD_BUTTERFLY:
15315     case EL_SP_ELECTRON:
15316       RaiseScore(level.score[SC_BUG]);
15317       break;
15318     case EL_SPACESHIP:
15319     case EL_BD_FIREFLY:
15320     case EL_SP_SNIKSNAK:
15321       RaiseScore(level.score[SC_SPACESHIP]);
15322       break;
15323     case EL_YAMYAM:
15324     case EL_DARK_YAMYAM:
15325       RaiseScore(level.score[SC_YAMYAM]);
15326       break;
15327     case EL_ROBOT:
15328       RaiseScore(level.score[SC_ROBOT]);
15329       break;
15330     case EL_PACMAN:
15331       RaiseScore(level.score[SC_PACMAN]);
15332       break;
15333     case EL_NUT:
15334       RaiseScore(level.score[SC_NUT]);
15335       break;
15336     case EL_DYNAMITE:
15337     case EL_EM_DYNAMITE:
15338     case EL_SP_DISK_RED:
15339     case EL_DYNABOMB_INCREASE_NUMBER:
15340     case EL_DYNABOMB_INCREASE_SIZE:
15341     case EL_DYNABOMB_INCREASE_POWER:
15342       RaiseScore(level.score[SC_DYNAMITE]);
15343       break;
15344     case EL_SHIELD_NORMAL:
15345     case EL_SHIELD_DEADLY:
15346       RaiseScore(level.score[SC_SHIELD]);
15347       break;
15348     case EL_EXTRA_TIME:
15349       RaiseScore(level.extra_time_score);
15350       break;
15351     case EL_KEY_1:
15352     case EL_KEY_2:
15353     case EL_KEY_3:
15354     case EL_KEY_4:
15355     case EL_EM_KEY_1:
15356     case EL_EM_KEY_2:
15357     case EL_EM_KEY_3:
15358     case EL_EM_KEY_4:
15359     case EL_EMC_KEY_5:
15360     case EL_EMC_KEY_6:
15361     case EL_EMC_KEY_7:
15362     case EL_EMC_KEY_8:
15363     case EL_DC_KEY_WHITE:
15364       RaiseScore(level.score[SC_KEY]);
15365       break;
15366     default:
15367       RaiseScore(element_info[element].collect_score);
15368       break;
15369   }
15370 }
15371
15372 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15373 {
15374   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15375   {
15376     // closing door required in case of envelope style request dialogs
15377     if (!skip_request)
15378     {
15379       // prevent short reactivation of overlay buttons while closing door
15380       SetOverlayActive(FALSE);
15381
15382       CloseDoor(DOOR_CLOSE_1);
15383     }
15384
15385     if (network.enabled)
15386       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15387     else
15388     {
15389       if (quick_quit)
15390         FadeSkipNextFadeIn();
15391
15392       SetGameStatus(GAME_MODE_MAIN);
15393
15394       DrawMainMenu();
15395     }
15396   }
15397   else          // continue playing the game
15398   {
15399     if (tape.playing && tape.deactivate_display)
15400       TapeDeactivateDisplayOff(TRUE);
15401
15402     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15403
15404     if (tape.playing && tape.deactivate_display)
15405       TapeDeactivateDisplayOn();
15406   }
15407 }
15408
15409 void RequestQuitGame(boolean ask_if_really_quit)
15410 {
15411   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15412   boolean skip_request = game.all_players_gone || quick_quit;
15413
15414   RequestQuitGameExt(skip_request, quick_quit,
15415                      "Do you really want to quit the game?");
15416 }
15417
15418 void RequestRestartGame(char *message)
15419 {
15420   game.restart_game_message = NULL;
15421
15422   boolean has_started_game = hasStartedNetworkGame();
15423   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15424
15425   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15426   {
15427     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15428   }
15429   else
15430   {
15431     // needed in case of envelope request to close game panel
15432     CloseDoor(DOOR_CLOSE_1);
15433
15434     SetGameStatus(GAME_MODE_MAIN);
15435
15436     DrawMainMenu();
15437   }
15438 }
15439
15440 void CheckGameOver(void)
15441 {
15442   static boolean last_game_over = FALSE;
15443   static int game_over_delay = 0;
15444   int game_over_delay_value = 50;
15445   boolean game_over = checkGameFailed();
15446
15447   // do not handle game over if request dialog is already active
15448   if (game.request_active)
15449     return;
15450
15451   // do not ask to play again if game was never actually played
15452   if (!game.GamePlayed)
15453     return;
15454
15455   if (!game_over)
15456   {
15457     last_game_over = FALSE;
15458     game_over_delay = game_over_delay_value;
15459
15460     return;
15461   }
15462
15463   if (game_over_delay > 0)
15464   {
15465     game_over_delay--;
15466
15467     return;
15468   }
15469
15470   if (last_game_over != game_over)
15471     game.restart_game_message = (hasStartedNetworkGame() ?
15472                                  "Game over! Play it again?" :
15473                                  "Game over!");
15474
15475   last_game_over = game_over;
15476 }
15477
15478 boolean checkGameSolved(void)
15479 {
15480   // set for all game engines if level was solved
15481   return game.LevelSolved_GameEnd;
15482 }
15483
15484 boolean checkGameFailed(void)
15485 {
15486   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15487     return (game_em.game_over && !game_em.level_solved);
15488   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15489     return (game_sp.game_over && !game_sp.level_solved);
15490   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15491     return (game_mm.game_over && !game_mm.level_solved);
15492   else                          // GAME_ENGINE_TYPE_RND
15493     return (game.GameOver && !game.LevelSolved);
15494 }
15495
15496 boolean checkGameEnded(void)
15497 {
15498   return (checkGameSolved() || checkGameFailed());
15499 }
15500
15501
15502 // ----------------------------------------------------------------------------
15503 // random generator functions
15504 // ----------------------------------------------------------------------------
15505
15506 unsigned int InitEngineRandom_RND(int seed)
15507 {
15508   game.num_random_calls = 0;
15509
15510   return InitEngineRandom(seed);
15511 }
15512
15513 unsigned int RND(int max)
15514 {
15515   if (max > 0)
15516   {
15517     game.num_random_calls++;
15518
15519     return GetEngineRandom(max);
15520   }
15521
15522   return 0;
15523 }
15524
15525
15526 // ----------------------------------------------------------------------------
15527 // game engine snapshot handling functions
15528 // ----------------------------------------------------------------------------
15529
15530 struct EngineSnapshotInfo
15531 {
15532   // runtime values for custom element collect score
15533   int collect_score[NUM_CUSTOM_ELEMENTS];
15534
15535   // runtime values for group element choice position
15536   int choice_pos[NUM_GROUP_ELEMENTS];
15537
15538   // runtime values for belt position animations
15539   int belt_graphic[4][NUM_BELT_PARTS];
15540   int belt_anim_mode[4][NUM_BELT_PARTS];
15541 };
15542
15543 static struct EngineSnapshotInfo engine_snapshot_rnd;
15544 static char *snapshot_level_identifier = NULL;
15545 static int snapshot_level_nr = -1;
15546
15547 static void SaveEngineSnapshotValues_RND(void)
15548 {
15549   static int belt_base_active_element[4] =
15550   {
15551     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15552     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15553     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15554     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15555   };
15556   int i, j;
15557
15558   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15559   {
15560     int element = EL_CUSTOM_START + i;
15561
15562     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15563   }
15564
15565   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15566   {
15567     int element = EL_GROUP_START + i;
15568
15569     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15570   }
15571
15572   for (i = 0; i < 4; i++)
15573   {
15574     for (j = 0; j < NUM_BELT_PARTS; j++)
15575     {
15576       int element = belt_base_active_element[i] + j;
15577       int graphic = el2img(element);
15578       int anim_mode = graphic_info[graphic].anim_mode;
15579
15580       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15581       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15582     }
15583   }
15584 }
15585
15586 static void LoadEngineSnapshotValues_RND(void)
15587 {
15588   unsigned int num_random_calls = game.num_random_calls;
15589   int i, j;
15590
15591   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15592   {
15593     int element = EL_CUSTOM_START + i;
15594
15595     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15596   }
15597
15598   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15599   {
15600     int element = EL_GROUP_START + i;
15601
15602     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15603   }
15604
15605   for (i = 0; i < 4; i++)
15606   {
15607     for (j = 0; j < NUM_BELT_PARTS; j++)
15608     {
15609       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15610       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15611
15612       graphic_info[graphic].anim_mode = anim_mode;
15613     }
15614   }
15615
15616   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15617   {
15618     InitRND(tape.random_seed);
15619     for (i = 0; i < num_random_calls; i++)
15620       RND(1);
15621   }
15622
15623   if (game.num_random_calls != num_random_calls)
15624   {
15625     Error("number of random calls out of sync");
15626     Error("number of random calls should be %d", num_random_calls);
15627     Error("number of random calls is %d", game.num_random_calls);
15628
15629     Fail("this should not happen -- please debug");
15630   }
15631 }
15632
15633 void FreeEngineSnapshotSingle(void)
15634 {
15635   FreeSnapshotSingle();
15636
15637   setString(&snapshot_level_identifier, NULL);
15638   snapshot_level_nr = -1;
15639 }
15640
15641 void FreeEngineSnapshotList(void)
15642 {
15643   FreeSnapshotList();
15644 }
15645
15646 static ListNode *SaveEngineSnapshotBuffers(void)
15647 {
15648   ListNode *buffers = NULL;
15649
15650   // copy some special values to a structure better suited for the snapshot
15651
15652   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15653     SaveEngineSnapshotValues_RND();
15654   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15655     SaveEngineSnapshotValues_EM();
15656   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15657     SaveEngineSnapshotValues_SP(&buffers);
15658   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15659     SaveEngineSnapshotValues_MM(&buffers);
15660
15661   // save values stored in special snapshot structure
15662
15663   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15664     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15665   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15666     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15667   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15668     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15669   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15670     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15671
15672   // save further RND engine values
15673
15674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15677
15678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15683
15684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15687
15688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15689
15690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15692
15693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15711
15712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15714
15715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15718
15719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15721
15722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15727
15728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15730
15731 #if 0
15732   ListNode *node = engine_snapshot_list_rnd;
15733   int num_bytes = 0;
15734
15735   while (node != NULL)
15736   {
15737     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15738
15739     node = node->next;
15740   }
15741
15742   Debug("game:playing:SaveEngineSnapshotBuffers",
15743         "size of engine snapshot: %d bytes", num_bytes);
15744 #endif
15745
15746   return buffers;
15747 }
15748
15749 void SaveEngineSnapshotSingle(void)
15750 {
15751   ListNode *buffers = SaveEngineSnapshotBuffers();
15752
15753   // finally save all snapshot buffers to single snapshot
15754   SaveSnapshotSingle(buffers);
15755
15756   // save level identification information
15757   setString(&snapshot_level_identifier, leveldir_current->identifier);
15758   snapshot_level_nr = level_nr;
15759 }
15760
15761 boolean CheckSaveEngineSnapshotToList(void)
15762 {
15763   boolean save_snapshot =
15764     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15765      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15766       game.snapshot.changed_action) ||
15767      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15768       game.snapshot.collected_item));
15769
15770   game.snapshot.changed_action = FALSE;
15771   game.snapshot.collected_item = FALSE;
15772   game.snapshot.save_snapshot = save_snapshot;
15773
15774   return save_snapshot;
15775 }
15776
15777 void SaveEngineSnapshotToList(void)
15778 {
15779   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15780       tape.quick_resume)
15781     return;
15782
15783   ListNode *buffers = SaveEngineSnapshotBuffers();
15784
15785   // finally save all snapshot buffers to snapshot list
15786   SaveSnapshotToList(buffers);
15787 }
15788
15789 void SaveEngineSnapshotToListInitial(void)
15790 {
15791   FreeEngineSnapshotList();
15792
15793   SaveEngineSnapshotToList();
15794 }
15795
15796 static void LoadEngineSnapshotValues(void)
15797 {
15798   // restore special values from snapshot structure
15799
15800   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15801     LoadEngineSnapshotValues_RND();
15802   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15803     LoadEngineSnapshotValues_EM();
15804   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15805     LoadEngineSnapshotValues_SP();
15806   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15807     LoadEngineSnapshotValues_MM();
15808 }
15809
15810 void LoadEngineSnapshotSingle(void)
15811 {
15812   LoadSnapshotSingle();
15813
15814   LoadEngineSnapshotValues();
15815 }
15816
15817 static void LoadEngineSnapshot_Undo(int steps)
15818 {
15819   LoadSnapshotFromList_Older(steps);
15820
15821   LoadEngineSnapshotValues();
15822 }
15823
15824 static void LoadEngineSnapshot_Redo(int steps)
15825 {
15826   LoadSnapshotFromList_Newer(steps);
15827
15828   LoadEngineSnapshotValues();
15829 }
15830
15831 boolean CheckEngineSnapshotSingle(void)
15832 {
15833   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15834           snapshot_level_nr == level_nr);
15835 }
15836
15837 boolean CheckEngineSnapshotList(void)
15838 {
15839   return CheckSnapshotList();
15840 }
15841
15842
15843 // ---------- new game button stuff -------------------------------------------
15844
15845 static struct
15846 {
15847   int graphic;
15848   struct XY *pos;
15849   int gadget_id;
15850   boolean *setup_value;
15851   boolean allowed_on_tape;
15852   boolean is_touch_button;
15853   char *infotext;
15854 } gamebutton_info[NUM_GAME_BUTTONS] =
15855 {
15856   {
15857     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15858     GAME_CTRL_ID_STOP,                          NULL,
15859     TRUE, FALSE,                                "stop game"
15860   },
15861   {
15862     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15863     GAME_CTRL_ID_PAUSE,                         NULL,
15864     TRUE, FALSE,                                "pause game"
15865   },
15866   {
15867     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15868     GAME_CTRL_ID_PLAY,                          NULL,
15869     TRUE, FALSE,                                "play game"
15870   },
15871   {
15872     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15873     GAME_CTRL_ID_UNDO,                          NULL,
15874     TRUE, FALSE,                                "undo step"
15875   },
15876   {
15877     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15878     GAME_CTRL_ID_REDO,                          NULL,
15879     TRUE, FALSE,                                "redo step"
15880   },
15881   {
15882     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15883     GAME_CTRL_ID_SAVE,                          NULL,
15884     TRUE, FALSE,                                "save game"
15885   },
15886   {
15887     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15888     GAME_CTRL_ID_PAUSE2,                        NULL,
15889     TRUE, FALSE,                                "pause game"
15890   },
15891   {
15892     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15893     GAME_CTRL_ID_LOAD,                          NULL,
15894     TRUE, FALSE,                                "load game"
15895   },
15896   {
15897     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15898     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15899     FALSE, FALSE,                               "stop game"
15900   },
15901   {
15902     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15903     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15904     FALSE, FALSE,                               "pause game"
15905   },
15906   {
15907     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15908     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15909     FALSE, FALSE,                               "play game"
15910   },
15911   {
15912     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15913     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15914     FALSE, TRUE,                                "stop game"
15915   },
15916   {
15917     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15918     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15919     FALSE, TRUE,                                "pause game"
15920   },
15921   {
15922     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15923     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15924     TRUE, FALSE,                                "background music on/off"
15925   },
15926   {
15927     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15928     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15929     TRUE, FALSE,                                "sound loops on/off"
15930   },
15931   {
15932     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15933     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15934     TRUE, FALSE,                                "normal sounds on/off"
15935   },
15936   {
15937     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15938     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15939     FALSE, FALSE,                               "background music on/off"
15940   },
15941   {
15942     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15943     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15944     FALSE, FALSE,                               "sound loops on/off"
15945   },
15946   {
15947     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15948     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15949     FALSE, FALSE,                               "normal sounds on/off"
15950   }
15951 };
15952
15953 void CreateGameButtons(void)
15954 {
15955   int i;
15956
15957   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15958   {
15959     int graphic = gamebutton_info[i].graphic;
15960     struct GraphicInfo *gfx = &graphic_info[graphic];
15961     struct XY *pos = gamebutton_info[i].pos;
15962     struct GadgetInfo *gi;
15963     int button_type;
15964     boolean checked;
15965     unsigned int event_mask;
15966     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15967     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15968     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15969     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15970     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15971     int gd_x   = gfx->src_x;
15972     int gd_y   = gfx->src_y;
15973     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15974     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15975     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15976     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15977     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15978     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15979     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15980     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15981     int id = i;
15982
15983     if (gfx->bitmap == NULL)
15984     {
15985       game_gadget[id] = NULL;
15986
15987       continue;
15988     }
15989
15990     if (id == GAME_CTRL_ID_STOP ||
15991         id == GAME_CTRL_ID_PANEL_STOP ||
15992         id == GAME_CTRL_ID_TOUCH_STOP ||
15993         id == GAME_CTRL_ID_PLAY ||
15994         id == GAME_CTRL_ID_PANEL_PLAY ||
15995         id == GAME_CTRL_ID_SAVE ||
15996         id == GAME_CTRL_ID_LOAD)
15997     {
15998       button_type = GD_TYPE_NORMAL_BUTTON;
15999       checked = FALSE;
16000       event_mask = GD_EVENT_RELEASED;
16001     }
16002     else if (id == GAME_CTRL_ID_UNDO ||
16003              id == GAME_CTRL_ID_REDO)
16004     {
16005       button_type = GD_TYPE_NORMAL_BUTTON;
16006       checked = FALSE;
16007       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16008     }
16009     else
16010     {
16011       button_type = GD_TYPE_CHECK_BUTTON;
16012       checked = (gamebutton_info[i].setup_value != NULL ?
16013                  *gamebutton_info[i].setup_value : FALSE);
16014       event_mask = GD_EVENT_PRESSED;
16015     }
16016
16017     gi = CreateGadget(GDI_CUSTOM_ID, id,
16018                       GDI_IMAGE_ID, graphic,
16019                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16020                       GDI_X, base_x + x,
16021                       GDI_Y, base_y + y,
16022                       GDI_WIDTH, gfx->width,
16023                       GDI_HEIGHT, gfx->height,
16024                       GDI_TYPE, button_type,
16025                       GDI_STATE, GD_BUTTON_UNPRESSED,
16026                       GDI_CHECKED, checked,
16027                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16028                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16029                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16030                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16031                       GDI_DIRECT_DRAW, FALSE,
16032                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16033                       GDI_EVENT_MASK, event_mask,
16034                       GDI_CALLBACK_ACTION, HandleGameButtons,
16035                       GDI_END);
16036
16037     if (gi == NULL)
16038       Fail("cannot create gadget");
16039
16040     game_gadget[id] = gi;
16041   }
16042 }
16043
16044 void FreeGameButtons(void)
16045 {
16046   int i;
16047
16048   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16049     FreeGadget(game_gadget[i]);
16050 }
16051
16052 static void UnmapGameButtonsAtSamePosition(int id)
16053 {
16054   int i;
16055
16056   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16057     if (i != id &&
16058         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16059         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16060       UnmapGadget(game_gadget[i]);
16061 }
16062
16063 static void UnmapGameButtonsAtSamePosition_All(void)
16064 {
16065   if (setup.show_snapshot_buttons)
16066   {
16067     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16068     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16069     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16070   }
16071   else
16072   {
16073     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16074     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16075     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16076
16077     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16078     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16079     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16080   }
16081 }
16082
16083 static void MapGameButtonsAtSamePosition(int id)
16084 {
16085   int i;
16086
16087   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16088     if (i != id &&
16089         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16090         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16091       MapGadget(game_gadget[i]);
16092
16093   UnmapGameButtonsAtSamePosition_All();
16094 }
16095
16096 void MapUndoRedoButtons(void)
16097 {
16098   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16099   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16100
16101   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16102   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16103 }
16104
16105 void UnmapUndoRedoButtons(void)
16106 {
16107   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16108   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16109
16110   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16111   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16112 }
16113
16114 void ModifyPauseButtons(void)
16115 {
16116   static int ids[] =
16117   {
16118     GAME_CTRL_ID_PAUSE,
16119     GAME_CTRL_ID_PAUSE2,
16120     GAME_CTRL_ID_PANEL_PAUSE,
16121     GAME_CTRL_ID_TOUCH_PAUSE,
16122     -1
16123   };
16124   int i;
16125
16126   for (i = 0; ids[i] > -1; i++)
16127     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16128 }
16129
16130 static void MapGameButtonsExt(boolean on_tape)
16131 {
16132   int i;
16133
16134   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16135     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16136         i != GAME_CTRL_ID_UNDO &&
16137         i != GAME_CTRL_ID_REDO)
16138       MapGadget(game_gadget[i]);
16139
16140   UnmapGameButtonsAtSamePosition_All();
16141
16142   RedrawGameButtons();
16143 }
16144
16145 static void UnmapGameButtonsExt(boolean on_tape)
16146 {
16147   int i;
16148
16149   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16150     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16151       UnmapGadget(game_gadget[i]);
16152 }
16153
16154 static void RedrawGameButtonsExt(boolean on_tape)
16155 {
16156   int i;
16157
16158   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16159     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16160       RedrawGadget(game_gadget[i]);
16161 }
16162
16163 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16164 {
16165   if (gi == NULL)
16166     return;
16167
16168   gi->checked = state;
16169 }
16170
16171 static void RedrawSoundButtonGadget(int id)
16172 {
16173   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16174              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16175              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16176              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16177              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16178              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16179              id);
16180
16181   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16182   RedrawGadget(game_gadget[id2]);
16183 }
16184
16185 void MapGameButtons(void)
16186 {
16187   MapGameButtonsExt(FALSE);
16188 }
16189
16190 void UnmapGameButtons(void)
16191 {
16192   UnmapGameButtonsExt(FALSE);
16193 }
16194
16195 void RedrawGameButtons(void)
16196 {
16197   RedrawGameButtonsExt(FALSE);
16198 }
16199
16200 void MapGameButtonsOnTape(void)
16201 {
16202   MapGameButtonsExt(TRUE);
16203 }
16204
16205 void UnmapGameButtonsOnTape(void)
16206 {
16207   UnmapGameButtonsExt(TRUE);
16208 }
16209
16210 void RedrawGameButtonsOnTape(void)
16211 {
16212   RedrawGameButtonsExt(TRUE);
16213 }
16214
16215 static void GameUndoRedoExt(void)
16216 {
16217   ClearPlayerAction();
16218
16219   tape.pausing = TRUE;
16220
16221   RedrawPlayfield();
16222   UpdateAndDisplayGameControlValues();
16223
16224   DrawCompleteVideoDisplay();
16225   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16226   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16227   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16228
16229   BackToFront();
16230 }
16231
16232 static void GameUndo(int steps)
16233 {
16234   if (!CheckEngineSnapshotList())
16235     return;
16236
16237   LoadEngineSnapshot_Undo(steps);
16238
16239   GameUndoRedoExt();
16240 }
16241
16242 static void GameRedo(int steps)
16243 {
16244   if (!CheckEngineSnapshotList())
16245     return;
16246
16247   LoadEngineSnapshot_Redo(steps);
16248
16249   GameUndoRedoExt();
16250 }
16251
16252 static void HandleGameButtonsExt(int id, int button)
16253 {
16254   static boolean game_undo_executed = FALSE;
16255   int steps = BUTTON_STEPSIZE(button);
16256   boolean handle_game_buttons =
16257     (game_status == GAME_MODE_PLAYING ||
16258      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16259
16260   if (!handle_game_buttons)
16261     return;
16262
16263   switch (id)
16264   {
16265     case GAME_CTRL_ID_STOP:
16266     case GAME_CTRL_ID_PANEL_STOP:
16267     case GAME_CTRL_ID_TOUCH_STOP:
16268       if (game_status == GAME_MODE_MAIN)
16269         break;
16270
16271       if (tape.playing)
16272         TapeStop();
16273       else
16274         RequestQuitGame(TRUE);
16275
16276       break;
16277
16278     case GAME_CTRL_ID_PAUSE:
16279     case GAME_CTRL_ID_PAUSE2:
16280     case GAME_CTRL_ID_PANEL_PAUSE:
16281     case GAME_CTRL_ID_TOUCH_PAUSE:
16282       if (network.enabled && game_status == GAME_MODE_PLAYING)
16283       {
16284         if (tape.pausing)
16285           SendToServer_ContinuePlaying();
16286         else
16287           SendToServer_PausePlaying();
16288       }
16289       else
16290         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16291
16292       game_undo_executed = FALSE;
16293
16294       break;
16295
16296     case GAME_CTRL_ID_PLAY:
16297     case GAME_CTRL_ID_PANEL_PLAY:
16298       if (game_status == GAME_MODE_MAIN)
16299       {
16300         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16301       }
16302       else if (tape.pausing)
16303       {
16304         if (network.enabled)
16305           SendToServer_ContinuePlaying();
16306         else
16307           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16308       }
16309       break;
16310
16311     case GAME_CTRL_ID_UNDO:
16312       // Important: When using "save snapshot when collecting an item" mode,
16313       // load last (current) snapshot for first "undo" after pressing "pause"
16314       // (else the last-but-one snapshot would be loaded, because the snapshot
16315       // pointer already points to the last snapshot when pressing "pause",
16316       // which is fine for "every step/move" mode, but not for "every collect")
16317       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16318           !game_undo_executed)
16319         steps--;
16320
16321       game_undo_executed = TRUE;
16322
16323       GameUndo(steps);
16324       break;
16325
16326     case GAME_CTRL_ID_REDO:
16327       GameRedo(steps);
16328       break;
16329
16330     case GAME_CTRL_ID_SAVE:
16331       TapeQuickSave();
16332       break;
16333
16334     case GAME_CTRL_ID_LOAD:
16335       TapeQuickLoad();
16336       break;
16337
16338     case SOUND_CTRL_ID_MUSIC:
16339     case SOUND_CTRL_ID_PANEL_MUSIC:
16340       if (setup.sound_music)
16341       { 
16342         setup.sound_music = FALSE;
16343
16344         FadeMusic();
16345       }
16346       else if (audio.music_available)
16347       { 
16348         setup.sound = setup.sound_music = TRUE;
16349
16350         SetAudioMode(setup.sound);
16351
16352         if (game_status == GAME_MODE_PLAYING)
16353           PlayLevelMusic();
16354       }
16355
16356       RedrawSoundButtonGadget(id);
16357
16358       break;
16359
16360     case SOUND_CTRL_ID_LOOPS:
16361     case SOUND_CTRL_ID_PANEL_LOOPS:
16362       if (setup.sound_loops)
16363         setup.sound_loops = FALSE;
16364       else if (audio.loops_available)
16365       {
16366         setup.sound = setup.sound_loops = TRUE;
16367
16368         SetAudioMode(setup.sound);
16369       }
16370
16371       RedrawSoundButtonGadget(id);
16372
16373       break;
16374
16375     case SOUND_CTRL_ID_SIMPLE:
16376     case SOUND_CTRL_ID_PANEL_SIMPLE:
16377       if (setup.sound_simple)
16378         setup.sound_simple = FALSE;
16379       else if (audio.sound_available)
16380       {
16381         setup.sound = setup.sound_simple = TRUE;
16382
16383         SetAudioMode(setup.sound);
16384       }
16385
16386       RedrawSoundButtonGadget(id);
16387
16388       break;
16389
16390     default:
16391       break;
16392   }
16393 }
16394
16395 static void HandleGameButtons(struct GadgetInfo *gi)
16396 {
16397   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16398 }
16399
16400 void HandleSoundButtonKeys(Key key)
16401 {
16402   if (key == setup.shortcut.sound_simple)
16403     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16404   else if (key == setup.shortcut.sound_loops)
16405     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16406   else if (key == setup.shortcut.sound_music)
16407     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16408 }